@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
@@ -4,21 +4,33 @@ import { INTENT_ACTIVATE_PREVIOUS } from './filtered_search_utils';
4
4
  import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
5
5
 
6
6
  var script = {
7
+ name: 'GlFilteredSearchTerm',
7
8
  components: {
8
9
  GlFilteredSearchTokenSegment,
9
10
  GlFilteredSearchSuggestion
10
11
  },
11
12
  inheritAttrs: false,
12
13
  props: {
14
+ /**
15
+ * Tokens available for this filtered search instance.
16
+ */
13
17
  availableTokens: {
14
18
  type: Array,
15
19
  required: true
16
20
  },
21
+
22
+ /**
23
+ * Determines if the term is being edited or not.
24
+ */
17
25
  active: {
18
26
  type: Boolean,
19
27
  required: false,
20
28
  default: false
21
29
  },
30
+
31
+ /**
32
+ * Current term value.
33
+ */
22
34
  value: {
23
35
  type: Object,
24
36
  required: false,
@@ -31,16 +43,28 @@ var script = {
31
43
  required: false,
32
44
  default: ''
33
45
  },
46
+
47
+ /**
48
+ * HTML attributes to add to the search input.
49
+ */
34
50
  searchInputAttributes: {
35
51
  type: Object,
36
52
  required: false,
37
53
  default: () => ({})
38
54
  },
55
+
56
+ /**
57
+ * If this is the last token.
58
+ */
39
59
  isLastToken: {
40
60
  type: Boolean,
41
61
  required: false,
42
62
  default: false
43
63
  },
64
+
65
+ /**
66
+ * The current `value` (tokens) of the ancestor GlFilteredSearch component.
67
+ */
44
68
  currentValue: {
45
69
  type: Array,
46
70
  required: false,
@@ -58,6 +82,12 @@ var script = {
58
82
  },
59
83
 
60
84
  set(data) {
85
+ /**
86
+ * Emitted when the token changes its value.
87
+ *
88
+ * @event input
89
+ * @type {object} dataObj Object containing the update value.
90
+ */
61
91
  this.$emit('input', {
62
92
  data
63
93
  });
@@ -67,6 +97,13 @@ var script = {
67
97
  },
68
98
  methods: {
69
99
  onBackspace() {
100
+ /**
101
+ * Emitted when token value is empty and backspace is pressed.
102
+ * Includes user intent to activate previous token.
103
+ *
104
+ * @event destroy
105
+ * @type {object} details The user intent
106
+ */
70
107
  this.$emit('destroy', {
71
108
  intent: INTENT_ACTIVATE_PREVIOUS
72
109
  });
@@ -1,4 +1,4 @@
1
- var filtered_search_token = "# Filtered Search Binary Token\n\nFiltered search token is a helper component, intended to\nsimplify the creation of filters tokens which consist of a title, operators\nand an editable value with autocomplete. This component abstracts token management\nlogic and allows you to focus on implementing autocomplete or view logic.\n\nThis component is not intended to be used outside of the `GlFilteredSearch` component.\n\n## Usage\n\nMake sure to pass `$listeners` to `gl-filtered-search-token`, or route events properly:\n\n```js\n<gl-filtered-search-token\n title=\"Confidential\"\n :active=\"active\"\n :value=\"value\"\n v-on=\"$listeners\"\n>\n <template #suggestions>\n <gl-filtered-search-suggestion value=\"Yes\"><gl-icon name=\"eye-slash\" :size=\"16\"/> Yes</gl-filtered-search-suggestion>\n <gl-filtered-search-suggestion value=\"No\"><gl-icon name=\"eye\" :size=\"16\"/> No</gl-filtered-search-suggestion>\n </template>\n</gl-filtered-search-token>\n```\n";
1
+ var filtered_search_token = "Filtered search token is a helper component, intended to\nsimplify the creation of filters tokens which consist of a title, operators\nand an editable value with autocomplete. This component abstracts token management\nlogic and allows you to focus on implementing autocomplete or view logic.\n\nThis component is not intended to be used outside of the `GlFilteredSearch` component.\n\n## Usage\n\nMake sure to pass `$listeners` to `gl-filtered-search-token`, or route events properly:\n\n```html\n<gl-filtered-search-token\n title=\"Confidential\"\n :active=\"active\"\n :value=\"value\"\n v-on=\"$listeners\"\n>\n <template #suggestions>\n <gl-filtered-search-suggestion value=\"Yes\"><gl-icon name=\"eye-slash\" :size=\"16\"/> Yes</gl-filtered-search-suggestion>\n <gl-filtered-search-suggestion value=\"No\"><gl-icon name=\"eye\" :size=\"16\"/> No</gl-filtered-search-suggestion>\n </template>\n</gl-filtered-search-token>\n```\n";
2
2
 
3
3
  var description = /*#__PURE__*/Object.freeze({
4
4
  __proto__: null,
@@ -6,36 +6,7 @@ var description = /*#__PURE__*/Object.freeze({
6
6
  });
7
7
 
8
8
  var filtered_search_token_documentation = {
9
- description,
10
- bootstrapComponent: null,
11
- propsInfo: {
12
- title: {
13
- additionalInfo: 'Token title'
14
- },
15
- config: {
16
- additionalInfo: 'Token configuration with available operators and options'
17
- },
18
- active: {
19
- additionalInfo: 'If this token is currently active'
20
- },
21
- value: {
22
- additionalInfo: 'Current value'
23
- },
24
- replace: {
25
- args: [{
26
- arg: 'token',
27
- description: '(Object) Replacement token configuration'
28
- }],
29
- description: 'Emitted when this token is converted to another type'
30
- }
31
- },
32
- slots: [{
33
- name: 'view',
34
- description: 'Template for token value in inactive state'
35
- }, {
36
- name: 'suggestions',
37
- description: 'Slot for rendering autocomplete suggestions when no options are provided.'
38
- }]
9
+ description
39
10
  };
40
11
 
41
12
  export default filtered_search_token_documentation;
@@ -1,7 +1,8 @@
1
+ import _cloneDeep from 'lodash/cloneDeep';
1
2
  import { COMMA } from '../../../utils/constants';
2
3
  import GlToken from '../token/token';
3
4
  import GlFilteredSearchTokenSegment from './filtered_search_token_segment';
4
- import { TERM_TOKEN_TYPE } from './filtered_search_utils';
5
+ import { createTerm } from './filtered_search_utils';
5
6
  import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
6
7
 
7
8
  const SEGMENT_TITLE = 'TYPE';
@@ -17,6 +18,7 @@ const DEFAULT_OPERATORS = [{
17
18
  description: 'is not'
18
19
  }];
19
20
  var script = {
21
+ name: 'GlFilteredSearchToken',
20
22
  components: {
21
23
  GlToken,
22
24
  GlFilteredSearchTokenSegment
@@ -28,11 +30,19 @@ var script = {
28
30
  required: false,
29
31
  default: () => []
30
32
  },
33
+
34
+ /**
35
+ * Token configuration with available operators and options.
36
+ */
31
37
  config: {
32
38
  type: Object,
33
39
  required: false,
34
40
  default: () => ({})
35
41
  },
42
+
43
+ /**
44
+ * Determines if the token is being edited or not.
45
+ */
36
46
  active: {
37
47
  type: Boolean,
38
48
  required: false,
@@ -43,6 +53,10 @@ var script = {
43
53
  required: false,
44
54
  default: () => []
45
55
  },
56
+
57
+ /**
58
+ * Current token value.
59
+ */
46
60
  value: {
47
61
  type: Object,
48
62
  required: false,
@@ -51,6 +65,10 @@ var script = {
51
65
  data: ''
52
66
  })
53
67
  },
68
+
69
+ /**
70
+ * Display operators' descriptions instead of their values (e.g., "is" instead of "=").
71
+ */
54
72
  showFriendlyText: {
55
73
  type: Boolean,
56
74
  required: false,
@@ -60,7 +78,8 @@ var script = {
60
78
 
61
79
  data() {
62
80
  return {
63
- activeSegment: null
81
+ activeSegment: null,
82
+ tokenValue: _cloneDeep(this.value)
64
83
  };
65
84
  },
66
85
 
@@ -70,7 +89,7 @@ var script = {
70
89
  },
71
90
 
72
91
  hasDataOrDataSegmentIsCurrentlyActive() {
73
- return this.value.data !== '' || this.isSegmentActive(SEGMENT_DATA);
92
+ return this.tokenValue.data !== '' || this.isSegmentActive(SEGMENT_DATA);
74
93
  },
75
94
 
76
95
  availableTokensWithSelf() {
@@ -80,7 +99,7 @@ var script = {
80
99
  },
81
100
 
82
101
  operatorDescription() {
83
- const operator = this.operators.find(op => op.value === this.value.operator);
102
+ const operator = this.operators.find(op => op.value === this.tokenValue.operator);
84
103
  return this.showFriendlyText ? operator === null || operator === void 0 ? void 0 : operator.description : operator === null || operator === void 0 ? void 0 : operator.value;
85
104
  }
86
105
 
@@ -91,13 +110,29 @@ var script = {
91
110
  SEGMENT_OPERATOR
92
111
  },
93
112
  watch: {
94
- value: {
113
+ tokenValue: {
95
114
  deep: true,
96
115
 
97
116
  handler(newValue) {
117
+ /**
118
+ * Emitted when the token changes its value.
119
+ *
120
+ * @event input
121
+ * @type {object} dataObj Object containing the update value.
122
+ */
98
123
  this.$emit('input', newValue);
99
124
  }
100
125
 
126
+ },
127
+ value: {
128
+ handler(newValue, oldValue) {
129
+ if ((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)) {
130
+ return;
131
+ }
132
+
133
+ this.tokenValue = _cloneDeep(newValue);
134
+ }
135
+
101
136
  },
102
137
  active: {
103
138
  immediate: true,
@@ -105,10 +140,16 @@ var script = {
105
140
  handler(newValue) {
106
141
  if (newValue) {
107
142
  if (!this.activeSegment) {
108
- this.activateSegment(this.value.data !== '' ? SEGMENT_DATA : SEGMENT_OPERATOR);
143
+ this.activateSegment(this.tokenValue.data !== '' ? SEGMENT_DATA : SEGMENT_OPERATOR);
109
144
  }
110
- } else if (this.value.data === '') {
145
+ } else if (this.tokenValue.data === '') {
111
146
  this.activeSegment = null;
147
+ /**
148
+ * Emitted when token is about to be destroyed.
149
+ *
150
+ * @event destroy
151
+ */
152
+
112
153
  this.$emit('destroy');
113
154
  }
114
155
  }
@@ -117,15 +158,15 @@ var script = {
117
158
  },
118
159
 
119
160
  created() {
120
- if (!('operator' in this.value)) {
161
+ if (!('operator' in this.tokenValue)) {
121
162
  if (this.operators.length === 1) {
122
163
  const operator = this.operators[0].value;
123
- this.$emit('input', { ...this.value,
164
+ this.$emit('input', { ...this.tokenValue,
124
165
  operator
125
166
  });
126
167
  this.activeSegment = SEGMENT_DATA;
127
168
  } else {
128
- this.$emit('input', { ...this.value,
169
+ this.$emit('input', { ...this.tokenValue,
129
170
  operator: ''
130
171
  });
131
172
  }
@@ -137,6 +178,11 @@ var script = {
137
178
  this.activeSegment = segment;
138
179
 
139
180
  if (!this.active) {
181
+ /**
182
+ * Emitted when this term token is clicked.
183
+ *
184
+ * @event activate
185
+ */
140
186
  this.$emit('activate');
141
187
  }
142
188
  },
@@ -152,13 +198,12 @@ var script = {
152
198
  },
153
199
 
154
200
  replaceWithTermIfEmpty() {
155
- if (this.value.operator === '' && this.value.data === '') {
156
- this.$emit('replace', {
157
- type: TERM_TOKEN_TYPE,
158
- value: {
159
- data: this.config.title
160
- }
161
- });
201
+ if (this.tokenValue.operator === '' && this.tokenValue.data === '') {
202
+ /**
203
+ * Emitted when this token is converted to another type
204
+ * @property {object} token Replacement token configuration
205
+ */
206
+ this.$emit('replace', createTerm(this.config.title));
162
207
  }
163
208
  },
164
209
 
@@ -167,6 +212,11 @@ var script = {
167
212
 
168
213
  if (newTokenConfig === this.config) {
169
214
  this.$nextTick(() => {
215
+ /**
216
+ * Emitted when this term token will lose its focus.
217
+ *
218
+ * @event deactivate
219
+ */
170
220
  this.$emit('deactivate');
171
221
  });
172
222
  return;
@@ -176,7 +226,7 @@ var script = {
176
226
  const isCompatible = this.config.dataType && this.config.dataType === newTokenConfig.dataType;
177
227
  this.$emit('replace', {
178
228
  type: newTokenConfig.type,
179
- value: isCompatible ? this.value : {
229
+ value: isCompatible ? this.tokenValue : {
180
230
  data: ''
181
231
  }
182
232
  });
@@ -206,7 +256,7 @@ var script = {
206
256
  } = _ref2;
207
257
  return value.startsWith(potentialValue);
208
258
  })) {
209
- if (this.value.data === '') {
259
+ if (this.tokenValue.data === '') {
210
260
  applySuggestion(suggestedValue);
211
261
  } else {
212
262
  evt.preventDefault();
@@ -216,7 +266,7 @@ var script = {
216
266
 
217
267
  activateDataSegment() {
218
268
  if (this.config.multiSelect) {
219
- this.$emit('input', { ...this.value,
269
+ this.$emit('input', { ...this.tokenValue,
220
270
  data: ''
221
271
  });
222
272
  }
@@ -226,10 +276,16 @@ var script = {
226
276
 
227
277
  handleComplete() {
228
278
  if (this.config.multiSelect) {
229
- this.$emit('input', { ...this.value,
279
+ this.$emit('input', { ...this.tokenValue,
230
280
  data: this.multiSelectValues.join(COMMA)
231
281
  });
232
282
  }
283
+ /**
284
+ * Emitted when the token entry has been completed.
285
+ *
286
+ * @event complete
287
+ */
288
+
233
289
 
234
290
  this.$emit('complete');
235
291
  },
@@ -237,6 +293,7 @@ var script = {
237
293
  destroyByClose(event) {
238
294
  if (event.target.closest(TOKEN_CLOSE_SELECTOR)) {
239
295
  event.preventDefault();
296
+ event.stopPropagation();
240
297
  this.$emit('destroy');
241
298
  }
242
299
  }
@@ -252,14 +309,14 @@ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=
252
309
  var inputValue = ref.inputValue;
253
310
  return [_c('gl-token',{staticClass:"gl-filtered-search-token-type",class:_vm.getAdditionalSegmentClasses(_vm.$options.segments.SEGMENT_TITLE),attrs:{"view-only":""}},[_vm._v(_vm._s(inputValue))])]}}])}),_vm._v(" "),_c('gl-filtered-search-token-segment',{key:"operator-segment",attrs:{"active":_vm.isSegmentActive(_vm.$options.segments.SEGMENT_OPERATOR),"options":_vm.operators,"custom-input-keydown-handler":_vm.handleOperatorKeydown,"view-only":""},on:{"activate":function($event){return _vm.activateSegment(_vm.$options.segments.SEGMENT_OPERATOR)},"backspace":_vm.replaceWithTermIfEmpty,"complete":function($event){return _vm.activateSegment(_vm.$options.segments.SEGMENT_DATA)},"deactivate":function($event){return _vm.$emit('deactivate')}},scopedSlots:_vm._u([{key:"view",fn:function(){return [_c('gl-token',{staticClass:"gl-filtered-search-token-operator",class:_vm.getAdditionalSegmentClasses(_vm.$options.segments.SEGMENT_OPERATOR),attrs:{"variant":"search-value","view-only":""}},[_vm._v(_vm._s(_vm.operatorDescription))])]},proxy:true},{key:"option",fn:function(ref){
254
311
  var option = ref.option;
255
- return [_c('div',{staticClass:"gl-display-flex"},[_vm._v("\n "+_vm._s(option.value)+"\n "),(option.description)?_c('span',{staticClass:"gl-filtered-search-token-operator-description"},[_vm._v("\n "+_vm._s(option.description)+"\n ")]):_vm._e()])]}}]),model:{value:(_vm.value.operator),callback:function ($$v) {_vm.$set(_vm.value, "operator", $$v);},expression:"value.operator"}}),_vm._v(" "),(_vm.hasDataOrDataSegmentIsCurrentlyActive)?_c('gl-filtered-search-token-segment',{key:"data-segment",attrs:{"active":_vm.isSegmentActive(_vm.$options.segments.SEGMENT_DATA),"multi-select":_vm.config.multiSelect,"options":_vm.config.options,"option-text-field":"title"},on:{"activate":_vm.activateDataSegment,"backspace":function($event){return _vm.activateSegment(_vm.$options.segments.SEGMENT_OPERATOR)},"complete":_vm.handleComplete,"select":function($event){return _vm.$emit('select', $event)},"submit":function($event){return _vm.$emit('submit')},"deactivate":function($event){return _vm.$emit('deactivate')},"split":function($event){return _vm.$emit('split', $event)}},scopedSlots:_vm._u([{key:"suggestions",fn:function(){return [_vm._t("suggestions")]},proxy:true},{key:"view",fn:function(ref){
312
+ return [_c('div',{staticClass:"gl-display-flex"},[_vm._v("\n "+_vm._s(option.value)+"\n "),(option.description)?_c('span',{staticClass:"gl-filtered-search-token-operator-description"},[_vm._v("\n "+_vm._s(option.description)+"\n ")]):_vm._e()])]}}]),model:{value:(_vm.tokenValue.operator),callback:function ($$v) {_vm.$set(_vm.tokenValue, "operator", $$v);},expression:"tokenValue.operator"}}),_vm._v(" "),(_vm.hasDataOrDataSegmentIsCurrentlyActive)?_c('gl-filtered-search-token-segment',{key:"data-segment",attrs:{"active":_vm.isSegmentActive(_vm.$options.segments.SEGMENT_DATA),"multi-select":_vm.config.multiSelect,"options":_vm.config.options,"option-text-field":"title"},on:{"activate":_vm.activateDataSegment,"backspace":function($event){return _vm.activateSegment(_vm.$options.segments.SEGMENT_OPERATOR)},"complete":_vm.handleComplete,"select":function($event){return _vm.$emit('select', $event)},"submit":function($event){return _vm.$emit('submit')},"deactivate":function($event){return _vm.$emit('deactivate')},"split":function($event){return _vm.$emit('split', $event)}},scopedSlots:_vm._u([{key:"suggestions",fn:function(){return [_vm._t("suggestions")]},proxy:true},{key:"view",fn:function(ref){
256
313
  var inputValue = ref.inputValue;
257
314
  return [_vm._t("view-token",[_c('gl-token',{staticClass:"gl-filtered-search-token-data",class:_vm.getAdditionalSegmentClasses(_vm.$options.segments.SEGMENT_DATA),attrs:{"variant":"search-value"},on:{"mousedown":_vm.destroyByClose}},[_c('span',{staticClass:"gl-filtered-search-token-data-content"},[_vm._t("view",[_vm._v(_vm._s(inputValue))],null,{ inputValue: inputValue })],2)])],null,{
258
315
  inputValue: inputValue,
259
316
  listeners: { mousedown: _vm.destroyByClose },
260
317
  cssClasses: Object.assign({}, {'gl-filtered-search-token-data': true},
261
318
  _vm.getAdditionalSegmentClasses(_vm.$options.segments.SEGMENT_DATA)),
262
- })]}}],null,true),model:{value:(_vm.value.data),callback:function ($$v) {_vm.$set(_vm.value, "data", $$v);},expression:"value.data"}}):_vm._e()],1)};
319
+ })]}}],null,true),model:{value:(_vm.tokenValue.data),callback:function ($$v) {_vm.$set(_vm.tokenValue, "data", $$v);},expression:"tokenValue.data"}}):_vm._e()],1)};
263
320
  var __vue_staticRenderFns__ = [];
264
321
 
265
322
  /* style */
@@ -1,4 +1,4 @@
1
- var filtered_search_token_segment = "# Filtered Search Token Segment\n\nThe filtered search token segment is a component for managing token input either via free typing\nor by selecting item through dropdown list\n\n## Usage\n\nThis component is internal and is not intended to be used by `@gitlab/ui` users.\n\n## Internet Explorer 11\n\nThis component uses [`String.prototype.startsWith()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith)\nand [`String.prototype.endsWith()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith)\nunder the hood. Make sure those methods are polyfilled if you plan on using the component on IE11.\n\n> NOTE: These methods are already polyfilled in GitLab: [`app/assets/javascripts/commons/polyfills.js#L15-16`](https://gitlab.com/gitlab-org/gitlab/blob/dc60dee6ed6234dda9f032195577cd8fad9646d8/app/assets/javascripts/commons/polyfills.js#L15-16)\n";
1
+ var filtered_search_token_segment = "The filtered search token segment is a component for managing token input either via free typing\nor by selecting item through dropdown list\n\n## Usage\n\nThis component is internal and is not intended to be used by `@gitlab/ui` users.\n\n## Internet Explorer 11\n\nThis component uses [`String.prototype.startsWith()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith)\nand [`String.prototype.endsWith()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith)\nunder the hood. Make sure those methods are polyfilled if you plan on using the component on IE11.\n\n> NOTE: These methods are already polyfilled in GitLab: [`app/assets/javascripts/commons/polyfills.js#L15-16`](https://gitlab.com/gitlab-org/gitlab/blob/dc60dee6ed6234dda9f032195577cd8fad9646d8/app/assets/javascripts/commons/polyfills.js#L15-16)\n";
2
2
 
3
3
  var description = /*#__PURE__*/Object.freeze({
4
4
  __proto__: null,
@@ -6,51 +6,7 @@ var description = /*#__PURE__*/Object.freeze({
6
6
  });
7
7
 
8
8
  var filtered_search_token_segment_documentation = {
9
- description,
10
- bootstrapComponent: null,
11
- propsInfo: {
12
- active: {
13
- additionalInfo: 'If this term token is currently active'
14
- },
15
- options: {
16
- additionalInfo: ''
17
- },
18
- optionTextField: {
19
- additionalInfo: ''
20
- },
21
- customInputKeydownHandler: {
22
- additionalInfo: ''
23
- },
24
- value: {
25
- additionalInfo: 'Current term value'
26
- },
27
- searchInputAttributes: {
28
- additionalInfo: 'HTML attributes to add to the search input'
29
- },
30
- isLastToken: {
31
- additionalInfo: 'If this is the last token'
32
- }
33
- },
34
- events: [{
35
- event: 'activate',
36
- description: 'Emitted on mousedown event on the main component'
37
- }, {
38
- event: 'backspace',
39
- description: 'Emitted when Backspace is pressed and the value is empty'
40
- }, {
41
- event: 'complete',
42
- description: 'Emitted when suggestion is selected from the suggestion list'
43
- }, {
44
- event: 'submit',
45
- description: 'Emitted when Enter is pressed and no suggestion is selected'
46
- }, {
47
- event: 'split',
48
- args: [{
49
- arg: 'newStrings',
50
- description: '(Array of strings) New strings to be converted into term tokens'
51
- }],
52
- description: 'Emitted when Space appears in token segment value'
53
- }]
9
+ description
54
10
  };
55
11
 
56
12
  export default filtered_search_token_segment_documentation;
@@ -7,6 +7,7 @@ import { splitOnQuotes, wrapTokenInQuotes } from './filtered_search_utils';
7
7
  import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
8
8
 
9
9
  var script = {
10
+ name: 'GlFilteredSearchTokenSegment',
10
11
  components: {
11
12
  Portal,
12
13
  GlFilteredSearchSuggestionList,
@@ -15,6 +16,9 @@ var script = {
15
16
  inject: ['portalName', 'alignSuggestions'],
16
17
  inheritAttrs: false,
17
18
  props: {
19
+ /**
20
+ * If this token segment is currently being edited.
21
+ */
18
22
  active: {
19
23
  type: Boolean,
20
24
  required: false,
@@ -45,15 +49,27 @@ var script = {
45
49
  required: false,
46
50
  default: () => () => false
47
51
  },
52
+
53
+ /**
54
+ * Current term value
55
+ */
48
56
  value: {
49
57
  required: true,
50
58
  validator: () => true
51
59
  },
60
+
61
+ /**
62
+ * HTML attributes to add to the search input
63
+ */
52
64
  searchInputAttributes: {
53
65
  type: Object,
54
66
  required: false,
55
67
  default: () => ({})
56
68
  },
69
+
70
+ /**
71
+ * If this is the last token
72
+ */
57
73
  isLastToken: {
58
74
  type: Boolean,
59
75
  required: false,
@@ -91,6 +107,11 @@ var script = {
91
107
  set(v) {
92
108
  var _this$getMatchingOpti, _this$getMatchingOpti2;
93
109
 
110
+ /**
111
+ * Emitted when this token segment's value changes.
112
+ *
113
+ * @type {object} option The current option.
114
+ */
94
115
  this.$emit('input', (_this$getMatchingOpti = (_this$getMatchingOpti2 = this.getMatchingOptionForInputValue(v)) === null || _this$getMatchingOpti2 === void 0 ? void 0 : _this$getMatchingOpti2.value) !== null && _this$getMatchingOpti !== void 0 ? _this$getMatchingOpti : v);
95
116
  }
96
117
 
@@ -152,6 +173,10 @@ var script = {
152
173
  this.$emit('input', (_this$getMatchingOpti3 = (_this$getMatchingOpti4 = this.getMatchingOptionForInputValue(firstWord)) === null || _this$getMatchingOpti4 === void 0 ? void 0 : _this$getMatchingOpti4.value) !== null && _this$getMatchingOpti3 !== void 0 ? _this$getMatchingOpti3 : firstWord);
153
174
 
154
175
  if (otherWords.length) {
176
+ /**
177
+ * Emitted when Space appears in token segment value
178
+ * @property {array|string} newStrings New strings to be converted into term tokens
179
+ */
155
180
  this.$emit('split', otherWords);
156
181
  }
157
182
  }
@@ -160,6 +185,9 @@ var script = {
160
185
  methods: {
161
186
  emitIfInactive(e) {
162
187
  if (!this.active) {
188
+ /**
189
+ * Emitted on mousedown event on the main component.
190
+ */
163
191
  this.$emit('activate');
164
192
  e.preventDefault();
165
193
  }
@@ -208,6 +236,12 @@ var script = {
208
236
 
209
237
  applySuggestion(suggestedValue) {
210
238
  const formattedSuggestedValue = wrapTokenInQuotes(suggestedValue);
239
+ /**
240
+ * Emitted when autocomplete entry is selected.
241
+ *
242
+ * @type {string} value The selected value.
243
+ */
244
+
211
245
  this.$emit('select', formattedSuggestedValue);
212
246
 
213
247
  if (!this.multiSelect) {
@@ -228,6 +262,10 @@ var script = {
228
262
  if (key === 'Backspace') {
229
263
  if (this.inputValue === '') {
230
264
  e.preventDefault();
265
+ /**
266
+ * Emitted when Backspace is pressed and the value is empty
267
+ */
268
+
231
269
  this.$emit('backspace');
232
270
  }
233
271
 
@@ -241,6 +279,9 @@ var script = {
241
279
  if (suggestedValue != null) {
242
280
  this.applySuggestion(suggestedValue);
243
281
  } else {
282
+ /**
283
+ * Emitted when Enter is pressed and no suggestion is selected
284
+ */
244
285
  this.$emit('submit');
245
286
  }
246
287
  },
@@ -252,6 +293,10 @@ var script = {
252
293
  },
253
294
  Escape: () => {
254
295
  e.preventDefault();
296
+ /**
297
+ * Emitted when suggestion is selected from the suggestion list
298
+ */
299
+
255
300
  this.$emit('complete');
256
301
  }
257
302
  };
@@ -282,6 +327,9 @@ var script = {
282
327
  if (this.multiSelect) {
283
328
  this.$emit('complete');
284
329
  } else if (this.active) {
330
+ /**
331
+ * Emitted when this term token will lose its focus.
332
+ */
285
333
  this.$emit('deactivate');
286
334
  }
287
335
  }
@@ -38,7 +38,45 @@ function needDenormalization(tokens) {
38
38
  }
39
39
 
40
40
  assertValidTokens(tokens);
41
- return tokens.some(t => typeof t === 'string');
41
+ return tokens.some(t => typeof t === 'string' || !t.id);
42
+ }
43
+ let tokenIdCounter = 0;
44
+
45
+ const getTokenId = () => {
46
+ const tokenId = `token-${tokenIdCounter}`;
47
+ tokenIdCounter += 1;
48
+ return tokenId;
49
+ };
50
+ /**
51
+ * Ensure the given token has an `id` property, which `GlFilteredSearch` relies
52
+ * on as a unique key for the token.
53
+ *
54
+ * If the given token does not have an `id`, it returns a shallow copy of the
55
+ * token with an `id`. Otherwise, it returns the given token.
56
+ *
57
+ * @param {object} token The token to check.
58
+ * @returns {object} A token with an `id`.
59
+ */
60
+
61
+
62
+ function ensureTokenId(token) {
63
+ if (!token.id) {
64
+ return { ...token,
65
+ id: getTokenId()
66
+ };
67
+ }
68
+
69
+ return token;
70
+ }
71
+ function createTerm() {
72
+ let data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
73
+ return {
74
+ id: getTokenId(),
75
+ type: TERM_TOKEN_TYPE,
76
+ value: {
77
+ data
78
+ }
79
+ };
42
80
  }
43
81
  function denormalizeTokens(inputTokens) {
44
82
  assertValidTokens(inputTokens);
@@ -47,14 +85,9 @@ function denormalizeTokens(inputTokens) {
47
85
  tokens.forEach(t => {
48
86
  if (typeof t === 'string') {
49
87
  const stringTokens = t.split(' ').filter(Boolean);
50
- stringTokens.forEach(strToken => result.push({
51
- type: TERM_TOKEN_TYPE,
52
- value: {
53
- data: strToken
54
- }
55
- }));
88
+ stringTokens.forEach(strToken => result.push(createTerm(strToken)));
56
89
  } else {
57
- result.push(t);
90
+ result.push(ensureTokenId(t));
58
91
  }
59
92
  });
60
93
  return result;
@@ -135,4 +168,4 @@ function wrapTokenInQuotes(token) {
135
168
  return `"${token}"`;
136
169
  }
137
170
 
138
- export { INTENT_ACTIVATE_PREVIOUS, TERM_TOKEN_TYPE, denormalizeTokens, isEmptyTerm, needDenormalization, normalizeTokens, splitOnQuotes, wrapTokenInQuotes };
171
+ export { INTENT_ACTIVATE_PREVIOUS, TERM_TOKEN_TYPE, createTerm, denormalizeTokens, ensureTokenId, isEmptyTerm, needDenormalization, normalizeTokens, splitOnQuotes, wrapTokenInQuotes };