@gitlab/ui 66.14.0 → 66.16.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/filtered_search/filtered_search_token.js +31 -21
- package/dist/components/base/filtered_search/filtered_search_token_segment.js +3 -2
- package/dist/index.css.map +1 -1
- package/dist/tokens/css/tokens.css +1 -1
- package/dist/tokens/css/tokens.dark.css +1 -1
- package/dist/tokens/js/tokens.dark.js +1 -1
- package/dist/tokens/js/tokens.js +1 -1
- package/dist/tokens/scss/_tokens.dark.scss +1 -1
- package/dist/tokens/scss/_tokens.scss +1 -1
- package/dist/utility_classes.css +1 -1
- package/dist/utility_classes.css.map +1 -1
- package/package.json +2 -2
- package/src/components/base/filtered_search/filtered_search.stories.js +11 -4
- package/src/components/base/filtered_search/filtered_search_token.spec.js +25 -7
- package/src/components/base/filtered_search/filtered_search_token.vue +29 -18
- package/src/components/base/filtered_search/filtered_search_token_segment.spec.js +1 -1
- package/src/components/base/filtered_search/filtered_search_token_segment.vue +4 -2
- package/src/scss/mixins.scss +19 -9
- package/src/scss/utilities.scss +350 -92
- package/src/scss/utility-mixins/typography.scss +153 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gitlab/ui",
|
|
3
|
-
"version": "66.
|
|
3
|
+
"version": "66.16.0",
|
|
4
4
|
"description": "GitLab UI Components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -126,7 +126,7 @@
|
|
|
126
126
|
"cypress-axe": "^1.4.0",
|
|
127
127
|
"dompurify": "^3.0.0",
|
|
128
128
|
"emoji-regex": "^10.0.0",
|
|
129
|
-
"eslint": "8.
|
|
129
|
+
"eslint": "8.50.0",
|
|
130
130
|
"eslint-import-resolver-jest": "3.0.2",
|
|
131
131
|
"eslint-plugin-cypress": "2.15.1",
|
|
132
132
|
"eslint-plugin-storybook": "0.6.14",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import last from 'lodash/last';
|
|
1
2
|
import GlLoadingIcon from '../loading_icon/loading_icon.vue';
|
|
2
3
|
import GlIcon from '../icon/icon.vue';
|
|
3
4
|
import GlToken from '../token/token.vue';
|
|
@@ -525,13 +526,19 @@ export const WithMultiSelect = () => {
|
|
|
525
526
|
data() {
|
|
526
527
|
return {
|
|
527
528
|
users: fakeUsers,
|
|
528
|
-
selectedUsernames: this.value.data
|
|
529
|
+
selectedUsernames: this.value.data || [],
|
|
529
530
|
activeUser: null,
|
|
530
531
|
};
|
|
531
532
|
},
|
|
532
533
|
computed: {
|
|
533
534
|
filteredUsers() {
|
|
534
|
-
|
|
535
|
+
let term = this.value.data;
|
|
536
|
+
|
|
537
|
+
if (Array.isArray(this.value.data) && this.value.data.length > 1) {
|
|
538
|
+
term = last(this.value.data);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
return this.users.filter((user) => user.username.includes(term));
|
|
535
542
|
},
|
|
536
543
|
selectedUsers() {
|
|
537
544
|
return this.config.multiSelect
|
|
@@ -593,7 +600,7 @@ export const WithMultiSelect = () => {
|
|
|
593
600
|
<template v-for="(user, index) in selectedUsers">
|
|
594
601
|
<gl-avatar :size="16" :entity-name="user.username" shape="circle" />
|
|
595
602
|
{{ user.name }}
|
|
596
|
-
<span v-if="!isLastUser(index)"
|
|
603
|
+
<span v-if="!isLastUser(index)">, </span>
|
|
597
604
|
</template>
|
|
598
605
|
</template>
|
|
599
606
|
<template #suggestions>
|
|
@@ -635,7 +642,7 @@ export const WithMultiSelect = () => {
|
|
|
635
642
|
multiSelect: true,
|
|
636
643
|
},
|
|
637
644
|
],
|
|
638
|
-
value: [{ type: 'assignee', value: { data: 'alpha,beta', operator: '=' } }],
|
|
645
|
+
value: [{ type: 'assignee', value: { data: ['alpha', 'beta'], operator: '=' } }],
|
|
639
646
|
};
|
|
640
647
|
},
|
|
641
648
|
template: `
|
|
@@ -334,14 +334,32 @@ describe('Filtered search token', () => {
|
|
|
334
334
|
active: true,
|
|
335
335
|
config: { multiSelect: true },
|
|
336
336
|
multiSelectValues: ['alpha', 'beta'],
|
|
337
|
-
value: { operator: '=', data: 'alpha' },
|
|
337
|
+
value: { operator: '=', data: ['alpha', 'beta'] },
|
|
338
338
|
});
|
|
339
339
|
});
|
|
340
340
|
|
|
341
|
-
it('emits input event when
|
|
342
|
-
|
|
341
|
+
it('emits input event when active is false', async () => {
|
|
342
|
+
wrapper.setProps({ value: { data: 'user', operator: '=' } });
|
|
343
|
+
wrapper.setProps({ active: false });
|
|
343
344
|
|
|
344
|
-
|
|
345
|
+
await nextTick();
|
|
346
|
+
|
|
347
|
+
expect(wrapper.emitted('input')).toEqual([
|
|
348
|
+
[{ data: 'user', operator: '=' }],
|
|
349
|
+
[{ data: ['alpha', 'beta'], operator: '=' }],
|
|
350
|
+
]);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it('emits input event when active is false and search term empty', async () => {
|
|
354
|
+
wrapper.setProps({ value: { data: '', operator: '=' } });
|
|
355
|
+
wrapper.setProps({ active: false });
|
|
356
|
+
|
|
357
|
+
await nextTick();
|
|
358
|
+
|
|
359
|
+
expect(wrapper.emitted('input')).toEqual([
|
|
360
|
+
[{ data: '', operator: '=' }],
|
|
361
|
+
[{ data: ['alpha', 'beta'], operator: '=' }],
|
|
362
|
+
]);
|
|
345
363
|
});
|
|
346
364
|
|
|
347
365
|
it('emits empty input event when data segment is activated, so blank text input shows all suggestions', () => {
|
|
@@ -352,14 +370,14 @@ describe('Filtered search token', () => {
|
|
|
352
370
|
|
|
353
371
|
it('passes down the value prop to the data segment if it changes', async () => {
|
|
354
372
|
createComponent({
|
|
355
|
-
value: { operator: '=', data: 'alpha' },
|
|
373
|
+
value: { operator: '=', data: ['alpha'] },
|
|
356
374
|
});
|
|
357
375
|
|
|
358
376
|
await wrapper.setProps({
|
|
359
|
-
value: { operator: '=', data: 'gamma' },
|
|
377
|
+
value: { operator: '=', data: ['gamma'] },
|
|
360
378
|
});
|
|
361
379
|
|
|
362
|
-
expect(findDataSegment().props('value')).toEqual('gamma');
|
|
380
|
+
expect(findDataSegment().props('value')).toEqual(['gamma']);
|
|
363
381
|
});
|
|
364
382
|
});
|
|
365
383
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import cloneDeep from 'lodash/cloneDeep';
|
|
3
|
-
import
|
|
3
|
+
import isEqual from 'lodash/isEqual';
|
|
4
4
|
import GlToken from '../token/token.vue';
|
|
5
5
|
import GlFilteredSearchTokenSegment from './filtered_search_token_segment.vue';
|
|
6
6
|
import { createTerm, tokenToOption, TOKEN_CLOSE_SELECTOR } from './filtered_search_utils';
|
|
@@ -97,8 +97,13 @@ export default {
|
|
|
97
97
|
return this.config.operators || DEFAULT_OPERATORS;
|
|
98
98
|
},
|
|
99
99
|
|
|
100
|
+
tokenEmpty() {
|
|
101
|
+
return this.tokenValue.data?.length === 0;
|
|
102
|
+
},
|
|
103
|
+
|
|
100
104
|
hasDataOrDataSegmentIsCurrentlyActive() {
|
|
101
|
-
|
|
105
|
+
const hasData = !this.tokenEmpty;
|
|
106
|
+
return hasData || this.isSegmentActive(SEGMENT_DATA);
|
|
102
107
|
},
|
|
103
108
|
|
|
104
109
|
availableTokensWithSelf() {
|
|
@@ -137,7 +142,7 @@ export default {
|
|
|
137
142
|
|
|
138
143
|
value: {
|
|
139
144
|
handler(newValue, oldValue) {
|
|
140
|
-
if (newValue?.data
|
|
145
|
+
if (isEqual(newValue?.data, oldValue?.data) && newValue?.operator === oldValue?.operator) {
|
|
141
146
|
return;
|
|
142
147
|
}
|
|
143
148
|
|
|
@@ -147,20 +152,29 @@ export default {
|
|
|
147
152
|
|
|
148
153
|
active: {
|
|
149
154
|
immediate: true,
|
|
150
|
-
handler(
|
|
151
|
-
if (
|
|
155
|
+
handler(tokenIsActive) {
|
|
156
|
+
if (tokenIsActive) {
|
|
152
157
|
this.intendedCursorPosition = this.cursorPosition;
|
|
153
158
|
if (!this.activeSegment) {
|
|
154
|
-
this.activateSegment(this.
|
|
159
|
+
this.activateSegment(this.tokenEmpty ? SEGMENT_OPERATOR : SEGMENT_DATA);
|
|
155
160
|
}
|
|
156
|
-
} else
|
|
161
|
+
} else {
|
|
157
162
|
this.activeSegment = null;
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
163
|
+
|
|
164
|
+
// restore multi select values if we have them
|
|
165
|
+
// otherwise destroy the token
|
|
166
|
+
if (this.config.multiSelect) {
|
|
167
|
+
this.$emit('input', { ...this.tokenValue, data: this.multiSelectValues || '' });
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (this.tokenEmpty && this.multiSelectValues.length === 0) {
|
|
171
|
+
/**
|
|
172
|
+
* Emitted when token is about to be destroyed.
|
|
173
|
+
*
|
|
174
|
+
* @event destroy
|
|
175
|
+
*/
|
|
176
|
+
this.$emit('destroy');
|
|
177
|
+
}
|
|
164
178
|
}
|
|
165
179
|
},
|
|
166
180
|
},
|
|
@@ -206,7 +220,7 @@ export default {
|
|
|
206
220
|
},
|
|
207
221
|
|
|
208
222
|
replaceWithTermIfEmpty() {
|
|
209
|
-
if (this.tokenValue.operator === '' && this.
|
|
223
|
+
if (this.tokenValue.operator === '' && this.tokenEmpty) {
|
|
210
224
|
/**
|
|
211
225
|
* Emitted when this token is converted to another type
|
|
212
226
|
* @property {object} token Replacement token configuration
|
|
@@ -252,7 +266,7 @@ export default {
|
|
|
252
266
|
key.length === 1 &&
|
|
253
267
|
!this.operators.find(({ value }) => value.startsWith(potentialValue))
|
|
254
268
|
) {
|
|
255
|
-
if (this.
|
|
269
|
+
if (this.tokenEmpty) {
|
|
256
270
|
applySuggestion(suggestedValue);
|
|
257
271
|
} else {
|
|
258
272
|
evt.preventDefault();
|
|
@@ -288,9 +302,6 @@ export default {
|
|
|
288
302
|
},
|
|
289
303
|
|
|
290
304
|
handleComplete() {
|
|
291
|
-
if (this.config.multiSelect) {
|
|
292
|
-
this.$emit('input', { ...this.tokenValue, data: this.multiSelectValues.join(COMMA) });
|
|
293
|
-
}
|
|
294
305
|
/**
|
|
295
306
|
* Emitted when the token entry has been completed.
|
|
296
307
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import last from 'lodash/last';
|
|
3
3
|
import { Portal } from 'portal-vue';
|
|
4
|
-
import {
|
|
4
|
+
import { LEFT_MOUSE_BUTTON } from '../../../utils/constants';
|
|
5
5
|
import GlFilteredSearchSuggestion from './filtered_search_suggestion.vue';
|
|
6
6
|
import GlFilteredSearchSuggestionList from './filtered_search_suggestion_list.vue';
|
|
7
7
|
import { splitOnQuotes, wrapTokenInQuotes, match, TERM_TOKEN_TYPE } from './filtered_search_utils';
|
|
@@ -152,7 +152,7 @@ export default {
|
|
|
152
152
|
},
|
|
153
153
|
|
|
154
154
|
nonMultipleValue() {
|
|
155
|
-
return this.
|
|
155
|
+
return Array.isArray(this.value) ? last(this.value) : this.value;
|
|
156
156
|
},
|
|
157
157
|
|
|
158
158
|
inputValue: {
|
|
@@ -228,6 +228,8 @@ export default {
|
|
|
228
228
|
inputValue(newValue) {
|
|
229
229
|
if (this.termsAsTokens()) return;
|
|
230
230
|
|
|
231
|
+
if (this.multiSelect) return;
|
|
232
|
+
|
|
231
233
|
const hasUnclosedQuote = newValue.split('"').length % 2 === 0;
|
|
232
234
|
if (newValue.indexOf(' ') === -1 || hasUnclosedQuote) {
|
|
233
235
|
return;
|
package/src/scss/mixins.scss
CHANGED
|
@@ -123,9 +123,14 @@
|
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
// Helper functions to aid maintenance of heading styles
|
|
127
126
|
|
|
128
|
-
|
|
127
|
+
/**
|
|
128
|
+
* Helper function to resolve font-size value from $gl-font-sizes and
|
|
129
|
+
* $gl-font-sizes-fixed maps.
|
|
130
|
+
*
|
|
131
|
+
* @param $size Number font-size scale
|
|
132
|
+
* @param $fixed Boolean toggle default and fixed font size scales
|
|
133
|
+
*/
|
|
129
134
|
@function get-font-size-variable($size, $fixed) {
|
|
130
135
|
@if $fixed == true {
|
|
131
136
|
@if map-has-key($gl-font-sizes-fixed, $size) {
|
|
@@ -144,17 +149,22 @@
|
|
|
144
149
|
}
|
|
145
150
|
}
|
|
146
151
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
152
|
+
/**
|
|
153
|
+
* Defines default properties for heading typography based on font-size
|
|
154
|
+
* scale value and default or fixed sizing.
|
|
155
|
+
*
|
|
156
|
+
* Note: overrides Bootstrap margin-top, other margin is determined by
|
|
157
|
+
* individual context
|
|
158
|
+
*
|
|
159
|
+
* @param $size Number font-size scale
|
|
160
|
+
* @param $fixed Boolean toggle default and fixed font size scales
|
|
161
|
+
*/
|
|
162
|
+
@mixin gl-heading-scale($size, $fixed: false) {
|
|
151
163
|
font-weight: $gl-font-weight-heading;
|
|
152
164
|
margin-top: 0; // override bootstrap reset in GitLab
|
|
153
|
-
|
|
154
|
-
// Because we can't interpolate into a variable name we have to do some stuff
|
|
155
165
|
font-size: get-font-size-variable($size, $fixed);
|
|
156
166
|
|
|
157
|
-
// Larger headings have
|
|
167
|
+
// Larger headings have reduced letter spacing
|
|
158
168
|
@if ($size <= 500) {
|
|
159
169
|
letter-spacing: $gl-letter-spacing-heading;
|
|
160
170
|
} @else {
|