@gitlab/ui 37.3.0 → 37.4.2

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 (65) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/components/base/filtered_search/filtered_search_term.js +6 -1
  3. package/dist/components/base/filtered_search/filtered_search_token_segment.js +10 -1
  4. package/dist/components/base/form/form_group/form_group.documentation.js +2 -6
  5. package/dist/components/base/search_box_by_click/search_box_by_click.documentation.js +2 -80
  6. package/dist/components/base/search_box_by_click/search_box_by_click.js +60 -1
  7. package/dist/components/base/search_box_by_type/search_box_by_type.documentation.js +2 -20
  8. package/dist/components/base/search_box_by_type/search_box_by_type.js +19 -5
  9. package/dist/directives/hover_load/hover_load.documentation.js +1 -1
  10. package/dist/directives/resize_observer/resize_observer.documentation.js +2 -5
  11. package/dist/index.css +1 -1
  12. package/dist/index.css.map +1 -1
  13. package/documentation/documented_stories.js +5 -0
  14. package/package.json +1 -1
  15. package/src/components/base/button/button.scss +5 -9
  16. package/src/components/base/filtered_search/filtered_search.spec.js +12 -0
  17. package/src/components/base/filtered_search/filtered_search_term.spec.js +8 -2
  18. package/src/components/base/filtered_search/filtered_search_term.vue +6 -0
  19. package/src/components/base/filtered_search/filtered_search_token_segment.spec.js +46 -37
  20. package/src/components/base/filtered_search/filtered_search_token_segment.vue +14 -1
  21. package/src/components/base/form/form_group/form_group.documentation.js +0 -3
  22. package/src/components/base/form/form_group/form_group.md +0 -2
  23. package/src/components/base/form/form_group/form_group.stories.js +106 -145
  24. package/src/components/base/form/form_select/form_select.spec.js +8 -4
  25. package/src/components/base/search_box_by_click/search_box_by_click.documentation.js +0 -90
  26. package/src/components/base/search_box_by_click/search_box_by_click.md +0 -2
  27. package/src/components/base/search_box_by_click/search_box_by_click.stories.js +91 -48
  28. package/src/components/base/search_box_by_click/search_box_by_click.vue +50 -1
  29. package/src/components/base/search_box_by_type/search_box_by_type.documentation.js +0 -18
  30. package/src/components/base/search_box_by_type/search_box_by_type.md +0 -2
  31. package/src/components/base/search_box_by_type/search_box_by_type.stories.js +49 -43
  32. package/src/components/base/search_box_by_type/search_box_by_type.vue +16 -6
  33. package/src/directives/hover_load/hover_load.md +0 -2
  34. package/src/directives/hover_load/hover_load.stories.js +39 -34
  35. package/src/directives/resize_observer/resize_observer.documentation.js +0 -2
  36. package/src/directives/resize_observer/resize_observer.md +0 -6
  37. package/src/directives/resize_observer/resize_observer.stories.js +70 -51
  38. package/dist/components/base/form/form_group/examples/form_group/form_group.basic.example.js +0 -38
  39. package/dist/components/base/form/form_group/examples/form_group/form_group.disabled.example.js +0 -48
  40. package/dist/components/base/form/form_group/examples/form_group/form_group.textarea.example.js +0 -48
  41. package/dist/components/base/form/form_group/examples/form_group/form_group.validation.example.js +0 -66
  42. package/dist/components/base/form/form_group/examples/form_group/index.js +0 -27
  43. package/dist/components/base/search_box_by_click/examples/index.js +0 -22
  44. package/dist/components/base/search_box_by_click/examples/search_box_by_click.default.example.js +0 -48
  45. package/dist/components/base/search_box_by_click/examples/search_box_by_click.history.example.js +0 -51
  46. package/dist/components/base/search_box_by_click/examples/search_box_by_click.v_model.example.js +0 -54
  47. package/dist/components/base/search_box_by_type/examples/index.js +0 -17
  48. package/dist/components/base/search_box_by_type/examples/search_box_by_type.default.example.js +0 -48
  49. package/dist/components/base/search_box_by_type/examples/search_box_by_type.loading.example.js +0 -65
  50. package/dist/directives/resize_observer/examples/index.js +0 -12
  51. package/dist/directives/resize_observer/examples/resize_observer.basic.example.js +0 -62
  52. package/src/components/base/form/form_group/examples/form_group/form_group.basic.example.vue +0 -11
  53. package/src/components/base/form/form_group/examples/form_group/form_group.disabled.example.vue +0 -21
  54. package/src/components/base/form/form_group/examples/form_group/form_group.textarea.example.vue +0 -15
  55. package/src/components/base/form/form_group/examples/form_group/form_group.validation.example.vue +0 -36
  56. package/src/components/base/form/form_group/examples/form_group/index.js +0 -32
  57. package/src/components/base/search_box_by_click/examples/index.js +0 -26
  58. package/src/components/base/search_box_by_click/examples/search_box_by_click.default.example.vue +0 -14
  59. package/src/components/base/search_box_by_click/examples/search_box_by_click.history.example.vue +0 -23
  60. package/src/components/base/search_box_by_click/examples/search_box_by_click.v_model.example.vue +0 -23
  61. package/src/components/base/search_box_by_type/examples/index.js +0 -20
  62. package/src/components/base/search_box_by_type/examples/search_box_by_type.default.example.vue +0 -14
  63. package/src/components/base/search_box_by_type/examples/search_box_by_type.loading.example.vue +0 -34
  64. package/src/directives/resize_observer/examples/index.js +0 -14
  65. package/src/directives/resize_observer/examples/resize_observer.basic.example.vue +0 -21
