@gitlab/ui 32.45.1 → 32.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/components/base/form/form_group/form_group.js +11 -1
  3. package/dist/components/charts/discrete_scatter/discrete_scatter.documentation.js +1 -7
  4. package/dist/components/charts/stacked_column/stacked_column.documentation.js +2 -5
  5. package/dist/index.css +1 -1
  6. package/dist/index.css.map +1 -1
  7. package/documentation/documented_stories.js +2 -0
  8. package/package.json +1 -1
  9. package/src/components/base/form/form_group/form_group.scss +4 -0
  10. package/src/components/base/form/form_group/form_group.spec.js +66 -64
  11. package/src/components/base/form/form_group/form_group.stories.js +36 -13
  12. package/src/components/base/form/form_group/form_group.vue +15 -2
  13. package/src/components/charts/discrete_scatter/discrete_scatter.documentation.js +0 -5
  14. package/src/components/charts/discrete_scatter/discrete_scatter.stories.js +34 -46
  15. package/src/components/charts/stacked_column/stacked_column.documentation.js +0 -2
  16. package/src/components/charts/stacked_column/stacked_column.md +0 -2
  17. package/src/components/charts/stacked_column/stacked_column.stories.js +74 -99
  18. package/dist/components/charts/discrete_scatter/examples/discrete_scatter.basic.example.js +0 -51
  19. package/dist/components/charts/discrete_scatter/examples/index.js +0 -13
  20. package/dist/components/charts/stacked_column/examples/index.js +0 -13
  21. package/dist/components/charts/stacked_column/examples/stacked_column.basic.example.js +0 -46
  22. package/src/components/charts/discrete_scatter/discrete_scatter.md +0 -1
  23. package/src/components/charts/discrete_scatter/examples/discrete_scatter.basic.example.vue +0 -31
  24. package/src/components/charts/discrete_scatter/examples/index.js +0 -15
  25. package/src/components/charts/stacked_column/examples/index.js +0 -15
  26. package/src/components/charts/stacked_column/examples/stacked_column.basic.example.vue +0 -18
@@ -102,6 +102,8 @@ export const setupStorybookReadme = () =>
102
102
  'GlPagination',
103
103
  'GlSkeletonLoader',
104
104
  'GlLabel',
105
+ 'GlStackedColumnChart',
106
+ 'GlDiscreteScatterChart',
105
107
  ],
106
108
  components: {
107
109
  GlComponentDocumentation,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "32.45.1",
3
+ "version": "32.46.0",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -13,6 +13,10 @@
13
13
  }
14
14
  }
15
15
 
16
+ .optional-label {
17
+ @include gl-font-weight-normal;
18
+ }
19
+
16
20
  .invalid-feedback,
