@gitlab/ui 47.0.1 → 48.1.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 +20 -0
- package/README.md +6 -6
- package/dist/components/base/new_dropdowns/listbox/listbox.js +14 -1
- package/dist/index.css.map +1 -1
- package/dist/utility_classes.css.map +1 -1
- package/package.json +2 -2
- package/src/components/base/new_dropdowns/listbox/listbox.spec.js +20 -4
- package/src/components/base/new_dropdowns/listbox/listbox.stories.js +18 -30
- package/src/components/base/new_dropdowns/listbox/listbox.vue +26 -4
- package/src/scss/utility-mixins/index.scss +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gitlab/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "48.1.0",
|
|
4
4
|
"description": "GitLab UI Components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
"eslint": "8.25.0",
|
|
108
108
|
"eslint-import-resolver-jest": "3.0.2",
|
|
109
109
|
"eslint-plugin-cypress": "2.12.1",
|
|
110
|
-
"eslint-plugin-storybook": "0.6.
|
|
110
|
+
"eslint-plugin-storybook": "0.6.6",
|
|
111
111
|
"file-loader": "^4.2.0",
|
|
112
112
|
"glob": "^7.2.0",
|
|
113
113
|
"identity-obj-proxy": "^3.0.0",
|
|
@@ -30,6 +30,7 @@ describe('GlListbox', () => {
|
|
|
30
30
|
const findListboxItems = (root = wrapper) => root.findAllComponents(GlListboxItem);
|
|
31
31
|
const findListboxGroups = () => wrapper.findAllComponents(GlListboxGroup);
|
|
32
32
|
const findListItem = (index) => findListboxItems().at(index).find(ITEM_SELECTOR);
|
|
33
|
+
const findHeaderText = () => wrapper.find("[data-testid='listbox-header-text']");
|
|
33
34
|
const findSearchBox = () => wrapper.find("[data-testid='listbox-search-input']");
|
|
34
35
|
const findNoResultsText = () => wrapper.find("[data-testid='listbox-no-results-text']");
|
|
35
36
|
const findLoadingIcon = () => wrapper.find("[data-testid='listbox-search-loader']");
|
|
@@ -263,13 +264,28 @@ describe('GlListbox', () => {
|
|
|
263
264
|
});
|
|
264
265
|
});
|
|
265
266
|
|
|
266
|
-
describe('when the header
|
|
267
|
+
describe('when the header prop is provided', () => {
|
|
267
268
|
const headerContent = 'Header Content';
|
|
268
|
-
const slots = { header: headerContent };
|
|
269
269
|
|
|
270
270
|
it('renders it', () => {
|
|
271
|
-
buildWrapper({
|
|
272
|
-
|
|
271
|
+
buildWrapper({ headerText: headerContent, items: mockOptions });
|
|
272
|
+
|
|
273
|
+
expect(findHeaderText().text()).toContain(headerContent);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it("uses the generated header ID as the list's aria-labelledby attribute", () => {
|
|
277
|
+
buildWrapper({ headerText: headerContent, items: mockOptions });
|
|
278
|
+
|
|
279
|
+
expect(findListContainer().attributes('aria-labelledby')).toBe(
|
|
280
|
+
findHeaderText().attributes('id')
|
|
281
|
+
);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('if a custom list label is passed, it overrides the header ID', () => {
|
|
285
|
+
const listAriaLabelledBy = 'listAriaLabelledBy';
|
|
286
|
+
buildWrapper({ listAriaLabelledBy, headerText: headerContent, items: mockOptions });
|
|
287
|
+
|
|
288
|
+
expect(findListContainer().attributes('aria-labelledby')).toBe(listAriaLabelledBy);
|
|
273
289
|
});
|
|
274
290
|
});
|
|
275
291
|
|
|
@@ -33,6 +33,7 @@ const generateProps = ({
|
|
|
33
33
|
right = defaultValue('right'),
|
|
34
34
|
toggleText,
|
|
35
35
|
textSrOnly = defaultValue('textSrOnly'),
|
|
36
|
+
headerText = defaultValue('headerText'),
|
|
36
37
|
icon = '',
|
|
37
38
|
multiple = defaultValue('multiple'),
|
|
38
39
|
isCheckCentered = defaultValue('isCheckCentered'),
|
|
@@ -53,6 +54,7 @@ const generateProps = ({
|
|
|
53
54
|
right,
|
|
54
55
|
toggleText,
|
|
55
56
|
textSrOnly,
|
|
57
|
+
headerText,
|
|
56
58
|
icon,
|
|
57
59
|
multiple,
|
|
58
60
|
isCheckCentered,
|
|
@@ -76,6 +78,7 @@ const makeBindings = (overrides = {}) =>
|
|
|
76
78
|
':right': 'right',
|
|
77
79
|
':toggle-text': 'toggleText',
|
|
78
80
|
':text-sr-only': 'textSrOnly',
|
|
81
|
+
':header-text': 'headerText',
|
|
79
82
|
':icon': 'icon',
|
|
80
83
|
':multiple': 'multiple',
|
|
81
84
|
':is-check-centered': 'isCheckCentered',
|
|
@@ -151,10 +154,7 @@ export const HeaderAndFooter = (args, { argTypes }) => ({
|
|
|
151
154
|
this.selected.push(mockOptions[index].value);
|
|
152
155
|
},
|
|
153
156
|
},
|
|
154
|
-
template: template(
|
|
155
|
-
`<template #header>
|
|
156
|
-
<p class="gl-font-weight-bold gl-font-sm gl-m-0 gl-text-center gl-py-2 gl-border-1 gl-border-b-solid gl-border-gray-200">Assign to department</p>
|
|
157
|
-
</template>
|
|
157
|
+
template: template(`
|
|
158
158
|
<template #footer>
|
|
159
159
|
<div class="gl-border-t-solid gl-border-t-1 gl-border-t-gray-100 gl-display-flex gl-justify-content-center gl-p-3">
|
|
160
160
|
<gl-button-group :vertical="false">
|
|
@@ -164,11 +164,11 @@ export const HeaderAndFooter = (args, { argTypes }) => ({
|
|
|
164
164
|
</gl-button-group>
|
|
165
165
|
</div>
|
|
166
166
|
</template>
|
|
167
|
-
`
|
|
168
|
-
),
|
|
167
|
+
`),
|
|
169
168
|
});
|
|
170
169
|
HeaderAndFooter.args = generateProps({
|
|
171
170
|
toggleText: 'Header and Footer',
|
|
171
|
+
headerText: 'Assign to department',
|
|
172
172
|
multiple: true,
|
|
173
173
|
});
|
|
174
174
|
HeaderAndFooter.decorators = [makeContainer({ height: '370px' })];
|
|
@@ -315,7 +315,6 @@ export const Searchable = (args, { argTypes }) => ({
|
|
|
315
315
|
filteredItems: mockOptions,
|
|
316
316
|
searchInProgress: false,
|
|
317
317
|
timeoutId: null,
|
|
318
|
-
headerId: 'listbox-header',
|
|
319
318
|
};
|
|
320
319
|
},
|
|
321
320
|
mounted() {
|
|
@@ -358,19 +357,12 @@ export const Searchable = (args, { argTypes }) => ({
|
|
|
358
357
|
},
|
|
359
358
|
},
|
|
360
359
|
template: template(
|
|
361
|
-
`<template #
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
Assign to department</p>
|
|
365
|
-
</template>
|
|
366
|
-
<template #search-summary-sr-only>
|
|
367
|
-
{{ numberOfSearchResults }}
|
|
368
|
-
</template>
|
|
369
|
-
`,
|
|
360
|
+
`<template #search-summary-sr-only>
|
|
361
|
+
{{ numberOfSearchResults }}
|
|
362
|
+
</template>`,
|
|
370
363
|
{
|
|
371
364
|
bindingOverrides: {
|
|
372
365
|
':items': 'filteredItems',
|
|
373
|
-
':list-aria-labelled-by': 'headerId',
|
|
374
366
|
':toggle-text': 'customToggleText',
|
|
375
367
|
':searching': 'searchInProgress',
|
|
376
368
|
'@search': 'filterList',
|
|
@@ -378,7 +370,7 @@ export const Searchable = (args, { argTypes }) => ({
|
|
|
378
370
|
}
|
|
379
371
|
),
|
|
380
372
|
});
|
|
381
|
-
Searchable.args = generateProps({ searchable: true });
|
|
373
|
+
Searchable.args = generateProps({ headerText: 'Assign to department', searchable: true });
|
|
382
374
|
Searchable.decorators = [makeContainer({ height: '370px' })];
|
|
383
375
|
|
|
384
376
|
export const SearchableGroups = (args, { argTypes }) => ({
|
|
@@ -392,7 +384,6 @@ export const SearchableGroups = (args, { argTypes }) => ({
|
|
|
392
384
|
filteredGroupOptions: mockGroups,
|
|
393
385
|
searchInProgress: false,
|
|
394
386
|
timeoutId: null,
|
|
395
|
-
headerId: 'listbox-header',
|
|
396
387
|
};
|
|
397
388
|
},
|
|
398
389
|
mounted() {
|
|
@@ -450,19 +441,12 @@ export const SearchableGroups = (args, { argTypes }) => ({
|
|
|
450
441
|
},
|
|
451
442
|
},
|
|
452
443
|
template: template(
|
|
453
|
-
`<template #
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
Assign to department</p>
|
|
457
|
-
</template>
|
|
458
|
-
<template #search-summary-sr-only>
|
|
459
|
-
{{ numberOfSearchResults }}
|
|
460
|
-
</template>
|
|
461
|
-
`,
|
|
444
|
+
`<template #search-summary-sr-only>
|
|
445
|
+
{{ numberOfSearchResults }}
|
|
446
|
+
</template>`,
|
|
462
447
|
{
|
|
463
448
|
bindingOverrides: {
|
|
464
449
|
':items': 'filteredGroupOptions',
|
|
465
|
-
':list-aria-labelled-by': 'headerId',
|
|
466
450
|
':toggle-text': 'customToggleText',
|
|
467
451
|
':searching': 'searchInProgress',
|
|
468
452
|
'@search': 'filterList',
|
|
@@ -470,5 +454,9 @@ export const SearchableGroups = (args, { argTypes }) => ({
|
|
|
470
454
|
}
|
|
471
455
|
),
|
|
472
456
|
});
|
|
473
|
-
SearchableGroups.args = generateProps({
|
|
457
|
+
SearchableGroups.args = generateProps({
|
|
458
|
+
headerText: 'Select ref',
|
|
459
|
+
searchable: true,
|
|
460
|
+
items: mockGroups,
|
|
461
|
+
});
|
|
474
462
|
SearchableGroups.decorators = [makeContainer({ height: '370px' })];
|
|
@@ -23,10 +23,12 @@ import GlListboxGroup from './listbox_group.vue';
|
|
|
23
23
|
import { isOption, itemsValidator, flattenedOptions } from './utils';
|
|
24
24
|
|
|
25
25
|
export const ITEM_SELECTOR = '[role="option"]';
|
|
26
|
+
const HEADER_ITEMS_BORDER_CLASSES = ['gl-border-b-1', 'gl-border-b-solid', 'gl-border-b-gray-200'];
|
|
26
27
|
const GROUP_TOP_BORDER_CLASSES = ['gl-border-t', 'gl-pt-3', 'gl-mt-3'];
|
|
27
28
|
export const SEARCH_INPUT_SELECTOR = '.gl-search-box-by-type-input';
|
|
28
29
|
|
|
29
30
|
export default {
|
|
31
|
+
HEADER_ITEMS_BORDER_CLASSES,
|
|
30
32
|
events: {
|
|
31
33
|
GL_DROPDOWN_SHOWN,
|
|
32
34
|
GL_DROPDOWN_HIDDEN,
|
|
@@ -84,6 +86,12 @@ export default {
|
|
|
84
86
|
required: false,
|
|
85
87
|
default: false,
|
|
86
88
|
},
|
|
89
|
+
/** The header text */
|
|
90
|
+
headerText: {
|
|
91
|
+
type: String,
|
|
92
|
+
required: false,
|
|
93
|
+
default: '',
|
|
94
|
+
},
|
|
87
95
|
/**
|
|
88
96
|
* Styling option - dropdown's toggle category
|
|
89
97
|
*/
|
|
@@ -255,6 +263,9 @@ export default {
|
|
|
255
263
|
this.searchable && !this.showNoResultsText && this.$scopedSlots['search-summary-sr-only']
|
|
256
264
|
);
|
|
257
265
|
},
|
|
266
|
+
headerId() {
|
|
267
|
+
return this.headerText && uniqueId('listbox-header-');
|
|
268
|
+
},
|
|
258
269
|
},
|
|
259
270
|
watch: {
|
|
260
271
|
selected: {
|
|
@@ -431,10 +442,21 @@ export default {
|
|
|
431
442
|
@[$options.events.GL_DROPDOWN_SHOWN]="onShow"
|
|
432
443
|
@[$options.events.GL_DROPDOWN_HIDDEN]="onHide"
|
|
433
444
|
>
|
|
434
|
-
|
|
435
|
-
|
|
445
|
+
<div
|
|
446
|
+
v-if="headerText"
|
|
447
|
+
class="gl-display-flex gl-align-items-center gl-p-3"
|
|
448
|
+
:class="$options.HEADER_ITEMS_BORDER_CLASSES"
|
|
449
|
+
>
|
|
450
|
+
<div
|
|
451
|
+
:id="headerId"
|
|
452
|
+
class="gl-flex-grow-1 gl-font-weight-bold gl-font-sm"
|
|
453
|
+
data-testid="listbox-header-text"
|
|
454
|
+
>
|
|
455
|
+
{{ headerText }}
|
|
456
|
+
</div>
|
|
457
|
+
</div>
|
|
436
458
|
|
|
437
|
-
<div v-if="searchable" class="
|
|
459
|
+
<div v-if="searchable" :class="$options.HEADER_ITEMS_BORDER_CLASSES">
|
|
438
460
|
<gl-search-box-by-type
|
|
439
461
|
ref="searchBox"
|
|
440
462
|
v-model="searchStr"
|
|
@@ -456,7 +478,7 @@ export default {
|
|
|
456
478
|
v-if="showList"
|
|
457
479
|
id="listbox"
|
|
458
480
|
ref="list"
|
|
459
|
-
:aria-labelledby="listAriaLabelledBy || toggleId"
|
|
481
|
+
:aria-labelledby="listAriaLabelledBy || headerId || toggleId"
|
|
460
482
|
role="listbox"
|
|
461
483
|
class="gl-new-dropdown-contents gl-list-style-none gl-pl-0 gl-mb-0"
|
|
462
484
|
tabindex="-1"
|