@@ -93,6 +93,7 @@ export const setupStorybookReadme = () =>
93
93
  'GlIntersperse',
94
94
  'GlFormSelect',
95
95
  'GlDaterangePicker',
96
+ 'GlFormGroup',
96
97
  'GlAvatarLabeled',
97
98
  'GlBarChart',
98
99
  'GlCarousel',
@@ -108,8 +109,10 @@ export const setupStorybookReadme = () =>
108
109
  'GlPagination',
109
110
  'GlSkeletonLoader',
110
111
  'GlLabel',
112
+ 'GlHoverLoadDirective',
111
113
  'GlStackedColumnChart',
112
114
  'GlDiscreteScatterChart',
115
+ 'GlResizeObserverDirective',
113
116
  'GlFormCombobox',
114
117
  'GlChartTooltip',
115
118
  'GlInputGroupText',
@@ -129,6 +132,7 @@ export const setupStorybookReadme = () =>
129
132
  'GlColumnChart',
130
133
  'GlTruncate',
131
134
  'GlNav',
135
+ 'GlSearchBoxByType',
132
136
  'GlNavItem',
133
137
  'GlNavItemDropdown',
134
138
  'GlPopover',
@@ -140,6 +144,7 @@ export const setupStorybookReadme = () =>
140
144
  'GlAreaChart',
141
145
  'GlLineChart',
142
146
  'GlFormInputGroup',
147
+ 'GlSearchBoxByClick',
143
148
  ],
144
149
  components: {
145
150
  GlComponentDocumentation,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "37.3.0",
3
+ "version": "37.4.2",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -108,9 +108,8 @@
108
108
  }
109
109
 