17
21
  .text-gl-muted {
18
22
  @include gl-font-base;
@@ -1,17 +1,17 @@
1
- import { shallowMount, mount } from '@vue/test-utils';
1
+ import { shallowMount } from '@vue/test-utils';
2
2
  import { BFormGroup } from 'bootstrap-vue';
3
- import GlFormText from '../form_text/form_text.vue';
4
3
  import GlFormGroup from './form_group.vue';
5
4
 
6
5
  describe('Form group component', () => {
7
6
  let wrapper;
8
7
 
9
- const findDescription = () => {
10
- return wrapper.find('[data-testid="label-description"]');
11
- };
8
+ const findByTestId = (testId) => wrapper.find(`[data-testid="${testId}"]`);
9
+
10
+ const findLabelDescription = () => findByTestId('label-description');
11
+ const findOptionalLabel = () => findByTestId('optional-label');
12
12
 
13
- const createComponent = (options, mountFn = shallowMount) => {
14
- wrapper = mountFn(GlFormGroup, {
13
+ const createComponent = (options) => {
14
+ wrapper = shallowMount(GlFormGroup, {
15
15
  ...options,
16
16
  });
17
17
  };
@@ -35,73 +35,75 @@ describe('Form group component', () => {
35
35
  }
36
36
  );
37
37
 
38
- describe('description slot', () => {
39
- it('renders text when label-description prop is defined', () => {
40
- const labelDescription = 'description via props';
41
-
42
- createComponent(
43
- {
44
- propsData: {
45
- labelDescription,
46
- },
47
- },
48
- mount
49
- );
50
-
51
- expect(findDescription().text()).toBe(labelDescription);
52
- });
53
-
54
- it('renders a user provided label-description slot', () => {
55
- const labelDescription = 'description via slot';
56
-
57
- createComponent(
58
- {
59
- slots: {
60
- 'label-description': 'description via slot',
61
- },
62
- stubs: {
63
- GlFormText,
64
- },
65
- },
66
- mount
67
- );
68
-
69
- expect(findDescription().text()).toBe(labelDescription);
70
- });
38
+ describe('label-description slot', () => {
39
+ const labelDescriptionFromPropOptions = {
40
+ propsData: { labelDescription: 'label-description from props' },
41
+ };
42
+ const labelDescriptionFromSlotOptions = {
43
+ slots: { 'label-description': 'label-description from slots' },
44
+ };
45
+
46
+ it.each`
47
+ labelDescriptionFrom | options
48
+ ${'prop'} | ${labelDescriptionFromPropOptions}
49
+ ${'prop'} | ${labelDescriptionFromPropOptions}
50
+ ${'slot'} | ${labelDescriptionFromSlotOptions}
51
+ ${'slot'} | ${labelDescriptionFromSlotOptions}
52
+ `(
53
+ 'when label-description is provided from $labelDescriptionFrom',
54
+ ({ labelDescriptionFrom, options }) => {
55
+ createComponent({
56
+ ...options,
57
+ });
58
+
59
+ expect(findLabelDescription().text()).toContain(
60
+ `label-description from ${labelDescriptionFrom}`
61
+ );
62
+ }
63
+ );
71
64
  });
72
65
 
73
66
  describe('label slot', () => {
74
- it('renders text when label prop is defined', () => {
75
- const label = 'label via props';
76
-
77
- createComponent(
78
- {
67
+ const labelFromPropOptions = {
68
+ propsData: { label: 'label from props' },
69
+ };
70
+ const labelFromSlotOptions = {
71
+ slots: { label: 'label from slots' },
72
+ };
73
+
74
+ it.each`
75
+ labelFrom | options | optional | expectedOptional
76
+ ${'prop'} | ${labelFromPropOptions} | ${true} | ${true}
77
+ ${'prop'} | ${labelFromPropOptions} | ${false} | ${false}
78
+ ${'slot'} | ${labelFromSlotOptions} | ${true} | ${false}
79
+ ${'slot'} | ${labelFromSlotOptions} | ${false} | ${false}
80
+ `(
81
+ 'when label is provided from $labelFrom, `optional` is $optional',
82
+ ({ labelFrom, options, optional, expectedOptional }) => {
83
+ createComponent({
84
+ ...options,
79
85
  propsData: {
80
- label,
86
+ ...options.propsData,
87
+ optional,
81
88
  },
82
- },
83
- mount
84
- );
89
+ });
85
90
 
86
- expect(wrapper.text()).toBe(label);
87
- });
91
+ expect(wrapper.text()).toContain(`label from ${labelFrom}`);
92
+ expect(findOptionalLabel().exists()).toBe(expectedOptional);
93
+ }
94
+ );
88
95
 
89
- it('renders a user provided label slot', () => {
90
- const label = 'label via slot';
96
+ it('renders optionalText when `optional` is true', () => {
97
+ const optionalText = '(not required)';
91
98
 
92
- createComponent(
93
- {
94
- slots: {
95
- label: 'label via slot',
96
- },
97
- stubs: {
98
- GlFormText,
99
- },
99
+ createComponent({
100
+ propsData: {
101
+ optional: true,
102
+ optionalText,
100
103
  },
101
- mount
102
- );
104
+ });
103
105
 
104
- expect(wrapper.text()).toBe(label);
106
+ expect(findOptionalLabel().text()).toBe(optionalText);
105
107
  });
106
108
  });
107
109
  });
@@ -13,6 +13,8 @@ function generateProps({
13
13
  label = 'Label Name',
14
14
  description = 'form group description',
15
15
  labelDescription = 'form label description',
16
+ optional = GlFormGroup.props.optional.default,
17
+ optionalText = GlFormGroup.props.optionalText.default,
16
18
  horizontal = false,
17
19
  } = {}) {
18
20
  return {
@@ -36,6 +38,14 @@ function generateProps({
36
38
  type: String,
37
39
  default: text('label-description', labelDescription),
38
40
  },
41
+ optional: {
42
+ type: Boolean,
43
+ default: boolean('optional', optional),
44
+ },
45
+ optionalText: {
46
+ type: String,
47
+ default: text('optional-text', optionalText),
48
+ },
39
49
  horizontal: {
40
50
  type: Boolean,
41
51
  default: boolean('horizontal', horizontal),
@@ -53,6 +63,8 @@ documentedStoriesOf('base/form/form-group', readme)
53
63
  :id="id"
54
64
  :label="label"
55
65
  :label-size="labelSize"
66
+ :optional="optional"
67
+ :optional-text="optionalText"
56
68
  :description="description"
57
69
  :horizontal="horizontal"
58
70
  label-for="label1"
@@ -69,6 +81,8 @@ documentedStoriesOf('base/form/form-group', readme)
69
81
  id="group-id"
70
82
  label="Label Name"
71
83
  label-size="sm"
84
+ :optional="optional"
85
+ :optional-text="optionalText"
72
86
  description="This feature is disabled"
73
87
  label-for="input1"
74
88
  >
@@ -77,14 +91,21 @@ documentedStoriesOf('base/form/form-group', readme)
77
91
  `,
78
92
  }))
79
93
  .add('with textarea', () => ({
94
+ props: generateProps({ optional: true }),
80
95
  template: `
81
- <gl-form-group id="group-id-textarea2" label="Label Name" label-for="textarea2">
96
+ <gl-form-group
97
+ id="group-id-textarea2"
98
+ label="Label Name"
99
+ label-for="textarea2"
100
+ :optional="optional"
101
+ :optional-text="optionalText"
102
+ >
82
103
  <gl-form-textarea id="textarea2" placeholder="Enter something" />
83
104
  </gl-form-group>
84
105
  `,
85
106
  }))
86
107
  .add('with label description', () => ({
87
- props: generateProps(),
108
+ props: generateProps({ optional: true }),
88
109
  components,
89
110
  template: `
90
111
  <gl-form-group
@@ -93,6 +114,8 @@ documentedStoriesOf('base/form/form-group', readme)
93
114
  :label-size="labelSize"
94
115
  :description="description"
95
116
  :label-description="labelDescription"
117
+ :optional="optional"
118
+ :optional-text="optionalText"
96
119
  :horizontal="horizontal"
97
120
  label-for="label1"
98
121
  >
@@ -125,16 +148,16 @@ documentedStoriesOf('base/form/form-group', readme)
125
148
  };
126
149
  },
127
150
  template: `
128
- <gl-form-group
129
- :id="id"
130
- :label="label"
131
- :label-size="labelSize"
132
- :description="description"
133
- :invalid-feedback="invalidFeedback"
134
- :state="state"
135
- label-for="label1"
136
- >
137
- <gl-form-input id="input1" :state="state" v-model.trim="name" />
138
- </gl-form-group>
151
+ <gl-form-group
152
+ :id="id"
153
+ :label="label"
154
+ :label-size="labelSize"
155
+ :description="description"
156
+ :invalid-feedback="invalidFeedback"
157
+ :state="state"
158
+ label-for="label1"
159
+ >
160
+ <gl-form-input id="input1" :state="state" v-model.trim="name" />
161
+ </gl-form-group>
139
162
  `,
140
163
  }));
@@ -20,6 +20,16 @@ export default {
20
20
  required: false,
21
21
  default: '',
22
22
  },
23
+ optional: {
24
+ type: Boolean,
25
+ required: false,
26
+ default: false,
27
+ },
28
+ optionalText: {
29
+ type: String,
30
+ required: false,
31
+ default: '(optional)',
32
+ },
23
33
  },
24
34
  computed: {
25
35
  actualLabelClass() {
@@ -45,11 +55,14 @@ export default {
45
55
  </script>
46
56
  <template>
47
57
  <b-form-group v-bind="$attrs" class="gl-form-group" :label-class="actualLabelClass">
48
- <template v-if="hasLabelDescription" #label>
58
+ <template #label>
49
59
  <slot name="label">
50
60
  {{ $attrs.label }}
61
+ <span v-if="optional" class="optional-label" data-testid="optional-label">{{
62
+ optionalText
63
+ }}</span>
51
64
  </slot>
52
- <gl-form-text data-testid="label-description">
65
+ <gl-form-text v-if="hasLabelDescription" data-testid="label-description">
53
66
  <slot name="label-description">{{ labelDescription }}</slot>
54
67
  </gl-form-text>
55
68
  </template>
@@ -1,8 +1,3 @@
1
- import description from './discrete_scatter.md';
2
- import examples from './examples';
3
-
4
1
  export default {
5
2
  followsDesignSystem: true,
6
- description,
7
- examples,
8
3
  };
@@ -1,13 +1,9 @@
1
- import { withKnobs, object, text } from '@storybook/addon-knobs';
2
1
  import { GlDiscreteScatterChart } from '../../../../charts';
3
- import { documentedStoriesOf } from '../../../../documentation/documented_stories';
4
- import readme from './discrete_scatter.md';
5
2
 
6
- const components = {
7
- GlDiscreteScatterChart,
8
- };
9
-
10
- const template = `
3
+ const Template = (args, { argTypes }) => ({
4
+ components: { GlDiscreteScatterChart },
5
+ props: Object.keys(argTypes),
6
+ template: `
11
7
  <gl-discrete-scatter-chart
12
8
  :data="data"
13
9
  :option="option"
@@ -15,9 +11,10 @@ const template = `
15
11
  :x-axis-title="xAxisTitle"
16
12
  data-testid="discrete-scatter-chart"
17
13
  />
18
- `;
14
+ `,
15
+ });
19
16
 
20
- function generateProps({
17
+ const generateProps = ({
21
18
  data = [
22
19
  {
23
20
  type: 'scatter',
@@ -35,41 +32,32 @@ function generateProps({
35
32
  option = {},
36
33
  yAxisTitle = 'Pushes per day',
37
34
  xAxisTitle = 'Date',
38
- } = {}) {
39
- return {
40
- data: {
41
- default: object('Chart Data', data),
42
- },
43
- option: {
44
- default: object('EChart Options', option),
45
- },
46
- yAxisTitle: {
47
- default: text('Y Axis Title', yAxisTitle),
48
- },
49
- xAxisTitle: {
50
- default: text('X Axis Title', xAxisTitle),
51
- },
52
- };
53
- }
35
+ } = {}) => ({
36
+ data,
37
+ option,
38
+ yAxisTitle,
39
+ xAxisTitle,
40
+ });
41
+
42
+ export const Default = Template.bind({});
43
+ Default.args = generateProps();
54
44
 
55
- documentedStoriesOf('charts/discrete-scatter-chart', readme)
56
- .addDecorator(withKnobs)
57
- .add('default', () => ({
58
- props: generateProps(),
59
- components,
60
- template,
61
- }))
62
- .add('with zoom and scroll', () => ({
63
- props: generateProps({
64
- option: {
65
- dataZoom: [
66
- {
67
- type: 'slider',
68
- startValue: 1,
69
- },
70
- ],
45
+ export const WithZoomAndScroll = Template.bind({});
46
+ WithZoomAndScroll.args = generateProps({
47
+ option: {
48
+ dataZoom: [
49
+ {
50
+ type: 'slider',
51
+ startValue: 1,
71
52
  },
72
- }),
73
- components,
74
- template,
75
- }));
53
+ ],
54
+ },
55
+ });
56
+
57
+ export default {
58
+ title: 'charts/discrete-scatter-chart',
59
+ component: GlDiscreteScatterChart,
60
+ parameters: {
61
+ knobs: { disable: true },
62
+ },
63
+ };
@@ -1,8 +1,6 @@
1
- import examples from './examples';
2
1
  import description from './stacked_column.md';
3
2
 
4
3
  export default {
5
4
  followsDesignSystem: false,
6
5
  description,
7
- examples,
8
6
  };
@@ -1,5 +1,3 @@
1
- # Stacked Column Chart
2
-
3
1
  The `presentation` property allows you to change between a stacked and tiled presentation style. It
4
2
  is only setup to accept `stacked` or `tiled` as values, the default value is `tiled`.
5
3
 
@@ -1,6 +1,4 @@
1
- import { withKnobs, object, text, array, select } from '@storybook/addon-knobs';
2
1
  import { GlStackedColumnChart } from '../../../../charts';
3
- import { documentedStoriesOf } from '../../../../documentation/documented_stories';
4
2
  import {
5
3
  mockDefaultStackedLineData,
6
4
  mockDefaultStackedBarData,
@@ -10,10 +8,6 @@ import { toolbox } from '../../../utils/charts/story_config';
10
8
  import { columnOptions } from '../../../utils/constants';
11
9
  import readme from './stacked_column.md';
12
10
 
13
- const components = {
14
- GlStackedColumnChart,
15
- };
16
-
17
11
  const template = `
18
12
  <gl-stacked-column-chart
19
13
  :bars="bars"
@@ -31,7 +25,7 @@ const template = `
31
25
 
32
26
  const mockSecondaryDataTitle = 'Merges';
33
27
 
34
- function generateProps({
28
+ const generateProps = ({
35
29
  bars = mockDefaultStackedBarData,
36
30
  lines = [],
37
31
  option = {},
@@ -42,98 +36,79 @@ function generateProps({
42
36
  presentation = columnOptions.stacked,
43
37
  secondaryData = [],
44
38
  secondaryDataTitle = '',
45
- } = {}) {
46
- return {
47
- bars: {
48
- default: object('Bar chart Data', bars),
49
- },
50
- lines: {
51
- default: object('Line chart Data', lines),
52
- },
53
- option: {
54
- default: object('Echart Options', option),
55
- },
56
- presentation: {
57
- default: select('presentation', columnOptions, presentation),
58
- },
59
- groupBy: {
60
- default: array('Group By', groupBy),
61
- },
62
- xAxisType: {
63
- default: text('X Axis Type', xAxisType),
64
- },
65
- xAxisTitle: {
66
- default: text('X Axis Title', xAxisTitle),
67
- },
68
- yAxisTitle: {
69
- default: text('Y Axis Title', yAxisTitle),
70
- },
71
- secondaryDataTitle: {
72
- default: text('Secondary Data Title', secondaryDataTitle),
73
- },
74
- secondaryData: {
75
- default: object('Secondary Data', secondaryData),
76
- },
77
- };
78
- }
39
+ } = {}) => ({
40
+ bars,
41
+ lines,
42
+ option,
43
+ presentation,
44
+ groupBy,
45
+ xAxisType,
46
+ xAxisTitle,
47
+ yAxisTitle,
48
+ secondaryDataTitle,
49
+ secondaryData,
50
+ });
79
51
 
80
- documentedStoriesOf('charts/stacked-column-chart', readme)
81
- .addDecorator(withKnobs)
82
- .add('stacked', () => ({
83
- props: generateProps(),
84
- components,
85
- template,
86
- }))
87
- .add('tiled', () => ({
88
- props: generateProps({ presentation: columnOptions.tiled }),
89
- components,
90
- template,
91
- }))
92
- .add('stacked with line data', () => ({
93
- props: generateProps({ lines: mockDefaultStackedLineData }),
94
- components,
95
- template,
96
- }))
97
- .add('tiled with line data', () => ({
98
- props: generateProps({ presentation: columnOptions.tiled, lines: mockDefaultStackedLineData }),
99
- components,
100
- template,
101
- }))
102
- .add('with zoom and scroll', () => ({
103
- props: generateProps({
104
- option: {
105
- dataZoom: [
106
- {
107
- startValue: 1,
108
- },
109
- ],
52
+ const Template = (args, { argTypes }) => ({
53
+ components: { GlStackedColumnChart },
54
+ props: Object.keys(argTypes),
55
+ template,
56
+ });
57
+
58
+ export const Stacked = Template.bind({});
59
+ Stacked.args = generateProps();
60
+
61
+ export const Tiled = Template.bind({});
62
+ Tiled.args = generateProps({ presentation: columnOptions.tiled });
63
+
64
+ export const StackedWithLineData = Template.bind({});
65
+ StackedWithLineData.args = generateProps({ lines: mockDefaultStackedLineData });
66
+
67
+ export const TiledWithLineData = Template.bind({});
68
+ TiledWithLineData.args = generateProps({
69
+ presentation: columnOptions.tiled,
70
+ lines: mockDefaultStackedLineData,
71
+ });
72
+
73
+ export const WithZoomAndScroll = Template.bind({});
74
+ WithZoomAndScroll.args = generateProps({
75
+ option: {
76
+ dataZoom: [
77
+ {
78
+ startValue: 1,
110
79
  },
111
- }),
112
- components,
113
- template,
114
- }))
115
- .add('with toolbox', () => ({
116
- props: generateProps({
117
- option: {
118
- toolbox,
80
+ ],
81
+ },
82
+ });
83
+
84
+ export const WithToolbox = Template.bind({});
85
+ WithToolbox.args = generateProps({
86
+ option: {
87
+ toolbox,
88
+ },
89
+ });
90
+
91
+ export const SecondaryYAxis = Template.bind({});
92
+ SecondaryYAxis.args = generateProps({
93
+ secondaryData: mockSecondaryData,
94
+ secondaryDataTitle: mockSecondaryDataTitle,
95
+ });
96
+
97
+ export const SecondaryYAxisLine = Template.bind({});
98
+ SecondaryYAxisLine.args = generateProps({
99
+ secondaryData: [{ ...mockSecondaryData[0], type: 'line' }],
100
+ secondaryDataTitle: mockSecondaryDataTitle,
101
+ });
102
+
103
+ export default {
104
+ title: 'charts/stacked-column-chart',
105
+ component: GlStackedColumnChart,
106
+ parameters: {
107
+ knobs: { disable: true },
108
+ docs: {
109
+ description: {
110
+ component: readme,
119
111
  },
120
- }),
121
- components,
122
- template,
123
- }))
124
- .add('secondary Y axis', () => ({
125
- props: generateProps({
126
- secondaryData: mockSecondaryData,
127
- secondaryDataTitle: mockSecondaryDataTitle,
128
- }),
129
- components,
130
- template,
131
- }))
132
- .add('secondary Y axis line', () => ({
133
- props: generateProps({
134
- secondaryData: [{ ...mockSecondaryData[0], type: 'line' }],
135
- secondaryDataTitle: mockSecondaryDataTitle,
136
- }),
137
- components,
138
- template,
139
- }));
112
+ },
113
+ },
114
+ };