-
+
+import { GlButtonGroup, GlButton } from '@gitlab/ui';
+
+// TODO: We're planning to move this component to GitLab UI
+// https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1787
+export default {
+ components: {
+ GlButtonGroup,
+ GlButton,
+ },
+ props: {
+ options: {
+ type: Array,
+ required: true,
+ },
+ value: {
+ type: [String, Number, Boolean],
+ required: true,
+ },
+ },
+};
+
+
+
+
+ {{ opt.text }}
+
+
+
diff --git a/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js b/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js
index cafb3f231bd1ee2917d6f06ffb0eb80538e266fa..7bb289408b8060155f28c36d437134304760c589 100644
--- a/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js
+++ b/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js
@@ -1,8 +1,8 @@
import { nextTick } from 'vue';
-import { GlSegmentedControl } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import CiCdAnalyticsAreaChart from '~/vue_shared/components/ci_cd_analytics/ci_cd_analytics_area_chart.vue';
import CiCdAnalyticsCharts from '~/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue';
+import SegmentedControlButtonGroup from '~/vue_shared/components/segmented_control_button_group.vue';
import { transformedAreaChartData, chartOptions } from '../mock_data';
const DEFAULT_PROPS = {
@@ -48,7 +48,7 @@ describe('~/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue', (
});
const findMetricsSlot = () => wrapper.findByTestId('metrics-slot');
- const findSegmentedControl = () => wrapper.findComponent(GlSegmentedControl);
+ const findSegmentedControl = () => wrapper.findComponent(SegmentedControlButtonGroup);
describe('segmented control', () => {
beforeEach(() => {
@@ -56,7 +56,7 @@ describe('~/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue', (
});
it('should default to the first chart', () => {
- expect(findSegmentedControl().props('checked')).toBe(0);
+ expect(findSegmentedControl().props('value')).toBe(0);
});
it('should use the title and index as values', () => {
diff --git a/spec/frontend/vue_shared/components/segmented_control_button_group_spec.js b/spec/frontend/vue_shared/components/segmented_control_button_group_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..88445b6684ce8c8d88a2d33e437bbf8775b5907a
--- /dev/null
+++ b/spec/frontend/vue_shared/components/segmented_control_button_group_spec.js
@@ -0,0 +1,104 @@
+import { GlButtonGroup, GlButton } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import SegmentedControlButtonGroup from '~/vue_shared/components/segmented_control_button_group.vue';
+
+const DEFAULT_OPTIONS = [
+ { text: 'Lorem', value: 'abc' },
+ { text: 'Ipsum', value: 'def' },
+ { text: 'Foo', value: 'x', disabled: true },
+ { text: 'Dolar', value: 'ghi' },
+];
+
+describe('~/vue_shared/components/segmented_control_button_group.vue', () => {
+ let wrapper;
+
+ const createComponent = (props = {}, scopedSlots = {}) => {
+ wrapper = shallowMount(SegmentedControlButtonGroup, {
+ propsData: {
+ value: DEFAULT_OPTIONS[0].value,
+ options: DEFAULT_OPTIONS,
+ ...props,
+ },
+ scopedSlots,
+ });
+ };
+
+ const findButtonGroup = () => wrapper.findComponent(GlButtonGroup);
+ const findButtons = () => findButtonGroup().findAllComponents(GlButton);
+ const findButtonsData = () =>
+ findButtons().wrappers.map((x) => ({
+ selected: x.props('selected'),
+ text: x.text(),
+ disabled: x.props('disabled'),
+ }));
+ const findButtonWithText = (text) => findButtons().wrappers.find((x) => x.text() === text);
+
+ const optionsAsButtonData = (options) =>
+ options.map(({ text, disabled = false }) => ({
+ selected: false,
+ text,
+ disabled,
+ }));
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('default', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders button group', () => {
+ expect(findButtonGroup().exists()).toBe(true);
+ });
+
+ it('renders buttons', () => {
+ const expectation = optionsAsButtonData(DEFAULT_OPTIONS);
+ expectation[0].selected = true;
+
+ expect(findButtonsData()).toEqual(expectation);
+ });
+
+ describe.each(DEFAULT_OPTIONS.filter((x) => !x.disabled))(
+ 'when button clicked %p',
+ ({ text, value }) => {
+ it('emits input with value', () => {
+ expect(wrapper.emitted('input')).toBeUndefined();
+
+ findButtonWithText(text).vm.$emit('click');
+
+ expect(wrapper.emitted('input')).toEqual([[value]]);
+ });
+ },
+ );
+ });
+
+ const VALUE_TEST_CASES = [0, 1, 3].map((index) => [DEFAULT_OPTIONS[index].value, index]);
+
+ describe.each(VALUE_TEST_CASES)('with value=%s', (value, index) => {
+ it(`renders selected button at ${index}`, () => {
+ createComponent({ value });
+
+ const expectation = optionsAsButtonData(DEFAULT_OPTIONS);
+ expectation[index].selected = true;
+
+ expect(findButtonsData()).toEqual(expectation);
+ });
+ });
+
+ describe('with button-content slot', () => {
+ it('renders button content based on slot', () => {
+ createComponent(
+ {},
+ {
+ 'button-content': `In a slot - {{ text }}`,
+ },
+ );
+
+ expect(findButtonsData().map((x) => x.text)).toEqual(
+ DEFAULT_OPTIONS.map((x) => `In a slot - ${x.text}`),
+ );
+ });
+ });
+});