@gitlab/ui 52.10.0 → 52.12.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 +14 -0
- package/dist/components/base/new_dropdowns/base_dropdown/base_dropdown.js +23 -5
- package/dist/components/base/new_dropdowns/disclosure/disclosure_dropdown.js +291 -0
- package/dist/components/base/new_dropdowns/disclosure/disclosure_dropdown_group.js +90 -0
- package/dist/components/base/new_dropdowns/disclosure/disclosure_dropdown_item.js +107 -0
- package/dist/components/base/new_dropdowns/disclosure/mock_data.js +128 -0
- package/dist/components/base/new_dropdowns/disclosure/utils.js +15 -0
- package/dist/components/base/new_dropdowns/listbox/listbox.js +4 -4
- package/dist/components/base/new_dropdowns/listbox/listbox_group.js +1 -1
- package/dist/components/base/new_dropdowns/listbox/listbox_item.js +1 -1
- package/dist/components/regions/empty_state/empty_state.js +12 -1
- package/dist/index.css +2 -2
- package/dist/index.css.map +1 -1
- package/dist/index.js +3 -0
- package/package.json +3 -3
- package/src/components/base/new_dropdowns/base_dropdown/base_dropdown.spec.js +3 -3
- package/src/components/base/new_dropdowns/base_dropdown/base_dropdown.vue +21 -3
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown.md +114 -0
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown.scss +7 -0
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown.spec.js +210 -0
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown.stories.js +306 -0
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown.vue +342 -0
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown_group.spec.js +82 -0
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown_group.vue +77 -0
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown_item.spec.js +94 -0
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown_item.vue +98 -0
- package/src/components/base/new_dropdowns/disclosure/mock_data.js +156 -0
- package/src/components/base/new_dropdowns/disclosure/utils.js +18 -0
- package/src/components/base/new_dropdowns/disclosure/utils.spec.js +73 -0
- package/src/components/base/new_dropdowns/dropdown.scss +6 -0
- package/src/components/base/new_dropdowns/listbox/listbox.scss +0 -6
- package/src/components/base/new_dropdowns/listbox/listbox.vue +4 -4
- package/src/components/base/new_dropdowns/listbox/listbox_group.vue +1 -1
- package/src/components/base/new_dropdowns/listbox/listbox_item.vue +1 -1
- package/src/components/regions/empty_state/empty_state.spec.js +35 -0
- package/src/components/regions/empty_state/empty_state.stories.js +5 -0
- package/src/components/regions/empty_state/empty_state.vue +15 -1
- package/src/index.js +3 -0
- package/src/scss/components.scss +2 -0
package/dist/index.js
CHANGED
|
@@ -46,6 +46,9 @@ export { default as GlDropdownText } from './components/base/dropdown/dropdown_t
|
|
|
46
46
|
export { default as GlDropdown } from './components/base/dropdown/dropdown';
|
|
47
47
|
export { default as GlCollapsibleListbox, default as GlListbox } from './components/base/new_dropdowns/listbox/listbox';
|
|
48
48
|
export { default as GlListboxItem } from './components/base/new_dropdowns/listbox/listbox_item';
|
|
49
|
+
export { default as GlDisclosureDropdown } from './components/base/new_dropdowns/disclosure/disclosure_dropdown';
|
|
50
|
+
export { default as GlDisclosureDropdownItem } from './components/base/new_dropdowns/disclosure/disclosure_dropdown_item';
|
|
51
|
+
export { default as GlDisclosureDropdownGroup } from './components/base/new_dropdowns/disclosure/disclosure_dropdown_group';
|
|
49
52
|
export { default as GlPath } from './components/base/path/path';
|
|
50
53
|
export { default as GlTable } from './components/base/table/table';
|
|
51
54
|
export { default as GlBreadcrumb } from './components/base/breadcrumb/breadcrumb';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gitlab/ui",
|
|
3
|
-
"version": "52.
|
|
3
|
+
"version": "52.12.0",
|
|
4
4
|
"description": "GitLab UI Components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -85,9 +85,9 @@
|
|
|
85
85
|
"@babel/core": "^7.20.12",
|
|
86
86
|
"@babel/preset-env": "^7.20.2",
|
|
87
87
|
"@gitlab/eslint-plugin": "18.1.0",
|
|
88
|
-
"@gitlab/fonts": "^1.0
|
|
88
|
+
"@gitlab/fonts": "^1.1.0",
|
|
89
89
|
"@gitlab/stylelint-config": "4.1.0",
|
|
90
|
-
"@gitlab/svgs": "3.
|
|
90
|
+
"@gitlab/svgs": "3.16.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",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils';
|
|
2
2
|
import { nextTick } from 'vue';
|
|
3
|
-
import { GL_DROPDOWN_HIDDEN, GL_DROPDOWN_SHOWN, POPPER_CONFIG } from '../constants';
|
|
3
|
+
import { ENTER, GL_DROPDOWN_HIDDEN, GL_DROPDOWN_SHOWN, POPPER_CONFIG } from '../constants';
|
|
4
4
|
import GlBaseDropdown from './base_dropdown.vue';
|
|
5
5
|
|
|
6
6
|
const destroyPopper = jest.fn();
|
|
@@ -197,14 +197,14 @@ describe('base dropdown', () => {
|
|
|
197
197
|
const toggle = findCustomDropdownToggle();
|
|
198
198
|
const menu = findDropdownMenu();
|
|
199
199
|
// open menu clicking toggle btn
|
|
200
|
-
await toggle.trigger('keydown
|
|
200
|
+
await toggle.trigger('keydown', { code: ENTER });
|
|
201
201
|
expect(menu.classes('show')).toBe(true);
|
|
202
202
|
expect(toggle.attributes('aria-expanded')).toBe('true');
|
|
203
203
|
await nextTick();
|
|
204
204
|
expect(wrapper.emitted(GL_DROPDOWN_SHOWN).length).toBe(1);
|
|
205
205
|
|
|
206
206
|
// close menu clicking toggle btn again
|
|
207
|
-
await toggle.trigger('keydown
|
|
207
|
+
await toggle.trigger('keydown', { code: ENTER });
|
|
208
208
|
expect(menu.classes('show')).toBe(false);
|
|
209
209
|
expect(toggle.attributes('aria-expanded')).toBeUndefined();
|
|
210
210
|
expect(wrapper.emitted(GL_DROPDOWN_HIDDEN).length).toBe(1);
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
<script>
|
|
2
|
+
import { uniqueId } from 'lodash';
|
|
2
3
|
import { createPopper } from '@popperjs/core';
|
|
3
4
|
import {
|
|
4
5
|
buttonCategoryOptions,
|
|
5
6
|
buttonSizeOptions,
|
|
6
7
|
dropdownVariantOptions,
|
|
7
8
|
} from '../../../../utils/constants';
|
|
8
|
-
import { POPPER_CONFIG, GL_DROPDOWN_SHOWN, GL_DROPDOWN_HIDDEN } from '../constants';
|
|
9
|
+
import { POPPER_CONFIG, GL_DROPDOWN_SHOWN, GL_DROPDOWN_HIDDEN, ENTER, SPACE } from '../constants';
|
|
9
10
|
|
|
10
11
|
import GlButton from '../../button/button.vue';
|
|
11
12
|
import GlIcon from '../../icon/icon.vue';
|
|
@@ -107,6 +108,7 @@ export default {
|
|
|
107
108
|
data() {
|
|
108
109
|
return {
|
|
109
110
|
visible: false,
|
|
111
|
+
baseDropdownId: uniqueId('base-dropdown-'),
|
|
110
112
|
};
|
|
111
113
|
},
|
|
112
114
|
computed: {
|
|
@@ -145,13 +147,21 @@ export default {
|
|
|
145
147
|
disabled: this.disabled,
|
|
146
148
|
loading: this.loading,
|
|
147
149
|
class: this.toggleButtonClasses,
|
|
150
|
+
listeners: {
|
|
151
|
+
click: () => this.toggle(),
|
|
152
|
+
},
|
|
148
153
|
};
|
|
149
154
|
}
|
|
150
155
|
|
|
151
156
|
return {
|
|
152
157
|
is: 'div',
|
|
158
|
+
role: 'button',
|
|
153
159
|
class: 'gl-dropdown-custom-toggle gl-hover-cursor-pointer',
|
|
154
160
|
tabindex: '0',
|
|
161
|
+
listeners: {
|
|
162
|
+
keydown: (event) => this.onKeydown(event),
|
|
163
|
+
click: () => this.toggle(),
|
|
164
|
+
},
|
|
155
165
|
};
|
|
156
166
|
},
|
|
157
167
|
toggleElement() {
|
|
@@ -216,6 +226,13 @@ export default {
|
|
|
216
226
|
focusToggle() {
|
|
217
227
|
this.toggleElement.focus();
|
|
218
228
|
},
|
|
229
|
+
onKeydown(event) {
|
|
230
|
+
const { code } = event;
|
|
231
|
+
|
|
232
|
+
if (code === ENTER || code === SPACE) {
|
|
233
|
+
this.toggle();
|
|
234
|
+
}
|
|
235
|
+
},
|
|
219
236
|
},
|
|
220
237
|
};
|
|
221
238
|
</script>
|
|
@@ -234,8 +251,8 @@ export default {
|
|
|
234
251
|
:aria-haspopup="ariaHaspopup"
|
|
235
252
|
:aria-expanded="visible"
|
|
236
253
|
:aria-labelledby="toggleLabelledBy"
|
|
237
|
-
|
|
238
|
-
|
|
254
|
+
:aria-controls="baseDropdownId"
|
|
255
|
+
v-on="toggleOptions.listeners"
|
|
239
256
|
>
|
|
240
257
|
<!-- @slot Custom toggle button content -->
|
|
241
258
|
<slot name="toggle">
|
|
@@ -247,6 +264,7 @@ export default {
|
|
|
247
264
|
</component>
|
|
248
265
|
|
|
249
266
|
<div
|
|
267
|
+
:id="baseDropdownId"
|
|
250
268
|
ref="content"
|
|
251
269
|
data-testid="base-dropdown-menu"
|
|
252
270
|
class="dropdown-menu"
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
A disclosure dropdown is a button that toggles a panel containing a list of actions and/or links. Use
|
|
2
|
+
[this decision tree](https://design.gitlab.com/components/dropdown-overview#which-component-should-you-use)
|
|
3
|
+
to make sure this is the right dropdown component for you.
|
|
4
|
+
|
|
5
|
+
### Basic usage
|
|
6
|
+
|
|
7
|
+
```html
|
|
8
|
+
<gl-disclosure-dropdown-dropdown
|
|
9
|
+
toggle-text="Actions"
|
|
10
|
+
:items="items"
|
|
11
|
+
/>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### Icon-only disclosure dropdown
|
|
15
|
+
|
|
16
|
+
Icon-only disclosure dropdowns must have an accessible name.
|
|
17
|
+
You can provide this with the combination of `toggleText` and `textSrOnly` props.
|
|
18
|
+
|
|
19
|
+
Optionally, you can use `no-caret` to remove the caret and `category="tertiary"` to remove the border.
|
|
20
|
+
|
|
21
|
+
```html
|
|
22
|
+
<gl-disclosure-dropdown
|
|
23
|
+
icon="ellipsis_v"
|
|
24
|
+
toggle-text="Actions"
|
|
25
|
+
text-sr-only
|
|
26
|
+
category="tertiary"
|
|
27
|
+
no-caret
|
|
28
|
+
/>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Opening the disclosure dropdown
|
|
32
|
+
|
|
33
|
+
Disclosure dropdown will open on toggle button click (if it was previously closed).
|
|
34
|
+
On open, `GlDisclosureDropdown` will emit the `shown` event.
|
|
35
|
+
|
|
36
|
+
### Closing the disclosure dropdown
|
|
37
|
+
|
|
38
|
+
The disclosure dropdown is closed by any of the following:
|
|
39
|
+
|
|
40
|
+
- pressing <kbd>Esc</kbd>
|
|
41
|
+
- clicking anywhere outside the component
|
|
42
|
+
|
|
43
|
+
After closing, `GlDisclosureDropdown` emits a `hidden` event.
|
|
44
|
+
|
|
45
|
+
### Setting disclosure dropdown items
|
|
46
|
+
|
|
47
|
+
Use the `items` prop to provide actions/links to the disclosure dropdown. Each item can be
|
|
48
|
+
either an item or a group. Below are the expected shapes of these objects:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
type Item = {
|
|
52
|
+
text: string
|
|
53
|
+
href?: string,
|
|
54
|
+
action?: function,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
type Group = {
|
|
58
|
+
name?: string
|
|
59
|
+
items: Array<Item>
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type ItemsProp = Array<Item> | Array<Group>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### Actions/links
|
|
66
|
+
|
|
67
|
+
The `text` property is used to render the default disclosure dropdown item
|
|
68
|
+
template. If you want to render a custom template for items, use the
|
|
69
|
+
`list-item` scoped slot:
|
|
70
|
+
|
|
71
|
+
```html
|
|
72
|
+
<gl-disclosure-dropdown :items="items">
|
|
73
|
+
<template #list-item="{ item }">
|
|
74
|
+
<a class="gl-hover-text-decoration-none gl-text-gray-900"
|
|
75
|
+
tabindex="-1"
|
|
76
|
+
:href="item.href"
|
|
77
|
+
v-bind="item.extraAttrs">
|
|
78
|
+
{{ item.text }}
|
|
79
|
+
<gl-badge pill variant="info" v-if="item.count">{{ item.count }}</gl-badge>
|
|
80
|
+
</a>
|
|
81
|
+
</template>
|
|
82
|
+
</gl-disclosure-dropdown>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Note:** when providing custom content to the item, user should
|
|
86
|
+
define the correct tab order inside the disclosure dropdown by setting
|
|
87
|
+
the `tabindex` attribute on the elements.
|
|
88
|
+
The `li` item will get the focus so you might want elements inside it
|
|
89
|
+
not to be focused - this can be done by setting `tabindex="-1"` on them.
|
|
90
|
+
|
|
91
|
+
#### Groups
|
|
92
|
+
|
|
93
|
+
Actions/links can be contained within groups. A group can have a `name`
|
|
94
|
+
property, which will be used as the group header if present.
|
|
95
|
+
It also has a required property `items` that must be an array of links/actions.
|
|
96
|
+
|
|
97
|
+
Groups can be at most one level deep: a group can only contain actions/links.
|
|
98
|
+
Items and groups _cannot_ be siblings. Either all items are actions/links,
|
|
99
|
+
or they are all groups.
|
|
100
|
+
|
|
101
|
+
To render custom group labels, use the `group-label` scoped slot:
|
|
102
|
+
|
|
103
|
+
```html
|
|
104
|
+
<gl-disclosure-dropdown :items="groups">
|
|
105
|
+
<template #group-label="{ group }">
|
|
106
|
+
{{ group.name }} <gl-badge size="sm">{{ group.items.length }}</gl-badge>
|
|
107
|
+
</template>
|
|
108
|
+
</gl-disclosure-dropdown>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### Miscellaneous content
|
|
112
|
+
|
|
113
|
+
Besides default components, disclosure dropdown can render miscellaneous content inside it.
|
|
114
|
+
In this case the user is responsible for handling all events and navigation inside the disclosure.
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import GlBaseDropdown from '../base_dropdown/base_dropdown.vue';
|
|
3
|
+
import {
|
|
4
|
+
GL_DROPDOWN_SHOWN,
|
|
5
|
+
GL_DROPDOWN_HIDDEN,
|
|
6
|
+
ARROW_DOWN,
|
|
7
|
+
ARROW_UP,
|
|
8
|
+
HOME,
|
|
9
|
+
END,
|
|
10
|
+
} from '../constants';
|
|
11
|
+
import GlDisclosureDropdown from './disclosure_dropdown.vue';
|
|
12
|
+
import GlDisclosureDropdownItem from './disclosure_dropdown_item.vue';
|
|
13
|
+
import GlDisclosureDropdownGroup from './disclosure_dropdown_group.vue';
|
|
14
|
+
import { mockItems, mockGroups } from './mock_data';
|
|
15
|
+
|
|
16
|
+
const ITEM_SELECTOR = '[data-testid="disclosure-dropdown-item"]';
|
|
17
|
+
|
|
18
|
+
describe('GlDisclosureDropdown', () => {
|
|
19
|
+
let wrapper;
|
|
20
|
+
|
|
21
|
+
const buildWrapper = (propsData, slots = {}) => {
|
|
22
|
+
wrapper = mount(GlDisclosureDropdown, {
|
|
23
|
+
propsData,
|
|
24
|
+
slots,
|
|
25
|
+
attachTo: document.body,
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const findBaseDropdown = () => wrapper.findComponent(GlBaseDropdown);
|
|
30
|
+
const findDisclosureContent = () => wrapper.find('[data-testid="disclosure-content"]');
|
|
31
|
+
const findDisclosureItems = (root = wrapper) => root.findAllComponents(GlDisclosureDropdownItem);
|
|
32
|
+
const findDisclosureGroups = () => wrapper.findAllComponents(GlDisclosureDropdownGroup);
|
|
33
|
+
const findListItem = (index) => findDisclosureItems().at(index).findComponent(ITEM_SELECTOR);
|
|
34
|
+
|
|
35
|
+
describe('toggle text', () => {
|
|
36
|
+
it('should pass toggle text to base dropdown', () => {
|
|
37
|
+
const toggleText = 'Merge requests';
|
|
38
|
+
buildWrapper({ items: mockItems, toggleText });
|
|
39
|
+
expect(findBaseDropdown().props('toggleText')).toBe(toggleText);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('ARIA attributes', () => {
|
|
44
|
+
it('should provide `toggleId` to the base dropdown and reference it in`aria-labelledby` attribute of the list container`', async () => {
|
|
45
|
+
await buildWrapper({ items: mockItems });
|
|
46
|
+
expect(findBaseDropdown().props('toggleId')).toBe(
|
|
47
|
+
findDisclosureContent().attributes('aria-labelledby')
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should reference `listAriaLabelledby`', async () => {
|
|
52
|
+
const listAriaLabelledBy = 'first-label-id second-label-id';
|
|
53
|
+
await buildWrapper({ items: mockItems, listAriaLabelledBy });
|
|
54
|
+
expect(findDisclosureContent().attributes('aria-labelledby')).toBe(listAriaLabelledBy);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('onShow', () => {
|
|
59
|
+
const showDropdown = () => {
|
|
60
|
+
buildWrapper({
|
|
61
|
+
items: mockItems,
|
|
62
|
+
});
|
|
63
|
+
findBaseDropdown().vm.$emit(GL_DROPDOWN_SHOWN);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
beforeEach(() => {
|
|
67
|
+
showDropdown();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should re-emit the event', () => {
|
|
71
|
+
expect(wrapper.emitted(GL_DROPDOWN_SHOWN)).toHaveLength(1);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should focus the first item', () => {
|
|
75
|
+
expect(findDisclosureItems().at(0).find(ITEM_SELECTOR).element).toHaveFocus();
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('onHide', () => {
|
|
80
|
+
beforeEach(() => {
|
|
81
|
+
buildWrapper();
|
|
82
|
+
findBaseDropdown().vm.$emit(GL_DROPDOWN_HIDDEN);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should re-emit the event', () => {
|
|
86
|
+
expect(wrapper.emitted(GL_DROPDOWN_HIDDEN)).toHaveLength(1);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('navigating the items', () => {
|
|
91
|
+
let firstItem;
|
|
92
|
+
let secondItem;
|
|
93
|
+
let thirdItem;
|
|
94
|
+
|
|
95
|
+
beforeEach(() => {
|
|
96
|
+
buildWrapper({ items: mockItems });
|
|
97
|
+
findBaseDropdown().vm.$emit(GL_DROPDOWN_SHOWN);
|
|
98
|
+
firstItem = findListItem(0);
|
|
99
|
+
secondItem = findListItem(1);
|
|
100
|
+
thirdItem = findListItem(2);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should move the focus down the list of items on `ARROW_DOWN` and stop on the last item', async () => {
|
|
104
|
+
expect(firstItem.element).toHaveFocus();
|
|
105
|
+
await firstItem.trigger('keydown', { code: ARROW_DOWN });
|
|
106
|
+
expect(secondItem.element).toHaveFocus();
|
|
107
|
+
await secondItem.trigger('keydown', { code: ARROW_DOWN });
|
|
108
|
+
expect(thirdItem.element).toHaveFocus();
|
|
109
|
+
await thirdItem.trigger('keydown', { code: ARROW_DOWN });
|
|
110
|
+
expect(thirdItem.element).toHaveFocus();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should move the focus up the list of items on `ARROW_UP` and stop on the first item', async () => {
|
|
114
|
+
await firstItem.trigger('keydown', { code: ARROW_DOWN });
|
|
115
|
+
await secondItem.trigger('keydown', { code: ARROW_DOWN });
|
|
116
|
+
expect(thirdItem.element).toHaveFocus();
|
|
117
|
+
await thirdItem.trigger('keydown', { code: ARROW_UP });
|
|
118
|
+
expect(secondItem.element).toHaveFocus();
|
|
119
|
+
await secondItem.trigger('keydown', { code: ARROW_UP });
|
|
120
|
+
expect(firstItem.element).toHaveFocus();
|
|
121
|
+
await firstItem.trigger('keydown', { code: ARROW_UP });
|
|
122
|
+
expect(firstItem.element).toHaveFocus();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should move focus to the last item on `END` keydown', async () => {
|
|
126
|
+
expect(firstItem.element).toHaveFocus();
|
|
127
|
+
await firstItem.trigger('keydown', { code: END });
|
|
128
|
+
expect(thirdItem.element).toHaveFocus();
|
|
129
|
+
await thirdItem.trigger('keydown', { code: END });
|
|
130
|
+
expect(thirdItem.element).toHaveFocus();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('should move focus to the first item on `HOME` keydown', async () => {
|
|
134
|
+
await firstItem.trigger('keydown', { code: ARROW_DOWN });
|
|
135
|
+
await secondItem.trigger('keydown', { code: ARROW_DOWN });
|
|
136
|
+
expect(thirdItem.element).toHaveFocus();
|
|
137
|
+
await thirdItem.trigger('keydown', { code: HOME });
|
|
138
|
+
expect(firstItem.element).toHaveFocus();
|
|
139
|
+
await thirdItem.trigger('keydown', { code: HOME });
|
|
140
|
+
expect(firstItem.element).toHaveFocus();
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe('slot content', () => {
|
|
145
|
+
const headerContent = 'Header Content';
|
|
146
|
+
const footerContent = 'Footer Content';
|
|
147
|
+
const toggleContent = 'Toggle Content';
|
|
148
|
+
const defaultContent = 'Toggle Content';
|
|
149
|
+
const slots = {
|
|
150
|
+
header: headerContent,
|
|
151
|
+
footer: footerContent,
|
|
152
|
+
toggle: toggleContent,
|
|
153
|
+
default: defaultContent,
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
it('renders all slot content', () => {
|
|
157
|
+
buildWrapper({}, slots);
|
|
158
|
+
expect(wrapper.text()).toContain(headerContent);
|
|
159
|
+
expect(wrapper.text()).toContain(footerContent);
|
|
160
|
+
expect(wrapper.text()).toContain(toggleContent);
|
|
161
|
+
expect(wrapper.text()).toContain(defaultContent);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe('with groups', () => {
|
|
166
|
+
it('renders groups of items', () => {
|
|
167
|
+
buildWrapper({ items: mockGroups });
|
|
168
|
+
|
|
169
|
+
const groups = findDisclosureGroups();
|
|
170
|
+
|
|
171
|
+
expect(groups.length).toBe(mockGroups.length);
|
|
172
|
+
|
|
173
|
+
mockGroups.forEach((group, i) => {
|
|
174
|
+
expect(findDisclosureItems(groups.at(i))).toHaveLength(group.items.length);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe('action', () => {
|
|
180
|
+
it('should re-emit the `action` event when it is emitted on the item for custom handling', () => {
|
|
181
|
+
buildWrapper({
|
|
182
|
+
items: mockItems,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
findListItem(0).vm.$emit('action', mockItems[0]);
|
|
186
|
+
expect(wrapper.emitted('action')).toHaveLength(1);
|
|
187
|
+
expect(wrapper.emitted('action')[0][0]).toEqual(mockItems[0]);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
describe('disclosure options', () => {
|
|
192
|
+
it('should render the `ul` as content tag and not add `role` attribute when it is a list of items only', () => {
|
|
193
|
+
buildWrapper({ items: mockItems });
|
|
194
|
+
expect(findDisclosureContent().element.tagName).toBe('UL');
|
|
195
|
+
expect(findDisclosureContent().attributes('role')).toBeUndefined();
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('should render the `div` as content tag and add `role` attribute when it is a list of groups', () => {
|
|
199
|
+
buildWrapper({ items: mockGroups });
|
|
200
|
+
expect(findDisclosureContent().element.tagName).toBe('DIV');
|
|
201
|
+
expect(findDisclosureContent().attributes('role')).toBe('group');
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('should render the `div` as content tag and NOT add `role` otherwise', () => {
|
|
205
|
+
buildWrapper({ items: null }, { default: 'Some other content' });
|
|
206
|
+
expect(findDisclosureContent().element.tagName).toBe('DIV');
|
|
207
|
+
expect(findDisclosureContent().attributes('role')).toBeUndefined();
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
});
|