@gitlab/ui 38.0.1 → 38.2.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.
Files changed (140) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +1 -1
  3. package/dist/components/base/breadcrumb/breadcrumb.js +10 -5
  4. package/dist/components/base/{filtered_search/examples/filtered_search.single_unique.example.js → breadcrumb/breadcrumb_item.js} +32 -28
  5. package/dist/components/base/dropdown/dropdown.documentation.js +1 -5
  6. package/dist/components/base/dropdown/dropdown_item.documentation.js +2 -3
  7. package/dist/components/base/filtered_search/filtered_search.documentation.js +2 -66
  8. package/dist/components/base/filtered_search/filtered_search.js +51 -20
  9. package/dist/components/base/filtered_search/filtered_search_suggestion.documentation.js +2 -8
  10. package/dist/components/base/filtered_search/filtered_search_suggestion.js +5 -1
  11. package/dist/components/base/filtered_search/filtered_search_suggestion_list.documentation.js +2 -7
  12. package/dist/components/base/filtered_search/filtered_search_suggestion_list.js +4 -0
  13. package/dist/components/base/filtered_search/filtered_search_term.documentation.js +2 -44
  14. package/dist/components/base/filtered_search/filtered_search_term.js +37 -0
  15. package/dist/components/base/filtered_search/filtered_search_token.documentation.js +2 -31
  16. package/dist/components/base/filtered_search/filtered_search_token.js +80 -23
  17. package/dist/components/base/filtered_search/filtered_search_token_segment.documentation.js +2 -46
  18. package/dist/components/base/filtered_search/filtered_search_token_segment.js +48 -0
  19. package/dist/components/base/filtered_search/filtered_search_utils.js +42 -9
  20. package/dist/components/base/form/form_checkbox_tree/form_checkbox_tree.documentation.js +2 -27
  21. package/dist/components/base/form/form_checkbox_tree/form_checkbox_tree.js +16 -1
  22. package/dist/components/charts/series_label/series_label.js +6 -1
  23. package/dist/index.css +1 -1
  24. package/dist/index.css.map +1 -1
  25. package/dist/utils/use_mock_intersection_observer.js +2 -2
  26. package/documentation/components_documentation.js +0 -4
  27. package/documentation/documented_stories.js +10 -1
  28. package/package.json +11 -9
  29. package/src/components/base/avatar_link/avatar_link.stories.js +2 -2
  30. package/src/components/base/breadcrumb/breadcrumb.spec.js +24 -10
  31. package/src/components/base/breadcrumb/breadcrumb.vue +11 -6
  32. package/src/components/base/breadcrumb/breadcrumb_item.spec.js +45 -0
  33. package/src/components/base/breadcrumb/breadcrumb_item.vue +43 -0
  34. package/src/components/base/dropdown/dropdown.documentation.js +0 -3
  35. package/src/components/base/dropdown/dropdown.md +7 -2
  36. package/src/components/base/dropdown/dropdown.stories.js +487 -439
  37. package/src/components/base/dropdown/dropdown_item.documentation.js +0 -1
  38. package/src/components/base/dropdown/dropdown_item.md +0 -6
  39. package/src/components/base/dropdown/dropdown_item.stories.js +107 -35
  40. package/src/components/base/filtered_search/filtered_search.documentation.js +0 -76
  41. package/src/components/base/filtered_search/filtered_search.md +3 -4
  42. package/src/components/base/filtered_search/filtered_search.spec.js +37 -12
  43. package/src/components/base/filtered_search/filtered_search.stories.js +260 -17
  44. package/src/components/base/filtered_search/filtered_search.vue +57 -14
  45. package/src/components/base/filtered_search/filtered_search_suggestion.documentation.js +0 -6
  46. package/src/components/base/filtered_search/filtered_search_suggestion.md +1 -7
  47. package/src/components/base/filtered_search/filtered_search_suggestion.stories.js +26 -18
  48. package/src/components/base/filtered_search/filtered_search_suggestion.vue +6 -0
  49. package/src/components/base/filtered_search/filtered_search_suggestion_list.documentation.js +0 -5
  50. package/src/components/base/filtered_search/filtered_search_suggestion_list.md +1 -7
  51. package/src/components/base/filtered_search/filtered_search_suggestion_list.stories.js +33 -25
  52. package/src/components/base/filtered_search/filtered_search_suggestion_list.vue +5 -0
  53. package/src/components/base/filtered_search/filtered_search_term.documentation.js +0 -41
  54. package/src/components/base/filtered_search/filtered_search_term.md +0 -2
  55. package/src/components/base/filtered_search/filtered_search_term.stories.js +33 -26
  56. package/src/components/base/filtered_search/filtered_search_term.vue +54 -0
  57. package/src/components/base/filtered_search/filtered_search_token.documentation.js +0 -26
  58. package/src/components/base/filtered_search/filtered_search_token.md +1 -3
  59. package/src/components/base/filtered_search/filtered_search_token.spec.js +31 -1
  60. package/src/components/base/filtered_search/filtered_search_token.stories.js +137 -132
  61. package/src/components/base/filtered_search/filtered_search_token.vue +93 -21
  62. package/src/components/base/filtered_search/filtered_search_token_segment.documentation.js +0 -43
  63. package/src/components/base/filtered_search/filtered_search_token_segment.md +0 -2
  64. package/src/components/base/filtered_search/filtered_search_token_segment.stories.js +86 -79
  65. package/src/components/base/filtered_search/filtered_search_token_segment.vue +42 -0
  66. package/src/components/base/filtered_search/filtered_search_utils.js +38 -5
  67. package/src/components/base/form/form.stories.js +2 -0
  68. package/src/components/base/form/form_checkbox_tree/form_checkbox_tree.documentation.js +0 -26
  69. package/src/components/base/form/form_checkbox_tree/form_checkbox_tree.md +0 -4
  70. package/src/components/base/form/form_checkbox_tree/form_checkbox_tree.stories.js +123 -92
  71. package/src/components/base/form/form_checkbox_tree/form_checkbox_tree.vue +13 -1
  72. package/src/components/base/form/form_radio/form_radio.spec.js +21 -8
  73. package/src/components/base/form/form_radio_group/form_radio_group.stories.js +2 -1
  74. package/src/components/base/markdown/markdown.scss +21 -0
  75. package/src/components/base/markdown/markdown_typescale_demo.html +17 -6
  76. package/src/components/base/navbar/navbar.stories.js +2 -1
  77. package/src/components/base/skeleton_loader/skeleton_loader.stories.js +67 -21
  78. package/src/components/base/tabs/tabs/tabs.stories.js +2 -2
  79. package/src/components/charts/series_label/series_label.stories.js +6 -3
  80. package/src/components/charts/series_label/series_label.vue +3 -0
  81. package/src/scss/typescale/typescale.md +0 -2
  82. package/src/scss/typescale/typescale.stories.js +17 -4
  83. package/src/utils/use_mock_intersection_observer.js +3 -3
  84. package/dist/components/base/dropdown/dropdown_divider.documentation.js +0 -8
  85. package/dist/components/base/dropdown/dropdown_form.documentation.js +0 -17
  86. package/dist/components/base/dropdown/dropdown_section_header.documentation.js +0 -8
  87. package/dist/components/base/dropdown/dropdown_text.documentation.js +0 -8
  88. package/dist/components/base/dropdown/examples/dropdown.default.example.js +0 -38
  89. package/dist/components/base/dropdown/examples/dropdown.links.example.js +0 -38
  90. package/dist/components/base/dropdown/examples/dropdown.with_avatar_and_secondary_text.example.js +0 -38
  91. package/dist/components/base/dropdown/examples/dropdown.with_checked_items.example.js +0 -38
  92. package/dist/components/base/dropdown/examples/dropdown.with_clear_all.example.js +0 -38
  93. package/dist/components/base/dropdown/examples/dropdown.with_divider.example.js +0 -38
  94. package/dist/components/base/dropdown/examples/dropdown.with_form.example.js +0 -38
  95. package/dist/components/base/dropdown/examples/dropdown.with_header.example.js +0 -38
  96. package/dist/components/base/dropdown/examples/dropdown.with_highlighted_items.example.js +0 -38
  97. package/dist/components/base/dropdown/examples/dropdown.with_icons.example.js +0 -38
  98. package/dist/components/base/dropdown/examples/dropdown.with_right_align.example.js +0 -38
  99. package/dist/components/base/dropdown/examples/dropdown.with_search.example.js +0 -67
  100. package/dist/components/base/dropdown/examples/dropdown.with_section_headers.example.js +0 -38
  101. package/dist/components/base/dropdown/examples/index.js +0 -85
  102. package/dist/components/base/filtered_search/examples/filtered_search.default.example.js +0 -422
  103. package/dist/components/base/filtered_search/examples/filtered_search.friendly.example.js +0 -423
  104. package/dist/components/base/filtered_search/examples/filtered_search.history.example.js +0 -91
  105. package/dist/components/base/filtered_search/examples/filtered_search.multi_select.example.js +0 -196
  106. package/dist/components/base/filtered_search/examples/index.js +0 -32
  107. package/dist/components/base/form/form_checkbox_tree/examples/form_checkbox_tree.basic.example.js +0 -103
  108. package/dist/components/base/form/form_checkbox_tree/examples/index.js +0 -13
  109. package/src/components/base/dropdown/dropdown_divider.documentation.js +0 -6
  110. package/src/components/base/dropdown/dropdown_divider.md +0 -7
  111. package/src/components/base/dropdown/dropdown_divider.stories.js +0 -16
  112. package/src/components/base/dropdown/dropdown_form.documentation.js +0 -9
  113. package/src/components/base/dropdown/dropdown_form.md +0 -4
  114. package/src/components/base/dropdown/dropdown_form.stories.js +0 -17
  115. package/src/components/base/dropdown/dropdown_section_header.documentation.js +0 -6
  116. package/src/components/base/dropdown/dropdown_section_header.stories.js +0 -17
  117. package/src/components/base/dropdown/dropdown_text.documentation.js +0 -6
  118. package/src/components/base/dropdown/dropdown_text.stories.js +0 -16
  119. package/src/components/base/dropdown/examples/dropdown.default.example.vue +0 -7
  120. package/src/components/base/dropdown/examples/dropdown.links.example.vue +0 -7
  121. package/src/components/base/dropdown/examples/dropdown.with_avatar_and_secondary_text.example.vue +0 -7
  122. package/src/components/base/dropdown/examples/dropdown.with_checked_items.example.vue +0 -6
  123. package/src/components/base/dropdown/examples/dropdown.with_clear_all.example.vue +0 -7
  124. package/src/components/base/dropdown/examples/dropdown.with_divider.example.vue +0 -9
  125. package/src/components/base/dropdown/examples/dropdown.with_form.example.vue +0 -10
  126. package/src/components/base/dropdown/examples/dropdown.with_header.example.vue +0 -7
  127. package/src/components/base/dropdown/examples/dropdown.with_highlighted_items.example.vue +0 -9
  128. package/src/components/base/dropdown/examples/dropdown.with_icons.example.vue +0 -43
  129. package/src/components/base/dropdown/examples/dropdown.with_right_align.example.vue +0 -7
  130. package/src/components/base/dropdown/examples/dropdown.with_search.example.vue +0 -38
  131. package/src/components/base/dropdown/examples/dropdown.with_section_headers.example.vue +0 -10
  132. package/src/components/base/dropdown/examples/index.js +0 -99
  133. package/src/components/base/filtered_search/examples/filtered_search.default.example.vue +0 -298
  134. package/src/components/base/filtered_search/examples/filtered_search.friendly.example.vue +0 -300
  135. package/src/components/base/filtered_search/examples/filtered_search.history.example.vue +0 -50
  136. package/src/components/base/filtered_search/examples/filtered_search.multi_select.example.vue +0 -132
  137. package/src/components/base/filtered_search/examples/filtered_search.single_unique.example.vue +0 -31
  138. package/src/components/base/filtered_search/examples/index.js +0 -38
  139. package/src/components/base/form/form_checkbox_tree/examples/form_checkbox_tree.basic.example.vue +0 -77
  140. package/src/components/base/form/form_checkbox_tree/examples/index.js +0 -15
