@gitlab/ui 39.3.2 → 39.5.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 +26 -0
- package/dist/components/base/alert/alert.js +1 -1
- package/dist/components/base/filtered_search/filtered_search_term.js +2 -1
- package/dist/components/base/filtered_search/filtered_search_token.js +3 -2
- package/dist/components/base/filtered_search/filtered_search_token_segment.js +3 -2
- package/dist/components/base/new_dropdowns/base_dropdown/base_dropdown.js +240 -0
- package/dist/components/base/new_dropdowns/constants.js +20 -0
- package/dist/components/base/new_dropdowns/listbox/listbox.js +381 -0
- package/dist/components/base/new_dropdowns/listbox/listbox_item.js +77 -0
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.js +2 -0
- package/dist/utility_classes.css +1 -1
- package/dist/utility_classes.css.map +1 -1
- package/dist/utils/utils.js +24 -1
- package/package.json +5 -4
- package/src/components/base/alert/alert.spec.js +3 -1
- package/src/components/base/alert/alert.vue +1 -1
- package/src/components/base/dropdown/dropdown.scss +10 -3
- package/src/components/base/dropdown/dropdown_item.scss +1 -0
- package/src/components/base/filtered_search/filtered_search_term.vue +9 -1
- package/src/components/base/filtered_search/filtered_search_token.vue +16 -3
- package/src/components/base/filtered_search/filtered_search_token_segment.vue +5 -4
- package/src/components/base/link/link.stories.js +9 -7
- package/src/components/base/new_dropdowns/base_dropdown/base_dropdown.spec.js +171 -0
- package/src/components/base/new_dropdowns/base_dropdown/base_dropdown.vue +221 -0
- package/src/components/base/new_dropdowns/constants.js +22 -0
- package/src/components/base/new_dropdowns/listbox/listbox.md +71 -0
- package/src/components/base/new_dropdowns/listbox/listbox.spec.js +236 -0
- package/src/components/base/new_dropdowns/listbox/listbox.stories.js +276 -0
- package/src/components/base/new_dropdowns/listbox/listbox.vue +348 -0
- package/src/components/base/new_dropdowns/listbox/listbox_item.spec.js +104 -0
- package/src/components/base/new_dropdowns/listbox/listbox_item.vue +59 -0
- package/src/components/utilities/friendly_wrap/friendly_wrap.stories.js +10 -11
- package/src/components/utilities/sprintf/sprintf.stories.js +11 -9
- package/src/index.js +4 -0
- package/src/scss/utilities.scss +10 -0
- package/src/scss/utility-mixins/composite.scss +20 -0
- package/src/scss/utility-mixins/index.scss +1 -0
- package/src/utils/data_utils.js +2 -21
- package/src/utils/utils.js +18 -0
- package/src/utils/utils.spec.js +41 -1
package/dist/utils/utils.js
CHANGED
|
@@ -119,5 +119,28 @@ function logWarning() {
|
|
|
119
119
|
console.warn(message); // eslint-disable-line no-console
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* Stop default event handling and propagation
|
|
124
|
+
*/
|
|
125
|
+
|
|
126
|
+
function stopEvent(event) {
|
|
127
|
+
let {
|
|
128
|
+
preventDefault = true,
|
|
129
|
+
stopPropagation = true,
|
|
130
|
+
stopImmediatePropagation = false
|
|
131
|
+
} = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
132
|
+
|
|
133
|
+
if (preventDefault) {
|
|
134
|
+
event.preventDefault();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (stopPropagation) {
|
|
138
|
+
event.stopPropagation();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (stopImmediatePropagation) {
|
|
142
|
+
event.stopImmediatePropagation();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
122
145
|
|
|
123
|
-
export { colorFromBackground, debounceByAnimationFrame, focusFirstFocusableElement, hexToRgba, isDev, isElementFocusable, logWarning, rgbFromHex, rgbFromString, throttle, uid };
|
|
146
|
+
export { colorFromBackground, debounceByAnimationFrame, focusFirstFocusableElement, hexToRgba, isDev, isElementFocusable, logWarning, rgbFromHex, rgbFromString, stopEvent, throttle, uid };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gitlab/ui",
|
|
3
|
-
"version": "39.
|
|
3
|
+
"version": "39.5.1",
|
|
4
4
|
"description": "GitLab UI Components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
"generate:component": "plop"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
+
"@popperjs/core": "^2.11.2",
|
|
58
59
|
"bootstrap-vue": "2.20.1",
|
|
59
60
|
"dompurify": "^2.3.6",
|
|
60
61
|
"echarts": "^5.2.1",
|
|
@@ -122,10 +123,10 @@
|
|
|
122
123
|
"npm-run-all": "^4.1.5",
|
|
123
124
|
"pikaday": "^1.8.0",
|
|
124
125
|
"plop": "^2.5.4",
|
|
125
|
-
"postcss": "8.4.
|
|
126
|
+
"postcss": "8.4.12",
|
|
126
127
|
"postcss-loader": "^3.0.0",
|
|
127
|
-
"postcss-scss": "
|
|
128
|
-
"prettier": "2.2
|
|
128
|
+
"postcss-scss": "4.0.4",
|
|
129
|
+
"prettier": "2.6.2",
|
|
129
130
|
"puppeteer": "11.0.0",
|
|
130
131
|
"raw-loader": "^0.5.1",
|
|
131
132
|
"rollup": "^2.53.1",
|
|
@@ -71,13 +71,15 @@ describe('Alert component', () => {
|
|
|
71
71
|
expect(findDismissButton().exists()).toBe(false);
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
-
it('renders the provided title', () => {
|
|
74
|
+
it('renders the provided title with heading level 2', () => {
|
|
75
75
|
const title = 'foo';
|
|
76
76
|
createComponent({ propsData: { title } });
|
|
77
77
|
|
|
78
78
|
const titleWrapper = findTitle();
|
|
79
79
|
expect(titleWrapper.exists()).toBe(true);
|
|
80
80
|
expect(titleWrapper.text()).toContain(title);
|
|
81
|
+
// the title needs to be in a level 2 heading for accessibility reasons
|
|
82
|
+
expect(titleWrapper.element.tagName).toEqual('H2');
|
|
81
83
|
});
|
|
82
84
|
|
|
83
85
|
describe('given primaryButtonText', () => {
|
|
@@ -184,7 +184,7 @@ export default {
|
|
|
184
184
|
/>
|
|
185
185
|
|
|
186
186
|
<div class="gl-alert-content" role="alert">
|
|
187
|
-
<
|
|
187
|
+
<h2 v-if="title" class="gl-alert-title">{{ title }}</h2>
|
|
188
188
|
|
|
189
189
|
<div class="gl-alert-body">
|
|
190
190
|
<!-- @slot The alert message to display. -->
|
|
@@ -128,8 +128,14 @@
|
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
.gl-dropdown-toggle
|
|
132
|
-
|
|
131
|
+
.gl-dropdown-toggle {
|
|
132
|
+
&.btn-block {
|
|
133
|
+
@include gl-justify-content-space-between;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.gl-button-text {
|
|
137
|
+
@include gl-display-inline-flex;
|
|
138
|
+
}
|
|
133
139
|
}
|
|
134
140
|
|
|
135
141
|
.gl-new-dropdown-button-text {
|
|
@@ -173,7 +179,8 @@
|
|
|
173
179
|
}
|
|
174
180
|
|
|
175
181
|
.dropdown-icon-only {
|
|
176
|
-
.dropdown-icon
|
|
182
|
+
.dropdown-icon,
|
|
183
|
+
.gl-button-icon.gl-button-icon {
|
|
177
184
|
@include gl-mr-0;
|
|
178
185
|
}
|
|
179
186
|
|
|
@@ -65,7 +65,8 @@ export default {
|
|
|
65
65
|
},
|
|
66
66
|
cursorPosition: {
|
|
67
67
|
type: String,
|
|
68
|
-
required:
|
|
68
|
+
required: false,
|
|
69
|
+
default: 'end',
|
|
69
70
|
validator: (value) => ['start', 'end'].includes(value),
|
|
70
71
|
},
|
|
71
72
|
},
|
|
@@ -111,24 +112,29 @@ export default {
|
|
|
111
112
|
Emitted when this term token is clicked.
|
|
112
113
|
@event activate
|
|
113
114
|
-->
|
|
115
|
+
|
|
114
116
|
<!--
|
|
115
117
|
Emitted when this term token will lose its focus.
|
|
116
118
|
@event deactivate
|
|
117
119
|
-->
|
|
120
|
+
|
|
118
121
|
<!--
|
|
119
122
|
Emitted when autocomplete entry is selected.
|
|
120
123
|
@event replace
|
|
121
124
|
@property {object} token Replacement token configuration.
|
|
122
125
|
-->
|
|
126
|
+
|
|
123
127
|
<!--
|
|
124
128
|
Emitted when the token is submitted.
|
|
125
129
|
@event submit
|
|
126
130
|
-->
|
|
131
|
+
|
|
127
132
|
<!--
|
|
128
133
|
Emitted when Space is pressed in-between term text.
|
|
129
134
|
@event split
|
|
130
135
|
@property {array} newTokens Token configurations
|
|
131
136
|
-->
|
|
137
|
+
|
|
132
138
|
<gl-filtered-search-token-segment
|
|
133
139
|
ref="segment"
|
|
134
140
|
v-model="internalValue"
|
|
@@ -158,6 +164,7 @@ export default {
|
|
|
158
164
|
{{ item.title }}
|
|
159
165
|
</gl-filtered-search-suggestion>
|
|
160
166
|
</template>
|
|
167
|
+
|
|
161
168
|
<template #view>
|
|
162
169
|
<input
|
|
163
170
|
v-if="placeholder"
|
|
@@ -167,6 +174,7 @@ export default {
|
|
|
167
174
|
:aria-label="placeholder"
|
|
168
175
|
data-testid="filtered-search-term-input"
|
|
169
176
|
/>
|
|
177
|
+
|
|
170
178
|
<template v-else>{{ value.data }}</template>
|
|
171
179
|
</template>
|
|
172
180
|
</gl-filtered-search-token-segment>
|
|
@@ -67,7 +67,8 @@ export default {
|
|
|
67
67
|
},
|
|
68
68
|
cursorPosition: {
|
|
69
69
|
type: String,
|
|
70
|
-
required:
|
|
70
|
+
required: false,
|
|
71
|
+
default: 'end',
|
|
71
72
|
validator: (value) => ['start', 'end'].includes(value),
|
|
72
73
|
},
|
|
73
74
|
},
|
|
@@ -299,6 +300,7 @@ export default {
|
|
|
299
300
|
Emitted when the token is submitted.
|
|
300
301
|
@event submit
|
|
301
302
|
-->
|
|
303
|
+
|
|
302
304
|
<gl-filtered-search-token-segment
|
|
303
305
|
key="title-segment"
|
|
304
306
|
:value="config.title"
|
|
@@ -318,10 +320,12 @@ export default {
|
|
|
318
320
|
class="gl-filtered-search-token-type"
|
|
319
321
|
:class="getAdditionalSegmentClasses($options.segments.SEGMENT_TITLE)"
|
|
320
322
|
view-only
|
|
321
|
-
>{{ inputValue }}</gl-token
|
|
322
323
|
>
|
|
324
|
+
{{ inputValue }}
|
|
325
|
+
</gl-token>
|
|
323
326
|
</template>
|
|
324
327
|
</gl-filtered-search-token-segment>
|
|
328
|
+
|
|
325
329
|
<gl-filtered-search-token-segment
|
|
326
330
|
key="operator-segment"
|
|
327
331
|
v-model="tokenValue.operator"
|
|
@@ -343,9 +347,11 @@ export default {
|
|
|
343
347
|
variant="search-value"
|
|
344
348
|
:class="getAdditionalSegmentClasses($options.segments.SEGMENT_OPERATOR)"
|
|
345
349
|
view-only
|
|
346
|
-
>{{ operatorDescription }}</gl-token
|
|
347
350
|
>
|
|
351
|
+
{{ operatorDescription }}
|
|
352
|
+
</gl-token>
|
|
348
353
|
</template>
|
|
354
|
+
|
|
349
355
|
<template #option="{ option }">
|
|
350
356
|
<div class="gl-display-flex">
|
|
351
357
|
{{ option.value }}
|
|
@@ -355,16 +361,19 @@ export default {
|
|
|
355
361
|
</div>
|
|
356
362
|
</template>
|
|
357
363
|
</gl-filtered-search-token-segment>
|
|
364
|
+
|
|
358
365
|
<!--
|
|
359
366
|
Emitted when a suggestion has been selected.
|
|
360
367
|
@event select
|
|
361
368
|
@type {string} value The value of the selected suggestion.
|
|
362
369
|
-->
|
|
370
|
+
|
|
363
371
|
<!--
|
|
364
372
|
Emitted when Space is pressed in-between term text.
|
|
365
373
|
@event split
|
|
366
374
|
@property {array} newTokens Token configurations
|
|
367
375
|
-->
|
|
376
|
+
|
|
368
377
|
<gl-filtered-search-token-segment
|
|
369
378
|
v-if="hasDataOrDataSegmentIsCurrentlyActive"
|
|
370
379
|
key="data-segment"
|
|
@@ -386,10 +395,13 @@ export default {
|
|
|
386
395
|
>
|
|
387
396
|
<template #suggestions>
|
|
388
397
|
<!-- @slot The suggestions (implemented with GlFilteredSearchSuggestion). -->
|
|
398
|
+
|
|
389
399
|
<slot name="suggestions"></slot>
|
|
390
400
|
</template>
|
|
401
|
+
|
|
391
402
|
<template #view="{ inputValue }">
|
|
392
403
|
<!-- @slot Used to customize how the token is rendered. -->
|
|
404
|
+
|
|
393
405
|
<slot
|
|
394
406
|
name="view-token"
|
|
395
407
|
v-bind="{
|
|
@@ -412,6 +424,7 @@ export default {
|
|
|
412
424
|
@slot Template for token value in inactive state
|
|
413
425
|
@binding {array} suggestions Slot for rendering autocomplete suggestions when no options are provided.
|
|
414
426
|
-->
|
|
427
|
+
|
|
415
428
|
<slot name="view" v-bind="{ inputValue }">{{ inputValue }}</slot>
|
|
416
429
|
</span>
|
|
417
430
|
</gl-token>
|
|
@@ -79,7 +79,8 @@ export default {
|
|
|
79
79
|
},
|
|
80
80
|
cursorPosition: {
|
|
81
81
|
type: String,
|
|
82
|
-
required:
|
|
82
|
+
required: false,
|
|
83
|
+
default: 'end',
|
|
83
84
|
validator: (value) => ['start', 'end'].includes(value),
|
|
84
85
|
},
|
|
85
86
|
},
|
|
@@ -346,6 +347,7 @@ export default {
|
|
|
346
347
|
@keydown="handleInputKeydown"
|
|
347
348
|
@blur="handleBlur"
|
|
348
349
|
/>
|
|
350
|
+
|
|
349
351
|
<portal :key="`operator-${_uid}`" :to="portalName">
|
|
350
352
|
<gl-filtered-search-suggestion-list
|
|
351
353
|
v-if="hasOptionsOrSuggestions"
|
|
@@ -361,11 +363,10 @@ export default {
|
|
|
361
363
|
:value="option.value"
|
|
362
364
|
:icon-name="option.icon"
|
|
363
365
|
>
|
|
364
|
-
<slot name="option" v-bind="{ option }">
|
|
365
|
-
{{ option[optionTextField] }}
|
|
366
|
-
</slot>
|
|
366
|
+
<slot name="option" v-bind="{ option }"> {{ option[optionTextField] }} </slot>
|
|
367
367
|
</gl-filtered-search-suggestion>
|
|
368
368
|
</template>
|
|
369
|
+
|
|
369
370
|
<slot v-else name="suggestions"></slot>
|
|
370
371
|
</gl-filtered-search-suggestion-list>
|
|
371
372
|
</portal>
|
|
@@ -7,13 +7,15 @@ const generateProps = ({ href = '#', target = null } = {}) => ({
|
|
|
7
7
|
target,
|
|
8
8
|
});
|
|
9
9
|
|
|
10
|
-
const makeStory =
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
const makeStory =
|
|
11
|
+
(options) =>
|
|
12
|
+
(args, { argTypes }) => ({
|
|
13
|
+
components: {
|
|
14
|
+
GlLink,
|
|
15
|
+
},
|
|
16
|
+
props: Object.keys(argTypes),
|
|
17
|
+
...options,
|
|
18
|
+
});
|
|
17
19
|
|
|
18
20
|
export const DefaultLink = makeStory({
|
|
19
21
|
components: { GlLink },
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import { nextTick } from 'vue';
|
|
3
|
+
import { GL_DROPDOWN_HIDDEN, GL_DROPDOWN_SHOWN, POPPER_CONFIG } from '../constants';
|
|
4
|
+
import GlBaseDropdown from './base_dropdown.vue';
|
|
5
|
+
|
|
6
|
+
const destroyPopper = jest.fn();
|
|
7
|
+
const updatePopper = jest.fn();
|
|
8
|
+
const mockCreatePopper = jest.fn().mockImplementation(() => ({
|
|
9
|
+
destroy: destroyPopper,
|
|
10
|
+
update: updatePopper,
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
jest.mock('@popperjs/core', () => ({
|
|
14
|
+
createPopper: (...args) => mockCreatePopper(...args),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
const DEFAULT_BTN_TOGGLE_CLASSES = [
|
|
18
|
+
'btn',
|
|
19
|
+
'btn-default',
|
|
20
|
+
'btn-md',
|
|
21
|
+
'gl-button',
|
|
22
|
+
'dropdown-toggle',
|
|
23
|
+
'gl-dropdown-toggle',
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
describe('base dropdown', () => {
|
|
27
|
+
let wrapper;
|
|
28
|
+
|
|
29
|
+
const buildWrapper = (propsData, slots = {}) => {
|
|
30
|
+
wrapper = mount(GlBaseDropdown, {
|
|
31
|
+
propsData: {
|
|
32
|
+
toggleId: 'dropdown-toggle-btn-1',
|
|
33
|
+
...propsData,
|
|
34
|
+
},
|
|
35
|
+
slots,
|
|
36
|
+
attachTo: document.body,
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
jest.clearAllMocks();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const findDropdownToggle = () => wrapper.find('.btn.gl-dropdown-toggle');
|
|
45
|
+
const findDropdownMenu = () => wrapper.find('.dropdown-menu');
|
|
46
|
+
|
|
47
|
+
describe('popper.js instance', () => {
|
|
48
|
+
it('should initialize popper.js instance with toggle and menu elements and config for left-aligned menu', async () => {
|
|
49
|
+
await buildWrapper();
|
|
50
|
+
expect(mockCreatePopper).toHaveBeenCalledWith(
|
|
51
|
+
findDropdownToggle().element,
|
|
52
|
+
findDropdownMenu().element,
|
|
53
|
+
{ ...POPPER_CONFIG, placement: 'bottom-start' }
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should initialize popper.js instance with toggle and menu elements and config for right-aligned menu', async () => {
|
|
58
|
+
await buildWrapper({ right: true });
|
|
59
|
+
expect(mockCreatePopper).toHaveBeenCalledWith(
|
|
60
|
+
findDropdownToggle().element,
|
|
61
|
+
findDropdownMenu().element,
|
|
62
|
+
{ ...POPPER_CONFIG, placement: 'bottom-end' }
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should update popper instance when component is updated', async () => {
|
|
67
|
+
await buildWrapper();
|
|
68
|
+
await findDropdownToggle().trigger('click');
|
|
69
|
+
await wrapper.setProps({ category: 'tertiary' });
|
|
70
|
+
expect(updatePopper).toHaveBeenCalled();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should destroy popper instance when component is destroyed', async () => {
|
|
74
|
+
await buildWrapper();
|
|
75
|
+
wrapper.destroy();
|
|
76
|
+
expect(destroyPopper).toHaveBeenCalled();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('renders content to the default slot', () => {
|
|
81
|
+
const defaultContent = 'Some content here';
|
|
82
|
+
const slots = { default: defaultContent };
|
|
83
|
+
|
|
84
|
+
it('renders the content', () => {
|
|
85
|
+
buildWrapper({}, slots);
|
|
86
|
+
expect(wrapper.find('.gl-new-dropdown-inner').html()).toContain(defaultContent);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe.each`
|
|
91
|
+
props | toggleClasses
|
|
92
|
+
${{}} | ${[]}
|
|
93
|
+
${{ toggleText: 'toggleText' }} | ${[]}
|
|
94
|
+
${{ toggleText: 'toggleText', icon: 'close' }} | ${['dropdown-icon-text']}
|
|
95
|
+
${{ icon: 'close' }} | ${['dropdown-icon-only']}
|
|
96
|
+
${{ icon: 'close', toggleText: 'toggleText', textSrOnly: true }} | ${['dropdown-icon-only']}
|
|
97
|
+
${{ icon: 'close', textSrOnly: true }} | ${['dropdown-icon-only']}
|
|
98
|
+
${{ toggleText: 'toggleText', noCaret: true }} | ${['dropdown-toggle-no-caret']}
|
|
99
|
+
`('dropdown with props $props', ({ props, toggleClasses }) => {
|
|
100
|
+
beforeEach(async () => {
|
|
101
|
+
buildWrapper(props);
|
|
102
|
+
|
|
103
|
+
await nextTick();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it(`sets toggle button classes to '${toggleClasses}'`, () => {
|
|
107
|
+
const classes = findDropdownToggle().classes().sort();
|
|
108
|
+
|
|
109
|
+
expect(classes).toEqual([...DEFAULT_BTN_TOGGLE_CLASSES, ...toggleClasses].sort());
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe.each`
|
|
114
|
+
toggleClass | expectedClasses | type
|
|
115
|
+
${'my-class'} | ${[...DEFAULT_BTN_TOGGLE_CLASSES, 'my-class']} | ${'string'}
|
|
116
|
+
${{ 'my-class': true }} | ${[...DEFAULT_BTN_TOGGLE_CLASSES, 'my-class']} | ${'object'}
|
|
117
|
+
${['cls-1', 'cls-2']} | ${[...DEFAULT_BTN_TOGGLE_CLASSES, 'cls-1', 'cls-2']} | ${'array'}
|
|
118
|
+
${null} | ${[...DEFAULT_BTN_TOGGLE_CLASSES]} | ${'null'}
|
|
119
|
+
`('with toggle classes', ({ toggleClass, expectedClasses, type }) => {
|
|
120
|
+
beforeEach(async () => {
|
|
121
|
+
buildWrapper({ toggleClass });
|
|
122
|
+
|
|
123
|
+
await nextTick();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it(`class is inherited from toggle class of type ${type}`, () => {
|
|
127
|
+
expect(findDropdownToggle().classes().sort()).toEqual(
|
|
128
|
+
expect.arrayContaining(expectedClasses.sort())
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('toggle visibility', () => {
|
|
134
|
+
beforeEach(() => {
|
|
135
|
+
buildWrapper();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should toggle menu visibility on toggle button click ', async () => {
|
|
139
|
+
const toggle = findDropdownToggle();
|
|
140
|
+
const menu = findDropdownMenu();
|
|
141
|
+
|
|
142
|
+
// open menu clicking toggle btn
|
|
143
|
+
await toggle.trigger('click');
|
|
144
|
+
expect(menu.classes('show')).toBe(true);
|
|
145
|
+
expect(toggle.attributes('aria-expanded')).toBe('true');
|
|
146
|
+
expect(wrapper.emitted(GL_DROPDOWN_SHOWN).length).toBe(1);
|
|
147
|
+
|
|
148
|
+
// close menu clicking toggle btn again
|
|
149
|
+
await toggle.trigger('click');
|
|
150
|
+
expect(menu.classes('show')).toBe(false);
|
|
151
|
+
expect(toggle.attributes('aria-expanded')).toBeUndefined();
|
|
152
|
+
expect(wrapper.emitted(GL_DROPDOWN_HIDDEN).length).toBe(1);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('should close the menu when Escape is pressed inside menu and focus toggle', async () => {
|
|
156
|
+
const toggle = findDropdownToggle();
|
|
157
|
+
const menu = findDropdownMenu();
|
|
158
|
+
|
|
159
|
+
// open menu clicking toggle btn
|
|
160
|
+
await toggle.trigger('click');
|
|
161
|
+
expect(menu.classes('show')).toBe(true);
|
|
162
|
+
|
|
163
|
+
// close menu pressing ESC on it
|
|
164
|
+
await menu.trigger('keydown.esc');
|
|
165
|
+
expect(menu.classes('show')).toBe(false);
|
|
166
|
+
expect(toggle.attributes('aria-expanded')).toBeUndefined();
|
|
167
|
+
expect(wrapper.emitted(GL_DROPDOWN_HIDDEN).length).toBe(1);
|
|
168
|
+
expect(toggle.element).toHaveFocus();
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
});
|