@gitlab/ui 37.10.0 → 38.1.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 +30 -0
- package/README.md +1 -1
- package/dist/components/base/breadcrumb/breadcrumb.js +10 -5
- package/dist/components/base/{filtered_search/examples/filtered_search.single_unique.example.js → breadcrumb/breadcrumb_item.js} +32 -28
- package/dist/components/base/filtered_search/filtered_search.documentation.js +2 -66
- package/dist/components/base/filtered_search/filtered_search.js +38 -11
- package/dist/components/base/filtered_search/filtered_search_suggestion.documentation.js +2 -8
- package/dist/components/base/filtered_search/filtered_search_suggestion.js +4 -0
- package/dist/components/base/filtered_search/filtered_search_suggestion_list.documentation.js +2 -7
- package/dist/components/base/filtered_search/filtered_search_suggestion_list.js +4 -0
- package/dist/components/base/filtered_search/filtered_search_term.documentation.js +2 -44
- package/dist/components/base/filtered_search/filtered_search_term.js +37 -0
- package/dist/components/base/filtered_search/filtered_search_token.documentation.js +2 -31
- package/dist/components/base/filtered_search/filtered_search_token.js +49 -0
- package/dist/components/base/filtered_search/filtered_search_token_segment.documentation.js +2 -46
- package/dist/components/base/filtered_search/filtered_search_token_segment.js +48 -0
- package/dist/components/base/form/form_radio/form_radio.js +1 -1
- package/dist/components/charts/series_label/series_label.js +6 -1
- package/dist/components/utilities/truncate/truncate.js +2 -2
- package/documentation/documented_stories.js +6 -0
- package/package.json +9 -7
- package/src/components/base/breadcrumb/breadcrumb.spec.js +24 -10
- package/src/components/base/breadcrumb/breadcrumb.vue +11 -6
- package/src/components/base/breadcrumb/breadcrumb_item.spec.js +45 -0
- package/src/components/base/breadcrumb/breadcrumb_item.vue +43 -0
- package/src/components/base/filtered_search/filtered_search.documentation.js +0 -76
- package/src/components/base/filtered_search/filtered_search.md +3 -4
- package/src/components/base/filtered_search/filtered_search.stories.js +248 -13
- package/src/components/base/filtered_search/filtered_search.vue +47 -10
- package/src/components/base/filtered_search/filtered_search_suggestion.documentation.js +0 -6
- package/src/components/base/filtered_search/filtered_search_suggestion.md +1 -7
- package/src/components/base/filtered_search/filtered_search_suggestion.stories.js +26 -18
- package/src/components/base/filtered_search/filtered_search_suggestion.vue +5 -0
- package/src/components/base/filtered_search/filtered_search_suggestion_list.documentation.js +0 -5
- package/src/components/base/filtered_search/filtered_search_suggestion_list.md +1 -7
- package/src/components/base/filtered_search/filtered_search_suggestion_list.stories.js +33 -25
- package/src/components/base/filtered_search/filtered_search_suggestion_list.vue +5 -0
- package/src/components/base/filtered_search/filtered_search_term.documentation.js +0 -41
- package/src/components/base/filtered_search/filtered_search_term.md +0 -2
- package/src/components/base/filtered_search/filtered_search_term.stories.js +33 -26
- package/src/components/base/filtered_search/filtered_search_term.vue +54 -0
- package/src/components/base/filtered_search/filtered_search_token.documentation.js +0 -26
- package/src/components/base/filtered_search/filtered_search_token.md +1 -3
- package/src/components/base/filtered_search/filtered_search_token.stories.js +136 -132
- package/src/components/base/filtered_search/filtered_search_token.vue +63 -0
- package/src/components/base/filtered_search/filtered_search_token_segment.documentation.js +0 -43
- package/src/components/base/filtered_search/filtered_search_token_segment.md +0 -2
- package/src/components/base/filtered_search/filtered_search_token_segment.stories.js +86 -79
- package/src/components/base/filtered_search/filtered_search_token_segment.vue +42 -0
- package/src/components/base/form/form_radio/form_radio.spec.js +21 -8
- package/src/components/base/form/form_radio/form_radio.vue +0 -1
- package/src/components/charts/series_label/series_label.stories.js +6 -3
- package/src/components/charts/series_label/series_label.vue +3 -0
- package/src/components/utilities/truncate/truncate.spec.js +14 -49
- package/src/components/utilities/truncate/truncate.stories.js +1 -59
- package/src/components/utilities/truncate/truncate.vue +3 -21
- package/dist/components/base/filtered_search/examples/filtered_search.default.example.js +0 -422
- package/dist/components/base/filtered_search/examples/filtered_search.friendly.example.js +0 -423
- package/dist/components/base/filtered_search/examples/filtered_search.history.example.js +0 -91
- package/dist/components/base/filtered_search/examples/filtered_search.multi_select.example.js +0 -196
- package/dist/components/base/filtered_search/examples/index.js +0 -32
- package/src/components/base/filtered_search/examples/filtered_search.default.example.vue +0 -298
- package/src/components/base/filtered_search/examples/filtered_search.friendly.example.vue +0 -300
- package/src/components/base/filtered_search/examples/filtered_search.history.example.vue +0 -50
- package/src/components/base/filtered_search/examples/filtered_search.multi_select.example.vue +0 -132
- package/src/components/base/filtered_search/examples/filtered_search.single_unique.example.vue +0 -31
- package/src/components/base/filtered_search/examples/index.js +0 -38
|
@@ -1,82 +1,6 @@
|
|
|
1
|
-
import examples from './examples';
|
|
2
1
|
import * as description from './filtered_search.md';
|
|
3
2
|
|
|
4
3
|
export default {
|
|
5
4
|
description,
|
|
6
5
|
followsDesignSystem: true,
|
|
7
|
-
bootstrapComponent: null,
|
|
8
|
-
examples,
|
|
9
|
-
propsInfo: {
|
|
10
|
-
value: {
|
|
11
|
-
additionalInfo: 'If provided, used as value of filtered search',
|
|
12
|
-
},
|
|
13
|
-
historyItems: {
|
|
14
|
-
additionalInfo: 'If provided, used as history items for this component',
|
|
15
|
-
},
|
|
16
|
-
availableTokens: {
|
|
17
|
-
additionalInfo: 'Available tokens',
|
|
18
|
-
},
|
|
19
|
-
placeholder: {
|
|
20
|
-
additionalInfo: 'If provided, used as history items for this component',
|
|
21
|
-
},
|
|
22
|
-
recentSearchesHeader: {
|
|
23
|
-
additionalInfo: 'i18n for recent searches title within history dropdown',
|
|
24
|
-
},
|
|
25
|
-
clearButtonTitle: {
|
|
26
|
-
additionalInfo: 'i18n for clear button title',
|
|
27
|
-
},
|
|
28
|
-
closeButtonTitle: {
|
|
29
|
-
additionalInfo: 'i18n for close button title within history dropdown',
|
|
30
|
-
},
|
|
31
|
-
clearRecentSearchesText: {
|
|
32
|
-
additionalInfo: 'i18n for recent searches clear text',
|
|
33
|
-
},
|
|
34
|
-
suggestionsListClass: {
|
|
35
|
-
additionalInfo:
|
|
36
|
-
'Additional classes to add to the suggestion list menu. NOTE: this not reactive, and the value must be available and fixed when the component is instantiated',
|
|
37
|
-
},
|
|
38
|
-
searchButtonAttributes: {
|
|
39
|
-
additionalInfo: 'HTML attributes to add to the search button',
|
|
40
|
-
},
|
|
41
|
-
searchInputAttributes: {
|
|
42
|
-
additionalInfo: 'HTML attributes to add to the search input',
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
events: [
|
|
46
|
-
{
|
|
47
|
-
event: 'clear',
|
|
48
|
-
description: 'Emitted when search is cleared',
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
event: 'submit',
|
|
52
|
-
args: [
|
|
53
|
-
{
|
|
54
|
-
arg: 'tokens',
|
|
55
|
-
description: '(Array)',
|
|
56
|
-
},
|
|
57
|
-
],
|
|
58
|
-
description: 'Emitted when search is submitted',
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
event: 'history-item-selected',
|
|
62
|
-
args: [
|
|
63
|
-
{
|
|
64
|
-
arg: 'value',
|
|
65
|
-
description: 'History item',
|
|
66
|
-
},
|
|
67
|
-
],
|
|
68
|
-
description: 'Emitted when item from history is selected',
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
event: 'clear-history',
|
|
72
|
-
description: 'Emitted when clear history button is clicked',
|
|
73
|
-
},
|
|
74
|
-
],
|
|
75
|
-
slots: [
|
|
76
|
-
{
|
|
77
|
-
name: 'history-item',
|
|
78
|
-
description:
|
|
79
|
-
'Slot to customize history item in history dropdown. Used only if using history items',
|
|
80
|
-
},
|
|
81
|
-
],
|
|
82
6
|
};
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# Filtered Search
|
|
2
|
-
|
|
3
1
|
The filtered search component is responsible for managing search with possible filters.
|
|
4
2
|
|
|
5
3
|
## Usage
|
|
@@ -61,6 +59,7 @@ const availableTokens = [
|
|
|
61
59
|
Pass the list of tokens to the search component. Optionally, you can use `v-model` to receive
|
|
62
60
|
realtime updates:
|
|
63
61
|
|
|
64
|
-
```
|
|
65
|
-
|
|
62
|
+
```html
|
|
63
|
+
|
|
64
|
+
<gl-filtered-search :available-tokens="tokens" v-model="value" />
|
|
66
65
|
```
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { withKnobs } from '@storybook/addon-knobs';
|
|
2
|
-
import { documentedStoriesOf } from '../../../../documentation/documented_stories';
|
|
3
1
|
import {
|
|
4
2
|
GlFilteredSearch,
|
|
5
3
|
GlFilteredSearchToken,
|
|
6
4
|
GlFilteredSearchSuggestion,
|
|
5
|
+
GlFilteredSearchSuggestionList,
|
|
6
|
+
GlFilteredSearchTerm,
|
|
7
|
+
GlFilteredSearchTokenSegment,
|
|
7
8
|
GlLoadingIcon,
|
|
8
9
|
GlToken,
|
|
9
10
|
GlAvatar,
|
|
10
11
|
} from '../../../index';
|
|
11
12
|
import { setStoryTimeout } from '../../../utils/test_utils';
|
|
13
|
+
import { makeContainer } from '../../../utils/story_decorators/container';
|
|
12
14
|
import readme from './filtered_search.md';
|
|
13
15
|
|
|
14
16
|
const fakeUsers = [
|
|
@@ -296,19 +298,252 @@ const components = {
|
|
|
296
298
|
GlFilteredSearch,
|
|
297
299
|
};
|
|
298
300
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
301
|
+
export const Default = () => ({
|
|
302
|
+
data() {
|
|
303
|
+
return {
|
|
304
|
+
tokens,
|
|
305
|
+
value: [
|
|
306
|
+
{ type: 'author', value: { data: 'beta', operator: '=' } },
|
|
307
|
+
{ type: 'label', value: { data: 'Bug', operator: '=' } },
|
|
308
|
+
'raw text',
|
|
309
|
+
],
|
|
310
|
+
};
|
|
311
|
+
},
|
|
312
|
+
components,
|
|
313
|
+
template: `<gl-filtered-search :available-tokens="tokens" :value="value" />`,
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
export const WithHistoryItems = () => ({
|
|
317
|
+
components,
|
|
318
|
+
data() {
|
|
319
|
+
return {
|
|
320
|
+
tokens: [
|
|
321
|
+
{
|
|
322
|
+
type: 'demotoken',
|
|
323
|
+
title: 'Unique',
|
|
324
|
+
icon: 'document',
|
|
325
|
+
token: 'gl-filtered-search-token',
|
|
326
|
+
operators: [{ value: '=', description: 'is', default: 'true' }],
|
|
327
|
+
options: [
|
|
328
|
+
{ icon: 'heart', title: 'heart', value: 1 },
|
|
329
|
+
{ icon: 'hook', title: 'hook', value: 2 },
|
|
330
|
+
],
|
|
331
|
+
unique: true,
|
|
332
|
+
},
|
|
333
|
+
],
|
|
334
|
+
value: [],
|
|
335
|
+
historyItems: [
|
|
336
|
+
[{ type: 'demotoken', value: { operator: '=', data: 1 } }, 'item 1'],
|
|
337
|
+
['item 2', { type: 'demotoken', value: { operator: '=', data: 2 } }],
|
|
338
|
+
],
|
|
339
|
+
};
|
|
340
|
+
},
|
|
341
|
+
methods: {
|
|
342
|
+
isString(val) {
|
|
343
|
+
return typeof val === 'string';
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
mounted() {
|
|
347
|
+
this.$nextTick(() => this.$el.querySelector('.gl-dropdown-toggle').click());
|
|
348
|
+
},
|
|
349
|
+
template: `
|
|
350
|
+
<div>
|
|
351
|
+
{{ value }}
|
|
352
|
+
<gl-filtered-search v-model="value" :available-tokens="tokens" :history-items="historyItems">
|
|
353
|
+
<template #history-item="{ historyItem }">
|
|
354
|
+
<template v-for="(token, idx) in historyItem">
|
|
355
|
+
<span v-if="isString(token)" :key="idx" class="gl-px-1">{{ token }}</span>
|
|
356
|
+
<span v-else :key="idx" class="gl-px-1">
|
|
357
|
+
<strong>{{ token.type }}</strong> {{ token.value.operator }}
|
|
358
|
+
<strong>{{ token.value.data }}</strong>
|
|
359
|
+
</span>
|
|
360
|
+
</template>
|
|
361
|
+
</template>
|
|
362
|
+
</gl-filtered-search>
|
|
363
|
+
</div>
|
|
364
|
+
`,
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
export const WithFriendlyText = () => ({
|
|
368
|
+
components,
|
|
369
|
+
data() {
|
|
370
|
+
return {
|
|
371
|
+
tokens: [
|
|
372
|
+
{
|
|
373
|
+
type: 'weight',
|
|
374
|
+
icon: 'weight',
|
|
375
|
+
title: 'Weight',
|
|
376
|
+
unique: true,
|
|
377
|
+
token: 'gl-filtered-search-token',
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
type: 'confidential',
|
|
381
|
+
icon: 'eye-slash',
|
|
382
|
+
title: 'Confidential',
|
|
383
|
+
unique: true,
|
|
384
|
+
token: 'gl-filtered-search-token',
|
|
385
|
+
options: [
|
|
386
|
+
{ icon: 'eye-slash', value: 'Yes', title: 'Yes' },
|
|
387
|
+
{ icon: 'eye', value: 'No', title: 'No' },
|
|
388
|
+
],
|
|
389
|
+
},
|
|
390
|
+
],
|
|
391
|
+
value: [
|
|
392
|
+
{ type: 'weight', value: { data: '3', operator: '=' } },
|
|
393
|
+
{ type: 'confidential', value: { data: 'Yes', operator: '!=' } },
|
|
394
|
+
],
|
|
395
|
+
};
|
|
396
|
+
},
|
|
397
|
+
template: `
|
|
398
|
+
<gl-filtered-search
|
|
399
|
+
v-model="value"
|
|
400
|
+
:available-tokens="tokens"
|
|
401
|
+
:show-friendly-text="true"
|
|
402
|
+
/>
|
|
403
|
+
`,
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
export const WithMultiSelect = () => {
|
|
407
|
+
const MultiUserToken = {
|
|
408
|
+
props: ['value', 'active', 'config'],
|
|
409
|
+
inheritAttrs: false,
|
|
302
410
|
data() {
|
|
303
411
|
return {
|
|
304
|
-
|
|
305
|
-
value: [
|
|
306
|
-
|
|
307
|
-
{ type: 'label', value: { data: 'Bug', operator: '=' } },
|
|
308
|
-
'raw text',
|
|
309
|
-
],
|
|
412
|
+
users: fakeUsers,
|
|
413
|
+
selectedUsernames: this.value.data ? this.value.data.split(',') : [],
|
|
414
|
+
activeUser: null,
|
|
310
415
|
};
|
|
311
416
|
},
|
|
417
|
+
computed: {
|
|
418
|
+
filteredUsers() {
|
|
419
|
+
return this.users.filter((user) => user.username.includes(this.value.data));
|
|
420
|
+
},
|
|
421
|
+
selectedUsers() {
|
|
422
|
+
return this.config.multiSelect
|
|
423
|
+
? this.users.filter((user) => this.selectedUsernames.includes(user.username))
|
|
424
|
+
: this.users.filter((user) => user.username === this.activeUser);
|
|
425
|
+
},
|
|
426
|
+
},
|
|
427
|
+
methods: {
|
|
428
|
+
loadView() {
|
|
429
|
+
this.activeUser = fakeUsers.find((u) => u.username === this.value.data);
|
|
430
|
+
},
|
|
431
|
+
loadSuggestions() {
|
|
432
|
+
this.users = fakeUsers;
|
|
433
|
+
},
|
|
434
|
+
handleSelect(username) {
|
|
435
|
+
if (!this.config.multiSelect) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (this.selectedUsernames.includes(username)) {
|
|
440
|
+
this.selectedUsernames = this.selectedUsernames.filter((user) => user !== username);
|
|
441
|
+
} else {
|
|
442
|
+
this.selectedUsernames.push(username);
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
isLastUser(index) {
|
|
446
|
+
return index === this.selectedUsers.length - 1;
|
|
447
|
+
},
|
|
448
|
+
},
|
|
449
|
+
watch: {
|
|
450
|
+
// eslint-disable-next-line func-names
|
|
451
|
+
'value.data': function () {
|
|
452
|
+
if (this.active) {
|
|
453
|
+
this.loadSuggestions();
|
|
454
|
+
}
|
|
455
|
+
},
|
|
456
|
+
active: {
|
|
457
|
+
immediate: true,
|
|
458
|
+
handler(newValue) {
|
|
459
|
+
if (!newValue) {
|
|
460
|
+
this.loadView();
|
|
461
|
+
} else {
|
|
462
|
+
this.loadSuggestions();
|
|
463
|
+
}
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
},
|
|
467
|
+
template: `
|
|
468
|
+
<gl-filtered-search-token
|
|
469
|
+
v-bind="{ ...this.$props, ...this.$attrs }"
|
|
470
|
+
v-on="$listeners"
|
|
471
|
+
:multi-select-values="selectedUsernames"
|
|
472
|
+
@select="handleSelect"
|
|
473
|
+
>
|
|
474
|
+
<template #view="{ inputValue }">
|
|
475
|
+
<template v-for="(user, index) in selectedUsers">
|
|
476
|
+
<gl-avatar :size="16" :entity-name="user.username" shape="circle" />
|
|
477
|
+
{{ user.name }}
|
|
478
|
+
<span v-if="!isLastUser(index)" class="gl-mx-2">, </span>
|
|
479
|
+
</template>
|
|
480
|
+
</template>
|
|
481
|
+
<template #suggestions>
|
|
482
|
+
<gl-filtered-search-suggestion :key="user.id" v-for="user in filteredUsers" :value="user.username">
|
|
483
|
+
<div class="gl-display-flex gl-align-items-center">
|
|
484
|
+
<gl-icon
|
|
485
|
+
v-if="config.multiSelect"
|
|
486
|
+
name="check"
|
|
487
|
+
class="gl-mr-3 gl-text-gray-700"
|
|
488
|
+
:class="{ 'gl-visibility-hidden': !selectedUsernames.includes(user.username) }"
|
|
489
|
+
/>
|
|
490
|
+
<gl-avatar :size="32" :entity-name="user.username" />
|
|
491
|
+
<div>
|
|
492
|
+
<p class="gl-m-0">{{ user.name }}</p>
|
|
493
|
+
<p class="gl-m-0">@{{ user.username }}</p>
|
|
494
|
+
</div>
|
|
495
|
+
</div>
|
|
496
|
+
</gl-filtered-search-suggestion>
|
|
497
|
+
</template>
|
|
498
|
+
</gl-filtered-search-token>
|
|
499
|
+
`,
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
return {
|
|
312
503
|
components,
|
|
313
|
-
|
|
314
|
-
|
|
504
|
+
data() {
|
|
505
|
+
return {
|
|
506
|
+
tokens: [
|
|
507
|
+
{
|
|
508
|
+
type: 'assignee',
|
|
509
|
+
icon: 'user',
|
|
510
|
+
title: 'Assignee',
|
|
511
|
+
token: MultiUserToken,
|
|
512
|
+
operators: [
|
|
513
|
+
{ value: '=', description: 'is', default: 'true' },
|
|
514
|
+
{ value: '!=', description: 'is not one of' },
|
|
515
|
+
{ value: '||', description: 'is one of' },
|
|
516
|
+
],
|
|
517
|
+
multiSelect: true,
|
|
518
|
+
},
|
|
519
|
+
],
|
|
520
|
+
value: [{ type: 'assignee', value: { data: 'alpha,beta', operator: '=' } }],
|
|
521
|
+
};
|
|
522
|
+
},
|
|
523
|
+
template: `
|
|
524
|
+
<gl-filtered-search v-model="value" :available-tokens="tokens" />
|
|
525
|
+
`,
|
|
526
|
+
};
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
export default {
|
|
530
|
+
title: 'base/filtered-search',
|
|
531
|
+
// Make room for suggestion lists
|
|
532
|
+
decorators: [makeContainer({ height: '250px' })],
|
|
533
|
+
component: GlFilteredSearch,
|
|
534
|
+
subcomponents: {
|
|
535
|
+
GlFilteredSearchSuggestion,
|
|
536
|
+
GlFilteredSearchSuggestionList,
|
|
537
|
+
GlFilteredSearchTerm,
|
|
538
|
+
GlFilteredSearchTokenSegment,
|
|
539
|
+
GlFilteredSearchToken,
|
|
540
|
+
},
|
|
541
|
+
parameters: {
|
|
542
|
+
docs: {
|
|
543
|
+
description: {
|
|
544
|
+
component: readme,
|
|
545
|
+
},
|
|
546
|
+
},
|
|
547
|
+
},
|
|
548
|
+
argTypes: {},
|
|
549
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import { cloneDeep
|
|
2
|
+
import { cloneDeep } from 'lodash';
|
|
3
3
|
import PortalVue from 'portal-vue';
|
|
4
4
|
import Vue from 'vue';
|
|
5
5
|
import { GlTooltipDirective } from '../../../directives/tooltip';
|
|
@@ -31,6 +31,7 @@ function initialState() {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export default {
|
|
34
|
+
name: 'GlFilteredSearch',
|
|
34
35
|
components: {
|
|
35
36
|
GlSearchBoxByClick,
|
|
36
37
|
GlIcon,
|
|
@@ -51,16 +52,25 @@ export default {
|
|
|
51
52
|
},
|
|
52
53
|
inheritAttrs: false,
|
|
53
54
|
props: {
|
|
55
|
+
/**
|
|
56
|
+
* If provided, used as value of filtered search
|
|
57
|
+
*/
|
|
54
58
|
value: {
|
|
55
59
|
required: false,
|
|
56
60
|
type: Array,
|
|
57
61
|
default: () => [],
|
|
58
62
|
},
|
|
63
|
+
/**
|
|
64
|
+
* Available tokens
|
|
65
|
+
*/
|
|
59
66
|
availableTokens: {
|
|
60
67
|
type: Array,
|
|
61
68
|
required: false,
|
|
62
69
|
default: () => [],
|
|
63
70
|
},
|
|
71
|
+
/**
|
|
72
|
+
* If provided, used as history items for this component
|
|
73
|
+
*/
|
|
64
74
|
placeholder: {
|
|
65
75
|
type: String,
|
|
66
76
|
required: false,
|
|
@@ -76,21 +86,34 @@ export default {
|
|
|
76
86
|
required: false,
|
|
77
87
|
default: null,
|
|
78
88
|
},
|
|
89
|
+
/**
|
|
90
|
+
* Additional classes to add to the suggestion list menu. NOTE: this not reactive, and the value
|
|
91
|
+
* must be available and fixed when the component is instantiated
|
|
92
|
+
*/
|
|
79
93
|
suggestionsListClass: {
|
|
80
94
|
type: [String, Array, Object],
|
|
81
95
|
required: false,
|
|
82
96
|
default: null,
|
|
83
97
|
},
|
|
98
|
+
/**
|
|
99
|
+
* Display operators' descriptions instead of their values (e.g., "is" instead of "=").
|
|
100
|
+
*/
|
|
84
101
|
showFriendlyText: {
|
|
85
102
|
type: Boolean,
|
|
86
103
|
required: false,
|
|
87
104
|
default: false,
|
|
88
105
|
},
|
|
106
|
+
/**
|
|
107
|
+
* HTML attributes to add to the search button
|
|
108
|
+
*/
|
|
89
109
|
searchButtonAttributes: {
|
|
90
110
|
type: Object,
|
|
91
111
|
required: false,
|
|
92
112
|
default: () => ({}),
|
|
93
113
|
},
|
|
114
|
+
/**
|
|
115
|
+
* HTML attributes to add to the search input
|
|
116
|
+
*/
|
|
94
117
|
searchInputAttributes: {
|
|
95
118
|
type: Object,
|
|
96
119
|
required: false,
|
|
@@ -141,26 +164,22 @@ export default {
|
|
|
141
164
|
this.tokens.push(createTerm());
|
|
142
165
|
}
|
|
143
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Emitted when the tokens (value) changes
|
|
169
|
+
* @property {array} tokens
|
|
170
|
+
*/
|
|
144
171
|
this.$emit('input', this.tokens);
|
|
145
172
|
},
|
|
146
173
|
deep: true,
|
|
147
174
|
immediate: true,
|
|
148
175
|
},
|
|
149
|
-
value: {
|
|
150
|
-
handler(newValue, oldValue) {
|
|
151
|
-
if (newValue.length && !isEqual(newValue, oldValue)) {
|
|
152
|
-
this.applyNewValue(cloneDeep(newValue));
|
|
153
|
-
}
|
|
154
|
-
},
|
|
155
|
-
deep: true,
|
|
156
|
-
immediate: true,
|
|
157
|
-
},
|
|
158
176
|
},
|
|
159
177
|
mounted() {
|
|
160
178
|
if (this.value.length) {
|
|
161
179
|
this.applyNewValue(cloneDeep(this.value));
|
|
162
180
|
}
|
|
163
181
|
},
|
|
182
|
+
|
|
164
183
|
methods: {
|
|
165
184
|
applyNewValue(newValue) {
|
|
166
185
|
this.tokens = needDenormalization(newValue) ? denormalizeTokens(newValue) : newValue;
|
|
@@ -269,6 +288,10 @@ export default {
|
|
|
269
288
|
},
|
|
270
289
|
|
|
271
290
|
submit() {
|
|
291
|
+
/**
|
|
292
|
+
* Emitted when search is submitted
|
|
293
|
+
* @property {array} tokens
|
|
294
|
+
*/
|
|
272
295
|
this.$emit('submit', normalizeTokens(cloneDeep(this.tokens)));
|
|
273
296
|
},
|
|
274
297
|
|
|
@@ -281,6 +304,19 @@ export default {
|
|
|
281
304
|
</script>
|
|
282
305
|
|
|
283
306
|
<template>
|
|
307
|
+
<!--
|
|
308
|
+
Emitted when search is cleared
|
|
309
|
+
@event clear
|
|
310
|
+
-->
|
|
311
|
+
<!--
|
|
312
|
+
Emitted when item from history is selected
|
|
313
|
+
@event history-item-selected
|
|
314
|
+
@property {object} value History item
|
|
315
|
+
-->
|
|
316
|
+
<!--
|
|
317
|
+
Emitted when clear history button is clicked
|
|
318
|
+
@event clear-history
|
|
319
|
+
-->
|
|
284
320
|
<gl-search-box-by-click
|
|
285
321
|
v-bind="$attrs"
|
|
286
322
|
:value="tokens"
|
|
@@ -295,6 +331,7 @@ export default {
|
|
|
295
331
|
@clear-history="$emit('clear-history')"
|
|
296
332
|
>
|
|
297
333
|
<template #history-item="slotScope">
|
|
334
|
+
<!-- @slot Slot to customize history item in history dropdown. Used only if using history items -->
|
|
298
335
|
<slot name="history-item" v-bind="slotScope"></slot>
|
|
299
336
|
</template>
|
|
300
337
|
<template #input>
|
|
@@ -2,10 +2,4 @@ import description from './filtered_search_suggestion.md';
|
|
|
2
2
|
|
|
3
3
|
export default {
|
|
4
4
|
description,
|
|
5
|
-
bootstrapComponent: 'b-dropdown-item',
|
|
6
|
-
propsInfo: {
|
|
7
|
-
value: {
|
|
8
|
-
additionalInfo: 'Value which will be emitted if this suggestion will be selected in list',
|
|
9
|
-
},
|
|
10
|
-
},
|
|
11
5
|
};
|
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
# Filtered Search Suggestion
|
|
2
|
-
|
|
3
|
-
<!-- STORY -->
|
|
4
|
-
|
|
5
|
-
## Usage
|
|
6
|
-
|
|
7
1
|
The filtered search suggestion component is a wrapper around `GlDropdownItem`, which registers
|
|
8
2
|
suggestions in a top-level suggestion list:
|
|
9
3
|
|
|
10
|
-
```
|
|
4
|
+
```html
|
|
11
5
|
<gl-filtered-search-suggestion-list>
|
|
12
6
|
<gl-filtered-search-suggestion value="foo">Example suggestion</gl-filtered-search-suggestion>
|
|
13
7
|
<gl-filtered-search-suggestion value="bar">Example suggestion 2</gl-filtered-search-suggestion>
|
|
@@ -1,25 +1,33 @@
|
|
|
1
|
-
import { withKnobs } from '@storybook/addon-knobs';
|
|
2
1
|
import { GlFilteredSearchSuggestion } from '../../../index';
|
|
3
|
-
import { documentedStoriesOf } from '../../../../documentation/documented_stories';
|
|
4
2
|
import readme from './filtered_search_suggestion.md';
|
|
5
3
|
|
|
6
4
|
const noop = () => {};
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
6
|
+
export const Default = () => ({
|
|
7
|
+
components: { GlFilteredSearchSuggestion },
|
|
8
|
+
provide: {
|
|
9
|
+
filteredSearchSuggestionListInstance: {
|
|
10
|
+
register: noop,
|
|
11
|
+
unregister: noop,
|
|
12
|
+
$emit: noop,
|
|
13
|
+
activeItem: null,
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
template: `
|
|
17
|
+
<ul>
|
|
18
|
+
<gl-filtered-search-suggestion value="demo">Demo suggestion</gl-filtered-search-suggestion>
|
|
19
|
+
</ul>
|
|
20
|
+
`,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export default {
|
|
24
|
+
title: 'base/filtered-search/suggestion',
|
|
25
|
+
component: GlFilteredSearchSuggestion,
|
|
26
|
+
parameters: {
|
|
27
|
+
docs: {
|
|
28
|
+
description: {
|
|
29
|
+
component: readme,
|
|
18
30
|
},
|
|
19
31
|
},
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
<gl-filtered-search-suggestion value="demo">Demo suggestion</gl-filtered-search-suggestion>
|
|
23
|
-
</ul>
|
|
24
|
-
`,
|
|
25
|
-
}));
|
|
32
|
+
},
|
|
33
|
+
};
|
|
@@ -2,12 +2,16 @@
|
|
|
2
2
|
import GlDropdownItem from '../dropdown/dropdown_item.vue';
|
|
3
3
|
|
|
4
4
|
export default {
|
|
5
|
+
name: 'GlFilteredSearchSuggestion',
|
|
5
6
|
components: {
|
|
6
7
|
GlDropdownItem,
|
|
7
8
|
},
|
|
8
9
|
inject: ['filteredSearchSuggestionListInstance'],
|
|
9
10
|
inheritAttrs: false,
|
|
10
11
|
props: {
|
|
12
|
+
/**
|
|
13
|
+
* Value that will be emitted if this suggestion is selected.
|
|
14
|
+
*/
|
|
11
15
|
value: {
|
|
12
16
|
required: true,
|
|
13
17
|
validator: () => true,
|
|
@@ -52,6 +56,7 @@ export default {
|
|
|
52
56
|
href="#"
|
|
53
57
|
@mousedown.native.prevent="emitValue"
|
|
54
58
|
>
|
|
59
|
+
<!-- @slot The suggestion content. -->
|
|
55
60
|
<slot></slot>
|
|
56
61
|
</gl-dropdown-item>
|
|
57
62
|
</template>
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
# Filtered Search Suggestion
|
|
2
|
-
|
|
3
|
-
<!-- STORY -->
|
|
4
|
-
|
|
5
|
-
## Usage
|
|
6
|
-
|
|
7
1
|
The filtered search suggestion list component is responsible for managing underlying suggestion instances.
|
|
8
2
|
You obtain the ref for this component and manage suggestion selection via the component public API:
|
|
9
3
|
|
|
@@ -11,7 +5,7 @@ You obtain the ref for this component and manage suggestion selection via the co
|
|
|
11
5
|
- `nextItem()` - Selects the next suggestion. If last suggestion was selected, selection is cleared.
|
|
12
6
|
- `prevItem()` - Selects the previous suggestion. If first suggestion was selected, selection is cleared.
|
|
13
7
|
|
|
14
|
-
```
|
|
8
|
+
```html
|
|
15
9
|
<gl-filtered-search-suggestion-list ref="suggestions">
|
|
16
10
|
<gl-filtered-search-suggestion value="foo">Example suggestion</gl-filtered-search-suggestion>
|
|
17
11
|
<gl-filtered-search-suggestion value="bar">Example suggestion 2</gl-filtered-search-suggestion>
|