@@ -1,7 +1,5 @@
1
- import { withKnobs, boolean } from '@storybook/addon-knobs';
2
1
  import PortalVue from 'portal-vue';
3
2
  import Vue from 'vue';
4
- import { documentedStoriesOf } from '../../../../documentation/documented_stories';
5
3
  import { GlIcon } from '../../../index';
6
4
  import { provide } from './common_story_options';
7
5
  import GlFilteredSearchSuggestion from './filtered_search_suggestion.vue';
@@ -10,152 +8,159 @@ import GlFilteredSearchToken from './filtered_search_token.vue';
10
8
 
11
9
  Vue.use(PortalVue);
12
10
 
13
- documentedStoriesOf('base/filtered-search/token', readme)
14
- .addDecorator(withKnobs)
15
- .add('default', () => ({
16
- components: {
17
- GlFilteredSearchToken,
18
- GlFilteredSearchSuggestion,
19
- GlIcon,
20
- },
21
- provide,
22
- props: {
23
- active: {
24
- type: Boolean,
25
- default: boolean('active', true),
11
+ const generateProps = ({ active = true } = {}) => ({
12
+ active,
13
+ });
14
+
15
+ // eslint-disable-next-line no-unused-vars
16
+ export const Default = (args, { argTypes }) => ({
17
+ components: {
18
+ GlFilteredSearchToken,
19
+ GlFilteredSearchSuggestion,
20
+ GlIcon,
21
+ },
22
+ provide,
23
+ props: ['active'],
24
+ data() {
25
+ return {
26
+ value: { operator: '=', data: 'Yes' },
27
+ config: {
28
+ title: 'Confidential',
26
29
  },
27
- },
28
- data() {
29
- return {
30
- value: { operator: '=', data: 'Yes' },
31
- config: {
32
- title: 'Confidential',
33
- },
34
- };
35
- },
36
- mounted() {
37
- this.$nextTick(() => {
38
- document.activeElement.blur();
39
- });
40
- },
41
- template: `
30
+ };
31
+ },
32
+ mounted() {
33
+ this.$nextTick(() => {
34
+ document.activeElement.blur();
35
+ });
36
+ },
37
+ template: `
38
+ <div>
39
+ <div> {{ value }} </div>
40
+ <div class="gl-border-1 gl-border-solid gl-border-gray-200">
41
+ <gl-filtered-search-token
42
+ v-model="value"
43
+ class="gl-h-full"
44
+ :config="config"
45
+ :active="active"
46
+ >
47
+ <template #suggestions>
48
+ <gl-filtered-search-suggestion value="Yes"><gl-icon name="eye-slash" :size="16"/> Yes</gl-filtered-search-suggestion>
49
+ <gl-filtered-search-suggestion value="No"><gl-icon name="eye" :size="16"/> No</gl-filtered-search-suggestion>
50
+ </template>
51
+ </gl-filtered-search-token>
52
+ </div>
42
53
  <div>
43
- <div> {{ value }} </div>
44
- <div class="gl-border-1 gl-border-solid gl-border-gray-200">
45
- <gl-filtered-search-token
46
- v-model="value"
47
- class="gl-h-full"
48
- :config="config"
49
- :active="active"
50
- >
51
- <template #suggestions>
52
- <gl-filtered-search-suggestion value="Yes"><gl-icon name="eye-slash" :size="16"/> Yes</gl-filtered-search-suggestion>
53
- <gl-filtered-search-suggestion value="No"><gl-icon name="eye" :size="16"/> No</gl-filtered-search-suggestion>
54
- </template>
55
- </gl-filtered-search-token>
56
- </div>
57
- <div>
58
- <portal-target name="portal" class="gl-relative" />
59
- </div>
54
+ <portal-target name="portal" class="gl-relative" />
60
55
  </div>
61
- `,
62
- }))
63
- .add('with custom operators options', () => ({
64
- components: {
65
- GlFilteredSearchToken,
66
- GlFilteredSearchSuggestion,
67
- },
68
- provide,
69
- props: {
70
- active: {
71
- type: Boolean,
72
- default: boolean('active', true),
73
- },
74
- },
75
- data() {
76
- return {
77
- value: { operator: '=', data: 'Yes' },
78
- config: {
79
- title: 'Confidential',
80
- },
56
+ </div>
57
+ `,
58
+ });
59
+ Default.args = generateProps();
60
+
61
+ // eslint-disable-next-line no-unused-vars
62
+ export const WithCustomOperatorsOptions = (args, { argTypes }) => ({
63
+ components: {
64
+ GlFilteredSearchToken,
65
+ GlFilteredSearchSuggestion,
66
+ GlIcon,
67
+ },
68
+ provide,
69
+ props: ['active'],
70
+ data() {
71
+ return {
72
+ value: { operator: '!', data: 'Yes' },
73
+ config: {
74
+ title: 'Confidential',
81
75
  operators: [
82
76
  { value: '^', description: 'or' },
83
77
  { value: '!', description: 'is not', default: 'true' },
84
78
  ],
85
- };
86
- },
87
- mounted() {
88
- this.$nextTick(() => {
89
- document.activeElement.blur();
90
- });
91
- },
92
- template: `
93
- <div>
94
- <div> {{ value }} </div>
95
- <div class="gl-border-1 gl-border-solid gl-border-gray-200">
96
- <gl-filtered-search-token
97
- v-model="value"
98
- class="gl-h-full"
99
- :config="config"
100
- :active="active"
101
- :operators="operators"
102
- >
79
+ },
80
+ };
81
+ },
82
+ mounted() {
83
+ this.$nextTick(() => {
84
+ document.activeElement.blur();
85
+ });
86
+ },
87
+ template: `
88
+ <div>
89
+ <div> {{ value }} </div>
90
+ <div class="gl-border-1 gl-border-solid gl-border-gray-200">
91
+ <gl-filtered-search-token
92
+ v-model="value"
93
+ class="gl-h-full"
94
+ :config="config"
95
+ :active="active"
96
+ >
103
97
  <template #suggestions>
104
98
  <gl-filtered-search-suggestion value="Yes"><gl-icon name="eye-slash" :size="16"/> Yes</gl-filtered-search-suggestion>
105
99
  <gl-filtered-search-suggestion value="No"><gl-icon name="eye" :size="16"/> No</gl-filtered-search-suggestion>
106
100
  </template>
107
- </div>
108
- <div>
109
- <portal-target name="portal" class="gl-relative" />
110
- </div>
101
+ </gl-filtered-search-token>
111
102
  </div>
112
- `,
113
- }))
114
- .add('with static options', () => ({
115
- components: {
116
- GlFilteredSearchToken,
117
- GlFilteredSearchSuggestion,
118
- },
119
- provide,
120
- props: {
121
- active: {
122
- type: Boolean,
123
- default: boolean('active', true),
124
- },
125
- },
126
- data() {
127
- return {
128
- value: { operator: '=', data: 'first' },
129
- config: {
130
- title: 'Confidential',
131
- },
103
+ <div>
104
+ <portal-target name="portal" class="gl-relative" />
105
+ </div>
106
+ </div>
107
+ `,
108
+ });
109
+ WithCustomOperatorsOptions.args = generateProps();
110
+
111
+ // eslint-disable-next-line no-unused-vars
112
+ export const WithStaticOptions = (args, { argTypes }) => ({
113
+ components: {
114
+ GlFilteredSearchToken,
115
+ GlFilteredSearchSuggestion,
116
+ },
117
+ provide,
118
+ props: ['active'],
119
+ data() {
120
+ return {
121
+ value: { operator: '=', data: 'first' },
122
+ config: {
123
+ title: 'Confidential',
132
124
  options: [
133
125
  { icon: 'hourglass', title: 'first', value: 'one' },
134
126
  { title: 'second-without-icon', value: 'two' },
135
127
  { icon: 'issues', title: 'third', value: 'three' },
136
128
  ],
137
- };
138
- },
139
- mounted() {
140
- this.$nextTick(() => {
141
- document.activeElement.blur();
142
- });
143
- },
144
- template: `
129
+ },
130
+ };
131
+ },
132
+ mounted() {
133
+ this.$nextTick(() => {
134
+ document.activeElement.blur();
135
+ });
136
+ },
137
+ template: `
138
+ <div>
139
+ <div> {{ value }} </div>
140
+ <div class="gl-border-1 gl-border-solid gl-border-gray-200">
141
+ <gl-filtered-search-token
142
+ v-model="value"
143
+ class="gl-h-full"
144
+ :config="config"
145
+ :active="active"
146
+ />
147
+ </div>
145
148
  <div>
146
- <div> {{ value }} </div>
147
- <div class="gl-border-1 gl-border-solid gl-border-gray-200">
148
- <gl-filtered-search-token
149
- v-model="value"
150
- class="gl-h-full"
151
- :config="config"
152
- :active="active"
153
- :options="options"
154
- />
155
- </div>
156
- <div>
157
- <portal-target name="portal" class="gl-relative" />
158
- </div>
149
+ <portal-target name="portal" class="gl-relative" />
159
150
  </div>
160
- `,
161
- }));
151
+ </div>
152
+ `,
153
+ });
154
+ WithStaticOptions.args = generateProps();
155
+
156
+ export default {
157
+ title: 'base/filtered-search/token',
158
+ component: GlFilteredSearchToken,
159
+ parameters: {
160
+ docs: {
161
+ description: {
162
+ component: readme,
163
+ },
164
+ },
165
+ },
166
+ };
@@ -1,8 +1,9 @@
1
1
  <script>
2
+ import { cloneDeep } from 'lodash';
2
3
  import { COMMA } from '../../../utils/constants';
3
4
  import GlToken from '../token/token.vue';
4
5
  import GlFilteredSearchTokenSegment from './filtered_search_token_segment.vue';
5
- import { TERM_TOKEN_TYPE } from './filtered_search_utils';
6
+ import { createTerm } from './filtered_search_utils';
6
7
 
7
8
  const SEGMENT_TITLE = 'TYPE';
8
9
  const SEGMENT_OPERATOR = 'OPERATOR';
@@ -15,6 +16,7 @@ const DEFAULT_OPERATORS = [
15
16
  ];
16
17
 
17
18
  export default {
19
+ name: 'GlFilteredSearchToken',
18
20
  components: {
19
21
  GlToken,
20
22
  GlFilteredSearchTokenSegment,
@@ -26,11 +28,17 @@ export default {
26
28
  required: false,
27
29
  default: () => [],
28
30
  },
31
+ /**
32
+ * Token configuration with available operators and options.
33
+ */
29
34
  config: {
30
35
  type: Object,
31
36
  required: false,
32
37
  default: () => ({}),
33
38
  },
39
+ /**
40
+ * Determines if the token is being edited or not.
41
+ */
34
42
  active: {
35
43
  type: Boolean,
36
44
  required: false,
@@ -41,11 +49,17 @@ export default {
41
49
  required: false,
42
50
  default: () => [],
43
51
  },
52
+ /**
53
+ * Current token value.
54
+ */
44
55
  value: {
45
56
  type: Object,
46
57
  required: false,
47
58
  default: () => ({ operator: '', data: '' }),
48
59
  },
60
+ /**
61
+ * Display operators' descriptions instead of their values (e.g., "is" instead of "=").
62
+ */
49
63
  showFriendlyText: {
50
64
  type: Boolean,
51
65
  required: false,
@@ -55,6 +69,7 @@ export default {
55
69
  data() {
56
70
  return {
57
71
  activeSegment: null,
72
+ tokenValue: cloneDeep(this.value),
58
73
  };
59
74
  },
60
75
 
@@ -64,7 +79,7 @@ export default {
64
79
  },
65
80
 
66
81
  hasDataOrDataSegmentIsCurrentlyActive() {
67
- return this.value.data !== '' || this.isSegmentActive(SEGMENT_DATA);
82
+ return this.tokenValue.data !== '' || this.isSegmentActive(SEGMENT_DATA);
68
83
  },
69
84
 
70
85
  availableTokensWithSelf() {
@@ -75,7 +90,7 @@ export default {
75
90
  },
76
91
 
77
92
  operatorDescription() {
78
- const operator = this.operators.find((op) => op.value === this.value.operator);
93
+ const operator = this.operators.find((op) => op.value === this.tokenValue.operator);
79
94
  return this.showFriendlyText ? operator?.description : operator?.value;
80
95
  },
81
96
  },
@@ -85,22 +100,43 @@ export default {
85
100
  SEGMENT_OPERATOR,
86
101
  },
87
102
  watch: {
88
- value: {
103
+ tokenValue: {
89
104
  deep: true,
90
105
  handler(newValue) {
106
+ /**
107
+ * Emitted when the token changes its value.
108
+ *
109
+ * @event input
110
+ * @type {object} dataObj Object containing the update value.
111
+ */
91
112
  this.$emit('input', newValue);
92
113
  },
93
114
  },
94
115
 
116
+ value: {
117
+ handler(newValue, oldValue) {
118
+ if (newValue?.data === oldValue?.data && newValue?.operator === oldValue?.operator) {
119
+ return;
120
+ }
121
+
122
+ this.tokenValue = cloneDeep(newValue);
123
+ },
124
+ },
125
+
95
126
  active: {
96
127
  immediate: true,
97
128
  handler(newValue) {
98
129
  if (newValue) {
99
130
  if (!this.activeSegment) {
100
- this.activateSegment(this.value.data !== '' ? SEGMENT_DATA : SEGMENT_OPERATOR);
131
+ this.activateSegment(this.tokenValue.data !== '' ? SEGMENT_DATA : SEGMENT_OPERATOR);
101
132
  }
102
- } else if (this.value.data === '') {
133
+ } else if (this.tokenValue.data === '') {
103
134
  this.activeSegment = null;
135
+ /**
136
+ * Emitted when token is about to be destroyed.
137
+ *
138
+ * @event destroy
139
+ */
104
140
  this.$emit('destroy');
105
141
  }
106
142
  },
@@ -108,13 +144,13 @@ export default {
108
144
  },
109
145
 
110
146
  created() {
111
- if (!('operator' in this.value)) {
147
+ if (!('operator' in this.tokenValue)) {
112
148
  if (this.operators.length === 1) {
113
149
  const operator = this.operators[0].value;
114
- this.$emit('input', { ...this.value, operator });
150
+ this.$emit('input', { ...this.tokenValue, operator });
115
151
  this.activeSegment = SEGMENT_DATA;
116
152
  } else {
117
- this.$emit('input', { ...this.value, operator: '' });
153
+ this.$emit('input', { ...this.tokenValue, operator: '' });
118
154
  }
119
155
  }
120
156
  },
@@ -124,6 +160,11 @@ export default {
124
160
  this.activeSegment = segment;
125
161
 
126
162
  if (!this.active) {
163
+ /**
164
+ * Emitted when this term token is clicked.
165
+ *
166
+ * @event activate
167
+ */
127
168
  this.$emit('activate');
128
169
  }
129
170
  },
@@ -137,8 +178,12 @@ export default {
137
178
  },
138
179
 
139
180
  replaceWithTermIfEmpty() {
140
- if (this.value.operator === '' && this.value.data === '') {
141
- this.$emit('replace', { type: TERM_TOKEN_TYPE, value: { data: this.config.title } });
181
+ if (this.tokenValue.operator === '' && this.tokenValue.data === '') {
182
+ /**
183
+ * Emitted when this token is converted to another type
184
+ * @property {object} token Replacement token configuration
185
+ */
186
+ this.$emit('replace', createTerm(this.config.title));
142
187
  }
143
188
  },
144
189
 
@@ -147,6 +192,11 @@ export default {
147
192
 
148
193
  if (newTokenConfig === this.config) {
149
194
  this.$nextTick(() => {
195
+ /**
196
+ * Emitted when this term token will lose its focus.
197
+ *
198
+ * @event deactivate
199
+ */
150
200
  this.$emit('deactivate');
151
201
  });
152
202
  return;
@@ -157,7 +207,7 @@ export default {
157
207
  this.config.dataType && this.config.dataType === newTokenConfig.dataType;
158
208
  this.$emit('replace', {
159
209
  type: newTokenConfig.type,
160
- value: isCompatible ? this.value : { data: '' },
210
+ value: isCompatible ? this.tokenValue : { data: '' },
161
211
  });
162
212
  }
163
213
  },
@@ -174,7 +224,7 @@ export default {
174
224
  key.length === 1 &&
175
225
  !this.operators.find(({ value }) => value.startsWith(potentialValue))
176
226
  ) {
177
- if (this.value.data === '') {
227
+ if (this.tokenValue.data === '') {
178
228
  applySuggestion(suggestedValue);
179
229
  } else {
180
230
  evt.preventDefault();
@@ -184,21 +234,27 @@ export default {
184
234
 
185
235
  activateDataSegment() {
186
236
  if (this.config.multiSelect) {
187
- this.$emit('input', { ...this.value, data: '' });
237
+ this.$emit('input', { ...this.tokenValue, data: '' });
188
238
  }
189
239
  this.activateSegment(this.$options.segments.SEGMENT_DATA);
190
240
  },
191
241
 
192
242
  handleComplete() {
193
243
  if (this.config.multiSelect) {
194
- this.$emit('input', { ...this.value, data: this.multiSelectValues.join(COMMA) });
244
+ this.$emit('input', { ...this.tokenValue, data: this.multiSelectValues.join(COMMA) });
195
245
  }
246
+ /**
247
+ * Emitted when the token entry has been completed.
248
+ *
249
+ * @event complete
250
+ */
196
251
  this.$emit('complete');
197
252
  },
198
253
 
199
254
  destroyByClose(event) {
200
255
  if (event.target.closest(TOKEN_CLOSE_SELECTOR)) {
201
256
  event.preventDefault();
257
+ event.stopPropagation();
202
258
  this.$emit('destroy');
203
259
  }
204
260
  },
@@ -208,6 +264,10 @@ export default {
208
264
 
209
265
  <template>
210
266
  <div class="gl-filtered-search-token" :class="{ 'gl-filtered-search-token-active': active }">
267
+ <!--
268
+ Emitted when the token is submitted.
269
+ @event submit
270
+ -->
211
271
  <gl-filtered-search-token-segment
212
272
  key="title-segment"
213
273
  :value="config.title"
@@ -228,10 +288,9 @@ export default {
228
288
  >
229
289
  </template>
230
290
  </gl-filtered-search-token-segment>
231
- <!-- eslint-disable vue/no-mutating-props -->
232
291
  <gl-filtered-search-token-segment
233
292
  key="operator-segment"
234
- v-model="value.operator"
293
+ v-model="tokenValue.operator"
235
294
  :active="isSegmentActive($options.segments.SEGMENT_OPERATOR)"
236
295
  :options="operators"
237
296
  :custom-input-keydown-handler="handleOperatorKeydown"
@@ -241,7 +300,6 @@ export default {
241
300
  @complete="activateSegment($options.segments.SEGMENT_DATA)"
242
301
  @deactivate="$emit('deactivate')"
243
302
  >
244
- <!-- eslint-enable vue/no-mutating-props -->
245
303
  <template #view>
246
304
  <gl-token
247
305
  class="gl-filtered-search-token-operator"
@@ -260,11 +318,20 @@ export default {
260
318
  </div>
261
319
  </template>
262
320
  </gl-filtered-search-token-segment>
263
- <!-- eslint-disable vue/no-mutating-props -->
321
+ <!--
322
+ Emitted when a suggestion has been selected.
323
+ @event select
324
+ @type {string} value The value of the selected suggestion.
325
+ -->
326
+ <!--
327
+ Emitted when Space is pressed in-between term text.
328
+ @event split
329
+ @property {array} newTokens Token configurations
330
+ -->
264
331
  <gl-filtered-search-token-segment
265
332
  v-if="hasDataOrDataSegmentIsCurrentlyActive"
266
333
  key="data-segment"
267
- v-model="value.data"
334
+ v-model="tokenValue.data"
268
335
  :active="isSegmentActive($options.segments.SEGMENT_DATA)"
269
336
  :multi-select="config.multiSelect"
270
337
  :options="config.options"
@@ -277,11 +344,12 @@ export default {
277
344
  @deactivate="$emit('deactivate')"
278
345
  @split="$emit('split', $event)"
279
346
  >
280
- <!-- eslint-enable vue/no-mutating-props -->
281
347
  <template #suggestions>
348
+ <!-- @slot The suggestions (implemented with GlFilteredSearchSuggestion). -->
282
349
  <slot name="suggestions"></slot>
283
350
  </template>
284
351
  <template #view="{ inputValue }">
352
+ <!-- @slot Used to customize how the token is rendered. -->
285
353
  <slot
286
354
  name="view-token"
287
355
  v-bind="{
@@ -300,6 +368,10 @@ export default {
300
368
  @mousedown="destroyByClose"
301
369
  >
302
370
  <span class="gl-filtered-search-token-data-content">
371
+ <!--
372
+ @slot Template for token value in inactive state
373
+ @binding {array} suggestions Slot for rendering autocomplete suggestions when no options are provided.
374
+ -->
303
375
  <slot name="view" v-bind="{ inputValue }">{{ inputValue }}</slot>
304
376
  </span>
305
377
  </gl-token>
@@ -2,47 +2,4 @@ import * as description from './filtered_search_token_segment.md';
2
2
 
3
3
  export default {
4
4
  description,
5
- bootstrapComponent: null,
6
- propsInfo: {
7
- active: {
8
- additionalInfo: 'If this term token is currently active',
9
- },
10
- options: {
11
- additionalInfo: '',
12
- },
13
- optionTextField: {
14
- additionalInfo: '',
15
- },
16
- customInputKeydownHandler: {
17
- additionalInfo: '',
18
- },
19
- value: {
20
- additionalInfo: 'Current term value',
21
- },
22
- searchInputAttributes: {
23
- additionalInfo: 'HTML attributes to add to the search input',
24
- },
25
- isLastToken: {
26
- additionalInfo: 'If this is the last token',
27
- },
28
- },
29
- events: [
30
- { event: 'activate', description: 'Emitted on mousedown event on the main component' },
31
- { event: 'backspace', description: 'Emitted when Backspace is pressed and the value is empty' },
32
- {
33
- event: 'complete',
34
- description: 'Emitted when suggestion is selected from the suggestion list',
35
- },
36
- { event: 'submit', description: 'Emitted when Enter is pressed and no suggestion is selected' },
37
- {
38
- event: 'split',
39
- args: [
40
- {
41
- arg: 'newStrings',
42
- description: '(Array of strings) New strings to be converted into term tokens',
43
- },
44
- ],
45
- description: 'Emitted when Space appears in token segment value',
46
- },
47
- ],
48
5
  };
@@ -1,5 +1,3 @@
1
- # Filtered Search Token Segment
2
-
3
1
  The filtered search token segment is a component for managing token input either via free typing
4
2
  or by selecting item through dropdown list
5
3