@gitlab/ui 43.12.0 → 43.15.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 +22 -0
- package/dist/components/base/banner/banner.js +1 -1
- package/dist/components/base/new_dropdowns/listbox/listbox.js +34 -19
- package/dist/components/base/new_dropdowns/listbox/listbox_group.js +54 -0
- package/dist/components/base/new_dropdowns/listbox/mock_data.js +61 -0
- package/dist/components/base/new_dropdowns/listbox/utils.js +34 -0
- package/dist/utility_classes.css +1 -1
- package/dist/utility_classes.css.map +1 -1
- package/package.json +7 -7
- package/src/components/base/banner/banner.spec.js +1 -1
- package/src/components/base/banner/banner.vue +1 -1
- package/src/components/base/new_dropdowns/listbox/listbox.md +56 -13
- package/src/components/base/new_dropdowns/listbox/listbox.spec.js +60 -33
- package/src/components/base/new_dropdowns/listbox/listbox.stories.js +86 -92
- package/src/components/base/new_dropdowns/listbox/listbox.vue +63 -20
- package/src/components/base/new_dropdowns/listbox/listbox_group.spec.js +47 -0
- package/src/components/base/new_dropdowns/listbox/listbox_group.vue +24 -0
- package/src/components/base/new_dropdowns/listbox/mock_data.js +68 -0
- package/src/components/base/new_dropdowns/listbox/utils.js +21 -0
- package/src/components/base/new_dropdowns/listbox/utils.spec.js +56 -0
- package/src/components/base/toggle/toggle.md +0 -2
- package/src/scss/utilities.scss +12 -0
- package/src/scss/utility-mixins/flex.scss +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gitlab/ui",
|
|
3
|
-
"version": "43.
|
|
3
|
+
"version": "43.15.0",
|
|
4
4
|
"description": "GitLab UI Components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"@babel/preset-env": "^7.18.10",
|
|
82
82
|
"@gitlab/eslint-plugin": "17.0.0",
|
|
83
83
|
"@gitlab/stylelint-config": "4.1.0",
|
|
84
|
-
"@gitlab/svgs": "3.
|
|
84
|
+
"@gitlab/svgs": "3.3.0",
|
|
85
85
|
"@rollup/plugin-commonjs": "^11.1.0",
|
|
86
86
|
"@rollup/plugin-node-resolve": "^7.1.3",
|
|
87
87
|
"@rollup/plugin-replace": "^2.3.2",
|
|
@@ -102,9 +102,9 @@
|
|
|
102
102
|
"babel-plugin-require-context-hook": "^1.0.0",
|
|
103
103
|
"babel-preset-vue": "^2.0.2",
|
|
104
104
|
"bootstrap": "4.5.3",
|
|
105
|
-
"cypress": "^10.
|
|
105
|
+
"cypress": "^10.7.0",
|
|
106
106
|
"emoji-regex": "^10.0.0",
|
|
107
|
-
"eslint": "8.
|
|
107
|
+
"eslint": "8.23.0",
|
|
108
108
|
"eslint-import-resolver-jest": "3.0.2",
|
|
109
109
|
"eslint-plugin-cypress": "2.12.1",
|
|
110
110
|
"eslint-plugin-storybook": "0.6.4",
|
|
@@ -112,9 +112,9 @@
|
|
|
112
112
|
"glob": "^7.2.0",
|
|
113
113
|
"identity-obj-proxy": "^3.0.0",
|
|
114
114
|
"inquirer-select-directory": "^1.2.0",
|
|
115
|
-
"jest": "^29.0.
|
|
116
|
-
"jest-circus": "29.0.
|
|
117
|
-
"jest-environment-jsdom": "29.0.
|
|
115
|
+
"jest": "^29.0.2",
|
|
116
|
+
"jest-circus": "29.0.2",
|
|
117
|
+
"jest-environment-jsdom": "29.0.2",
|
|
118
118
|
"jest-serializer-vue": "^2.0.2",
|
|
119
119
|
"markdownlint-cli": "^0.29.0",
|
|
120
120
|
"mockdate": "^2.0.5",
|
|
@@ -25,7 +25,7 @@ describe('banner component', () => {
|
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
it('should render the correct title', () => {
|
|
28
|
-
expect(wrapper.find('
|
|
28
|
+
expect(wrapper.find('h2').text()).toEqual(propsData.title);
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
it('should render the button', () => {
|
|
@@ -98,7 +98,7 @@ export default {
|
|
|
98
98
|
<img :src="svgPath" alt="" role="presentation" />
|
|
99
99
|
</div>
|
|
100
100
|
<div class="gl-banner-content">
|
|
101
|
-
<
|
|
101
|
+
<h2 class="gl-banner-title">{{ title }}</h2>
|
|
102
102
|
<!-- @slot The banner content to display -->
|
|
103
103
|
<slot></slot>
|
|
104
104
|
<gl-button
|
|
@@ -51,21 +51,64 @@ On selection the listbox will emit the `select` event with the selected values.
|
|
|
51
51
|
|
|
52
52
|
### Setting listbox options
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
Use the `items` prop to provide options to the listbox. Each item can be
|
|
55
|
+
either an option or a group. Below are the expected shapes of these
|
|
56
|
+
objects:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
type Option = {
|
|
60
|
+
value: string
|
|
61
|
+
text?: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
type Group = {
|
|
65
|
+
text: string
|
|
66
|
+
options: Array<Option>
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
type ItemsProp = Array<Option> | Array<Group>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
#### Options
|
|
73
|
+
|
|
74
|
+
The `value` property of options must be unique across all options
|
|
75
|
+
provided to the listbox, as it's used as a primary key.
|
|
76
|
+
|
|
77
|
+
The optional `text` property is used to render the default listbox item
|
|
78
|
+
template. If you want to render a custom template for items, use the
|
|
79
|
+
`list-item` scoped slot:
|
|
58
80
|
|
|
59
81
|
```html
|
|
60
82
|
<gl-listbox :items="items">
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
83
|
+
<template #list-item="{ item }">
|
|
84
|
+
<span class="gl-display-flex gl-align-items-center">
|
|
85
|
+
<gl-avatar :size="32" class-="gl-mr-3"/>
|
|
86
|
+
<span class="gl-display-flex gl-flex-direction-column">
|
|
87
|
+
<span class="gl-font-weight-bold gl-white-space-nowrap">{{ item.text }}</span>
|
|
88
|
+
<span class="gl-text-gray-400"> {{ item.secondaryText }}</span>
|
|
89
|
+
</span>
|
|
90
|
+
</span>
|
|
91
|
+
</template>
|
|
92
|
+
</gl-listbox>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### Groups
|
|
96
|
+
|
|
97
|
+
Options can be contained within groups. A group has a required `text`
|
|
98
|
+
property, which must be unique across all groups within the listbox, as
|
|
99
|
+
it's used as a primary key. It also has a required property `items` that
|
|
100
|
+
must be an array of options.
|
|
101
|
+
|
|
102
|
+
Groups can be at most one level deep: a group can only contain options.
|
|
103
|
+
Options and groups _cannot_ be siblings. Either all items are options,
|
|
104
|
+
or they are all groups.
|
|
105
|
+
|
|
106
|
+
To render custom group labels, use the `group-label` scoped slot:
|
|
107
|
+
|
|
108
|
+
```html
|
|
109
|
+
<gl-listbox :items="groups">
|
|
110
|
+
<template #group-label="{ group }">
|
|
111
|
+
{{ group.text }} <gl-badge size="sm">{{ group.options.length }}</gl-badge>
|
|
112
|
+
</template>
|
|
70
113
|
</gl-listbox>
|
|
71
114
|
```
|
|
@@ -11,21 +11,8 @@ import {
|
|
|
11
11
|
} from '../constants';
|
|
12
12
|
import GlListbox, { ITEM_SELECTOR } from './listbox.vue';
|
|
13
13
|
import GlListboxItem from './listbox_item.vue';
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
{
|
|
17
|
-
value: 'eng',
|
|
18
|
-
text: 'Engineering',
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
value: 'sales',
|
|
22
|
-
text: 'Sales',
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
value: 'marketing',
|
|
26
|
-
text: 'Marketing',
|
|
27
|
-
},
|
|
28
|
-
];
|
|
14
|
+
import GlListboxGroup from './listbox_group.vue';
|
|
15
|
+
import { mockOptions, mockGroups } from './mock_data';
|
|
29
16
|
|
|
30
17
|
describe('GlListbox', () => {
|
|
31
18
|
let wrapper;
|
|
@@ -40,19 +27,20 @@ describe('GlListbox', () => {
|
|
|
40
27
|
|
|
41
28
|
const findBaseDropdown = () => wrapper.findComponent(GlBaseDropdown);
|
|
42
29
|
const findListContainer = () => wrapper.find('[role="listbox"]');
|
|
43
|
-
const findListboxItems = () =>
|
|
30
|
+
const findListboxItems = (root = wrapper) => root.findAllComponents(GlListboxItem);
|
|
31
|
+
const findListboxGroups = () => wrapper.findAllComponents(GlListboxGroup);
|
|
44
32
|
const findListItem = (index) => findListboxItems().at(index).find(ITEM_SELECTOR);
|
|
45
33
|
|
|
46
34
|
describe('toggle text', () => {
|
|
47
35
|
describe.each`
|
|
48
|
-
toggleText | multiple | selected
|
|
49
|
-
${'Toggle caption'} | ${true} | ${[
|
|
50
|
-
${''} | ${true} | ${[
|
|
51
|
-
${''} | ${false} | ${
|
|
52
|
-
${''} | ${false} | ${''}
|
|
36
|
+
toggleText | multiple | selected | expectedToggleText
|
|
37
|
+
${'Toggle caption'} | ${true} | ${[mockOptions[0].value]} | ${'Toggle caption'}
|
|
38
|
+
${''} | ${true} | ${[mockOptions[0]].value} | ${''}
|
|
39
|
+
${''} | ${false} | ${mockOptions[0].value} | ${mockOptions[0].text}
|
|
40
|
+
${''} | ${false} | ${''} | ${''}
|
|
53
41
|
`('when listbox', ({ toggleText, multiple, selected, expectedToggleText }) => {
|
|
54
42
|
beforeEach(() => {
|
|
55
|
-
buildWrapper({ items:
|
|
43
|
+
buildWrapper({ items: mockOptions, toggleText, multiple, selected });
|
|
56
44
|
});
|
|
57
45
|
|
|
58
46
|
it(`is ${multiple ? 'multi' : 'single'}-select, toggleText is ${
|
|
@@ -77,8 +65,8 @@ describe('GlListbox', () => {
|
|
|
77
65
|
beforeEach(() => {
|
|
78
66
|
buildWrapper({
|
|
79
67
|
multiple: true,
|
|
80
|
-
selected: [
|
|
81
|
-
items:
|
|
68
|
+
selected: [mockOptions[1].value, mockOptions[2].value],
|
|
69
|
+
items: mockOptions,
|
|
82
70
|
});
|
|
83
71
|
});
|
|
84
72
|
|
|
@@ -90,28 +78,29 @@ describe('GlListbox', () => {
|
|
|
90
78
|
it('should deselect previously selected', async () => {
|
|
91
79
|
findListboxItems().at(1).vm.$emit('select', false);
|
|
92
80
|
await nextTick();
|
|
93
|
-
expect(wrapper.emitted('select')[0][0]).toEqual([
|
|
81
|
+
expect(wrapper.emitted('select')[0][0]).toEqual([mockOptions[2].value]);
|
|
94
82
|
});
|
|
95
83
|
|
|
96
84
|
it('should add to selection', async () => {
|
|
97
85
|
findListboxItems().at(0).vm.$emit('select', true);
|
|
98
86
|
await nextTick();
|
|
99
87
|
expect(wrapper.emitted('select')[0][0]).toEqual(
|
|
100
|
-
|
|
88
|
+
// The first three items should now be selected.
|
|
89
|
+
expect.arrayContaining(mockOptions.slice(0, 3).map(({ value }) => value))
|
|
101
90
|
);
|
|
102
91
|
});
|
|
103
92
|
});
|
|
104
93
|
|
|
105
94
|
describe('single-select', () => {
|
|
106
95
|
beforeEach(() => {
|
|
107
|
-
buildWrapper({ selected:
|
|
96
|
+
buildWrapper({ selected: mockOptions[1].value, items: mockOptions });
|
|
108
97
|
});
|
|
109
98
|
|
|
110
99
|
it('should throw an error when array of selections is provided', () => {
|
|
111
100
|
expect(() => {
|
|
112
101
|
buildWrapper({
|
|
113
|
-
selected: [
|
|
114
|
-
items:
|
|
102
|
+
selected: [mockOptions[1].value, mockOptions[2].value],
|
|
103
|
+
items: mockOptions,
|
|
115
104
|
});
|
|
116
105
|
}).toThrowError('To allow multi-selection, please, set "multiple" property to "true"');
|
|
117
106
|
expect(wrapper).toHaveLoggedVueErrors();
|
|
@@ -124,7 +113,25 @@ describe('GlListbox', () => {
|
|
|
124
113
|
it('should deselect previously selected and select a new item', async () => {
|
|
125
114
|
findListboxItems().at(2).vm.$emit('select', true);
|
|
126
115
|
await nextTick();
|
|
127
|
-
expect(wrapper.emitted('select')[0][0]).toEqual(
|
|
116
|
+
expect(wrapper.emitted('select')[0][0]).toEqual(mockOptions[2].value);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe('with groups', () => {
|
|
121
|
+
const selected = mockGroups[1].options[1].value;
|
|
122
|
+
|
|
123
|
+
beforeEach(() => {
|
|
124
|
+
buildWrapper({ selected, items: mockGroups });
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should render item as selected when `selected` provided ', () => {
|
|
128
|
+
expect(findListboxItems().at(3).props('isSelected')).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should deselect previously selected and select a new item', async () => {
|
|
132
|
+
findListboxItems().at(0).vm.$emit('select', true);
|
|
133
|
+
await nextTick();
|
|
134
|
+
expect(wrapper.emitted('select')[0][0]).toEqual(mockGroups[0].options[0].value);
|
|
128
135
|
});
|
|
129
136
|
});
|
|
130
137
|
});
|
|
@@ -133,8 +140,8 @@ describe('GlListbox', () => {
|
|
|
133
140
|
beforeEach(async () => {
|
|
134
141
|
buildWrapper({
|
|
135
142
|
multiple: true,
|
|
136
|
-
items:
|
|
137
|
-
selected: [
|
|
143
|
+
items: mockOptions,
|
|
144
|
+
selected: [mockOptions[2].value, mockOptions[1].value],
|
|
138
145
|
});
|
|
139
146
|
findBaseDropdown().vm.$emit(GL_DROPDOWN_SHOWN);
|
|
140
147
|
await nextTick();
|
|
@@ -166,7 +173,8 @@ describe('GlListbox', () => {
|
|
|
166
173
|
let thirdItem;
|
|
167
174
|
|
|
168
175
|
beforeEach(() => {
|
|
169
|
-
|
|
176
|
+
// These tests are more easily written with a small list of items.
|
|
177
|
+
buildWrapper({ items: mockOptions.slice(0, 3) });
|
|
170
178
|
findBaseDropdown().vm.$emit(GL_DROPDOWN_SHOWN);
|
|
171
179
|
firstItem = findListItem(0);
|
|
172
180
|
secondItem = findListItem(1);
|
|
@@ -233,4 +241,23 @@ describe('GlListbox', () => {
|
|
|
233
241
|
expect(wrapper.text()).toContain(footerContent);
|
|
234
242
|
});
|
|
235
243
|
});
|
|
244
|
+
|
|
245
|
+
describe('with groups', () => {
|
|
246
|
+
it('renders groups of items', () => {
|
|
247
|
+
buildWrapper({ items: mockGroups });
|
|
248
|
+
|
|
249
|
+
const groups = findListboxGroups();
|
|
250
|
+
|
|
251
|
+
expect(groups.length).toBe(mockGroups.length);
|
|
252
|
+
|
|
253
|
+
const expectedNameProps = mockGroups.map((group) => group.text);
|
|
254
|
+
const actualNameProps = groups.wrappers.map((group) => group.props('name'));
|
|
255
|
+
|
|
256
|
+
expect(actualNameProps).toEqual(expectedNameProps);
|
|
257
|
+
|
|
258
|
+
mockGroups.forEach((group, i) => {
|
|
259
|
+
expect(findListboxItems(groups.at(i))).toHaveLength(group.options.length);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
});
|
|
236
263
|
});
|
|
@@ -9,65 +9,17 @@ import {
|
|
|
9
9
|
GlSearchBoxByType,
|
|
10
10
|
GlButtonGroup,
|
|
11
11
|
GlButton,
|
|
12
|
+
GlBadge,
|
|
12
13
|
GlAvatar,
|
|
13
14
|
} from '../../../../index';
|
|
14
15
|
import { makeContainer } from '../../../../utils/story_decorators/container';
|
|
15
16
|
import readme from './listbox.md';
|
|
17
|
+
import { mockOptions, mockGroups } from './mock_data';
|
|
16
18
|
|
|
17
19
|
const defaultValue = (prop) => GlListbox.props[prop].default;
|
|
18
20
|
|
|
19
|
-
const defaultItems = [
|
|
20
|
-
{
|
|
21
|
-
value: 'prod',
|
|
22
|
-
text: 'Product',
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
value: 'ppl',
|
|
26
|
-
text: 'People',
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
value: 'fin',
|
|
30
|
-
text: 'Finance',
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
value: 'leg',
|
|
34
|
-
text: 'Legal',
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
value: 'eng',
|
|
38
|
-
text: 'Engineering',
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
value: 'sales',
|
|
42
|
-
text: 'Sales',
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
value: 'marketing',
|
|
46
|
-
text: 'Marketing',
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
value: 'acc',
|
|
50
|
-
text: 'Accounting',
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
value: 'hr',
|
|
54
|
-
text: 'Human Resource Management',
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
value: 'rnd',
|
|
58
|
-
text: 'Research and Development',
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
value: 'cust',
|
|
62
|
-
text: 'Customer Service',
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
value: 'sup',
|
|
66
|
-
text: 'Support',
|
|
67
|
-
},
|
|
68
|
-
];
|
|
69
21
|
const generateProps = ({
|
|
70
|
-
items =
|
|
22
|
+
items = mockOptions,
|
|
71
23
|
category = defaultValue('category'),
|
|
72
24
|
variant = defaultValue('variant'),
|
|
73
25
|
size = defaultValue('size'),
|
|
@@ -137,7 +89,7 @@ export const Default = (args, { argTypes }) => ({
|
|
|
137
89
|
},
|
|
138
90
|
data() {
|
|
139
91
|
return {
|
|
140
|
-
selected:
|
|
92
|
+
selected: mockOptions[1].value,
|
|
141
93
|
};
|
|
142
94
|
},
|
|
143
95
|
mounted() {
|
|
@@ -170,21 +122,23 @@ export const HeaderAndFooter = (args, { argTypes }) => ({
|
|
|
170
122
|
},
|
|
171
123
|
methods: {
|
|
172
124
|
selectItem(index) {
|
|
173
|
-
this.selected.push(
|
|
125
|
+
this.selected.push(mockOptions[index].value);
|
|
174
126
|
},
|
|
175
127
|
},
|
|
176
|
-
template: template(
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
128
|
+
template: template(`
|
|
129
|
+
<template #header>
|
|
130
|
+
<gl-search-box-by-type/>
|
|
131
|
+
</template>
|
|
132
|
+
<template #footer>
|
|
133
|
+
<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">
|
|
134
|
+
<gl-button-group :vertical="false">
|
|
135
|
+
<gl-button @click="selectItem(0)">1st</gl-button>
|
|
136
|
+
<gl-button @click="selectItem(1)">2nd</gl-button>
|
|
137
|
+
<gl-button @click="selectItem(2)">3rd</gl-button>
|
|
138
|
+
</gl-button-group>
|
|
139
|
+
</div>
|
|
140
|
+
</template>
|
|
141
|
+
`),
|
|
188
142
|
});
|
|
189
143
|
HeaderAndFooter.args = generateProps({
|
|
190
144
|
toggleText: 'Header and Footer',
|
|
@@ -217,33 +171,33 @@ export const CustomListItem = (args, { argTypes }) => ({
|
|
|
217
171
|
},
|
|
218
172
|
},
|
|
219
173
|
template: `
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
174
|
+
<gl-listbox
|
|
175
|
+
v-model="selected"
|
|
176
|
+
:items="items"
|
|
177
|
+
:category="category"
|
|
178
|
+
:variant="variant"
|
|
179
|
+
:size="size"
|
|
180
|
+
:disabled="disabled"
|
|
181
|
+
:loading="loading"
|
|
182
|
+
:no-caret="noCaret"
|
|
183
|
+
:right="right"
|
|
184
|
+
:toggle-text="headerText"
|
|
185
|
+
:text-sr-only="textSrOnly"
|
|
186
|
+
:icon="icon"
|
|
187
|
+
:multiple="multiple"
|
|
188
|
+
:is-check-centered="isCheckCentered"
|
|
189
|
+
:aria-labelledby="ariaLabelledby"
|
|
190
|
+
>
|
|
191
|
+
<template #list-item="{ item }">
|
|
192
|
+
<span class="gl-display-flex gl-align-items-center">
|
|
193
|
+
<gl-avatar :size="32" class-="gl-mr-3"/>
|
|
194
|
+
<span class="gl-display-flex gl-flex-direction-column">
|
|
195
|
+
<span class="gl-font-weight-bold gl-white-space-nowrap">{{ item.text }}</span>
|
|
196
|
+
<span class="gl-text-gray-400"> {{ item.secondaryText }}</span>
|
|
197
|
+
</span>
|
|
198
|
+
</span>
|
|
199
|
+
</template>
|
|
200
|
+
</gl-listbox>
|
|
247
201
|
`,
|
|
248
202
|
});
|
|
249
203
|
|
|
@@ -258,6 +212,46 @@ CustomListItem.args = generateProps({
|
|
|
258
212
|
});
|
|
259
213
|
CustomListItem.decorators = [makeContainer({ height: '200px' })];
|
|
260
214
|
|
|
215
|
+
const makeGroupedExample = (changes) => {
|
|
216
|
+
const story = (args, { argTypes }) => ({
|
|
217
|
+
props: Object.keys(argTypes),
|
|
218
|
+
components: {
|
|
219
|
+
GlBadge,
|
|
220
|
+
GlListbox,
|
|
221
|
+
},
|
|
222
|
+
data() {
|
|
223
|
+
return {
|
|
224
|
+
selected: 'v1.0',
|
|
225
|
+
};
|
|
226
|
+
},
|
|
227
|
+
mounted() {
|
|
228
|
+
if (this.startOpened) {
|
|
229
|
+
openListbox(this);
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
template: template(''),
|
|
233
|
+
...changes,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
story.args = generateProps({ items: mockGroups });
|
|
237
|
+
story.decorators = [makeContainer({ height: '280px' })];
|
|
238
|
+
|
|
239
|
+
return story;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
export const Groups = makeGroupedExample();
|
|
243
|
+
|
|
244
|
+
export const CustomGroupsAndItems = makeGroupedExample({
|
|
245
|
+
template: template(`
|
|
246
|
+
<template #group-label="{ group }">
|
|
247
|
+
{{ group.text }} <gl-badge size="sm">{{ group.options.length }}</gl-badge>
|
|
248
|
+
</template>
|
|
249
|
+
<template #list-item="{ item }">
|
|
250
|
+
{{ item.text }} <gl-badge v-if="item.value === 'main'" size="sm">default</gl-badge>
|
|
251
|
+
</template>
|
|
252
|
+
`),
|
|
253
|
+
});
|
|
254
|
+
|
|
261
255
|
export default {
|
|
262
256
|
title: 'base/new-dropdowns/listbox',
|
|
263
257
|
component: GlListbox,
|