110
110
  &.selected {
111
- @include gl-bg-gray-10;
112
- box-shadow: inset 0 0 0 $gl-border-size-1 $gray-200,
113
- inset 0 $gl-border-size-2 $gl-border-size-4 $gray-200;
111
+ @include gl-bg-white;
112
+ box-shadow: inset 0 0 0 $gl-border-size-2 $gray-300;
114
113
 
115
114
  &:hover,
116
115
  &:focus {
@@ -118,20 +117,17 @@
118
117
  }
119
118
 
120
119
  &:hover {
121
- box-shadow: inset 0 0 0 $gl-border-size-2 $gray-400,
122
- inset 0 $gl-border-size-4 $gl-border-size-4 $gray-200;
120
+ box-shadow: inset 0 0 0 $gl-border-size-2 $gray-400;
123
121
  }
124
122
 
125
123
  &:focus {
126
- box-shadow: inset 0 0 0 $gl-border-size-2 $gray-400,
127
- inset 0 $gl-border-size-4 $gl-border-size-4 $gray-200, $focus-ring;
124
+ box-shadow: inset 0 0 0 $gl-border-size-2 $gray-400, $focus-ring;
128
125
  }
129
126
 
130
127
  &:active,
131
128
  &:active:focus {
132
129
  @include gl-bg-gray-100;
133
- box-shadow: inset 0 0 0 $gl-border-size-2 $gray-600,
134
- inset 0 $gl-border-size-4 $gl-border-size-4 $gray-200, $focus-ring;
130
+ box-shadow: inset 0 0 0 $gl-border-size-2 $gray-600, $focus-ring;
135
131
  }
136
132
  }
137
133
 
@@ -378,6 +378,18 @@ describe('Filtered search', () => {
378
378
  expect(filteredSearchTerms.at(0).props('isLastToken')).toBe(false);
379
379
  expect(filteredSearchTerms.at(1).props('isLastToken')).toBe(true);
380
380
  });
381
+
382
+ it('passes `currentValue` prop to search terms', async () => {
383
+ createComponent({
384
+ value: ['one'],
385
+ });
386
+ await nextTick();
387
+
388
+ expect(wrapper.findComponent(GlFilteredSearchTerm).props('currentValue')).toEqual([
389
+ { type: 'filtered-search-term', value: { data: 'one' } },
390
+ { type: 'filtered-search-term', value: { data: '' } },
391
+ ]);
392
+ });
381
393
  });
382
394
 
