@gitlab/ui 64.9.0 → 64.10.1
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/keyset_pagination/keyset_pagination.js +11 -1
- package/dist/components/base/new_dropdowns/disclosure/constants.js +5 -1
- package/dist/components/base/new_dropdowns/disclosure/disclosure_dropdown_group.js +20 -5
- package/dist/components/base/new_dropdowns/listbox/listbox.js +23 -2
- package/package.json +1 -1
- package/src/components/base/keyset_pagination/keyset_pagination.vue +42 -30
- package/src/components/base/new_dropdowns/disclosure/constants.js +4 -0
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown.md +14 -4
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown.stories.js +19 -7
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown_group.spec.js +15 -16
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown_group.vue +23 -4
- package/src/components/base/new_dropdowns/listbox/listbox.spec.js +63 -11
- package/src/components/base/new_dropdowns/listbox/listbox.vue +25 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
## [64.10.1](https://gitlab.com/gitlab-org/gitlab-ui/compare/v64.10.0...v64.10.1) (2023-06-14)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **GlCollapsibleListbox:** listbox stays open ([a83355d](https://gitlab.com/gitlab-org/gitlab-ui/commit/a83355d527c483ca6bd6587da64f565ef118b8da))
|
|
7
|
+
|
|
8
|
+
# [64.10.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v64.9.0...v64.10.0) (2023-06-14)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **DisclosureDropdown:** borderPosition property ([ea7822e](https://gitlab.com/gitlab-org/gitlab-ui/commit/ea7822e20992c6a0fc074f8ab3766b3145c09c00))
|
|
14
|
+
* **GlKeysetPagination:** add navigation landmark ([3ffd2f7](https://gitlab.com/gitlab-org/gitlab-ui/commit/3ffd2f79b3029504a6e1b831df23904ca8473d06))
|
|
15
|
+
|
|
1
16
|
# [64.9.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v64.8.0...v64.9.0) (2023-06-14)
|
|
2
17
|
|
|
3
18
|
|
|
@@ -10,6 +10,7 @@ var script = {
|
|
|
10
10
|
GlButton,
|
|
11
11
|
GlIcon
|
|
12
12
|
},
|
|
13
|
+
inheritAttrs: false,
|
|
13
14
|
props: {
|
|
14
15
|
// The following 4 properties match the default names of the
|
|
15
16
|
// [PageInfo](https://docs.gitlab.com/ee/api/graphql/reference/index.html#pageinfo)
|
|
@@ -68,6 +69,15 @@ var script = {
|
|
|
68
69
|
required: false,
|
|
69
70
|
default: null
|
|
70
71
|
},
|
|
72
|
+
/**
|
|
73
|
+
* The aria-label that needs to be set for the
|
|
74
|
+
* pagination landmark region.
|
|
75
|
+
*/
|
|
76
|
+
navigationLabel: {
|
|
77
|
+
type: String,
|
|
78
|
+
required: false,
|
|
79
|
+
default: 'Pagination'
|
|
80
|
+
},
|
|
71
81
|
/**
|
|
72
82
|
* The text that will be rendered inside the "Next" button.
|
|
73
83
|
* It\'s important to provide this parameter since the default text is not translatable.
|
|
@@ -101,7 +111,7 @@ var script = {
|
|
|
101
111
|
const __vue_script__ = script;
|
|
102
112
|
|
|
103
113
|
/* template */
|
|
104
|
-
var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('gl-button-group',_vm._g(_vm._b({staticClass:"gl-keyset-pagination"},'gl-button-group',_vm.$attrs,false),_vm.$listeners),[_c('gl-button',{attrs:{"href":_vm.prevButtonLink,"disabled":_vm.disabled || !_vm.hasPreviousPage,"data-testid":"prevButton"},on:{"click":function($event){return _vm.$emit('prev', _vm.startCursor)}}},[_vm._t("previous-button-content",function(){return [_c('div',{staticClass:"gl-display-flex gl-align-center"},[_c('gl-icon',{attrs:{"name":"chevron-left"}}),_vm._v("\n
|
|
114
|
+
var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('nav',{staticClass:"gl-pagination",attrs:{"aria-label":_vm.navigationLabel}},[_c('gl-button-group',_vm._g(_vm._b({staticClass:"gl-keyset-pagination"},'gl-button-group',_vm.$attrs,false),_vm.$listeners),[_c('gl-button',{attrs:{"href":_vm.prevButtonLink,"disabled":_vm.disabled || !_vm.hasPreviousPage,"data-testid":"prevButton"},on:{"click":function($event){return _vm.$emit('prev', _vm.startCursor)}}},[_vm._t("previous-button-content",function(){return [_c('div',{staticClass:"gl-display-flex gl-align-center"},[_c('gl-icon',{attrs:{"name":"chevron-left"}}),_vm._v("\n "+_vm._s(_vm.prevText)+"\n ")],1)]})],2),_vm._v(" "),_c('gl-button',{attrs:{"href":_vm.nextButtonLink,"disabled":_vm.disabled || !_vm.hasNextPage,"data-testid":"nextButton"},on:{"click":function($event){return _vm.$emit('next', _vm.endCursor)}}},[_vm._t("next-button-content",function(){return [_c('div',{staticClass:"gl-display-flex gl-align-center"},[_vm._v("\n "+_vm._s(_vm.nextText)+"\n "),_c('gl-icon',{attrs:{"name":"chevron-right"}})],1)]})],2)],1)],1)};
|
|
105
115
|
var __vue_staticRenderFns__ = [];
|
|
106
116
|
|
|
107
117
|
/* style */
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
const DISCLOSURE_DROPDOWN_ITEM_NAME = 'GlDisclosureDropdownItem';
|
|
2
2
|
const DISCLOSURE_DROPDOWN_GROUP_NAME = 'GlDisclosureDropdownGroup';
|
|
3
|
+
const DISCLOSURE_DROPDOWN_GROUP_BORDER_POSITIONS = {
|
|
4
|
+
top: 'top',
|
|
5
|
+
bottom: 'bottom'
|
|
6
|
+
};
|
|
3
7
|
|
|
4
|
-
export { DISCLOSURE_DROPDOWN_GROUP_NAME, DISCLOSURE_DROPDOWN_ITEM_NAME };
|
|
8
|
+
export { DISCLOSURE_DROPDOWN_GROUP_BORDER_POSITIONS, DISCLOSURE_DROPDOWN_GROUP_NAME, DISCLOSURE_DROPDOWN_ITEM_NAME };
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import uniqueId from 'lodash/uniqueId';
|
|
2
2
|
import GlDisclosureDropdownItem from './disclosure_dropdown_item';
|
|
3
3
|
import { isGroup } from './utils';
|
|
4
|
-
import { DISCLOSURE_DROPDOWN_GROUP_NAME } from './constants';
|
|
4
|
+
import { DISCLOSURE_DROPDOWN_GROUP_BORDER_POSITIONS, DISCLOSURE_DROPDOWN_GROUP_NAME } from './constants';
|
|
5
5
|
import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const BORDER_CLASSES = {
|
|
8
|
+
[DISCLOSURE_DROPDOWN_GROUP_BORDER_POSITIONS.top]: 'gl-border-t gl-pt-2 gl-mt-2',
|
|
9
|
+
[DISCLOSURE_DROPDOWN_GROUP_BORDER_POSITIONS.bottom]: 'gl-border-b gl-pb-2 gl-mb-2'
|
|
10
|
+
};
|
|
8
11
|
var script = {
|
|
9
12
|
name: DISCLOSURE_DROPDOWN_GROUP_NAME,
|
|
10
13
|
components: {
|
|
@@ -22,17 +25,29 @@ var script = {
|
|
|
22
25
|
},
|
|
23
26
|
/**
|
|
24
27
|
* If 'true', will set top border for the group
|
|
25
|
-
* to separate from other groups
|
|
28
|
+
* to separate from other groups. You can control
|
|
29
|
+
* the border position using the `borderPosition`
|
|
30
|
+
* property.
|
|
26
31
|
*/
|
|
27
32
|
bordered: {
|
|
28
33
|
type: Boolean,
|
|
29
34
|
required: false,
|
|
30
35
|
default: false
|
|
36
|
+
},
|
|
37
|
+
/**
|
|
38
|
+
* Controls the position of the group's border. Valid
|
|
39
|
+
* values are 'top' and 'bottom'.
|
|
40
|
+
*/
|
|
41
|
+
borderPosition: {
|
|
42
|
+
type: String,
|
|
43
|
+
required: false,
|
|
44
|
+
default: DISCLOSURE_DROPDOWN_GROUP_BORDER_POSITIONS.top,
|
|
45
|
+
validator: value => Object.keys(DISCLOSURE_DROPDOWN_GROUP_BORDER_POSITIONS).includes(value)
|
|
31
46
|
}
|
|
32
47
|
},
|
|
33
48
|
computed: {
|
|
34
49
|
borderClass() {
|
|
35
|
-
return this.bordered ?
|
|
50
|
+
return this.bordered ? BORDER_CLASSES[this.borderPosition] : null;
|
|
36
51
|
},
|
|
37
52
|
showHeader() {
|
|
38
53
|
var _this$group;
|
|
@@ -92,4 +107,4 @@ var __vue_staticRenderFns__ = [];
|
|
|
92
107
|
);
|
|
93
108
|
|
|
94
109
|
export default __vue_component__;
|
|
95
|
-
export {
|
|
110
|
+
export { BORDER_CLASSES };
|
|
@@ -329,7 +329,7 @@ var script = {
|
|
|
329
329
|
},
|
|
330
330
|
computed: {
|
|
331
331
|
listboxTag() {
|
|
332
|
-
if (this.
|
|
332
|
+
if (!this.hasItems || isOption(this.items[0])) return 'ul';
|
|
333
333
|
return 'div';
|
|
334
334
|
},
|
|
335
335
|
listboxClasses() {
|
|
@@ -345,6 +345,9 @@ var script = {
|
|
|
345
345
|
flattenedOptions() {
|
|
346
346
|
return flattenedOptions(this.items);
|
|
347
347
|
},
|
|
348
|
+
hasItems() {
|
|
349
|
+
return this.items.length > 0;
|
|
350
|
+
},
|
|
348
351
|
listboxToggleText() {
|
|
349
352
|
if (!this.toggleText) {
|
|
350
353
|
if (!this.multiple && this.selectedValues.length) {
|
|
@@ -384,6 +387,17 @@ var script = {
|
|
|
384
387
|
if (!this.resetButtonLabel) {
|
|
385
388
|
return false;
|
|
386
389
|
}
|
|
390
|
+
if (!this.multiple) {
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* if dropdown has no items
|
|
396
|
+
* reset all should be hidden
|
|
397
|
+
*/
|
|
398
|
+
if (!this.hasItems) {
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
387
401
|
if (this.multiple) {
|
|
388
402
|
return this.selected.length > 0;
|
|
389
403
|
}
|
|
@@ -396,6 +410,14 @@ var script = {
|
|
|
396
410
|
if (!this.multiple) {
|
|
397
411
|
return false;
|
|
398
412
|
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* if dropdown has no items
|
|
416
|
+
* select all should be hidden
|
|
417
|
+
*/
|
|
418
|
+
if (!this.hasItems) {
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
399
421
|
return this.selected.length === 0;
|
|
400
422
|
},
|
|
401
423
|
showIntersectionObserver() {
|
|
@@ -614,7 +636,6 @@ var script = {
|
|
|
614
636
|
* @event reset
|
|
615
637
|
*/
|
|
616
638
|
this.$emit('reset');
|
|
617
|
-
this.closeAndFocus();
|
|
618
639
|
},
|
|
619
640
|
onSelectAllButtonClicked() {
|
|
620
641
|
/**
|
package/package.json
CHANGED
|
@@ -10,6 +10,7 @@ export default {
|
|
|
10
10
|
GlButton,
|
|
11
11
|
GlIcon,
|
|
12
12
|
},
|
|
13
|
+
inheritAttrs: false,
|
|
13
14
|
props: {
|
|
14
15
|
// The following 4 properties match the default names of the
|
|
15
16
|
// [PageInfo](https://docs.gitlab.com/ee/api/graphql/reference/index.html#pageinfo)
|
|
@@ -68,6 +69,15 @@ export default {
|
|
|
68
69
|
required: false,
|
|
69
70
|
default: null,
|
|
70
71
|
},
|
|
72
|
+
/**
|
|
73
|
+
* The aria-label that needs to be set for the
|
|
74
|
+
* pagination landmark region.
|
|
75
|
+
*/
|
|
76
|
+
navigationLabel: {
|
|
77
|
+
type: String,
|
|
78
|
+
required: false,
|
|
79
|
+
default: 'Pagination',
|
|
80
|
+
},
|
|
71
81
|
/**
|
|
72
82
|
* The text that will be rendered inside the "Next" button.
|
|
73
83
|
* It\'s important to provide this parameter since the default text is not translatable.
|
|
@@ -99,34 +109,36 @@ export default {
|
|
|
99
109
|
</script>
|
|
100
110
|
|
|
101
111
|
<template>
|
|
102
|
-
<
|
|
103
|
-
<gl-button
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
<
|
|
112
|
-
<
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
<
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
112
|
+
<nav class="gl-pagination" :aria-label="navigationLabel">
|
|
113
|
+
<gl-button-group class="gl-keyset-pagination" v-bind="$attrs" v-on="$listeners">
|
|
114
|
+
<gl-button
|
|
115
|
+
:href="prevButtonLink"
|
|
116
|
+
:disabled="disabled || !hasPreviousPage"
|
|
117
|
+
data-testid="prevButton"
|
|
118
|
+
@click="$emit('prev', startCursor)"
|
|
119
|
+
>
|
|
120
|
+
<!-- @slot Used to customize the appearance of the "Prev" button -->
|
|
121
|
+
<slot name="previous-button-content">
|
|
122
|
+
<div class="gl-display-flex gl-align-center">
|
|
123
|
+
<gl-icon name="chevron-left" />
|
|
124
|
+
{{ prevText }}
|
|
125
|
+
</div>
|
|
126
|
+
</slot>
|
|
127
|
+
</gl-button>
|
|
128
|
+
<gl-button
|
|
129
|
+
:href="nextButtonLink"
|
|
130
|
+
:disabled="disabled || !hasNextPage"
|
|
131
|
+
data-testid="nextButton"
|
|
132
|
+
@click="$emit('next', endCursor)"
|
|
133
|
+
>
|
|
134
|
+
<!-- @slot Used to customize the appearance of the "Next" button -->
|
|
135
|
+
<slot name="next-button-content">
|
|
136
|
+
<div class="gl-display-flex gl-align-center">
|
|
137
|
+
{{ nextText }}
|
|
138
|
+
<gl-icon name="chevron-right" />
|
|
139
|
+
</div>
|
|
140
|
+
</slot>
|
|
141
|
+
</gl-button>
|
|
142
|
+
</gl-button-group>
|
|
143
|
+
</nav>
|
|
132
144
|
</template>
|
|
@@ -91,7 +91,7 @@ template. If you want to render a custom template for items, use the
|
|
|
91
91
|
<template #list-item="{ item }">
|
|
92
92
|
<span class="gl-display-flex gl-align-items-center gl-justify-content-space-between">
|
|
93
93
|
{{item.text}}
|
|
94
|
-
<gl-icon v-if="item.icon" :name="item.icon"/>
|
|
94
|
+
<gl-icon v-if="item.icon" :name="item.icon" />
|
|
95
95
|
</span>
|
|
96
96
|
</template>
|
|
97
97
|
</gl-disclosure-dropdown>
|
|
@@ -117,6 +117,16 @@ To render custom group labels, use the `group-label` scoped slot:
|
|
|
117
117
|
</gl-disclosure-dropdown>
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
+
To draw a horizontal line that separates two groups, set the `bordered` property.
|
|
121
|
+
By default, the border appears above the group. You can change the border position
|
|
122
|
+
using the `border-position` property:
|
|
123
|
+
|
|
124
|
+
```html
|
|
125
|
+
<gl-disclosure-dropdown>
|
|
126
|
+
<gl-disclosure-dropdown-group bordered border-position="bottom" :group="group" />
|
|
127
|
+
</gl-disclosure-dropdown>
|
|
128
|
+
```
|
|
129
|
+
|
|
120
130
|
#### Miscellaneous content
|
|
121
131
|
|
|
122
132
|
Besides default components, disclosure dropdown can render miscellaneous content inside it.
|
|
@@ -125,7 +135,7 @@ In this case the user is responsible for handling all events and navigation insi
|
|
|
125
135
|
#### Dealing with long option texts
|
|
126
136
|
|
|
127
137
|
- Some options might have long non-wrapping text that would overflow the dropdown maximum width. In
|
|
128
|
-
such cases, it's recommended to override the `#list-item` slot and to truncate the option text using
|
|
129
|
-
`GlTruncate`.
|
|
138
|
+
such cases, it's recommended to override the `#list-item` slot and to truncate the option text using
|
|
139
|
+
`GlTruncate`.
|
|
130
140
|
- If the toggle text reflects the selected option text, it might be necessary to truncate
|
|
131
|
-
it too by overriding the `#toggle` slot.
|
|
141
|
+
it too by overriding the `#toggle` slot.
|
|
@@ -14,6 +14,7 @@ import { makeContainer } from '../../../../utils/story_decorators/container';
|
|
|
14
14
|
import GlDisclosureDropdown from './disclosure_dropdown.vue';
|
|
15
15
|
import GlDisclosureDropdownItem from './disclosure_dropdown_item.vue';
|
|
16
16
|
import GlDisclosureDropdownGroup from './disclosure_dropdown_group.vue';
|
|
17
|
+
import { DISCLOSURE_DROPDOWN_GROUP_BORDER_POSITIONS } from './constants';
|
|
17
18
|
import readme from './disclosure_dropdown.md';
|
|
18
19
|
import {
|
|
19
20
|
mockItems,
|
|
@@ -229,7 +230,7 @@ export const CustomGroupsItemsAndToggle = makeGroupedExample({
|
|
|
229
230
|
</template>
|
|
230
231
|
</gl-disclosure-dropdown-item>
|
|
231
232
|
</gl-disclosure-dropdown-group>
|
|
232
|
-
<gl-disclosure-dropdown-group bordered :group="$options.groups[0]" @action="closeDropdown">
|
|
233
|
+
<gl-disclosure-dropdown-group :bordered="bordered" :border-position="borderPosition" :group="$options.groups[0]" @action="closeDropdown">
|
|
233
234
|
<template #list-item="{ item }">
|
|
234
235
|
<span class="gl-display-flex gl-align-items-center gl-justify-content-space-between">
|
|
235
236
|
{{item.text}}
|
|
@@ -237,7 +238,7 @@ export const CustomGroupsItemsAndToggle = makeGroupedExample({
|
|
|
237
238
|
</span>
|
|
238
239
|
</template>
|
|
239
240
|
</gl-disclosure-dropdown-group>
|
|
240
|
-
<gl-disclosure-dropdown-group bordered>
|
|
241
|
+
<gl-disclosure-dropdown-group :bordered="bordered" :border-position="borderPosition">
|
|
241
242
|
<template #group-label>
|
|
242
243
|
<span class="gl-font-sm">Navigation redesign</span>
|
|
243
244
|
<gl-badge size="sm" variant="info">Beta</gl-badge>
|
|
@@ -253,7 +254,7 @@ export const CustomGroupsItemsAndToggle = makeGroupedExample({
|
|
|
253
254
|
<template #list-item>Provide feedback</template>
|
|
254
255
|
</gl-disclosure-dropdown-item>
|
|
255
256
|
</gl-disclosure-dropdown-group>
|
|
256
|
-
<gl-disclosure-dropdown-group bordered :group="$options.groups[1]" @action="closeDropdown"/>
|
|
257
|
+
<gl-disclosure-dropdown-group :bordered="bordered" :border-position="borderPosition" :group="$options.groups[1]" @action="closeDropdown"/>
|
|
257
258
|
`,
|
|
258
259
|
{
|
|
259
260
|
after: `
|
|
@@ -292,7 +293,18 @@ CustomGroupsItemsAndToggle.args = {
|
|
|
292
293
|
toggleText: 'User profile menu',
|
|
293
294
|
textSrOnly: true,
|
|
294
295
|
autoClose: false,
|
|
296
|
+
bordered: true,
|
|
297
|
+
borderPosition: DISCLOSURE_DROPDOWN_GROUP_BORDER_POSITIONS.top,
|
|
295
298
|
};
|
|
299
|
+
CustomGroupsItemsAndToggle.argTypes = {
|
|
300
|
+
borderPosition: {
|
|
301
|
+
options: DISCLOSURE_DROPDOWN_GROUP_BORDER_POSITIONS,
|
|
302
|
+
control: {
|
|
303
|
+
type: 'select',
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
|
|
296
308
|
CustomGroupsItemsAndToggle.decorators = [makeContainer({ height: '400px' })];
|
|
297
309
|
|
|
298
310
|
export const MiscellaneousContent = (args, { argTypes }) => ({
|
|
@@ -334,26 +346,26 @@ export default {
|
|
|
334
346
|
category: {
|
|
335
347
|
control: {
|
|
336
348
|
type: 'select',
|
|
337
|
-
options: buttonCategoryOptions,
|
|
338
349
|
},
|
|
350
|
+
options: buttonCategoryOptions,
|
|
339
351
|
},
|
|
340
352
|
variant: {
|
|
341
353
|
control: {
|
|
342
354
|
type: 'select',
|
|
343
|
-
options: buttonVariantOptions,
|
|
344
355
|
},
|
|
356
|
+
options: buttonVariantOptions,
|
|
345
357
|
},
|
|
346
358
|
size: {
|
|
347
359
|
control: {
|
|
348
360
|
type: 'select',
|
|
349
|
-
options: Object.keys(buttonSizeOptions),
|
|
350
361
|
},
|
|
362
|
+
options: Object.keys(buttonSizeOptions),
|
|
351
363
|
},
|
|
352
364
|
placement: {
|
|
353
365
|
control: {
|
|
354
366
|
type: 'select',
|
|
355
|
-
options: Object.keys(dropdownPlacements),
|
|
356
367
|
},
|
|
368
|
+
options: Object.keys(dropdownPlacements),
|
|
357
369
|
},
|
|
358
370
|
},
|
|
359
371
|
args: {
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { shallowMount } from '@vue/test-utils';
|
|
2
|
-
import GlDisclosureDropdownGroup, {
|
|
3
|
-
GROUP_TOP_BORDER_CLASSES,
|
|
4
|
-
} from './disclosure_dropdown_group.vue';
|
|
2
|
+
import GlDisclosureDropdownGroup, { BORDER_CLASSES } from './disclosure_dropdown_group.vue';
|
|
5
3
|
import GlDisclosureDropdownItem from './disclosure_dropdown_item.vue';
|
|
6
4
|
import { mockGroups, mockProfileGroups } from './mock_data';
|
|
5
|
+
import { DISCLOSURE_DROPDOWN_GROUP_BORDER_POSITIONS as borderPositions } from './constants';
|
|
7
6
|
|
|
8
7
|
describe('GlDisclosureDropdownGroup', () => {
|
|
9
8
|
let wrapper;
|
|
@@ -69,17 +68,17 @@ describe('GlDisclosureDropdownGroup', () => {
|
|
|
69
68
|
});
|
|
70
69
|
});
|
|
71
70
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
buildWrapper({ propsData: { bordered
|
|
82
|
-
expect(wrapper.classes()).toEqual(
|
|
83
|
-
}
|
|
84
|
-
|
|
71
|
+
it.each`
|
|
72
|
+
bordered | borderPosition | classes
|
|
73
|
+
${true} | ${borderPositions.top} | ${BORDER_CLASSES[borderPositions.top].split(' ')}
|
|
74
|
+
${true} | ${borderPositions.bottom} | ${BORDER_CLASSES[borderPositions.bottom].split(' ')}
|
|
75
|
+
${false} | ${borderPositions.top} | ${[]}
|
|
76
|
+
${false} | ${borderPositions.bottom} | ${[]}
|
|
77
|
+
`(
|
|
78
|
+
'adds border classes `$classes` when bordered=$bordered and borderPosition=$borderPosition',
|
|
79
|
+
({ bordered, borderPosition, classes }) => {
|
|
80
|
+
buildWrapper({ propsData: { bordered, borderPosition } });
|
|
81
|
+
expect(wrapper.classes()).toEqual(classes);
|
|
82
|
+
}
|
|
83
|
+
);
|
|
85
84
|
});
|
|
@@ -2,9 +2,15 @@
|
|
|
2
2
|
import uniqueId from 'lodash/uniqueId';
|
|
3
3
|
import GlDisclosureDropdownItem from './disclosure_dropdown_item.vue';
|
|
4
4
|
import { isGroup } from './utils';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
DISCLOSURE_DROPDOWN_GROUP_NAME,
|
|
7
|
+
DISCLOSURE_DROPDOWN_GROUP_BORDER_POSITIONS as borderPositions,
|
|
8
|
+
} from './constants';
|
|
6
9
|
|
|
7
|
-
export const
|
|
10
|
+
export const BORDER_CLASSES = {
|
|
11
|
+
[borderPositions.top]: 'gl-border-t gl-pt-2 gl-mt-2',
|
|
12
|
+
[borderPositions.bottom]: 'gl-border-b gl-pb-2 gl-mb-2',
|
|
13
|
+
};
|
|
8
14
|
|
|
9
15
|
export default {
|
|
10
16
|
name: DISCLOSURE_DROPDOWN_GROUP_NAME,
|
|
@@ -23,17 +29,30 @@ export default {
|
|
|
23
29
|
},
|
|
24
30
|
/**
|
|
25
31
|
* If 'true', will set top border for the group
|
|
26
|
-
* to separate from other groups
|
|
32
|
+
* to separate from other groups. You can control
|
|
33
|
+
* the border position using the `borderPosition`
|
|
34
|
+
* property.
|
|
27
35
|
*/
|
|
28
36
|
bordered: {
|
|
29
37
|
type: Boolean,
|
|
30
38
|
required: false,
|
|
31
39
|
default: false,
|
|
32
40
|
},
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Controls the position of the group's border. Valid
|
|
44
|
+
* values are 'top' and 'bottom'.
|
|
45
|
+
*/
|
|
46
|
+
borderPosition: {
|
|
47
|
+
type: String,
|
|
48
|
+
required: false,
|
|
49
|
+
default: borderPositions.top,
|
|
50
|
+
validator: (value) => Object.keys(borderPositions).includes(value),
|
|
51
|
+
},
|
|
33
52
|
},
|
|
34
53
|
computed: {
|
|
35
54
|
borderClass() {
|
|
36
|
-
return this.bordered ?
|
|
55
|
+
return this.bordered ? BORDER_CLASSES[this.borderPosition] : null;
|
|
37
56
|
},
|
|
38
57
|
showHeader() {
|
|
39
58
|
return this.$scopedSlots['group-label'] || this.group?.name;
|
|
@@ -179,6 +179,15 @@ describe('GlCollapsibleListbox', () => {
|
|
|
179
179
|
await nextTick();
|
|
180
180
|
expect(wrapper.emitted('select')[0][0]).toEqual(mockOptions[2].value);
|
|
181
181
|
});
|
|
182
|
+
|
|
183
|
+
it('close dropdown for single selection', () => {
|
|
184
|
+
jest.spyOn(wrapper.vm, 'closeAndFocus');
|
|
185
|
+
expect(wrapper.vm.closeAndFocus).not.toHaveBeenCalled();
|
|
186
|
+
|
|
187
|
+
findListboxItems().at(2).vm.$emit('select', true);
|
|
188
|
+
|
|
189
|
+
expect(wrapper.vm.closeAndFocus).toHaveBeenCalled();
|
|
190
|
+
});
|
|
182
191
|
});
|
|
183
192
|
|
|
184
193
|
describe('with groups', () => {
|
|
@@ -475,17 +484,47 @@ describe('GlCollapsibleListbox', () => {
|
|
|
475
484
|
expect(wrapper).toHaveLoggedVueErrors();
|
|
476
485
|
});
|
|
477
486
|
|
|
487
|
+
it.each`
|
|
488
|
+
multiple
|
|
489
|
+
${true}
|
|
490
|
+
${false}
|
|
491
|
+
`(
|
|
492
|
+
'shows the reset button if the label is provided and the selection is not empty and mode if multiple mode is $multiple',
|
|
493
|
+
({ multiple }) => {
|
|
494
|
+
buildWrapper({
|
|
495
|
+
headerText: 'Select assignee',
|
|
496
|
+
resetButtonLabel: 'Unassign',
|
|
497
|
+
selected: mockOptions[1].value,
|
|
498
|
+
items: mockOptions,
|
|
499
|
+
multiple,
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
expect(findResetButton().exists()).toBe(multiple);
|
|
503
|
+
}
|
|
504
|
+
);
|
|
505
|
+
|
|
478
506
|
it('shows the reset button if the label is provided and the selection is not empty', () => {
|
|
479
507
|
buildWrapper({
|
|
480
508
|
headerText: 'Select assignee',
|
|
481
509
|
resetButtonLabel: 'Unassign',
|
|
482
510
|
selected: mockOptions[1].value,
|
|
483
511
|
items: mockOptions,
|
|
512
|
+
multiple: true,
|
|
484
513
|
});
|
|
485
|
-
const button = findResetButton();
|
|
486
514
|
|
|
487
|
-
expect(
|
|
488
|
-
|
|
515
|
+
expect(findResetButton().text()).toBe('Unassign');
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it('hides reset button if dropdown has no items', () => {
|
|
519
|
+
buildWrapper({
|
|
520
|
+
headerText: 'Select assignee',
|
|
521
|
+
resetButtonLabel: 'Unassign',
|
|
522
|
+
selected: mockOptions[1].value,
|
|
523
|
+
items: [],
|
|
524
|
+
multiple: true,
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
expect(findResetButton().exists()).toBe(false);
|
|
489
528
|
});
|
|
490
529
|
|
|
491
530
|
it.each`
|
|
@@ -503,12 +542,13 @@ describe('GlCollapsibleListbox', () => {
|
|
|
503
542
|
expect(findResetButton().exists()).toBe(false);
|
|
504
543
|
});
|
|
505
544
|
|
|
506
|
-
it('on click, emits the reset event and
|
|
545
|
+
it('on click, emits the reset event does not and call closeAndFocus() for multiple mode', () => {
|
|
507
546
|
buildWrapper({
|
|
508
547
|
headerText: 'Select assignee',
|
|
509
548
|
resetButtonLabel: 'Unassign',
|
|
510
549
|
selected: mockOptions[1].value,
|
|
511
550
|
items: mockOptions,
|
|
551
|
+
multiple: true,
|
|
512
552
|
});
|
|
513
553
|
jest.spyOn(wrapper.vm, 'closeAndFocus');
|
|
514
554
|
|
|
@@ -518,7 +558,7 @@ describe('GlCollapsibleListbox', () => {
|
|
|
518
558
|
findResetButton().trigger('click');
|
|
519
559
|
|
|
520
560
|
expect(wrapper.emitted('reset')).toHaveLength(1);
|
|
521
|
-
expect(wrapper.vm.closeAndFocus).toHaveBeenCalled();
|
|
561
|
+
expect(wrapper.vm.closeAndFocus).not.toHaveBeenCalled();
|
|
522
562
|
});
|
|
523
563
|
});
|
|
524
564
|
|
|
@@ -533,12 +573,12 @@ describe('GlCollapsibleListbox', () => {
|
|
|
533
573
|
});
|
|
534
574
|
|
|
535
575
|
it.each`
|
|
536
|
-
multiple |
|
|
537
|
-
${false} | ${false}
|
|
538
|
-
${true} | ${true}
|
|
576
|
+
multiple | resetVisible | selectAllVisible
|
|
577
|
+
${false} | ${false} | ${false}
|
|
578
|
+
${true} | ${false} | ${true}
|
|
539
579
|
`(
|
|
540
580
|
'shows the select all button if the label is provided and the selection is empty and multiple option is $multiple',
|
|
541
|
-
({ multiple,
|
|
581
|
+
({ multiple, resetVisible, selectAllVisible }) => {
|
|
542
582
|
buildWrapper({
|
|
543
583
|
headerText: 'Select assignee',
|
|
544
584
|
resetButtonLabel: 'Unassign',
|
|
@@ -548,11 +588,23 @@ describe('GlCollapsibleListbox', () => {
|
|
|
548
588
|
multiple,
|
|
549
589
|
});
|
|
550
590
|
|
|
551
|
-
expect(findResetButton().exists()).toBe(
|
|
552
|
-
expect(findSelectAllButton().exists()).toBe(
|
|
591
|
+
expect(findResetButton().exists()).toBe(resetVisible);
|
|
592
|
+
expect(findSelectAllButton().exists()).toBe(selectAllVisible);
|
|
553
593
|
}
|
|
554
594
|
);
|
|
555
595
|
|
|
596
|
+
it('hides select all button if dropdown has no items', () => {
|
|
597
|
+
buildWrapper({
|
|
598
|
+
headerText: 'Select assignee',
|
|
599
|
+
resetButtonLabel: 'Unassign',
|
|
600
|
+
showSelectAllButtonLabel: 'Select All',
|
|
601
|
+
items: [],
|
|
602
|
+
multiple: true,
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
expect(findSelectAllButton().exists()).toBe(false);
|
|
606
|
+
});
|
|
607
|
+
|
|
556
608
|
it('has the label text "Select All" if the label is provided and the selection is empty', () => {
|
|
557
609
|
buildWrapper({
|
|
558
610
|
headerText: 'Select assignee',
|
|
@@ -345,7 +345,7 @@ export default {
|
|
|
345
345
|
},
|
|
346
346
|
computed: {
|
|
347
347
|
listboxTag() {
|
|
348
|
-
if (this.
|
|
348
|
+
if (!this.hasItems || isOption(this.items[0])) return 'ul';
|
|
349
349
|
return 'div';
|
|
350
350
|
},
|
|
351
351
|
listboxClasses() {
|
|
@@ -361,6 +361,9 @@ export default {
|
|
|
361
361
|
flattenedOptions() {
|
|
362
362
|
return flattenedOptions(this.items);
|
|
363
363
|
},
|
|
364
|
+
hasItems() {
|
|
365
|
+
return this.items.length > 0;
|
|
366
|
+
},
|
|
364
367
|
listboxToggleText() {
|
|
365
368
|
if (!this.toggleText) {
|
|
366
369
|
if (!this.multiple && this.selectedValues.length) {
|
|
@@ -394,6 +397,19 @@ export default {
|
|
|
394
397
|
if (!this.resetButtonLabel) {
|
|
395
398
|
return false;
|
|
396
399
|
}
|
|
400
|
+
|
|
401
|
+
if (!this.multiple) {
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* if dropdown has no items
|
|
407
|
+
* reset all should be hidden
|
|
408
|
+
*/
|
|
409
|
+
if (!this.hasItems) {
|
|
410
|
+
return false;
|
|
411
|
+
}
|
|
412
|
+
|
|
397
413
|
if (this.multiple) {
|
|
398
414
|
return this.selected.length > 0;
|
|
399
415
|
}
|
|
@@ -408,6 +424,14 @@ export default {
|
|
|
408
424
|
return false;
|
|
409
425
|
}
|
|
410
426
|
|
|
427
|
+
/**
|
|
428
|
+
* if dropdown has no items
|
|
429
|
+
* select all should be hidden
|
|
430
|
+
*/
|
|
431
|
+
if (!this.hasItems) {
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
|
|
411
435
|
return this.selected.length === 0;
|
|
412
436
|
},
|
|
413
437
|
showIntersectionObserver() {
|
|
@@ -636,7 +660,6 @@ export default {
|
|
|
636
660
|
* @event reset
|
|
637
661
|
*/
|
|
638
662
|
this.$emit('reset');
|
|
639
|
-
this.closeAndFocus();
|
|
640
663
|
},
|
|
641
664
|
onSelectAllButtonClicked() {
|
|
642
665
|
/**
|