@gitlab/ui 66.14.0 → 66.15.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 +7 -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/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/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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [66.15.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v66.14.0...v66.15.0) (2023-09-26)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **GLFilteredSearch:** Allow commas in multiSelect values ([160781a](https://gitlab.com/gitlab-org/gitlab-ui/commit/160781ad70105069163bc56631fb333a75764163))
|
|
7
|
+
|
|
1
8
|
# [66.14.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v66.13.1...v66.14.0) (2023-09-25)
|
|
2
9
|
|
|
3
10
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import cloneDeep from 'lodash/cloneDeep';
|
|
2
|
-
import
|
|
2
|
+
import isEqual from 'lodash/isEqual';
|
|
3
3
|
import GlToken from '../token/token';
|
|
4
4
|
import GlFilteredSearchTokenSegment from './filtered_search_token_segment';
|
|
5
5
|
import { tokenToOption, createTerm, TOKEN_CLOSE_SELECTOR } from './filtered_search_utils';
|
|
@@ -99,8 +99,13 @@ var script = {
|
|
|
99
99
|
operators() {
|
|
100
100
|
return this.config.operators || DEFAULT_OPERATORS;
|
|
101
101
|
},
|
|
102
|
+
tokenEmpty() {
|
|
103
|
+
var _this$tokenValue$data;
|
|
104
|
+
return ((_this$tokenValue$data = this.tokenValue.data) === null || _this$tokenValue$data === void 0 ? void 0 : _this$tokenValue$data.length) === 0;
|
|
105
|
+
},
|
|
102
106
|
hasDataOrDataSegmentIsCurrentlyActive() {
|
|
103
|
-
|
|
107
|
+
const hasData = !this.tokenEmpty;
|
|
108
|
+
return hasData || this.isSegmentActive(SEGMENT_DATA);
|
|
104
109
|
},
|
|
105
110
|
availableTokensWithSelf() {
|
|
106
111
|
return [this.config, ...this.availableTokens.filter(token => token !== this.config)].map(tokenToOption);
|
|
@@ -135,7 +140,7 @@ var script = {
|
|
|
135
140
|
},
|
|
136
141
|
value: {
|
|
137
142
|
handler(newValue, oldValue) {
|
|
138
|
-
if ((newValue === null || newValue === void 0 ? void 0 : newValue.data
|
|
143
|
+
if (isEqual(newValue === null || newValue === void 0 ? void 0 : newValue.data, oldValue === null || oldValue === void 0 ? void 0 : oldValue.data) && (newValue === null || newValue === void 0 ? void 0 : newValue.operator) === (oldValue === null || oldValue === void 0 ? void 0 : oldValue.operator)) {
|
|
139
144
|
return;
|
|
140
145
|
}
|
|
141
146
|
this.tokenValue = cloneDeep(newValue);
|
|
@@ -143,20 +148,31 @@ var script = {
|
|
|
143
148
|
},
|
|
144
149
|
active: {
|
|
145
150
|
immediate: true,
|
|
146
|
-
handler(
|
|
147
|
-
if (
|
|
151
|
+
handler(tokenIsActive) {
|
|
152
|
+
if (tokenIsActive) {
|
|
148
153
|
this.intendedCursorPosition = this.cursorPosition;
|
|
149
154
|
if (!this.activeSegment) {
|
|
150
|
-
this.activateSegment(this.
|
|
155
|
+
this.activateSegment(this.tokenEmpty ? SEGMENT_OPERATOR : SEGMENT_DATA);
|
|
151
156
|
}
|
|
152
|
-
} else
|
|
157
|
+
} else {
|
|
153
158
|
this.activeSegment = null;
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
159
|
+
|
|
160
|
+
// restore multi select values if we have them
|
|
161
|
+
// otherwise destroy the token
|
|
162
|
+
if (this.config.multiSelect) {
|
|
163
|
+
this.$emit('input', {
|
|
164
|
+
...this.tokenValue,
|
|
165
|
+
data: this.multiSelectValues || ''
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if (this.tokenEmpty && this.multiSelectValues.length === 0) {
|
|
169
|
+
/**
|
|
170
|
+
* Emitted when token is about to be destroyed.
|
|
171
|
+
*
|
|
172
|
+
* @event destroy
|
|
173
|
+
*/
|
|
174
|
+
this.$emit('destroy');
|
|
175
|
+
}
|
|
160
176
|
}
|
|
161
177
|
}
|
|
162
178
|
}
|
|
@@ -203,7 +219,7 @@ var script = {
|
|
|
203
219
|
return this.active && this.activeSegment === segment;
|
|
204
220
|
},
|
|
205
221
|
replaceWithTermIfEmpty() {
|
|
206
|
-
if (this.tokenValue.operator === '' && this.
|
|
222
|
+
if (this.tokenValue.operator === '' && this.tokenEmpty) {
|
|
207
223
|
/**
|
|
208
224
|
* Emitted when this token is converted to another type
|
|
209
225
|
* @property {object} token Replacement token configuration
|
|
@@ -259,7 +275,7 @@ var script = {
|
|
|
259
275
|
} = _ref3;
|
|
260
276
|
return value.startsWith(potentialValue);
|
|
261
277
|
})) {
|
|
262
|
-
if (this.
|
|
278
|
+
if (this.tokenEmpty) {
|
|
263
279
|
applySuggestion(suggestedValue);
|
|
264
280
|
} else {
|
|
265
281
|
evt.preventDefault();
|
|
@@ -292,12 +308,6 @@ var script = {
|
|
|
292
308
|
this.intendedCursorPosition = 'start';
|
|
293
309
|
},
|
|
294
310
|
handleComplete() {
|
|
295
|
-
if (this.config.multiSelect) {
|
|
296
|
-
this.$emit('input', {
|
|
297
|
-
...this.tokenValue,
|
|
298
|
-
data: this.multiSelectValues.join(COMMA)
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
311
|
/**
|
|
302
312
|
* Emitted when the token entry has been completed.
|
|
303
313
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import last from 'lodash/last';
|
|
2
2
|
import { Portal } from 'portal-vue';
|
|
3
|
-
import {
|
|
3
|
+
import { LEFT_MOUSE_BUTTON } from '../../../utils/constants';
|
|
4
4
|
import GlFilteredSearchSuggestion from './filtered_search_suggestion';
|
|
5
5
|
import GlFilteredSearchSuggestionList from './filtered_search_suggestion_list';
|
|
6
6
|
import { TERM_TOKEN_TYPE, splitOnQuotes, match, wrapTokenInQuotes } from './filtered_search_utils';
|
|
@@ -151,7 +151,7 @@ var script = {
|
|
|
151
151
|
return (_this$options = this.options) === null || _this$options === void 0 ? void 0 : _this$options.find(o => o.value === this.value);
|
|
152
152
|
},
|
|
153
153
|
nonMultipleValue() {
|
|
154
|
-
return this.
|
|
154
|
+
return Array.isArray(this.value) ? last(this.value) : this.value;
|
|
155
155
|
},
|
|
156
156
|
inputValue: {
|
|
157
157
|
get() {
|
|
@@ -210,6 +210,7 @@ var script = {
|
|
|
210
210
|
},
|
|
211
211
|
inputValue(newValue) {
|
|
212
212
|
if (this.termsAsTokens()) return;
|
|
213
|
+
if (this.multiSelect) return;
|
|
213
214
|
const hasUnclosedQuote = newValue.split('"').length % 2 === 0;
|
|
214
215
|
if (newValue.indexOf(' ') === -1 || hasUnclosedQuote) {
|
|
215
216
|
return;
|
package/dist/tokens/js/tokens.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gitlab/ui",
|
|
3
|
-
"version": "66.
|
|
3
|
+
"version": "66.15.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;
|