@gitlab/ui 46.1.0 → 47.0.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 +25 -0
- package/dist/components/base/breadcrumb/breadcrumb.js +1 -1
- package/dist/components/base/datepicker/datepicker.js +17 -11
- package/dist/components/base/dropdown/dropdown.js +22 -5
- package/dist/components/base/filtered_search/filtered_search.js +1 -1
- package/dist/components/base/form/form_checkbox_tree/form_checkbox_tree.js +1 -1
- package/dist/components/base/form/form_checkbox_tree/models/tree.js +0 -1
- package/dist/components/base/infinite_scroll/infinite_scroll.js +17 -26
- package/dist/components/base/pagination/pagination.js +3 -1
- package/dist/components/base/search_box_by_click/search_box_by_click.js +1 -1
- package/dist/components/base/sorting/sorting_item.js +9 -2
- package/dist/components/charts/column/column.js +0 -1
- package/dist/components/utilities/intersperse/intersperse.js +1 -1
- package/dist/utility_classes.css +1 -1
- package/dist/utility_classes.css.map +1 -1
- package/package.json +7 -7
- package/src/components/base/alert/alert.spec.js +3 -3
- package/src/components/base/breadcrumb/breadcrumb.spec.js +0 -2
- package/src/components/base/breadcrumb/breadcrumb.vue +1 -1
- package/src/components/base/datepicker/datepicker.vue +18 -11
- package/src/components/base/daterange_picker/daterange_picker.spec.js +1 -3
- package/src/components/base/dropdown/dropdown.spec.js +37 -0
- package/src/components/base/dropdown/dropdown.stories.js +24 -1
- package/src/components/base/dropdown/dropdown.vue +22 -3
- package/src/components/base/filtered_search/filtered_search.vue +29 -30
- package/src/components/base/form/form_checkbox_tree/form_checkbox_tree.vue +1 -1
- package/src/components/base/form/form_checkbox_tree/models/tree.js +0 -1
- package/src/components/base/form/form_group/form_group.spec.js +1 -0
- package/src/components/base/infinite_scroll/infinite_scroll.vue +15 -24
- package/src/components/base/new_dropdowns/base_dropdown/base_dropdown.spec.js +1 -0
- package/src/components/base/new_dropdowns/listbox/listbox.spec.js +1 -0
- package/src/components/base/new_dropdowns/listbox/listbox.stories.js +64 -99
- package/src/components/base/pagination/pagination.spec.js +1 -1
- package/src/components/base/pagination/pagination.vue +6 -4
- package/src/components/base/search_box_by_click/search_box_by_click.spec.js +6 -5
- package/src/components/base/search_box_by_click/search_box_by_click.vue +1 -1
- package/src/components/base/sorting/sorting.spec.js +0 -17
- package/src/components/base/sorting/sorting_item.spec.js +3 -1
- package/src/components/base/sorting/sorting_item.vue +9 -10
- package/src/components/base/tabs/tab/tab.spec.js +2 -3
- package/src/components/base/tabs/tabs/scrollable_tabs.spec.js +3 -1
- package/src/components/charts/column/column.vue +1 -1
- package/src/components/utilities/intersection_observer/intersection_observer.spec.js +1 -1
- package/src/components/utilities/intersperse/intersperse.vue +1 -1
- package/src/components/utilities/sprintf/sprintf.spec.js +1 -5
- package/src/components/utilities/truncate/truncate.spec.js +4 -5
- package/src/directives/hover_load/hover_load.spec.js +1 -1
- package/src/directives/safe_link/safe_link.spec.js +11 -13
- package/src/scss/utilities.scss +2 -12
- package/src/scss/utility-mixins/spacing.scss +1 -8
|
@@ -61,37 +61,45 @@ const generateProps = ({
|
|
|
61
61
|
startOpened,
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
+
const makeBindings = (overrides = {}) =>
|
|
65
|
+
Object.entries({
|
|
66
|
+
':items': 'items',
|
|
67
|
+
':category': 'category',
|
|
68
|
+
':variant': 'variant',
|
|
69
|
+
':size': 'size',
|
|
70
|
+
':disabled': 'disabled',
|
|
71
|
+
':loading': 'loading',
|
|
72
|
+
':searchable': 'searchable',
|
|
73
|
+
':searching': 'searching',
|
|
74
|
+
':no-results-text': 'noResultsText',
|
|
75
|
+
':no-caret': 'noCaret',
|
|
76
|
+
':right': 'right',
|
|
77
|
+
':toggle-text': 'toggleText',
|
|
78
|
+
':text-sr-only': 'textSrOnly',
|
|
79
|
+
':icon': 'icon',
|
|
80
|
+
':multiple': 'multiple',
|
|
81
|
+
':is-check-centered': 'isCheckCentered',
|
|
82
|
+
':toggle-aria-labelled-by': 'toggleAriaLabelledBy',
|
|
83
|
+
':list-aria-labelled-by': 'listAriaLabelledBy',
|
|
84
|
+
...overrides,
|
|
85
|
+
})
|
|
86
|
+
.map(([key, value]) => `${key}="${value}"`)
|
|
87
|
+
.join('\n');
|
|
88
|
+
|
|
64
89
|
function openListbox(component) {
|
|
65
90
|
component.$nextTick(() => {
|
|
66
91
|
component.$refs.listbox.open();
|
|
67
92
|
});
|
|
68
93
|
}
|
|
69
94
|
|
|
70
|
-
const template = (content, label = '') => `
|
|
95
|
+
const template = (content, { label = '', bindingOverrides = {} } = {}) => `
|
|
71
96
|
<div>
|
|
72
97
|
${label}
|
|
73
|
-
<br/>
|
|
98
|
+
${label && '<br/>'}
|
|
74
99
|
<gl-listbox
|
|
75
100
|
ref="listbox"
|
|
76
101
|
v-model="selected"
|
|
77
|
-
|
|
78
|
-
:category="category"
|
|
79
|
-
:variant="variant"
|
|
80
|
-
:size="size"
|
|
81
|
-
:disabled="disabled"
|
|
82
|
-
:loading="loading"
|
|
83
|
-
:searchable="searchable"
|
|
84
|
-
:searching="searching"
|
|
85
|
-
:no-results-text="noResultsText"
|
|
86
|
-
:no-caret="noCaret"
|
|
87
|
-
:right="right"
|
|
88
|
-
:toggle-text="toggleText"
|
|
89
|
-
:text-sr-only="textSrOnly"
|
|
90
|
-
:icon="icon"
|
|
91
|
-
:multiple="multiple"
|
|
92
|
-
:is-check-centered="isCheckCentered"
|
|
93
|
-
:toggle-aria-labelled-by="toggleAriaLabelledBy"
|
|
94
|
-
:list-aria-labelled-by="listAriaLabelledBy"
|
|
102
|
+
${makeBindings(bindingOverrides)}
|
|
95
103
|
>
|
|
96
104
|
${content}
|
|
97
105
|
</gl-listbox>
|
|
@@ -113,7 +121,9 @@ export const Default = (args, { argTypes }) => ({
|
|
|
113
121
|
openListbox(this);
|
|
114
122
|
}
|
|
115
123
|
},
|
|
116
|
-
template: template('',
|
|
124
|
+
template: template('', {
|
|
125
|
+
label: `<span class="gl-my-0" id="listbox-label">Select a department</span>`,
|
|
126
|
+
}),
|
|
117
127
|
});
|
|
118
128
|
Default.args = generateProps({ toggleAriaLabelledBy: 'listbox-label' });
|
|
119
129
|
Default.decorators = [makeContainer({ height: '370px' })];
|
|
@@ -181,36 +191,14 @@ export const CustomListItem = (args, { argTypes }) => ({
|
|
|
181
191
|
}
|
|
182
192
|
},
|
|
183
193
|
computed: {
|
|
184
|
-
|
|
194
|
+
customToggleText() {
|
|
185
195
|
return this.selected.length !== 1
|
|
186
196
|
? `${this.selected.length} assignees`
|
|
187
197
|
: this.items.find(({ value }) => value === this.selected[0]).text;
|
|
188
198
|
},
|
|
189
199
|
},
|
|
190
|
-
template:
|
|
191
|
-
|
|
192
|
-
ref="listbox"
|
|
193
|
-
v-model="selected"
|
|
194
|
-
:items="items"
|
|
195
|
-
:category="category"
|
|
196
|
-
:variant="variant"
|
|
197
|
-
:size="size"
|
|
198
|
-
:disabled="disabled"
|
|
199
|
-
:loading="loading"
|
|
200
|
-
:searchable="searchable"
|
|
201
|
-
:searching="searching"
|
|
202
|
-
:no-results-text="noResultsText"
|
|
203
|
-
:no-caret="noCaret"
|
|
204
|
-
:right="right"
|
|
205
|
-
:toggle-text="headerText"
|
|
206
|
-
:text-sr-only="textSrOnly"
|
|
207
|
-
:icon="icon"
|
|
208
|
-
:multiple="multiple"
|
|
209
|
-
:is-check-centered="isCheckCentered"
|
|
210
|
-
:toggle-aria-labelled-by="toggleAriaLabelledBy"
|
|
211
|
-
:list-aria-labelled-by="listAriaLabelledBy"
|
|
212
|
-
>
|
|
213
|
-
<template #list-item="{ item }">
|
|
200
|
+
template: template(
|
|
201
|
+
`<template #list-item="{ item }">
|
|
214
202
|
<span class="gl-display-flex gl-align-items-center">
|
|
215
203
|
<gl-avatar :size="32" class-="gl-mr-3"/>
|
|
216
204
|
<span class="gl-display-flex gl-flex-direction-column">
|
|
@@ -219,8 +207,13 @@ export const CustomListItem = (args, { argTypes }) => ({
|
|
|
219
207
|
</span>
|
|
220
208
|
</span>
|
|
221
209
|
</template>
|
|
222
|
-
</gl-listbox>
|
|
223
210
|
`,
|
|
211
|
+
{
|
|
212
|
+
bindingOverrides: {
|
|
213
|
+
':toggle-text': 'customToggleText',
|
|
214
|
+
},
|
|
215
|
+
}
|
|
216
|
+
),
|
|
224
217
|
});
|
|
225
218
|
|
|
226
219
|
CustomListItem.args = generateProps({
|
|
@@ -364,31 +357,8 @@ export const Searchable = (args, { argTypes }) => ({
|
|
|
364
357
|
return this.filteredItems.length === 1 ? '1 result' : `${this.filteredItems.length} results`;
|
|
365
358
|
},
|
|
366
359
|
},
|
|
367
|
-
template:
|
|
368
|
-
|
|
369
|
-
ref="listbox"
|
|
370
|
-
v-model="selected"
|
|
371
|
-
:items="filteredItems"
|
|
372
|
-
:category="category"
|
|
373
|
-
:variant="variant"
|
|
374
|
-
:size="size"
|
|
375
|
-
:disabled="disabled"
|
|
376
|
-
:loading="loading"
|
|
377
|
-
:no-caret="noCaret"
|
|
378
|
-
:right="right"
|
|
379
|
-
:toggle-text="customToggleText"
|
|
380
|
-
:text-sr-only="textSrOnly"
|
|
381
|
-
:icon="icon"
|
|
382
|
-
:multiple="multiple"
|
|
383
|
-
:is-check-centered="isCheckCentered"
|
|
384
|
-
:toggle-aria-labelled-by="toggleAriaLabelledBy"
|
|
385
|
-
:list-aria-labelled-by="headerId"
|
|
386
|
-
:searchable="searchable"
|
|
387
|
-
:searching="searchInProgress"
|
|
388
|
-
:no-results-text="noResultsText"
|
|
389
|
-
@search="filterList"
|
|
390
|
-
>
|
|
391
|
-
<template #header>
|
|
360
|
+
template: template(
|
|
361
|
+
`<template #header>
|
|
392
362
|
<p :id="headerId"
|
|
393
363
|
class="gl-font-weight-bold gl-font-sm gl-m-0 gl-text-center gl-py-2 gl-border-1 gl-border-b-solid gl-border-gray-200">
|
|
394
364
|
Assign to department</p>
|
|
@@ -396,8 +366,17 @@ export const Searchable = (args, { argTypes }) => ({
|
|
|
396
366
|
<template #search-summary-sr-only>
|
|
397
367
|
{{ numberOfSearchResults }}
|
|
398
368
|
</template>
|
|
399
|
-
</gl-listbox>
|
|
400
369
|
`,
|
|
370
|
+
{
|
|
371
|
+
bindingOverrides: {
|
|
372
|
+
':items': 'filteredItems',
|
|
373
|
+
':list-aria-labelled-by': 'headerId',
|
|
374
|
+
':toggle-text': 'customToggleText',
|
|
375
|
+
':searching': 'searchInProgress',
|
|
376
|
+
'@search': 'filterList',
|
|
377
|
+
},
|
|
378
|
+
}
|
|
379
|
+
),
|
|
401
380
|
});
|
|
402
381
|
Searchable.args = generateProps({ searchable: true });
|
|
403
382
|
Searchable.decorators = [makeContainer({ height: '370px' })];
|
|
@@ -470,31 +449,8 @@ export const SearchableGroups = (args, { argTypes }) => ({
|
|
|
470
449
|
}, 2000);
|
|
471
450
|
},
|
|
472
451
|
},
|
|
473
|
-
template:
|
|
474
|
-
|
|
475
|
-
ref="listbox"
|
|
476
|
-
v-model="selected"
|
|
477
|
-
:items="filteredGroupOptions"
|
|
478
|
-
:category="category"
|
|
479
|
-
:variant="variant"
|
|
480
|
-
:size="size"
|
|
481
|
-
:disabled="disabled"
|
|
482
|
-
:loading="loading"
|
|
483
|
-
:no-caret="noCaret"
|
|
484
|
-
:right="right"
|
|
485
|
-
:toggle-text="customToggleText"
|
|
486
|
-
:text-sr-only="textSrOnly"
|
|
487
|
-
:icon="icon"
|
|
488
|
-
:multiple="multiple"
|
|
489
|
-
:is-check-centered="isCheckCentered"
|
|
490
|
-
:toggle-aria-labelled-by="toggleAriaLabelledBy"
|
|
491
|
-
:list-aria-labelled-by="headerId"
|
|
492
|
-
:searching="searchInProgress"
|
|
493
|
-
:no-results-text="noResultsText"
|
|
494
|
-
:searchable="searchable"
|
|
495
|
-
@search="filterList"
|
|
496
|
-
>
|
|
497
|
-
<template #header>
|
|
452
|
+
template: template(
|
|
453
|
+
`<template #header>
|
|
498
454
|
<p :id="headerId"
|
|
499
455
|
class="gl-font-weight-bold gl-font-sm gl-m-0 gl-text-center gl-py-2 gl-border-1 gl-border-b-solid gl-border-gray-200">
|
|
500
456
|
Assign to department</p>
|
|
@@ -502,8 +458,17 @@ export const SearchableGroups = (args, { argTypes }) => ({
|
|
|
502
458
|
<template #search-summary-sr-only>
|
|
503
459
|
{{ numberOfSearchResults }}
|
|
504
460
|
</template>
|
|
505
|
-
</gl-listbox>
|
|
506
461
|
`,
|
|
462
|
+
{
|
|
463
|
+
bindingOverrides: {
|
|
464
|
+
':items': 'filteredGroupOptions',
|
|
465
|
+
':list-aria-labelled-by': 'headerId',
|
|
466
|
+
':toggle-text': 'customToggleText',
|
|
467
|
+
':searching': 'searchInProgress',
|
|
468
|
+
'@search': 'filterList',
|
|
469
|
+
},
|
|
470
|
+
}
|
|
471
|
+
),
|
|
507
472
|
});
|
|
508
473
|
SearchableGroups.args = generateProps({ searchable: true, items: mockGroups });
|
|
509
474
|
SearchableGroups.decorators = [makeContainer({ height: '370px' })];
|
|
@@ -339,6 +339,8 @@ export default {
|
|
|
339
339
|
slot: `ellipsis-${side}`,
|
|
340
340
|
component: 'span',
|
|
341
341
|
disabled: true,
|
|
342
|
+
slotData: {},
|
|
343
|
+
listeners: {},
|
|
342
344
|
};
|
|
343
345
|
},
|
|
344
346
|
handleClick(event, value) {
|
|
@@ -395,8 +397,8 @@ export default {
|
|
|
395
397
|
:href="prevPageHref"
|
|
396
398
|
@click="handlePrevious($event, value - 1)"
|
|
397
399
|
>
|
|
398
|
-
<!--
|
|
399
|
-
@slot Content for the "previous" button. Overrides the "prevText" prop.
|
|
400
|
+
<!--
|
|
401
|
+
@slot Content for the "previous" button. Overrides the "prevText" prop.
|
|
400
402
|
@binding {boolean} active
|
|
401
403
|
@binding {boolean} disabled
|
|
402
404
|
@binding {number} number
|
|
@@ -458,8 +460,8 @@ export default {
|
|
|
458
460
|
:href="nextPageHref"
|
|
459
461
|
@click="handleNext($event, value + 1)"
|
|
460
462
|
>
|
|
461
|
-
<!--
|
|
462
|
-
@slot Content for the "next" button. Overrides the "nextText" prop.
|
|
463
|
+
<!--
|
|
464
|
+
@slot Content for the "next" button. Overrides the "nextText" prop.
|
|
463
465
|
@binding {boolean} active
|
|
464
466
|
@binding {boolean} disabled
|
|
465
467
|
@binding {number} number
|
|
@@ -125,17 +125,19 @@ describe('search box by click component', () => {
|
|
|
125
125
|
|
|
126
126
|
it('displays disabled history dropdown', () => {
|
|
127
127
|
expect(wrapper.findComponent({ ref: 'historyDropdown' }).exists()).toBe(true);
|
|
128
|
-
expect(
|
|
128
|
+
expect(
|
|
129
|
+
wrapper.findComponent({ ref: 'historyDropdown' }).attributes('disabled')
|
|
130
|
+
).toBeDefined();
|
|
129
131
|
});
|
|
130
132
|
|
|
131
133
|
it('displays disabled input', () => {
|
|
132
134
|
expect(wrapper.findComponent({ ref: 'input' }).exists()).toBe(true);
|
|
133
|
-
expect(wrapper.findComponent({ ref: 'input' }).attributes('disabled')).
|
|
135
|
+
expect(wrapper.findComponent({ ref: 'input' }).attributes('disabled')).toBeDefined();
|
|
134
136
|
});
|
|
135
137
|
|
|
136
138
|
it('displays disabled search button', () => {
|
|
137
139
|
expect(findSearchButton().exists()).toBe(true);
|
|
138
|
-
expect(findSearchButton().attributes('disabled')).
|
|
140
|
+
expect(findSearchButton().attributes('disabled')).toBeDefined();
|
|
139
141
|
});
|
|
140
142
|
|
|
141
143
|
it('does not render clear icon even with value', () => {
|
|
@@ -146,9 +148,8 @@ describe('search box by click component', () => {
|
|
|
146
148
|
it('emits submit event when Enter key is pressed', async () => {
|
|
147
149
|
createComponent({ value: 'some-input' });
|
|
148
150
|
|
|
149
|
-
wrapper.findComponent(GlFormInput).
|
|
151
|
+
await wrapper.findComponent(GlFormInput).trigger('keydown.enter');
|
|
150
152
|
|
|
151
|
-
await wrapper.vm.$nextTick();
|
|
152
153
|
expect(wrapper.emitted('submit')[0]).toEqual(['some-input']);
|
|
153
154
|
});
|
|
154
155
|
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils';
|
|
2
2
|
import GlDropdown from '../dropdown/dropdown.vue';
|
|
3
|
-
import GlDropdownItem from '../dropdown/dropdown_item.vue';
|
|
4
3
|
import GlIcon from '../icon/icon.vue';
|
|
5
4
|
import GlSorting from './sorting.vue';
|
|
6
|
-
import GlSortingItem from './sorting_item.vue';
|
|
7
5
|
|
|
8
6
|
describe('sorting component', () => {
|
|
9
7
|
let wrapper;
|
|
@@ -22,25 +20,10 @@ describe('sorting component', () => {
|
|
|
22
20
|
|
|
23
21
|
const createComponent = (propsData) => {
|
|
24
22
|
wrapper = mount(GlSorting, {
|
|
25
|
-
attachTo: document.body,
|
|
26
|
-
components: {
|
|
27
|
-
GlSortingItem,
|
|
28
|
-
},
|
|
29
|
-
slots: {
|
|
30
|
-
default: [
|
|
31
|
-
'<gl-sorting-item :active="true">First item</gl-sorting-item>',
|
|
32
|
-
'<gl-sorting-item>Second item</gl-sorting-item>',
|
|
33
|
-
'<gl-sorting-item>Third item</gl-sorting-item>',
|
|
34
|
-
],
|
|
35
|
-
},
|
|
36
23
|
propsData: {
|
|
37
24
|
...defaultProps,
|
|
38
25
|
...propsData,
|
|
39
26
|
},
|
|
40
|
-
stubs: {
|
|
41
|
-
GlSortingItem,
|
|
42
|
-
GlDropdownItem,
|
|
43
|
-
},
|
|
44
27
|
});
|
|
45
28
|
};
|
|
46
29
|
|
|
@@ -15,12 +15,14 @@ describe('sorting item component', () => {
|
|
|
15
15
|
const createComponent = (propsData) => {
|
|
16
16
|
wrapper = shallowMount(GlSortingItem, {
|
|
17
17
|
context: {
|
|
18
|
-
children: [sortingItemText],
|
|
19
18
|
props: {
|
|
20
19
|
...defaultProps,
|
|
21
20
|
...propsData,
|
|
22
21
|
},
|
|
23
22
|
},
|
|
23
|
+
slots: {
|
|
24
|
+
default: sortingItemText,
|
|
25
|
+
},
|
|
24
26
|
stubs: {
|
|
25
27
|
GlDropdownItem,
|
|
26
28
|
},
|
|
@@ -39,7 +39,7 @@ export default {
|
|
|
39
39
|
* The content of the item.
|
|
40
40
|
* @slot default
|
|
41
41
|
*/
|
|
42
|
-
render(createElement, {
|
|
42
|
+
render(createElement, { scopedSlots, data, props = {} }) {
|
|
43
43
|
const classNames = `gl-sorting-item js-active-icon gl-flex-shrink-0 gl-mr-2 ${
|
|
44
44
|
props.active ? '' : 'inactive gl-visibility-hidden'
|
|
45
45
|
}`;
|
|
@@ -52,16 +52,15 @@ export default {
|
|
|
52
52
|
},
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
-
return createElement(
|
|
56
|
-
|
|
57
|
-
{
|
|
58
|
-
...
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
return createElement(GlDropdownItem, {
|
|
56
|
+
...data,
|
|
57
|
+
attrs: {
|
|
58
|
+
...props,
|
|
59
|
+
},
|
|
60
|
+
scopedSlots: {
|
|
61
|
+
default: () => [icon, scopedSlots.default?.()],
|
|
62
62
|
},
|
|
63
|
-
|
|
64
|
-
);
|
|
63
|
+
});
|
|
65
64
|
},
|
|
66
65
|
};
|
|
67
66
|
</script>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import { BTab } from 'bootstrap-vue';
|
|
2
3
|
import { DEFAULT_TAB_TITLE_LINK_CLASS } from '../constants';
|
|
3
4
|
import GlTab from './tab.vue';
|
|
4
5
|
|
|
@@ -27,9 +28,7 @@ describe('Tab component', () => {
|
|
|
27
28
|
},
|
|
28
29
|
});
|
|
29
30
|
|
|
30
|
-
expect(wrapper.
|
|
31
|
-
`<b-tab-stub tag="div" title="" titlelinkclass="${expectedProp}"></b-tab-stub>`
|
|
32
|
-
);
|
|
31
|
+
expect(wrapper.findComponent(BTab).props('titleLinkClass')).toStrictEqual(expectedProp);
|
|
33
32
|
}
|
|
34
33
|
);
|
|
35
34
|
});
|
|
@@ -58,7 +58,9 @@ describe('GlScrollableTabs', () => {
|
|
|
58
58
|
...props,
|
|
59
59
|
},
|
|
60
60
|
slots: {
|
|
61
|
-
default:
|
|
61
|
+
default: {
|
|
62
|
+
template: `<div><gl-tab v-for="tab in tabs" :key="tab.key" :title="tab.title">{{ tab.content }}</gl-tab></div>`,
|
|
63
|
+
},
|
|
62
64
|
},
|
|
63
65
|
stubs: {
|
|
64
66
|
'gl-tab': GlTab,
|
|
@@ -96,7 +96,7 @@ export default {
|
|
|
96
96
|
barSeries() {
|
|
97
97
|
return this.bars.map(({ name, data }, index) => {
|
|
98
98
|
const color = colorFromDefaultPalette(index);
|
|
99
|
-
return generateBarSeries({
|
|
99
|
+
return generateBarSeries({ name, data, color });
|
|
100
100
|
});
|
|
101
101
|
},
|
|
102
102
|
lineSeries() {
|
|
@@ -7,7 +7,7 @@ import filter from 'lodash/fp/filter';
|
|
|
7
7
|
import { intersperse, insert } from '../../../utils/data_utils';
|
|
8
8
|
|
|
9
9
|
const containsWhitespaceOnly = (vNode) => vNode.text.trim() === '';
|
|
10
|
-
const isTag = (vNode) => vNode.tag
|
|
10
|
+
const isTag = (vNode) => typeof vNode.tag === 'string';
|
|
11
11
|
const filterWhitespaceNodes = filter((vNode) => isTag(vNode) || !containsWhitespaceOnly(vNode));
|
|
12
12
|
|
|
13
13
|
const insertAfterSecondLastItem = insert(-1);
|
|
@@ -89,11 +89,7 @@ describe('sprintf component', () => {
|
|
|
89
89
|
});
|
|
90
90
|
|
|
91
91
|
it('should work with a default slot', () => {
|
|
92
|
-
createComponent(
|
|
93
|
-
`<sprintf message="Written by %{default}">
|
|
94
|
-
<template>Author</template>
|
|
95
|
-
</sprintf>`
|
|
96
|
-
);
|
|
92
|
+
createComponent(`<sprintf message="Written by %{default}">Author</sprintf>`);
|
|
97
93
|
|
|
98
94
|
expect(wrapper.element.innerHTML).toBe('Written by Author');
|
|
99
95
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { shallowMount } from '@vue/test-utils';
|
|
2
|
-
import {
|
|
2
|
+
import { getBinding } from '~helpers/vue_mock_directive';
|
|
3
3
|
import { POSITION } from './constants';
|
|
4
4
|
import Truncate from './truncate.vue';
|
|
5
5
|
|
|
@@ -17,10 +17,9 @@ describe('Truncate component', () => {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
const createComponent = (props) => {
|
|
20
|
-
wrapper = shallowMount(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
);
|
|
20
|
+
wrapper = shallowMount(Truncate, {
|
|
21
|
+
propsData: { ...defaultProps, ...props },
|
|
22
|
+
});
|
|
24
23
|
};
|
|
25
24
|
|
|
26
25
|
describe('All', () => {
|
|
@@ -17,20 +17,18 @@ describe('safe link directive', () => {
|
|
|
17
17
|
mountFn = shallowMount,
|
|
18
18
|
} = {}) => {
|
|
19
19
|
const component = {
|
|
20
|
+
props: {
|
|
21
|
+
href: { type: String },
|
|
22
|
+
safeLinkConfig: { type: Object },
|
|
23
|
+
},
|
|
20
24
|
directives: {
|
|
21
25
|
SafeLink: SafeLinkDirective,
|
|
22
26
|
},
|
|
23
27
|
components,
|
|
24
|
-
data() {
|
|
25
|
-
return {
|
|
26
|
-
href,
|
|
27
|
-
safeLinkConfig,
|
|
28
|
-
};
|
|
29
|
-
},
|
|
30
28
|
template,
|
|
31
29
|
};
|
|
32
30
|
|
|
33
|
-
wrapper = mountFn(component);
|
|
31
|
+
wrapper = mountFn(component, { propsData: { href, safeLinkConfig } });
|
|
34
32
|
};
|
|
35
33
|
|
|
36
34
|
describe('default', () => {
|
|
@@ -64,7 +62,7 @@ describe('safe link directive', () => {
|
|
|
64
62
|
describe('valid urls', () => {
|
|
65
63
|
const validUrls = [...absoluteUrls, ...relativeUrls, ...encodedJavaScriptUrls];
|
|
66
64
|
/* Note:
|
|
67
|
-
/* Encoded JavaScript URLs are safe urls in Vue context, since
|
|
65
|
+
/* Encoded JavaScript URLs are safe urls in Vue context, since
|
|
68
66
|
/* Vue attribute bindings are also automatically escaped
|
|
69
67
|
/* https://vuejs.org/v2/guide/security.html#Injecting-URLs
|
|
70
68
|
*/
|
|
@@ -99,13 +97,13 @@ describe('safe link directive', () => {
|
|
|
99
97
|
expect(wrapper.attributes('href')).toBe('about:blank');
|
|
100
98
|
|
|
101
99
|
// set href to a valid url
|
|
102
|
-
wrapper.
|
|
103
|
-
await Vue.nextTick();
|
|
100
|
+
await wrapper.setProps({ href: 'https://gitlab.com' });
|
|
104
101
|
expect(wrapper.attributes('href')).toBe('https://gitlab.com');
|
|
105
102
|
|
|
106
103
|
// set href to back to an invalid url
|
|
107
|
-
wrapper.
|
|
104
|
+
await wrapper.setProps({ href: javascriptUrls[1] });
|
|
108
105
|
await Vue.nextTick();
|
|
106
|
+
|
|
109
107
|
expect(wrapper.attributes('href')).toBe('about:blank');
|
|
110
108
|
});
|
|
111
109
|
|
|
@@ -123,12 +121,12 @@ describe('safe link directive', () => {
|
|
|
123
121
|
expect(wrapper.attributes('href')).toBe(url);
|
|
124
122
|
|
|
125
123
|
// set href to a valid url
|
|
126
|
-
wrapper.
|
|
124
|
+
await wrapper.setProps({ href: 'https://gitlab.com' });
|
|
127
125
|
await Vue.nextTick();
|
|
128
126
|
expect(wrapper.attributes('href')).toBe('https://gitlab.com');
|
|
129
127
|
|
|
130
128
|
// set href to back to an invalid url
|
|
131
|
-
wrapper.
|
|
129
|
+
await wrapper.setProps({ href: url });
|
|
132
130
|
await Vue.nextTick();
|
|
133
131
|
expect(wrapper.attributes('href')).toBe(url);
|
|
134
132
|
});
|
package/src/scss/utilities.scss
CHANGED
|
@@ -6633,12 +6633,12 @@
|
|
|
6633
6633
|
}
|
|
6634
6634
|
}
|
|
6635
6635
|
.gl-sm-pr-2 {
|
|
6636
|
-
@include gl-media-breakpoint-
|
|
6636
|
+
@include gl-media-breakpoint-up(sm) {
|
|
6637
6637
|
padding-right: $gl-spacing-scale-2;
|
|
6638
6638
|
}
|
|
6639
6639
|
}
|
|
6640
6640
|
.gl-sm-pr-2\! {
|
|
6641
|
-
@include gl-media-breakpoint-
|
|
6641
|
+
@include gl-media-breakpoint-up(sm) {
|
|
6642
6642
|
padding-right: $gl-spacing-scale-2 !important;
|
|
6643
6643
|
}
|
|
6644
6644
|
}
|
|
@@ -6762,16 +6762,6 @@
|
|
|
6762
6762
|
padding-right: $gl-spacing-scale-1 !important;
|
|
6763
6763
|
}
|
|
6764
6764
|
}
|
|
6765
|
-
.gl-md-pr-2 {
|
|
6766
|
-
@include gl-media-breakpoint-up(md) {
|
|
6767
|
-
padding-right: $gl-spacing-scale-2;
|
|
6768
|
-
}
|
|
6769
|
-
}
|
|
6770
|
-
.gl-md-pr-2\! {
|
|
6771
|
-
@include gl-media-breakpoint-up(md) {
|
|
6772
|
-
padding-right: $gl-spacing-scale-2 !important;
|
|
6773
|
-
}
|
|
6774
|
-
}
|
|
6775
6765
|
.gl-md-pr-3 {
|
|
6776
6766
|
@include gl-media-breakpoint-up(md) {
|
|
6777
6767
|
padding-right: $gl-spacing-scale-3;
|
|
@@ -1061,8 +1061,7 @@
|
|
|
1061
1061
|
*/
|
|
1062
1062
|
|
|
1063
1063
|
@mixin gl-sm-pr-2 {
|
|
1064
|
-
|
|
1065
|
-
@include gl-media-breakpoint-down(sm) {
|
|
1064
|
+
@include gl-media-breakpoint-up(sm) {
|
|
1066
1065
|
@include gl-pr-2;
|
|
1067
1066
|
}
|
|
1068
1067
|
}
|
|
@@ -1143,12 +1142,6 @@
|
|
|
1143
1142
|
}
|
|
1144
1143
|
}
|
|
1145
1144
|
|
|
1146
|
-
@mixin gl-md-pr-2 {
|
|
1147
|
-
@include gl-media-breakpoint-up(md) {
|
|
1148
|
-
@include gl-pr-2;
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
1145
|
@mixin gl-md-pr-3 {
|
|
1153
1146
|
@include gl-media-breakpoint-up(md) {
|
|
1154
1147
|
@include gl-pr-3;
|