@gitlab/ui 64.0.2 → 64.1.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 +45 -22
- package/dist/components/base/new_dropdowns/constants.js +6 -1
- package/dist/components/base/new_dropdowns/disclosure/disclosure_dropdown.js +13 -2
- package/dist/components/base/new_dropdowns/listbox/listbox.js +13 -2
- package/dist/components/base/new_dropdowns/listbox/mock_data.js +30 -1
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/package.json +1 -1
- package/src/components/base/new_dropdowns/base_dropdown/base_dropdown.spec.js +53 -4
- package/src/components/base/new_dropdowns/base_dropdown/base_dropdown.vue +48 -21
- package/src/components/base/new_dropdowns/constants.js +5 -0
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown.spec.js +13 -0
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown.stories.js +1 -0
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown.vue +14 -0
- package/src/components/base/new_dropdowns/dropdown.scss +0 -1
- package/src/components/base/new_dropdowns/listbox/listbox.spec.js +17 -5
- package/src/components/base/new_dropdowns/listbox/listbox.stories.js +31 -1
- package/src/components/base/new_dropdowns/listbox/listbox.vue +14 -0
- package/src/components/base/new_dropdowns/listbox/mock_data.js +43 -0
package/package.json
CHANGED
|
@@ -62,7 +62,7 @@ describe('base dropdown', () => {
|
|
|
62
62
|
expect(autoUpdate).toHaveBeenCalledTimes(1);
|
|
63
63
|
});
|
|
64
64
|
|
|
65
|
-
it(
|
|
65
|
+
it('stops Floating UI when closing the dropdown', async () => {
|
|
66
66
|
buildWrapper();
|
|
67
67
|
await findDefaultDropdownToggle().trigger('click');
|
|
68
68
|
await findDefaultDropdownToggle().trigger('click');
|
|
@@ -71,7 +71,7 @@ describe('base dropdown', () => {
|
|
|
71
71
|
expect(mockStopAutoUpdate).toHaveBeenCalledTimes(1);
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
-
it(
|
|
74
|
+
it('restarts Floating UI when reopening the dropdown', async () => {
|
|
75
75
|
buildWrapper();
|
|
76
76
|
await findDefaultDropdownToggle().trigger('click');
|
|
77
77
|
await findDefaultDropdownToggle().trigger('click');
|
|
@@ -103,6 +103,7 @@ describe('base dropdown', () => {
|
|
|
103
103
|
findDropdownMenu().element,
|
|
104
104
|
{
|
|
105
105
|
placement: 'bottom-start',
|
|
106
|
+
strategy: 'absolute',
|
|
106
107
|
middleware: [offset({ mainAxis: DEFAULT_OFFSET })],
|
|
107
108
|
}
|
|
108
109
|
);
|
|
@@ -115,7 +116,11 @@ describe('base dropdown', () => {
|
|
|
115
116
|
expect(computePosition).toHaveBeenCalledWith(
|
|
116
117
|
findDefaultDropdownToggle().element,
|
|
117
118
|
findDropdownMenu().element,
|
|
118
|
-
{
|
|
119
|
+
{
|
|
120
|
+
placement: 'bottom',
|
|
121
|
+
strategy: 'absolute',
|
|
122
|
+
middleware: [offset({ mainAxis: DEFAULT_OFFSET })],
|
|
123
|
+
}
|
|
119
124
|
);
|
|
120
125
|
});
|
|
121
126
|
|
|
@@ -126,7 +131,11 @@ describe('base dropdown', () => {
|
|
|
126
131
|
expect(computePosition).toHaveBeenCalledWith(
|
|
127
132
|
findDefaultDropdownToggle().element,
|
|
128
133
|
findDropdownMenu().element,
|
|
129
|
-
{
|
|
134
|
+
{
|
|
135
|
+
placement: 'bottom-end',
|
|
136
|
+
strategy: 'absolute',
|
|
137
|
+
middleware: [offset({ mainAxis: DEFAULT_OFFSET })],
|
|
138
|
+
}
|
|
130
139
|
);
|
|
131
140
|
});
|
|
132
141
|
|
|
@@ -143,10 +152,45 @@ describe('base dropdown', () => {
|
|
|
143
152
|
findDropdownMenu().element,
|
|
144
153
|
{
|
|
145
154
|
placement: 'bottom-end',
|
|
155
|
+
strategy: 'absolute',
|
|
146
156
|
middleware: [offset(customOffset)],
|
|
147
157
|
}
|
|
148
158
|
);
|
|
149
159
|
});
|
|
160
|
+
|
|
161
|
+
describe('positioningStrategy', () => {
|
|
162
|
+
it('uses the absolute positioning strategy by default', async () => {
|
|
163
|
+
buildWrapper();
|
|
164
|
+
|
|
165
|
+
await findDefaultDropdownToggle().trigger('click');
|
|
166
|
+
|
|
167
|
+
expect(computePosition).toHaveBeenCalledWith(
|
|
168
|
+
findDefaultDropdownToggle().element,
|
|
169
|
+
findDropdownMenu().element,
|
|
170
|
+
expect.objectContaining({
|
|
171
|
+
strategy: 'absolute',
|
|
172
|
+
})
|
|
173
|
+
);
|
|
174
|
+
expect(findDropdownMenu().classes()).toContain('gl-absolute');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('applies the fixed positioning strategy properly', async () => {
|
|
178
|
+
buildWrapper({
|
|
179
|
+
positioningStrategy: 'fixed',
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
await findDefaultDropdownToggle().trigger('click');
|
|
183
|
+
|
|
184
|
+
expect(computePosition).toHaveBeenCalledWith(
|
|
185
|
+
findDefaultDropdownToggle().element,
|
|
186
|
+
findDropdownMenu().element,
|
|
187
|
+
expect.objectContaining({
|
|
188
|
+
strategy: 'fixed',
|
|
189
|
+
})
|
|
190
|
+
);
|
|
191
|
+
expect(findDropdownMenu().classes()).toContain('gl-fixed');
|
|
192
|
+
});
|
|
193
|
+
});
|
|
150
194
|
});
|
|
151
195
|
});
|
|
152
196
|
|
|
@@ -291,6 +335,11 @@ describe('base dropdown', () => {
|
|
|
291
335
|
});
|
|
292
336
|
|
|
293
337
|
describe('toggle visibility', () => {
|
|
338
|
+
beforeEach(() => {
|
|
339
|
+
autoUpdate.mockImplementation(jest.requireActual('@floating-ui/dom').autoUpdate);
|
|
340
|
+
computePosition.mockImplementation(() => Promise.resolve);
|
|
341
|
+
});
|
|
342
|
+
|
|
294
343
|
it('should toggle menu visibility on toggle click', async () => {
|
|
295
344
|
const toggle = findCustomDropdownToggle();
|
|
296
345
|
const firstToggleChild = findFirstToggleElement();
|
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
SPACE,
|
|
16
16
|
ARROW_DOWN,
|
|
17
17
|
GL_DROPDOWN_CONTENTS_CLASS,
|
|
18
|
+
POSITION_ABSOLUTE,
|
|
19
|
+
POSITION_FIXED,
|
|
18
20
|
} from '../constants';
|
|
19
21
|
import { logWarning, isElementTabbable, isElementFocusable } from '../../../../utils/utils';
|
|
20
22
|
|
|
@@ -133,11 +135,21 @@ export default {
|
|
|
133
135
|
required: false,
|
|
134
136
|
default: false,
|
|
135
137
|
},
|
|
138
|
+
/**
|
|
139
|
+
* Strategy to be applied by computePosition. If this is set to fixed, the dropdown's position
|
|
140
|
+
* needs to be set to fixed in CSS as well.
|
|
141
|
+
* https://floating-ui.com/docs/computePosition#strategy
|
|
142
|
+
*/
|
|
143
|
+
positioningStrategy: {
|
|
144
|
+
type: String,
|
|
145
|
+
required: false,
|
|
146
|
+
default: POSITION_ABSOLUTE,
|
|
147
|
+
validator: (strategy) => [POSITION_ABSOLUTE, POSITION_FIXED].includes(strategy),
|
|
148
|
+
},
|
|
136
149
|
},
|
|
137
150
|
data() {
|
|
138
151
|
return {
|
|
139
152
|
visible: false,
|
|
140
|
-
openedYet: false,
|
|
141
153
|
baseDropdownId: uniqueId('base-dropdown-'),
|
|
142
154
|
};
|
|
143
155
|
},
|
|
@@ -209,11 +221,17 @@ export default {
|
|
|
209
221
|
return {
|
|
210
222
|
'gl-display-block!': this.visible,
|
|
211
223
|
[FIXED_WIDTH_CLASS]: !this.fluidWidth,
|
|
224
|
+
'gl-fixed': this.isFixed,
|
|
225
|
+
'gl-absolute': !this.isFixed,
|
|
212
226
|
};
|
|
213
227
|
},
|
|
228
|
+
isFixed() {
|
|
229
|
+
return this.positioningStrategy === POSITION_FIXED;
|
|
230
|
+
},
|
|
214
231
|
floatingUIConfig() {
|
|
215
232
|
return {
|
|
216
233
|
placement: dropdownPlacements[this.placement],
|
|
234
|
+
strategy: this.positioningStrategy,
|
|
217
235
|
middleware: [
|
|
218
236
|
offset(this.offset),
|
|
219
237
|
flip(),
|
|
@@ -263,7 +281,7 @@ export default {
|
|
|
263
281
|
);
|
|
264
282
|
}
|
|
265
283
|
},
|
|
266
|
-
startFloating() {
|
|
284
|
+
async startFloating() {
|
|
267
285
|
this.calculateNonScrollableAreaHeight();
|
|
268
286
|
this.observer = new MutationObserver(this.calculateNonScrollableAreaHeight);
|
|
269
287
|
this.observer.observe(this.$refs.content, {
|
|
@@ -272,24 +290,29 @@ export default {
|
|
|
272
290
|
subtree: true,
|
|
273
291
|
});
|
|
274
292
|
|
|
275
|
-
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
293
|
+
await new Promise((resolve) => {
|
|
294
|
+
const stopAutoUpdate = autoUpdate(this.toggleElement, this.$refs.content, async () => {
|
|
295
|
+
const { x, y } = await computePosition(
|
|
296
|
+
this.toggleElement,
|
|
297
|
+
this.$refs.content,
|
|
298
|
+
this.floatingUIConfig
|
|
299
|
+
);
|
|
281
300
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
301
|
+
/**
|
|
302
|
+
* Due to the asynchronous nature of computePosition, it's technically possible for the
|
|
303
|
+
* component to have been destroyed by the time the promise resolves. In such case, we exit
|
|
304
|
+
* early to prevent a TypeError.
|
|
305
|
+
*/
|
|
306
|
+
if (!this.$refs.content) return;
|
|
307
|
+
|
|
308
|
+
Object.assign(this.$refs.content.style, {
|
|
309
|
+
left: `${x}px`,
|
|
310
|
+
top: `${y}px`,
|
|
311
|
+
});
|
|
288
312
|
|
|
289
|
-
|
|
290
|
-
left: `${x}px`,
|
|
291
|
-
top: `${y}px`,
|
|
313
|
+
resolve(stopAutoUpdate);
|
|
292
314
|
});
|
|
315
|
+
this.stopAutoUpdate = stopAutoUpdate;
|
|
293
316
|
});
|
|
294
317
|
},
|
|
295
318
|
stopFloating() {
|
|
@@ -300,12 +323,16 @@ export default {
|
|
|
300
323
|
this.visible = !this.visible;
|
|
301
324
|
|
|
302
325
|
if (this.visible) {
|
|
326
|
+
// The dropdown needs to be actually visible before we compute its position with Floating UI.
|
|
327
|
+
await this.$nextTick();
|
|
328
|
+
|
|
303
329
|
/**
|
|
304
|
-
* We
|
|
305
|
-
*
|
|
330
|
+
* We wait until the dropdown's position has been computed before emitting the `shown` event.
|
|
331
|
+
* This ensures that, if the parent component attempts to focus an inner element, the dropdown
|
|
332
|
+
* is already properly placed in the page. Otherwise, the page would scroll back to the top.
|
|
306
333
|
*/
|
|
307
|
-
await this
|
|
308
|
-
|
|
334
|
+
await this.startFloating();
|
|
335
|
+
|
|
309
336
|
this.$emit(GL_DROPDOWN_SHOWN);
|
|
310
337
|
} else {
|
|
311
338
|
this.stopFloating();
|
|
@@ -11,4 +11,9 @@ export const ENTER = 'Enter';
|
|
|
11
11
|
export const HOME = 'Home';
|
|
12
12
|
export const SPACE = 'Space';
|
|
13
13
|
|
|
14
|
+
// Positioning strategies
|
|
15
|
+
// https://floating-ui.com/docs/computePosition#strategy
|
|
16
|
+
export const POSITION_ABSOLUTE = 'absolute';
|
|
17
|
+
export const POSITION_FIXED = 'fixed';
|
|
18
|
+
|
|
14
19
|
export const GL_DROPDOWN_CONTENTS_CLASS = 'gl-new-dropdown-contents';
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
ARROW_UP,
|
|
11
11
|
HOME,
|
|
12
12
|
END,
|
|
13
|
+
POSITION_ABSOLUTE,
|
|
14
|
+
POSITION_FIXED,
|
|
13
15
|
} from '../constants';
|
|
14
16
|
import GlDisclosureDropdown from './disclosure_dropdown.vue';
|
|
15
17
|
import GlDisclosureDropdownItem from './disclosure_dropdown_item.vue';
|
|
@@ -345,4 +347,15 @@ describe('GlDisclosureDropdown', () => {
|
|
|
345
347
|
expect(closeSpy).not.toHaveBeenCalled();
|
|
346
348
|
});
|
|
347
349
|
});
|
|
350
|
+
|
|
351
|
+
describe('positioningStrategy', () => {
|
|
352
|
+
it.each([POSITION_ABSOLUTE, POSITION_FIXED])(
|
|
353
|
+
'passes the %s positioning strategy to the base dropdown',
|
|
354
|
+
(positioningStrategy) => {
|
|
355
|
+
buildWrapper({ positioningStrategy });
|
|
356
|
+
|
|
357
|
+
expect(findBaseDropdown().props('positioningStrategy')).toBe(positioningStrategy);
|
|
358
|
+
}
|
|
359
|
+
);
|
|
360
|
+
});
|
|
348
361
|
});
|
|
@@ -41,6 +41,7 @@ const makeBindings = (overrides = {}) =>
|
|
|
41
41
|
':list-aria-labelled-by': 'listAriaLabelledBy',
|
|
42
42
|
':fluid-width': 'fluidWidth',
|
|
43
43
|
':auto-close': 'autoClose',
|
|
44
|
+
':positioning-strategy': 'positioningStrategy',
|
|
44
45
|
...overrides,
|
|
45
46
|
})
|
|
46
47
|
.map(([key, value]) => `${key}="${value}"`)
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
ARROW_DOWN,
|
|
15
15
|
ARROW_UP,
|
|
16
16
|
GL_DROPDOWN_CONTENTS_CLASS,
|
|
17
|
+
POSITION_ABSOLUTE,
|
|
18
|
+
POSITION_FIXED,
|
|
17
19
|
} from '../constants';
|
|
18
20
|
import {
|
|
19
21
|
buttonCategoryOptions,
|
|
@@ -196,6 +198,17 @@ export default {
|
|
|
196
198
|
required: false,
|
|
197
199
|
default: true,
|
|
198
200
|
},
|
|
201
|
+
/**
|
|
202
|
+
* Strategy to be applied by computePosition. If the dropdown's container is too short for it to
|
|
203
|
+
* fit in, setting this to fixed will let it position itself above its container.
|
|
204
|
+
* https://floating-ui.com/docs/computePosition#strategy
|
|
205
|
+
*/
|
|
206
|
+
positioningStrategy: {
|
|
207
|
+
type: String,
|
|
208
|
+
required: false,
|
|
209
|
+
default: POSITION_ABSOLUTE,
|
|
210
|
+
validator: (strategy) => [POSITION_ABSOLUTE, POSITION_FIXED].includes(strategy),
|
|
211
|
+
},
|
|
199
212
|
},
|
|
200
213
|
data() {
|
|
201
214
|
return {
|
|
@@ -323,6 +336,7 @@ export default {
|
|
|
323
336
|
:placement="placement"
|
|
324
337
|
:offset="dropdownOffset"
|
|
325
338
|
:fluid-width="fluidWidth"
|
|
339
|
+
:positioning-strategy="positioningStrategy"
|
|
326
340
|
class="gl-disclosure-dropdown"
|
|
327
341
|
@[$options.events.GL_DROPDOWN_SHOWN]="onShow"
|
|
328
342
|
@[$options.events.GL_DROPDOWN_HIDDEN]="onHide"
|
|
@@ -11,12 +11,14 @@ import {
|
|
|
11
11
|
HOME,
|
|
12
12
|
END,
|
|
13
13
|
ENTER,
|
|
14
|
+
POSITION_ABSOLUTE,
|
|
15
|
+
POSITION_FIXED,
|
|
14
16
|
} from '../constants';
|
|
15
17
|
import GlIntersectionObserver from '../../../utilities/intersection_observer/intersection_observer.vue';
|
|
16
18
|
import GlCollapsibleListbox, { ITEM_SELECTOR } from './listbox.vue';
|
|
17
19
|
import GlListboxItem from './listbox_item.vue';
|
|
18
20
|
import GlListboxGroup from './listbox_group.vue';
|
|
19
|
-
import { mockOptions, mockGroups } from './mock_data';
|
|
21
|
+
import { mockOptions, mockGroups, mockGroupsWithTextSrOnly } from './mock_data';
|
|
20
22
|
|
|
21
23
|
jest.mock('@floating-ui/dom');
|
|
22
24
|
autoUpdate.mockImplementation(() => {
|
|
@@ -396,14 +398,13 @@ describe('GlCollapsibleListbox', () => {
|
|
|
396
398
|
});
|
|
397
399
|
|
|
398
400
|
it('passes the `textSrOnly` prop', () => {
|
|
399
|
-
const mockGroupsWithTextSrOnly = JSON.parse(JSON.stringify(mockGroups));
|
|
400
|
-
mockGroupsWithTextSrOnly[0].textSrOnly = true;
|
|
401
|
-
mockGroupsWithTextSrOnly[1].textSrOnly = false;
|
|
402
401
|
buildWrapper({ items: mockGroupsWithTextSrOnly });
|
|
403
402
|
|
|
404
403
|
const groups = findListboxGroups();
|
|
405
404
|
|
|
406
|
-
const expectedTextSrOnlyProps = mockGroupsWithTextSrOnly.map(
|
|
405
|
+
const expectedTextSrOnlyProps = mockGroupsWithTextSrOnly.map(
|
|
406
|
+
(group) => group.textSrOnly ?? false
|
|
407
|
+
);
|
|
407
408
|
const actualTextSrOnlyProps = groups.wrappers.map((group) => group.props('textSrOnly'));
|
|
408
409
|
|
|
409
410
|
expect(actualTextSrOnlyProps).toEqual(expectedTextSrOnlyProps);
|
|
@@ -744,4 +745,15 @@ describe('GlCollapsibleListbox', () => {
|
|
|
744
745
|
expect(findBaseDropdown().props('fluidWidth')).toBe(true);
|
|
745
746
|
});
|
|
746
747
|
});
|
|
748
|
+
|
|
749
|
+
describe('positioningStrategy', () => {
|
|
750
|
+
it.each([POSITION_ABSOLUTE, POSITION_FIXED])(
|
|
751
|
+
'passes the %s positioning strategy to the base dropdown',
|
|
752
|
+
(positioningStrategy) => {
|
|
753
|
+
buildWrapper({ positioningStrategy });
|
|
754
|
+
|
|
755
|
+
expect(findBaseDropdown().props('positioningStrategy')).toBe(positioningStrategy);
|
|
756
|
+
}
|
|
757
|
+
);
|
|
758
|
+
});
|
|
747
759
|
});
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
} from '../../../../utils/stories_constants';
|
|
25
25
|
import { POSITION } from '../../../utilities/truncate/constants';
|
|
26
26
|
import readme from './listbox.md';
|
|
27
|
-
import { mockOptions, mockGroups, mockUsers } from './mock_data';
|
|
27
|
+
import { mockOptions, mockGroups, mockGroupsWithTextSrOnly, mockUsers } from './mock_data';
|
|
28
28
|
import { flattenedOptions } from './utils';
|
|
29
29
|
import GlCollapsibleListbox from './listbox.vue';
|
|
30
30
|
|
|
@@ -59,6 +59,7 @@ const generateProps = ({
|
|
|
59
59
|
showSelectAllButtonLabel = defaultValue('showSelectAllButtonLabel'),
|
|
60
60
|
startOpened = true,
|
|
61
61
|
fluidWidth,
|
|
62
|
+
positioningStrategy,
|
|
62
63
|
} = {}) => ({
|
|
63
64
|
items,
|
|
64
65
|
category,
|
|
@@ -88,6 +89,7 @@ const generateProps = ({
|
|
|
88
89
|
showSelectAllButtonLabel,
|
|
89
90
|
startOpened,
|
|
90
91
|
fluidWidth,
|
|
92
|
+
positioningStrategy,
|
|
91
93
|
});
|
|
92
94
|
|
|
93
95
|
const makeBindings = (overrides = {}) =>
|
|
@@ -119,6 +121,7 @@ const makeBindings = (overrides = {}) =>
|
|
|
119
121
|
':reset-button-label': 'resetButtonLabel',
|
|
120
122
|
':show-select-all-button-label': 'showSelectAllButtonLabel',
|
|
121
123
|
':fluid-width': 'fluidWidth',
|
|
124
|
+
':positioning-strategy': 'positioningStrategy',
|
|
122
125
|
...overrides,
|
|
123
126
|
})
|
|
124
127
|
.map(([key, value]) => `${key}="${value}"`)
|
|
@@ -459,6 +462,33 @@ export const CustomGroupsAndItems = makeGroupedExample({
|
|
|
459
462
|
`),
|
|
460
463
|
});
|
|
461
464
|
|
|
465
|
+
export const GroupWithoutLabel = (args, { argTypes }) => ({
|
|
466
|
+
props: Object.keys(argTypes),
|
|
467
|
+
components: {
|
|
468
|
+
GlBadge,
|
|
469
|
+
GlCollapsibleListbox,
|
|
470
|
+
},
|
|
471
|
+
data() {
|
|
472
|
+
return {
|
|
473
|
+
selected: mockGroupsWithTextSrOnly[1].options[1].value,
|
|
474
|
+
};
|
|
475
|
+
},
|
|
476
|
+
mounted() {
|
|
477
|
+
if (this.startOpened) {
|
|
478
|
+
openListbox(this);
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
template: template(`
|
|
482
|
+
<template #list-item="{ item }">
|
|
483
|
+
{{ item.text }} <gl-badge v-if="item.value === 'main'" size="sm">default</gl-badge>
|
|
484
|
+
</template>
|
|
485
|
+
`),
|
|
486
|
+
});
|
|
487
|
+
GroupWithoutLabel.args = generateProps({
|
|
488
|
+
items: mockGroupsWithTextSrOnly,
|
|
489
|
+
headerText: 'Select branch',
|
|
490
|
+
});
|
|
491
|
+
|
|
462
492
|
export default {
|
|
463
493
|
title: 'base/new-dropdowns/listbox',
|
|
464
494
|
component: GlCollapsibleListbox,
|
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
ARROW_DOWN,
|
|
13
13
|
ARROW_UP,
|
|
14
14
|
GL_DROPDOWN_CONTENTS_CLASS,
|
|
15
|
+
POSITION_ABSOLUTE,
|
|
16
|
+
POSITION_FIXED,
|
|
15
17
|
} from '../constants';
|
|
16
18
|
import {
|
|
17
19
|
buttonCategoryOptions,
|
|
@@ -318,6 +320,17 @@ export default {
|
|
|
318
320
|
required: false,
|
|
319
321
|
default: false,
|
|
320
322
|
},
|
|
323
|
+
/**
|
|
324
|
+
* Strategy to be applied by computePosition. If the dropdown's container is too short for it to
|
|
325
|
+
* fit in, setting this to fixed will let it position itself above its container.
|
|
326
|
+
* https://floating-ui.com/docs/computePosition#strategy
|
|
327
|
+
*/
|
|
328
|
+
positioningStrategy: {
|
|
329
|
+
type: String,
|
|
330
|
+
required: false,
|
|
331
|
+
default: POSITION_ABSOLUTE,
|
|
332
|
+
validator: (strategy) => [POSITION_ABSOLUTE, POSITION_FIXED].includes(strategy),
|
|
333
|
+
},
|
|
321
334
|
},
|
|
322
335
|
data() {
|
|
323
336
|
return {
|
|
@@ -709,6 +722,7 @@ export default {
|
|
|
709
722
|
:placement="placement"
|
|
710
723
|
:offset="dropdownOffset"
|
|
711
724
|
:fluid-width="fluidWidth"
|
|
725
|
+
:positioning-strategy="positioningStrategy"
|
|
712
726
|
@[$options.events.GL_DROPDOWN_SHOWN]="onShow"
|
|
713
727
|
@[$options.events.GL_DROPDOWN_HIDDEN]="onHide"
|
|
714
728
|
>
|
|
@@ -67,6 +67,49 @@ export const mockGroups = [
|
|
|
67
67
|
},
|
|
68
68
|
];
|
|
69
69
|
|
|
70
|
+
export const mockGroupsWithTextSrOnly = [
|
|
71
|
+
{
|
|
72
|
+
text: 'Default',
|
|
73
|
+
options: [
|
|
74
|
+
{
|
|
75
|
+
text: 'main',
|
|
76
|
+
value: 'main',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
text: 'development',
|
|
80
|
+
value: 'development',
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
textSrOnly: true,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
text: 'Feature branches',
|
|
87
|
+
options: [
|
|
88
|
+
{
|
|
89
|
+
text: 'feature/add-avatar',
|
|
90
|
+
value: 'add',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
text: 'feature/improve-panel',
|
|
94
|
+
value: 'improve',
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
text: 'Bugfix branches',
|
|
100
|
+
options: [
|
|
101
|
+
{
|
|
102
|
+
text: 'fix/border-of-avatar',
|
|
103
|
+
value: 'fix-border',
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
text: 'fix/radius-panel',
|
|
107
|
+
value: 'fix-radius',
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
];
|
|
112
|
+
|
|
70
113
|
export const mockUsers = [
|
|
71
114
|
{
|
|
72
115
|
value: 'mikegreiling',
|