@gitlab/ui 55.2.0 → 55.3.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.
- package/CHANGELOG.md +15 -0
- package/dist/components/base/form/form_group/form_group.js +2 -4
- package/dist/components/base/toggle/toggle.js +18 -1
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/package.json +13 -13
- package/src/components/base/form/form_group/form_group.scss +3 -2
- package/src/components/base/form/form_group/form_group.vue +2 -4
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown.stories.js +17 -51
- package/src/components/base/toggle/toggle.scss +1 -2
- package/src/components/base/toggle/toggle.spec.js +24 -0
- package/src/components/base/toggle/toggle.stories.js +20 -2
- package/src/components/base/toggle/toggle.vue +27 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gitlab/ui",
|
|
3
|
-
"version": "55.
|
|
3
|
+
"version": "55.3.0",
|
|
4
4
|
"description": "GitLab UI Components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -87,18 +87,18 @@
|
|
|
87
87
|
"@gitlab/eslint-plugin": "18.1.0",
|
|
88
88
|
"@gitlab/fonts": "^1.2.0",
|
|
89
89
|
"@gitlab/stylelint-config": "4.1.0",
|
|
90
|
-
"@gitlab/svgs": "3.
|
|
90
|
+
"@gitlab/svgs": "3.20.0",
|
|
91
91
|
"@rollup/plugin-commonjs": "^11.1.0",
|
|
92
92
|
"@rollup/plugin-node-resolve": "^7.1.3",
|
|
93
93
|
"@rollup/plugin-replace": "^2.3.2",
|
|
94
|
-
"@storybook/addon-a11y": "6.5.
|
|
95
|
-
"@storybook/addon-docs": "6.5.
|
|
96
|
-
"@storybook/addon-essentials": "6.5.
|
|
97
|
-
"@storybook/addon-storyshots": "6.5.
|
|
98
|
-
"@storybook/addon-storyshots-puppeteer": "6.5.
|
|
99
|
-
"@storybook/addon-viewport": "6.5.
|
|
100
|
-
"@storybook/theming": "6.5.
|
|
101
|
-
"@storybook/vue": "6.5.
|
|
94
|
+
"@storybook/addon-a11y": "6.5.16",
|
|
95
|
+
"@storybook/addon-docs": "6.5.16",
|
|
96
|
+
"@storybook/addon-essentials": "6.5.16",
|
|
97
|
+
"@storybook/addon-storyshots": "6.5.16",
|
|
98
|
+
"@storybook/addon-storyshots-puppeteer": "6.5.16",
|
|
99
|
+
"@storybook/addon-viewport": "6.5.16",
|
|
100
|
+
"@storybook/theming": "6.5.16",
|
|
101
|
+
"@storybook/vue": "6.5.16",
|
|
102
102
|
"@vue/compat": "^3.2.40",
|
|
103
103
|
"@vue/compiler-sfc": "^3.2.40",
|
|
104
104
|
"@vue/test-utils": "1.3.0",
|
|
@@ -123,9 +123,9 @@
|
|
|
123
123
|
"glob": "^7.2.0",
|
|
124
124
|
"identity-obj-proxy": "^3.0.0",
|
|
125
125
|
"inquirer-select-directory": "^1.2.0",
|
|
126
|
-
"jest": "^29.4.
|
|
127
|
-
"jest-circus": "29.4.
|
|
128
|
-
"jest-environment-jsdom": "29.4.
|
|
126
|
+
"jest": "^29.4.2",
|
|
127
|
+
"jest-circus": "29.4.2",
|
|
128
|
+
"jest-environment-jsdom": "29.4.2",
|
|
129
129
|
"markdownlint-cli": "^0.29.0",
|
|
130
130
|
"mockdate": "^2.0.5",
|
|
131
131
|
"npm-run-all": "^4.1.5",
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { BFormGroup } from 'bootstrap-vue';
|
|
3
3
|
import { isString, isArray, isPlainObject } from 'lodash';
|
|
4
|
-
import GlFormText from '../form_text/form_text.vue';
|
|
5
4
|
|
|
6
5
|
export default {
|
|
7
6
|
components: {
|
|
8
7
|
BFormGroup,
|
|
9
|
-
GlFormText,
|
|
10
8
|
},
|
|
11
9
|
inheritAttrs: false,
|
|
12
10
|
props: {
|
|
@@ -63,9 +61,9 @@ export default {
|
|
|
63
61
|
optionalText
|
|
64
62
|
}}</span>
|
|
65
63
|
</slot>
|
|
66
|
-
<
|
|
64
|
+
<div v-if="hasLabelDescription" data-testid="label-description" class="label-description">
|
|
67
65
|
<slot name="label-description">{{ labelDescription }}</slot>
|
|
68
|
-
</
|
|
66
|
+
</div>
|
|
69
67
|
</template>
|
|
70
68
|
|
|
71
69
|
<!-- eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots -->
|
|
@@ -25,42 +25,6 @@ import {
|
|
|
25
25
|
mockGroupsCustomItem,
|
|
26
26
|
} from './mock_data';
|
|
27
27
|
|
|
28
|
-
const defaultValue = (prop) => GlDisclosureDropdown.props[prop].default;
|
|
29
|
-
|
|
30
|
-
const generateProps = ({
|
|
31
|
-
items = mockItems,
|
|
32
|
-
category = defaultValue('category'),
|
|
33
|
-
variant = defaultValue('variant'),
|
|
34
|
-
size = defaultValue('size'),
|
|
35
|
-
disabled = defaultValue('disabled'),
|
|
36
|
-
loading = defaultValue('loading'),
|
|
37
|
-
noCaret = defaultValue('noCaret'),
|
|
38
|
-
placement = defaultValue('placement'),
|
|
39
|
-
toggleId = defaultValue('toggleId')(),
|
|
40
|
-
toggleText,
|
|
41
|
-
textSrOnly = defaultValue('textSrOnly'),
|
|
42
|
-
icon = '',
|
|
43
|
-
toggleAriaLabelledBy,
|
|
44
|
-
listAriaLabelledBy,
|
|
45
|
-
startOpened = true,
|
|
46
|
-
} = {}) => ({
|
|
47
|
-
items,
|
|
48
|
-
category,
|
|
49
|
-
variant,
|
|
50
|
-
size,
|
|
51
|
-
disabled,
|
|
52
|
-
loading,
|
|
53
|
-
noCaret,
|
|
54
|
-
placement,
|
|
55
|
-
toggleId,
|
|
56
|
-
toggleText,
|
|
57
|
-
textSrOnly,
|
|
58
|
-
icon,
|
|
59
|
-
toggleAriaLabelledBy,
|
|
60
|
-
listAriaLabelledBy,
|
|
61
|
-
startOpened,
|
|
62
|
-
});
|
|
63
|
-
|
|
64
28
|
const makeBindings = (overrides = {}) =>
|
|
65
29
|
Object.entries({
|
|
66
30
|
':items': 'items',
|
|
@@ -119,13 +83,14 @@ export const Default = (args, { argTypes }) => ({
|
|
|
119
83
|
</div>
|
|
120
84
|
`,
|
|
121
85
|
});
|
|
122
|
-
Default.args =
|
|
86
|
+
Default.args = {
|
|
87
|
+
items: mockItems,
|
|
123
88
|
icon: 'ellipsis_v',
|
|
124
89
|
noCaret: true,
|
|
125
90
|
toggleText: 'Disclosure',
|
|
126
91
|
textSrOnly: true,
|
|
127
92
|
toggleId: TOGGLE_ID,
|
|
128
|
-
}
|
|
93
|
+
};
|
|
129
94
|
Default.decorators = [makeContainer({ height: '200px' })];
|
|
130
95
|
|
|
131
96
|
export const CustomListItem = (args, { argTypes }) => ({
|
|
@@ -162,11 +127,11 @@ export const CustomListItem = (args, { argTypes }) => ({
|
|
|
162
127
|
),
|
|
163
128
|
});
|
|
164
129
|
|
|
165
|
-
CustomListItem.args =
|
|
130
|
+
CustomListItem.args = {
|
|
166
131
|
items: mockItemsCustomItem,
|
|
167
132
|
toggleText: 'Merge requests',
|
|
168
133
|
placement: 'center',
|
|
169
|
-
}
|
|
134
|
+
};
|
|
170
135
|
CustomListItem.decorators = [makeContainer({ height: '200px' })];
|
|
171
136
|
|
|
172
137
|
const makeGroupedExample = (changes) => {
|
|
@@ -190,7 +155,7 @@ const makeGroupedExample = (changes) => {
|
|
|
190
155
|
...changes,
|
|
191
156
|
});
|
|
192
157
|
|
|
193
|
-
story.args =
|
|
158
|
+
story.args = { items: mockGroups };
|
|
194
159
|
story.decorators = [makeContainer({ height: '340px' })];
|
|
195
160
|
|
|
196
161
|
return story;
|
|
@@ -199,12 +164,12 @@ const makeGroupedExample = (changes) => {
|
|
|
199
164
|
export const Groups = makeGroupedExample({
|
|
200
165
|
template: template(''),
|
|
201
166
|
});
|
|
202
|
-
Groups.args =
|
|
167
|
+
Groups.args = {
|
|
203
168
|
icon: 'plus-square',
|
|
204
169
|
items: mockGroups,
|
|
205
170
|
toggleText: 'Create new',
|
|
206
171
|
textSrOnly: true,
|
|
207
|
-
}
|
|
172
|
+
};
|
|
208
173
|
|
|
209
174
|
export const CustomGroupsAndItems = (args, { argTypes }) => ({
|
|
210
175
|
props: Object.keys(argTypes),
|
|
@@ -245,10 +210,10 @@ export const CustomGroupsAndItems = (args, { argTypes }) => ({
|
|
|
245
210
|
),
|
|
246
211
|
});
|
|
247
212
|
|
|
248
|
-
CustomGroupsAndItems.args =
|
|
213
|
+
CustomGroupsAndItems.args = {
|
|
249
214
|
items: mockGroupsCustomItem,
|
|
250
215
|
toggleText: 'Merge requests',
|
|
251
|
-
}
|
|
216
|
+
};
|
|
252
217
|
CustomGroupsAndItems.decorators = [makeContainer({ height: '200px' })];
|
|
253
218
|
|
|
254
219
|
export const CustomGroupsItemsAndToggle = makeGroupedExample({
|
|
@@ -313,12 +278,11 @@ export const CustomGroupsItemsAndToggle = makeGroupedExample({
|
|
|
313
278
|
},
|
|
314
279
|
groups: mockProfileGroups,
|
|
315
280
|
});
|
|
316
|
-
CustomGroupsItemsAndToggle.args =
|
|
281
|
+
CustomGroupsItemsAndToggle.args = {
|
|
317
282
|
icon: 'plus-square',
|
|
318
283
|
toggleText: 'User profile menu',
|
|
319
284
|
textSrOnly: true,
|
|
320
|
-
|
|
321
|
-
});
|
|
285
|
+
};
|
|
322
286
|
CustomGroupsItemsAndToggle.decorators = [makeContainer({ height: '400px' })];
|
|
323
287
|
|
|
324
288
|
export const MiscellaneousContent = (args, { argTypes }) => ({
|
|
@@ -338,12 +302,11 @@ export const MiscellaneousContent = (args, { argTypes }) => ({
|
|
|
338
302
|
),
|
|
339
303
|
});
|
|
340
304
|
|
|
341
|
-
MiscellaneousContent.args =
|
|
305
|
+
MiscellaneousContent.args = {
|
|
342
306
|
icon: 'doc-text',
|
|
343
307
|
toggleText: 'Miscellaneous content',
|
|
344
308
|
textSrOnly: true,
|
|
345
|
-
|
|
346
|
-
});
|
|
309
|
+
};
|
|
347
310
|
MiscellaneousContent.decorators = [makeContainer({ height: '200px' })];
|
|
348
311
|
|
|
349
312
|
export default {
|
|
@@ -382,4 +345,7 @@ export default {
|
|
|
382
345
|
},
|
|
383
346
|
},
|
|
384
347
|
},
|
|
348
|
+
args: {
|
|
349
|
+
startOpened: true,
|
|
350
|
+
},
|
|
385
351
|
};
|
|
@@ -8,6 +8,7 @@ describe('toggle', () => {
|
|
|
8
8
|
let wrapper;
|
|
9
9
|
|
|
10
10
|
const label = 'toggle label';
|
|
11
|
+
const descriptionText = 'description text';
|
|
11
12
|
const helpText = 'help text';
|
|
12
13
|
|
|
13
14
|
const createWrapper = (props = {}, options = {}) => {
|
|
@@ -21,6 +22,7 @@ describe('toggle', () => {
|
|
|
21
22
|
};
|
|
22
23
|
|
|
23
24
|
const findButton = () => wrapper.find('button');
|
|
25
|
+
const findDescriptionElement = () => wrapper.find('[data-testid="toggle-description"]');
|
|
24
26
|
const findHelpElement = () => wrapper.find('[data-testid="toggle-help"]');
|
|
25
27
|
|
|
26
28
|
it('has role=switch', () => {
|
|
@@ -85,6 +87,28 @@ describe('toggle', () => {
|
|
|
85
87
|
});
|
|
86
88
|
});
|
|
87
89
|
|
|
90
|
+
describe.each`
|
|
91
|
+
state | description | props | options
|
|
92
|
+
${'with description'} | ${descriptionText} | ${{ description: descriptionText }} | ${undefined}
|
|
93
|
+
${'with description in slot'} | ${descriptionText} | ${undefined} | ${{ slots: { description: descriptionText } }}
|
|
94
|
+
${'without description'} | ${undefined} | ${undefined} | ${undefined}
|
|
95
|
+
${'with description and labelPosition left'} | ${undefined} | ${{ desciption: descriptionText, labelPosition: toggleLabelPosition.left }} | ${undefined}
|
|
96
|
+
`('$state', ({ description, props, options }) => {
|
|
97
|
+
beforeEach(() => {
|
|
98
|
+
createWrapper(props, options);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (description) {
|
|
102
|
+
it('shows description', () => {
|
|
103
|
+
expect(findDescriptionElement().text()).toBe(description);
|
|
104
|
+
});
|
|
105
|
+
} else {
|
|
106
|
+
it('does not show description', () => {
|
|
107
|
+
expect(findDescriptionElement().exists()).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
88
112
|
describe.each`
|
|
89
113
|
state | help | props | options | getAriaDescribedBy
|
|
90
114
|
${'with help'} | ${helpText} | ${{ help: helpText }} | ${undefined} | ${() => findHelpElement().attributes('id')}
|
|
@@ -5,7 +5,9 @@ import readme from './toggle.md';
|
|
|
5
5
|
|
|
6
6
|
const defaultValue = (prop) => GlToggle.props[prop].default;
|
|
7
7
|
|
|
8
|
-
const
|
|
8
|
+
const withDescription = 'A dark color theme that is easier on the eyes.';
|
|
9
|
+
|
|
10
|
+
const longHelp = `This is a toggle component with a long help message.
|
|
9
11
|
You can notice how the text wraps when the width of the container
|
|
10
12
|
is not enough to fix the entire text.`;
|
|
11
13
|
|
|
@@ -15,7 +17,8 @@ const generateProps = ({
|
|
|
15
17
|
isLoading = defaultValue('isLoading'),
|
|
16
18
|
label = 'Dark mode',
|
|
17
19
|
labelId = 'dark-mode-toggle',
|
|
18
|
-
|
|
20
|
+
description = '',
|
|
21
|
+
help = 'Toggle dark mode for the website.',
|
|
19
22
|
labelPosition = defaultValue('labelPosition'),
|
|
20
23
|
} = {}) => ({
|
|
21
24
|
value,
|
|
@@ -23,6 +26,7 @@ const generateProps = ({
|
|
|
23
26
|
isLoading,
|
|
24
27
|
label,
|
|
25
28
|
labelId,
|
|
29
|
+
description,
|
|
26
30
|
help,
|
|
27
31
|
labelPosition,
|
|
28
32
|
});
|
|
@@ -35,6 +39,7 @@ const Template = (args, { argTypes }) => ({
|
|
|
35
39
|
<gl-toggle
|
|
36
40
|
v-model="value"
|
|
37
41
|
:disabled="disabled"
|
|
42
|
+
:description="description"
|
|
38
43
|
:help="help"
|
|
39
44
|
:label-id="labelId"
|
|
40
45
|
:is-loading="isLoading"
|
|
@@ -47,11 +52,21 @@ const Template = (args, { argTypes }) => ({
|
|
|
47
52
|
export const Default = Template.bind({});
|
|
48
53
|
Default.args = generateProps();
|
|
49
54
|
|
|
55
|
+
export const WithDescription = Template.bind({});
|
|
56
|
+
WithDescription.args = generateProps({
|
|
57
|
+
description: withDescription,
|
|
58
|
+
});
|
|
59
|
+
|
|
50
60
|
export const WithLongHelp = Template.bind({});
|
|
51
61
|
WithLongHelp.args = generateProps({
|
|
52
62
|
help: longHelp,
|
|
53
63
|
});
|
|
54
64
|
|
|
65
|
+
export const LabelPositionLeft = Template.bind({});
|
|
66
|
+
LabelPositionLeft.args = generateProps({
|
|
67
|
+
labelPosition: 'left',
|
|
68
|
+
});
|
|
69
|
+
|
|
55
70
|
export default {
|
|
56
71
|
title: 'base/toggle',
|
|
57
72
|
component: GlToggle,
|
|
@@ -71,6 +86,9 @@ export default {
|
|
|
71
86
|
label: {
|
|
72
87
|
control: 'text',
|
|
73
88
|
},
|
|
89
|
+
description: {
|
|
90
|
+
control: 'text',
|
|
91
|
+
},
|
|
74
92
|
help: {
|
|
75
93
|
control: 'text',
|
|
76
94
|
},
|
|
@@ -55,6 +55,14 @@ export default {
|
|
|
55
55
|
type: String,
|
|
56
56
|
required: true,
|
|
57
57
|
},
|
|
58
|
+
/**
|
|
59
|
+
* The toggle's description.
|
|
60
|
+
*/
|
|
61
|
+
description: {
|
|
62
|
+
type: String,
|
|
63
|
+
required: false,
|
|
64
|
+
default: undefined,
|
|
65
|
+
},
|
|
58
66
|
/**
|
|
59
67
|
* A help text to be shown below the toggle.
|
|
60
68
|
*/
|
|
@@ -81,10 +89,20 @@ export default {
|
|
|
81
89
|
};
|
|
82
90
|
},
|
|
83
91
|
computed: {
|
|
92
|
+
shouldRenderDescription() {
|
|
93
|
+
// eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots
|
|
94
|
+
return Boolean(this.$scopedSlots.description || this.description) && this.isVerticalLayout;
|
|
95
|
+
},
|
|
84
96
|
shouldRenderHelp() {
|
|
85
97
|
// eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots
|
|
86
98
|
return Boolean(this.$slots.help || this.help) && this.isVerticalLayout;
|
|
87
99
|
},
|
|
100
|
+
toggleClasses() {
|
|
101
|
+
return [
|
|
102
|
+
{ 'gl-sr-only': this.labelPosition === 'hidden' },
|
|
103
|
+
this.shouldRenderDescription ? 'gl-mb-2' : 'gl-mb-3',
|
|
104
|
+
];
|
|
105
|
+
},
|
|
88
106
|
icon() {
|
|
89
107
|
return this.value ? 'mobile-issue-close' : 'close';
|
|
90
108
|
},
|
|
@@ -132,13 +150,21 @@ export default {
|
|
|
132
150
|
>
|
|
133
151
|
<span
|
|
134
152
|
:id="labelId"
|
|
135
|
-
:class="
|
|
153
|
+
:class="toggleClasses"
|
|
136
154
|
class="gl-toggle-label gl-flex-shrink-0"
|
|
137
155
|
data-testid="toggle-label"
|
|
138
156
|
>
|
|
139
157
|
<!-- @slot The toggle's label. -->
|
|
140
158
|
<slot name="label">{{ label }}</slot>
|
|
141
159
|
</span>
|
|
160
|
+
<span
|
|
161
|
+
v-if="shouldRenderDescription"
|
|
162
|
+
class="gl-description-label gl-mb-3"
|
|
163
|
+
data-testid="toggle-description"
|
|
164
|
+
>
|
|
165
|
+
<!-- @slot A description text to be shown below the label. -->
|
|
166
|
+
<slot name="description">{{ description }}</slot>
|
|
167
|
+
</span>
|
|
142
168
|
<input v-if="name" :name="name" :value="value" type="hidden" />
|
|
143
169
|
<button
|
|
144
170
|
role="switch"
|