@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.
- package/CHANGELOG.md +22 -0
- package/dist/components/base/filtered_search/filtered_search_term.js +6 -1
- package/dist/components/base/filtered_search/filtered_search_token_segment.js +10 -1
- package/dist/components/base/form/form_group/form_group.documentation.js +2 -6
- package/dist/components/base/search_box_by_click/search_box_by_click.documentation.js +2 -80
- package/dist/components/base/search_box_by_click/search_box_by_click.js +60 -1
- package/dist/components/base/search_box_by_type/search_box_by_type.documentation.js +2 -20
- package/dist/components/base/search_box_by_type/search_box_by_type.js +19 -5
- package/dist/directives/hover_load/hover_load.documentation.js +1 -1
- package/dist/directives/resize_observer/resize_observer.documentation.js +2 -5
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/documentation/documented_stories.js +5 -0
- package/package.json +1 -1
- package/src/components/base/button/button.scss +5 -9
- package/src/components/base/filtered_search/filtered_search.spec.js +12 -0
- package/src/components/base/filtered_search/filtered_search_term.spec.js +8 -2
- package/src/components/base/filtered_search/filtered_search_term.vue +6 -0
- package/src/components/base/filtered_search/filtered_search_token_segment.spec.js +46 -37
- package/src/components/base/filtered_search/filtered_search_token_segment.vue +14 -1
- package/src/components/base/form/form_group/form_group.documentation.js +0 -3
- package/src/components/base/form/form_group/form_group.md +0 -2
- package/src/components/base/form/form_group/form_group.stories.js +106 -145
- package/src/components/base/form/form_select/form_select.spec.js +8 -4
- package/src/components/base/search_box_by_click/search_box_by_click.documentation.js +0 -90
- package/src/components/base/search_box_by_click/search_box_by_click.md +0 -2
- package/src/components/base/search_box_by_click/search_box_by_click.stories.js +91 -48
- package/src/components/base/search_box_by_click/search_box_by_click.vue +50 -1
- package/src/components/base/search_box_by_type/search_box_by_type.documentation.js +0 -18
- package/src/components/base/search_box_by_type/search_box_by_type.md +0 -2
- package/src/components/base/search_box_by_type/search_box_by_type.stories.js +49 -43
- package/src/components/base/search_box_by_type/search_box_by_type.vue +16 -6
- package/src/directives/hover_load/hover_load.md +0 -2
- package/src/directives/hover_load/hover_load.stories.js +39 -34
- package/src/directives/resize_observer/resize_observer.documentation.js +0 -2
- package/src/directives/resize_observer/resize_observer.md +0 -6
- package/src/directives/resize_observer/resize_observer.stories.js +70 -51
- package/dist/components/base/form/form_group/examples/form_group/form_group.basic.example.js +0 -38
- package/dist/components/base/form/form_group/examples/form_group/form_group.disabled.example.js +0 -48
- package/dist/components/base/form/form_group/examples/form_group/form_group.textarea.example.js +0 -48
- package/dist/components/base/form/form_group/examples/form_group/form_group.validation.example.js +0 -66
- package/dist/components/base/form/form_group/examples/form_group/index.js +0 -27
- package/dist/components/base/search_box_by_click/examples/index.js +0 -22
- package/dist/components/base/search_box_by_click/examples/search_box_by_click.default.example.js +0 -48
- package/dist/components/base/search_box_by_click/examples/search_box_by_click.history.example.js +0 -51
- package/dist/components/base/search_box_by_click/examples/search_box_by_click.v_model.example.js +0 -54
- package/dist/components/base/search_box_by_type/examples/index.js +0 -17
- package/dist/components/base/search_box_by_type/examples/search_box_by_type.default.example.js +0 -48
- package/dist/components/base/search_box_by_type/examples/search_box_by_type.loading.example.js +0 -65
- package/dist/directives/resize_observer/examples/index.js +0 -12
- package/dist/directives/resize_observer/examples/resize_observer.basic.example.js +0 -62
- package/src/components/base/form/form_group/examples/form_group/form_group.basic.example.vue +0 -11
- package/src/components/base/form/form_group/examples/form_group/form_group.disabled.example.vue +0 -21
- package/src/components/base/form/form_group/examples/form_group/form_group.textarea.example.vue +0 -15
- package/src/components/base/form/form_group/examples/form_group/form_group.validation.example.vue +0 -36
- package/src/components/base/form/form_group/examples/form_group/index.js +0 -32
- package/src/components/base/search_box_by_click/examples/index.js +0 -26
- package/src/components/base/search_box_by_click/examples/search_box_by_click.default.example.vue +0 -14
- package/src/components/base/search_box_by_click/examples/search_box_by_click.history.example.vue +0 -23
- package/src/components/base/search_box_by_click/examples/search_box_by_click.v_model.example.vue +0 -23
- package/src/components/base/search_box_by_type/examples/index.js +0 -20
- package/src/components/base/search_box_by_type/examples/search_box_by_type.default.example.vue +0 -14
- package/src/components/base/search_box_by_type/examples/search_box_by_type.loading.example.vue +0 -34
- package/src/directives/resize_observer/examples/index.js +0 -14
- 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
|
@@ -108,9 +108,8 @@
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
&.selected {
|
|
111
|
-
@include gl-bg-
|
|
112
|
-
box-shadow: inset 0 0 0 $gl-border-size-
|
|
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 `
|
|
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
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
|
|
262
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
-
|
|
295
|
-
|
|
296
|
-
|
|
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
|
-
|
|
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="
|
|
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,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 {
|
|
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
|
-
|
|
9
|
+
const generateProps = ({
|
|
12
10
|
id = 'group-1',
|
|
13
11
|
label = 'Label Name',
|
|
14
12
|
description = 'form group description',
|
|
15
|
-
labelDescription = '
|
|
13
|
+
labelDescription = '',
|
|
16
14
|
optional = GlFormGroup.props.optional.default,
|
|
17
15
|
optionalText = GlFormGroup.props.optionalText.default,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
118
|
+
},
|
|
119
|
+
argTypes: {
|
|
120
|
+
...disableControls(['labelClass']),
|
|
121
|
+
label: {
|
|
122
|
+
control: { type: 'text' },
|
|
150
123
|
},
|
|
151
|
-
|
|
152
|
-
|
|
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(
|
|
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(
|
|
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
|
|