383
395
  describe('Filtered search integration tests', () => {
@@ -21,7 +21,7 @@ describe('Filtered search term', () => {
21
21
  const segmentStub = {
22
22
  name: 'gl-filtered-search-token-segment-stub',
23
23
  template: '<div><slot name="view"></slot><slot name="suggestions"></slot></div>',
24
- props: ['searchInputAttributes', 'isLastToken'],
24
+ props: ['searchInputAttributes', 'isLastToken', 'currentValue'],
25
25
  };
26
26
 
27
27
  const createComponent = (props) => {
@@ -79,18 +79,24 @@ describe('Filtered search term', () => {
79
79
  }
80
80
  );
81
81
 
82
- it('passes `searchInputAttributes` and `isLastToken` prop to `GlFilteredSearchTokenSegment`', () => {
82
+ it('passes `searchInputAttributes`, `isLastToken`, and `currentValue` props to `GlFilteredSearchTokenSegment`', () => {
83
83
  const isLastToken = true;
84
+ const currentValue = [
85
+ { type: 'filtered-search-term', value: { data: 'something' } },
86
+ { type: 'filtered-search-term', value: { data: '' } },
87
+ ];
84
88
 
85
89
  createComponent({
86
90
  value: { data: 'something' },
87
91
  searchInputAttributes,
88
92
  isLastToken,
93
+ currentValue,
89
94
  });
90
95
 
91
96
  expect(findTokenSegmentComponent().props()).toEqual({
92
97
  searchInputAttributes,
93
98
  isLastToken,
99
+ currentValue,
94
100
  });
95
101
  });
96
102
 
@@ -38,6 +38,11 @@ export default {
38
38
  required: false,
39
39
  default: false,
40
40
  },
41
+ currentValue: {
42
+ type: Array,
43
+ required: false,
44
+ default: () => [],
45
+ },
41
46
  },
42
47
  computed: {
43
48
  suggestedTokens() {
@@ -66,6 +71,7 @@ export default {
66
71
  :class="{ 'gl-w-full': placeholder }"
67
72
  :search-input-attributes="searchInputAttributes"
68
73
  :is-last-token="isLastToken"
74
+ :current-value="currentValue"
69
75
  @activate="$emit('activate')"
70
76
  @deactivate="$emit('deactivate')"
71
77
  @complete="$emit('replace', { type: $event })"
@@ -254,54 +254,63 @@ describe('Filtered search token segment', () => {
254
254
  });
255
255
  });
256
256
 
257
- describe('when input is active', () => {
258
- it('adds `searchInputAttributes` prop to search token segment input', () => {
259
- createComponent({ active: true, value: 'something', searchInputAttributes });
257
+ const currentValueNoTokens = [{ type: 'filtered-search-term', value: { data: '' } }];
258
+ const currentValueOneToken = [
259
+ { type: 'filtered-search-term', value: { data: 'one' } },
260
+ { type: 'filtered-search-term', value: { data: '' } },
261
+ ];
262
+
263
+ describe.each`
264
+ active | isLastToken | currentValue
265
+ ${true} | ${true} | ${currentValueNoTokens}
266
+ ${true} | ${false} | ${currentValueNoTokens}
267
+ ${false} | ${true} | ${currentValueNoTokens}
268
+ ${true} | ${true} | ${currentValueOneToken}
269
+ ${true} | ${false} | ${currentValueOneToken}
270
+ `(
271
+ 'when `active` is `$active`, `isLastToken` is `$isLastToken` and `currentValue` is `$currentValue`',
272
+ ({ active, isLastToken, currentValue }) => {
273
+ beforeEach(() => {
274
+ createComponent({
275
+ value: 'something',
276
+ active,
277
+ isLastToken,
278
+ currentValue,
279
+ searchInputAttributes,
280
+ });
281
+ });
260
282
 
261
- expect(wrapper.find('input').attributes('data-qa-selector')).toBe(
262
- searchInputAttributes['data-qa-selector']
263
- );
264
- });
283
+ it('does not add `searchInputAttributes` prop to search token segment', () => {
284
+ expect(wrapper.attributes('data-qa-selector')).toBe(undefined);
285
+ });
286
+ }
287
+ );
265
288
 
266
- it('does not add `searchInputAttributes` prop to search token segment', () => {
289
+ describe('when `active` is `false`, `isLastToken` is `true` and there is one or more tokens', () => {
290
+ beforeEach(() => {
267
291
  createComponent({
268
- active: true,
269
292
  value: 'something',
270
- searchInputAttributes,
293
+ active: false,
271
294
  isLastToken: true,
295
+ currentValue: currentValueOneToken,
296
+ searchInputAttributes,
272
297
  });
273
-
274
- expect(wrapper.attributes('data-qa-selector')).toBe(undefined);
275
298
  });
276
- });
277
299
 
278
- describe('when input is not active', () => {
279
- describe('when `isLastToken` prop is `true`', () => {
280
- it('adds `searchInputAttributes` prop to search token segment', () => {
281
- createComponent({
282
- active: false,
283
- value: 'something',
284
- searchInputAttributes,
285
- isLastToken: true,
286
- });
287
-
288
- expect(wrapper.attributes('data-qa-selector')).toBe(
289
- searchInputAttributes['data-qa-selector']
290
- );
291
- });
300
+ it('adds `searchInputAttributes` prop to search token segment', () => {
301
+ expect(wrapper.attributes('data-qa-selector')).toBe(
302
+ searchInputAttributes['data-qa-selector']
303
+ );
292
304
  });
305
+ });
293
306
 
294
- describe('when `isLastToken` prop is `false`', () => {
295
- it('does not add `searchInputAttributes` prop to search token segment', () => {
296
- createComponent({
297
- active: false,
298
- value: 'something',
299
- searchInputAttributes,
300
- isLastToken: false,
301
- });
307
+ describe('when input is active', () => {
308
+ it('adds `searchInputAttributes` prop to search token segment input', () => {
309
+ createComponent({ active: true, value: 'something', searchInputAttributes });
302
310
 
303
- expect(wrapper.attributes('data-qa-selector')).toBe(undefined);
304
- });
311
+ expect(wrapper.find('input').attributes('data-qa-selector')).toBe(
312
+ searchInputAttributes['data-qa-selector']
313
+ );
305
314
  });
306
315
  });
307
316
  });
@@ -59,6 +59,11 @@ export default {
59
59
  required: false,
60
60
  default: false,
61
61
  },
62
+ currentValue: {
63
+ type: Array,
64
+ required: false,
65
+ default: () => [],
66
+ },
62
67
  },
63
68
 
64
69
  data() {
@@ -106,6 +111,14 @@ export default {
106
111
  const defaultSuggestion = this.options.find((op) => op.default);
107
112
  return (defaultSuggestion ?? this.options[0])?.value;
108
113
  },
114
+ containerAttributes() {
115
+ return (
116
+ this.isLastToken &&
117
+ !this.active &&
118
+ this.currentValue.length > 1 &&
119
+ this.searchInputAttributes
120
+ );
121
+ },
109
122
  },
110
123
 
111
124
  watch: {
@@ -254,7 +267,7 @@ export default {
254
267
 
255
268
  <template>
256
269
  <div
257
- v-bind="isLastToken && !active && searchInputAttributes"
270
+ v-bind="containerAttributes"
258
271
  class="gl-filtered-search-token-segment"
259
272
  :class="{ 'gl-filtered-search-token-segment-active': active }"
260
273
  data-testid="filtered-search-token-segment"
@@ -1,8 +1,5 @@
1
- import examples from './examples/form_group';
2
1
  import description from './form_group.md';
3
2
 
4
3
  export default {
5
4
  description,
6
- examples,
7
- bootstrapComponent: 'b-form-group',
8
5
  };
@@ -1,3 +1 @@
1
- # Form Group
2
-
3
1
  Form group adds structure to forms.
@@ -1,164 +1,125 @@
1
- import { withKnobs, text, select, boolean } from '@storybook/addon-knobs';
2
- import { documentedStoriesOf } from '../../../../../documentation/documented_stories';
3
1
  import { GlFormGroup, GlFormInput, GlFormTextarea } from '../../../../index';
4
- import { sizeOptions } from '../../../../utils/constants';
2
+ import { disableControls } from '../../../../utils/stories_utils';
5
3
  import readme from './form_group.md';
6
4
 
7
5
  const components = {
8
6
  GlFormGroup,
9
7
  };
10
8
 
11
- function generateProps({
9
+ const generateProps = ({
12
10
  id = 'group-1',
13
11
  label = 'Label Name',
14
12
  description = 'form group description',
15
- labelDescription = 'form label description',
13
+ labelDescription = '',
16
14
  optional = GlFormGroup.props.optional.default,
17
15
  optionalText = GlFormGroup.props.optionalText.default,
18
- horizontal = false,
19
- } = {}) {
20
- return {
21
- id: {
22
- type: String,
23
- default: text('id', id),
24
- },
25
- label: {
26
- type: String,
27
- default: text('label', label),
28
- },
29
- labelSize: {
30
- type: String,
31
- default: select('label-size', sizeOptions, sizeOptions.sm),
32
- },
33
- description: {
34
- type: String,
35
- default: text('description', description),
36
- },
37
- labelDescription: {
38
- type: String,
39
- default: text('label-description', labelDescription),
40
- },
41
- optional: {
42
- type: Boolean,
43
- default: boolean('optional', optional),
44
- },
45
- optionalText: {
46
- type: String,
47
- default: text('optional-text', optionalText),
48
- },
49
- horizontal: {
50
- type: Boolean,
51
- default: boolean('horizontal', horizontal),
16
+ } = {}) => ({
17
+ id,
18
+ label,
19
+ labelDescription,
20
+ optional,
21
+ optionalText,
22
+ description,
23
+ });
24
+
25
+ const wrap = (template, bindings = '') => `
26
+ <gl-form-group
27
+ :id="id + '_group'"
28
+ :label="label"
29
+ :label-description="labelDescription"
30
+ :optional="optional"
31
+ :optional-text="optionalText"
32
+ :description="description"
33
+ ${bindings}
34
+ :label-for="id">
35
+ ${template}
36
+ </gl-form-group>
37
+ `;
38
+
39
+ export const Default = (_args, { argTypes }) => ({
40
+ props: Object.keys(argTypes),
41
+ components: { ...components, GlFormInput },
42
+ template: wrap('<gl-form-input :id="id" />'),
43
+ });
44
+ Default.args = generateProps();
45
+
46
+ export const Disabled = (_args, { argTypes }) => ({
47
+ props: Object.keys(argTypes),
48
+ components: { ...components, GlFormInput },
49
+ template: wrap('<gl-form-input :id="id" type="text" value="Disabled" disabled />'),
50
+ });
51
+ Disabled.args = generateProps({ description: 'This feature is disabled' });
52
+
53
+ export const WithTextarea = (_args, { argTypes }) => ({
54
+ props: Object.keys(argTypes),
55
+ components: { ...components, GlFormTextarea },
56
+ template: wrap('<gl-form-textarea :id="id" placeholder="Enter something" />'),
57
+ });
58
+ WithTextarea.args = generateProps({
59
+ id: 'textarea2',
60
+ optional: true,
61
+ description: '',
62
+ });
63
+
64
+ export const WithLabelDescription = (_args, { argTypes }) => ({
65
+ props: Object.keys(argTypes),
66
+ components: { ...components, GlFormInput },
67
+ template: wrap('<gl-form-input :id="id" />'),
68
+ });
69
+ WithLabelDescription.args = generateProps({
70
+ optional: true,
71
+ labelDescription: 'form label description',
72
+ });
73
+
74
+ export const WithValidations = (_args, { argTypes }) => ({
75
+ props: Object.keys(argTypes),
76
+ components: { ...components, GlFormInput },
77
+ computed: {
78
+ state() {
79
+ return this.name.length >= 4;
52
80
  },
53
- };
54
- }
81
+ invalidFeedback() {
82
+ let feedbackText = 'This field is required.';
55
83
 
56
- documentedStoriesOf('base/form/form-group', readme)
57
- .addDecorator(withKnobs)
58
- .add('default', () => ({
59
- props: generateProps(),
60
- components: { ...components, GlFormInput },
61
- template: `
62
- <gl-form-group
63
- :id="id"
64
- :label="label"
65
- :label-size="labelSize"
66
- :optional="optional"
67
- :optional-text="optionalText"
68
- :description="description"
69
- :horizontal="horizontal"
70
- label-for="label1"
71
- >
72
- <gl-form-input id="input1" />
73
- </gl-form-group>
74
- `,
75
- }))
76
- .add('disabled', () => ({
77
- props: generateProps(),
78
- components: { ...components, GlFormInput },
79
- template: `
80
- <gl-form-group
81
- id="group-id"
82
- label="Label Name"
83
- label-size="sm"
84
- :optional="optional"
85
- :optional-text="optionalText"
86
- description="This feature is disabled"
87
- label-for="input1"
88
- >
89
- <gl-form-input id="input1" type="text" :disabled="true" value="Disabled" />
90
- </gl-form-group>
91
- `,
92
- }))
93
- .add('with textarea', () => ({
94
- components: { ...components, GlFormTextarea },
95
- props: generateProps({ optional: true }),
96
- template: `
97
- <gl-form-group
98
- id="group-id-textarea2"
99
- label="Label Name"
100
- label-for="textarea2"
101
- :optional="optional"
102
- :optional-text="optionalText"
103
- >
104
- <gl-form-textarea id="textarea2" placeholder="Enter something" />
105
- </gl-form-group>
106
- `,
107
- }))
108
- .add('with label description', () => ({
109
- props: generateProps({ optional: true }),
110
- components: { ...components, GlFormInput },
111
- template: `
112
- <gl-form-group
113
- :id="id"
114
- :label="label"
115
- :label-size="labelSize"
116
- :description="description"
117
- :label-description="labelDescription"
118
- :optional="optional"
119
- :optional-text="optionalText"
120
- :horizontal="horizontal"
121
- label-for="label1"
122
- >
123
- <gl-form-input id="input1" />
124
- </gl-form-group>
125
- `,
126
- }))
127
- .add('with validations', () => ({
128
- props: generateProps({ label: 'Name', description: 'Enter a first and last name.' }),
129
- components: { ...components, GlFormInput },
130
- computed: {
131
- state() {
132
- return this.name.length >= 4;
133
- },
134
- invalidFeedback() {
135
- let feedbackText = 'This field is required.';
84
+ if (this.name.length > 4) {
85
+ feedbackText = '';
86
+ } else if (this.name.length > 0) {
87
+ feedbackText = 'Enter at least 4 characters.';
88
+ }
136
89
 
137
- if (this.name.length > 4) {
138
- feedbackText = '';
139
- } else if (this.name.length > 0) {
140
- feedbackText = 'Enter at least 4 characters.';
141
- }
90
+ return feedbackText;
91
+ },
92
+ },
93
+ data() {
94
+ return {
95
+ name: '',
96
+ };
97
+ },
98
+ template: wrap(
99
+ '<gl-form-input :id="id" :state="state" v-model.trim="name" />',
100
+ ':invalid-feedback="invalidFeedback"'
101
+ ),
102
+ });
103
+ WithValidations.args = generateProps({
104
+ label: 'Name',
105
+ description: 'Enter a first and last name.',
106
+ });
142
107
 
143
- return feedbackText;
108
+ export default {
109
+ title: 'base/form/form-group',
110
+ component: GlFormGroup,
111
+ parameters: {
112
+ bootstrapComponent: 'b-form-group',
113
+ docs: {
114
+ description: {
115
+ component: readme,
144
116
  },
145
117
  },
146
- data() {
147
- return {
148
- name: '',
149
- };
118
+ },
119
+ argTypes: {
120
+ ...disableControls(['labelClass']),
121
+ label: {
122
+ control: { type: 'text' },
150
123
  },
151
- template: `
152
- <gl-form-group
153
- :id="id"
154
- :label="label"
155
- :label-size="labelSize"
156
- :description="description"
157
- :invalid-feedback="invalidFeedback"
158
- :state="state"
159
- label-for="label1"
160
- >
161
- <gl-form-input id="input1" :state="state" v-model.trim="name" />
162
- </gl-form-group>
163
- `,
164
- }));
124
+ },
125
+ };
@@ -25,7 +25,9 @@ describe('GlFormSelect', () => {
25
25
  `('adds $expectedClass class for state $state', ({ state, expectedClasses }) => {
26
26
  createComponent({ state });
27
27
 
28
- expect(wrapper.classes()).toEqual([...DEFAULT_SELECT_CLASSES, ...expectedClasses]);
28
+ expect(wrapper.classes().sort()).toEqual(
29
+ [...DEFAULT_SELECT_CLASSES, ...expectedClasses].sort()
30
+ );
29
31
  });
30
32
  });
31
33
 
@@ -36,19 +38,21 @@ describe('GlFormSelect', () => {
36
38
  it.each(nonNullSizes)('adds correct class for size %s', (size) => {
37
39
  createComponent({ size });
38
40
 
39
- expect(wrapper.classes()).toEqual([...DEFAULT_SELECT_CLASSES, `custom-select-${size}`]);
41
+ expect(wrapper.classes().sort()).toEqual(
42
+ [...DEFAULT_SELECT_CLASSES, `custom-select-${size}`].sort()
43
+ );
40
44
  });
41
45
 
42
46
  it('does not add a size class if not given the size prop', () => {
43
47
  createComponent();
44
48
 
45
- expect(wrapper.classes()).toEqual([...DEFAULT_SELECT_CLASSES]);
49
+ expect(wrapper.classes().sort()).toEqual([...DEFAULT_SELECT_CLASSES].sort());
46
50
  });
47
51
 
48
52
  it('does not add a size class if passed null', () => {
49
53
  createComponent({ size: null });
50
54
 
51
- expect(wrapper.classes()).toEqual([...DEFAULT_SELECT_CLASSES]);
55
+ expect(wrapper.classes().sort()).toEqual([...DEFAULT_SELECT_CLASSES].sort());
52
56
  });
53
57
  });
54
58