@gitlab/ui 58.1.0 → 58.2.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 +14 -0
- package/dist/components/base/new_dropdowns/base_dropdown/base_dropdown.js +18 -3
- package/dist/components/base/new_dropdowns/constants.js +2 -1
- package/dist/components/base/new_dropdowns/disclosure/disclosure_dropdown.js +4 -8
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/package.json +3 -3
- package/src/components/base/new_dropdowns/base_dropdown/base_dropdown.spec.js +36 -16
- package/src/components/base/new_dropdowns/base_dropdown/base_dropdown.vue +29 -3
- package/src/components/base/new_dropdowns/constants.js +1 -0
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown.scss +32 -0
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown.spec.js +14 -5
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown.vue +3 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gitlab/ui",
|
|
3
|
-
"version": "58.1
|
|
3
|
+
"version": "58.2.1",
|
|
4
4
|
"description": "GitLab UI Components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -83,12 +83,12 @@
|
|
|
83
83
|
},
|
|
84
84
|
"devDependencies": {
|
|
85
85
|
"@arkweid/lefthook": "0.7.7",
|
|
86
|
-
"@babel/core": "^7.21.
|
|
86
|
+
"@babel/core": "^7.21.3",
|
|
87
87
|
"@babel/preset-env": "^7.20.2",
|
|
88
88
|
"@gitlab/eslint-plugin": "18.2.0",
|
|
89
89
|
"@gitlab/fonts": "^1.2.0",
|
|
90
90
|
"@gitlab/stylelint-config": "4.1.0",
|
|
91
|
-
"@gitlab/svgs": "3.
|
|
91
|
+
"@gitlab/svgs": "3.26.0",
|
|
92
92
|
"@rollup/plugin-commonjs": "^11.1.0",
|
|
93
93
|
"@rollup/plugin-node-resolve": "^7.1.3",
|
|
94
94
|
"@rollup/plugin-replace": "^2.3.2",
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils';
|
|
2
2
|
import { nextTick } from 'vue';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ARROW_DOWN,
|
|
5
|
+
GL_DROPDOWN_FOCUS_CONTENT,
|
|
6
|
+
GL_DROPDOWN_HIDDEN,
|
|
7
|
+
GL_DROPDOWN_SHOWN,
|
|
8
|
+
POPPER_CONFIG,
|
|
9
|
+
} from '../constants';
|
|
4
10
|
import GlBaseDropdown from './base_dropdown.vue';
|
|
5
11
|
|
|
6
12
|
const destroyPopper = jest.fn();
|
|
@@ -157,42 +163,50 @@ describe('base dropdown', () => {
|
|
|
157
163
|
});
|
|
158
164
|
});
|
|
159
165
|
|
|
160
|
-
describe('toggle
|
|
166
|
+
describe('default toggle', () => {
|
|
161
167
|
beforeEach(() => {
|
|
162
168
|
buildWrapper();
|
|
163
169
|
});
|
|
164
170
|
|
|
165
|
-
it('should
|
|
171
|
+
it('should open the menu on click but keep focus on toggle', async () => {
|
|
166
172
|
const toggle = findDefaultDropdownToggle();
|
|
167
173
|
const menu = findDropdownMenu();
|
|
168
174
|
|
|
175
|
+
toggle.element.focus();
|
|
169
176
|
// open menu clicking toggle btn
|
|
170
177
|
await toggle.trigger('click');
|
|
171
178
|
expect(menu.classes('gl-display-block!')).toBe(true);
|
|
172
179
|
expect(toggle.attributes('aria-expanded')).toBe('true');
|
|
173
|
-
|
|
174
|
-
expect(wrapper.emitted(GL_DROPDOWN_SHOWN).length).toBe(1);
|
|
180
|
+
expect(toggle.element).toHaveFocus();
|
|
175
181
|
|
|
176
|
-
// close menu clicking toggle btn
|
|
182
|
+
// close menu clicking toggle btn
|
|
177
183
|
await toggle.trigger('click');
|
|
178
184
|
expect(menu.classes('gl-display-block!')).toBe(false);
|
|
179
185
|
expect(toggle.attributes('aria-expanded')).toBeUndefined();
|
|
180
|
-
expect(wrapper.emitted(GL_DROPDOWN_HIDDEN)
|
|
186
|
+
expect(wrapper.emitted(GL_DROPDOWN_HIDDEN)).toHaveLength(1);
|
|
187
|
+
expect(toggle.element).toHaveFocus();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('should emit `GL_DROPDOWN_FOCUS_CONTENT` event on `ARROW_DOWN`', () => {
|
|
191
|
+
findDefaultDropdownToggle().trigger('keydown', { code: ARROW_DOWN });
|
|
192
|
+
expect(wrapper.emitted(GL_DROPDOWN_FOCUS_CONTENT)).toHaveLength(1);
|
|
181
193
|
});
|
|
182
194
|
|
|
183
|
-
it('should close
|
|
195
|
+
it('should close menu on Escape and focus toggle', async () => {
|
|
184
196
|
const toggle = findDefaultDropdownToggle();
|
|
185
197
|
const menu = findDropdownMenu();
|
|
186
198
|
|
|
187
199
|
// open menu clicking toggle btn
|
|
188
200
|
await toggle.trigger('click');
|
|
189
201
|
expect(menu.classes('gl-display-block!')).toBe(true);
|
|
202
|
+
expect(toggle.attributes('aria-expanded')).toBe('true');
|
|
190
203
|
|
|
191
|
-
// close menu
|
|
204
|
+
// close menu clicking toggle btn
|
|
205
|
+
menu.element.focus();
|
|
192
206
|
await menu.trigger('keydown.esc');
|
|
193
207
|
expect(menu.classes('gl-display-block!')).toBe(false);
|
|
194
208
|
expect(toggle.attributes('aria-expanded')).toBeUndefined();
|
|
195
|
-
expect(wrapper.emitted(GL_DROPDOWN_HIDDEN)
|
|
209
|
+
expect(wrapper.emitted(GL_DROPDOWN_HIDDEN)).toHaveLength(1);
|
|
196
210
|
expect(toggle.element).toHaveFocus();
|
|
197
211
|
});
|
|
198
212
|
});
|
|
@@ -221,22 +235,22 @@ describe('base dropdown', () => {
|
|
|
221
235
|
});
|
|
222
236
|
|
|
223
237
|
describe('toggle visibility', () => {
|
|
224
|
-
it('should toggle menu visibility on toggle
|
|
238
|
+
it('should toggle menu visibility on toggle click', async () => {
|
|
225
239
|
const toggle = findCustomDropdownToggle();
|
|
226
240
|
const firstToggleChild = findFirstToggleElement();
|
|
227
241
|
const menu = findDropdownMenu();
|
|
228
242
|
// open menu clicking toggle btn
|
|
229
|
-
await toggle.trigger('
|
|
243
|
+
await toggle.trigger('click');
|
|
230
244
|
expect(menu.classes('gl-display-block!')).toBe(true);
|
|
231
245
|
expect(firstToggleChild.attributes('aria-expanded')).toBe('true');
|
|
232
246
|
await nextTick();
|
|
233
|
-
expect(wrapper.emitted(GL_DROPDOWN_SHOWN)
|
|
247
|
+
expect(wrapper.emitted(GL_DROPDOWN_SHOWN)).toHaveLength(1);
|
|
234
248
|
|
|
235
249
|
// close menu clicking toggle btn again
|
|
236
|
-
await toggle.trigger('
|
|
250
|
+
await toggle.trigger('click');
|
|
237
251
|
expect(menu.classes('gl-display-block!')).toBe(false);
|
|
238
252
|
expect(firstToggleChild.attributes('aria-expanded')).toBe('false');
|
|
239
|
-
expect(wrapper.emitted(GL_DROPDOWN_HIDDEN)
|
|
253
|
+
expect(wrapper.emitted(GL_DROPDOWN_HIDDEN)).toHaveLength(1);
|
|
240
254
|
});
|
|
241
255
|
|
|
242
256
|
it('should close the menu when Escape is pressed inside menu and focus first child in the toggle', async () => {
|
|
@@ -251,9 +265,15 @@ describe('base dropdown', () => {
|
|
|
251
265
|
await menu.trigger('keydown.esc');
|
|
252
266
|
expect(menu.classes('gl-display-block!')).toBe(false);
|
|
253
267
|
expect(firstToggleChild.attributes('aria-expanded')).toBe('false');
|
|
254
|
-
expect(wrapper.emitted(GL_DROPDOWN_HIDDEN)
|
|
268
|
+
expect(wrapper.emitted(GL_DROPDOWN_HIDDEN)).toHaveLength(1);
|
|
255
269
|
expect(toggle.find(`[data-testid="${customToggleTestId}"]`).element).toHaveFocus();
|
|
256
270
|
});
|
|
257
271
|
});
|
|
272
|
+
|
|
273
|
+
it('should emit `GL_DROPDOWN_FOCUS_CONTENT` event on `ARROW_DOWN`', () => {
|
|
274
|
+
const toggle = findCustomDropdownToggle();
|
|
275
|
+
toggle.trigger('keydown', { code: ARROW_DOWN });
|
|
276
|
+
expect(wrapper.emitted(GL_DROPDOWN_FOCUS_CONTENT)).toHaveLength(1);
|
|
277
|
+
});
|
|
258
278
|
});
|
|
259
279
|
});
|
|
@@ -7,7 +7,15 @@ import {
|
|
|
7
7
|
dropdownPlacements,
|
|
8
8
|
dropdownVariantOptions,
|
|
9
9
|
} from '../../../../utils/constants';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
POPPER_CONFIG,
|
|
12
|
+
GL_DROPDOWN_SHOWN,
|
|
13
|
+
GL_DROPDOWN_HIDDEN,
|
|
14
|
+
GL_DROPDOWN_FOCUS_CONTENT,
|
|
15
|
+
ENTER,
|
|
16
|
+
SPACE,
|
|
17
|
+
ARROW_DOWN,
|
|
18
|
+
} from '../constants';
|
|
11
19
|
import { logWarning, isElementTabbable, isElementFocusable } from '../../../../utils/utils';
|
|
12
20
|
|
|
13
21
|
import GlButton from '../../button/button.vue';
|
|
@@ -162,6 +170,7 @@ export default {
|
|
|
162
170
|
class: this.toggleButtonClasses,
|
|
163
171
|
...this.ariaAttributes,
|
|
164
172
|
listeners: {
|
|
173
|
+
keydown: (event) => this.onKeydown(event),
|
|
165
174
|
click: () => this.toggle(),
|
|
166
175
|
},
|
|
167
176
|
};
|
|
@@ -260,11 +269,28 @@ export default {
|
|
|
260
269
|
this.toggleElement.focus();
|
|
261
270
|
},
|
|
262
271
|
onKeydown(event) {
|
|
263
|
-
const {
|
|
272
|
+
const {
|
|
273
|
+
code,
|
|
274
|
+
target: { tagName },
|
|
275
|
+
} = event;
|
|
276
|
+
|
|
277
|
+
let toggleOnEnter = true;
|
|
278
|
+
let toggleOnSpace = true;
|
|
279
|
+
|
|
280
|
+
if (tagName === 'BUTTON') {
|
|
281
|
+
toggleOnEnter = false;
|
|
282
|
+
toggleOnSpace = false;
|
|
283
|
+
} else if (tagName === 'A') {
|
|
284
|
+
toggleOnEnter = false;
|
|
285
|
+
}
|
|
264
286
|
|
|
265
|
-
if (code === ENTER || code === SPACE) {
|
|
287
|
+
if ((code === ENTER && toggleOnEnter) || (code === SPACE && toggleOnSpace)) {
|
|
266
288
|
this.toggle();
|
|
267
289
|
}
|
|
290
|
+
|
|
291
|
+
if (code === ARROW_DOWN) {
|
|
292
|
+
this.$emit(GL_DROPDOWN_FOCUS_CONTENT, event);
|
|
293
|
+
}
|
|
268
294
|
},
|
|
269
295
|
},
|
|
270
296
|
};
|
|
@@ -12,6 +12,7 @@ export const POPPER_CONFIG = {
|
|
|
12
12
|
// base dropdown events
|
|
13
13
|
export const GL_DROPDOWN_SHOWN = 'shown';
|
|
14
14
|
export const GL_DROPDOWN_HIDDEN = 'hidden';
|
|
15
|
+
export const GL_DROPDOWN_FOCUS_CONTENT = 'focusContent';
|
|
15
16
|
|
|
16
17
|
// KEY Codes
|
|
17
18
|
export const ARROW_DOWN = 'ArrowDown';
|
|
@@ -1,3 +1,35 @@
|
|
|
1
|
+
.btn-group {
|
|
2
|
+
.gl-disclosure-dropdown:not(:last-child) {
|
|
3
|
+
.gl-new-dropdown-toggle {
|
|
4
|
+
@include gl-rounded-top-right-none;
|
|
5
|
+
@include gl-rounded-bottom-right-none;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.gl-disclosure-dropdown:not(:first-child) {
|
|
10
|
+
.gl-new-dropdown-toggle {
|
|
11
|
+
@include gl-rounded-top-left-none;
|
|
12
|
+
@include gl-rounded-bottom-left-none;
|
|
13
|
+
|
|
14
|
+
// Prevent double borders when buttons are next to each other
|
|
15
|
+
margin-left: -1px;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.gl-disclosure-dropdown:hover {
|
|
20
|
+
.gl-new-dropdown-toggle {
|
|
21
|
+
@include gl-z-index-1;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.gl-disclosure-dropdown {
|
|
26
|
+
.gl-new-dropdown-toggle:focus,
|
|
27
|
+
.gl-new-dropdown-toggle:active {
|
|
28
|
+
@include gl-z-index-1;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
1
33
|
.gl-disclosure-dropdown {
|
|
2
34
|
&.gl-new-dropdown {
|
|
3
35
|
.gl-new-dropdown-inner {
|
|
@@ -4,6 +4,7 @@ import GlBaseDropdown from '../base_dropdown/base_dropdown.vue';
|
|
|
4
4
|
import {
|
|
5
5
|
GL_DROPDOWN_SHOWN,
|
|
6
6
|
GL_DROPDOWN_HIDDEN,
|
|
7
|
+
GL_DROPDOWN_FOCUS_CONTENT,
|
|
7
8
|
ARROW_DOWN,
|
|
8
9
|
ARROW_UP,
|
|
9
10
|
HOME,
|
|
@@ -82,10 +83,6 @@ describe('GlDisclosureDropdown', () => {
|
|
|
82
83
|
it('should re-emit the event', () => {
|
|
83
84
|
expect(wrapper.emitted(GL_DROPDOWN_SHOWN)).toHaveLength(1);
|
|
84
85
|
});
|
|
85
|
-
|
|
86
|
-
it('should focus the first item', () => {
|
|
87
|
-
expect(findDisclosureItems().at(0).find(ITEM_SELECTOR).element).toHaveFocus();
|
|
88
|
-
});
|
|
89
86
|
});
|
|
90
87
|
|
|
91
88
|
describe('onHide', () => {
|
|
@@ -112,7 +109,11 @@ describe('GlDisclosureDropdown', () => {
|
|
|
112
109
|
thirdItem = findListItem(2);
|
|
113
110
|
});
|
|
114
111
|
|
|
115
|
-
it('should move the focus down the list of items on `ARROW_DOWN` and stop on the last item', async () => {
|
|
112
|
+
it('should move the focus from toggle and down the list of items on `ARROW_DOWN` and stop on the last item', async () => {
|
|
113
|
+
findBaseDropdown().vm.$emit(
|
|
114
|
+
GL_DROPDOWN_FOCUS_CONTENT,
|
|
115
|
+
new KeyboardEvent('keydown', { code: ARROW_DOWN })
|
|
116
|
+
);
|
|
116
117
|
expect(firstItem.element).toHaveFocus();
|
|
117
118
|
await firstItem.trigger('keydown', { code: ARROW_DOWN });
|
|
118
119
|
expect(secondItem.element).toHaveFocus();
|
|
@@ -135,6 +136,10 @@ describe('GlDisclosureDropdown', () => {
|
|
|
135
136
|
});
|
|
136
137
|
|
|
137
138
|
it('should move focus to the last item on `END` keydown', async () => {
|
|
139
|
+
findBaseDropdown().vm.$emit(
|
|
140
|
+
GL_DROPDOWN_FOCUS_CONTENT,
|
|
141
|
+
new KeyboardEvent('keydown', { code: ARROW_DOWN })
|
|
142
|
+
);
|
|
138
143
|
expect(firstItem.element).toHaveFocus();
|
|
139
144
|
await firstItem.trigger('keydown', { code: END });
|
|
140
145
|
expect(thirdItem.element).toHaveFocus();
|
|
@@ -158,6 +163,10 @@ describe('GlDisclosureDropdown', () => {
|
|
|
158
163
|
});
|
|
159
164
|
|
|
160
165
|
it('should skip over it', async () => {
|
|
166
|
+
findBaseDropdown().vm.$emit(
|
|
167
|
+
GL_DROPDOWN_FOCUS_CONTENT,
|
|
168
|
+
new KeyboardEvent('keydown', { code: ARROW_DOWN })
|
|
169
|
+
);
|
|
161
170
|
expect(firstItem.element).toHaveFocus();
|
|
162
171
|
await firstItem.trigger('keydown', { code: ARROW_DOWN });
|
|
163
172
|
|
|
@@ -6,6 +6,7 @@ import { stopEvent, filterVisible } from '../../../../utils/utils';
|
|
|
6
6
|
import {
|
|
7
7
|
GL_DROPDOWN_SHOWN,
|
|
8
8
|
GL_DROPDOWN_HIDDEN,
|
|
9
|
+
GL_DROPDOWN_FOCUS_CONTENT,
|
|
9
10
|
HOME,
|
|
10
11
|
END,
|
|
11
12
|
ARROW_DOWN,
|
|
@@ -26,6 +27,7 @@ export default {
|
|
|
26
27
|
events: {
|
|
27
28
|
GL_DROPDOWN_SHOWN,
|
|
28
29
|
GL_DROPDOWN_HIDDEN,
|
|
30
|
+
GL_DROPDOWN_FOCUS_CONTENT,
|
|
29
31
|
},
|
|
30
32
|
components: {
|
|
31
33
|
GlBaseDropdown,
|
|
@@ -188,12 +190,6 @@ export default {
|
|
|
188
190
|
this.$refs.baseDropdown.close();
|
|
189
191
|
},
|
|
190
192
|
onShow() {
|
|
191
|
-
const items = this.getFocusableListItemElements();
|
|
192
|
-
|
|
193
|
-
if (items.length) {
|
|
194
|
-
this.focusItem(0, items);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
193
|
/**
|
|
198
194
|
* Emitted when dropdown is shown
|
|
199
195
|
*
|
|
@@ -285,6 +281,7 @@ export default {
|
|
|
285
281
|
class="gl-disclosure-dropdown"
|
|
286
282
|
@[$options.events.GL_DROPDOWN_SHOWN]="onShow"
|
|
287
283
|
@[$options.events.GL_DROPDOWN_HIDDEN]="onHide"
|
|
284
|
+
@[$options.events.GL_DROPDOWN_FOCUS_CONTENT]="onKeydown"
|
|
288
285
|
>
|
|
289
286
|
<template v-if="hasCustomToggle" #toggle>
|
|
290
287
|
<!-- @slot Custom toggle content -->
|