@gitlab/ui 42.14.0 → 42.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ # [42.15.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v42.14.0...v42.15.0) (2022-07-07)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * fixed token container test ([21b3ae6](https://gitlab.com/gitlab-org/gitlab-ui/commit/21b3ae6ee8c84727726893454e012551b13c16ab))
7
+
8
+
9
+ ### Features
10
+
11
+ * added clear all functionality to token selector ([fcf5200](https://gitlab.com/gitlab-org/gitlab-ui/commit/fcf52007d519ededf128eff7e264c7c7507700ca))
12
+
1
13
  # [42.14.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v42.13.1...v42.14.0) (2022-07-06)
2
14
 
3
15
 
@@ -1,11 +1,13 @@
1
1
  import GlToken from '../token/token';
2
+ import GlButton from '../button/button';
2
3
  import { tokensValidator } from './helpers';
3
4
  import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
4
5
 
5
6
  var script = {
6
7
  name: 'GlTokenContainer',
7
8
  components: {
8
- GlToken
9
+ GlToken,
10
+ GlButton
9
11
  },
10
12
  props: {
11
13
  tokens: {
@@ -27,6 +29,11 @@ var script = {
27
29
  type: Boolean,
28
30
  required: false,
29
31
  default: false
32
+ },
33
+ showClearAllButton: {
34
+ type: Boolean,
35
+ required: false,
36
+ default: false
30
37
  }
31
38
  },
32
39
 
@@ -147,7 +154,7 @@ var script = {
147
154
  const __vue_script__ = script;
148
155
 
149
156
  /* template */
150
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{ref:"tokenContainer",staticClass:"gl-display-flex gl-flex-wrap gl-align-items-center gl-my-n1 gl-mx-n1",attrs:{"role":"listbox","aria-multiselectable":"false","aria-orientation":"horizontal","aria-invalid":_vm.state === false && 'true'},on:{"keydown":[function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"left",37,$event.key,["Left","ArrowLeft"])){ return null; }if('button' in $event && $event.button !== 0){ return null; }return _vm.handleLeftArrow($event)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"right",39,$event.key,["Right","ArrowRight"])){ return null; }if('button' in $event && $event.button !== 2){ return null; }return _vm.handleRightArrow($event)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"home",undefined,$event.key,undefined)){ return null; }return _vm.handleHome($event)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"end",undefined,$event.key,undefined)){ return null; }return _vm.handleEnd($event)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"delete",[8,46],$event.key,["Backspace","Delete","Del"])){ return null; }return _vm.handleDelete($event)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"esc",27,$event.key,["Esc","Escape"])){ return null; }return _vm.handleEscape($event)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"tab",9,$event.key,"Tab")){ return null; }if($event.ctrlKey||$event.shiftKey||$event.altKey||$event.metaKey){ return null; }$event.preventDefault();return _vm.handleTab($event)}]}},[_vm._l((_vm.tokens),function(token,index){return _c('div',{key:token.id,ref:"tokens",refInFor:true,staticClass:"gl-token-selector-token-container gl-px-1 gl-py-1 gl-outline-none",attrs:{"data-token-id":token.id,"role":"option","tabindex":"-1"},on:{"focus":function($event){_vm.bindFocusEvent ? _vm.handleTokenFocus(index) : null;}}},[_c('gl-token',{staticClass:"gl-cursor-default",class:token.class,style:(token.style),attrs:{"view-only":_vm.viewOnly},on:{"close":function($event){return _vm.handleClose(token)}}},[_vm._t("token-content",[_c('span',[_vm._v("\n "+_vm._s(token.name)+"\n ")])],{"token":token})],2)],1)}),_vm._v(" "),_vm._t("text-input")],2)};
157
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{ref:"tokenContainer",staticClass:"gl-display-flex gl-flex-wrap gl-align-items-center gl-my-n1 gl-mx-n1 gl-w-full",attrs:{"role":"listbox","aria-multiselectable":"false","aria-orientation":"horizontal","aria-invalid":_vm.state === false && 'true'},on:{"keydown":[function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"left",37,$event.key,["Left","ArrowLeft"])){ return null; }if('button' in $event && $event.button !== 0){ return null; }return _vm.handleLeftArrow($event)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"right",39,$event.key,["Right","ArrowRight"])){ return null; }if('button' in $event && $event.button !== 2){ return null; }return _vm.handleRightArrow($event)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"home",undefined,$event.key,undefined)){ return null; }return _vm.handleHome($event)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"end",undefined,$event.key,undefined)){ return null; }return _vm.handleEnd($event)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"delete",[8,46],$event.key,["Backspace","Delete","Del"])){ return null; }return _vm.handleDelete($event)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"esc",27,$event.key,["Esc","Escape"])){ return null; }return _vm.handleEscape($event)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"tab",9,$event.key,"Tab")){ return null; }if($event.ctrlKey||$event.shiftKey||$event.altKey||$event.metaKey){ return null; }$event.preventDefault();return _vm.handleTab($event)}]}},[_vm._l((_vm.tokens),function(token,index){return _c('div',{key:token.id,ref:"tokens",refInFor:true,staticClass:"gl-token-selector-token-container gl-px-1 gl-py-1 gl-outline-none",attrs:{"data-token-id":token.id,"role":"option","tabindex":"-1"},on:{"focus":function($event){_vm.bindFocusEvent ? _vm.handleTokenFocus(index) : null;}}},[_c('gl-token',{staticClass:"gl-cursor-default",class:token.class,style:(token.style),attrs:{"view-only":_vm.viewOnly},on:{"close":function($event){return _vm.handleClose(token)}}},[_vm._t("token-content",[_c('span',[_vm._v("\n "+_vm._s(token.name)+"\n ")])],{"token":token})],2)],1)}),_vm._v(" "),_vm._t("text-input"),_vm._v(" "),(_vm.showClearAllButton)?_c('gl-button',{attrs:{"size":"small","aria-label":"Clear all","icon":"clear","category":"tertiary","data-testid":"clear-all-button"},on:{"click":function($event){$event.stopPropagation();return _vm.$emit('clear-all')}}}):_vm._e()],2)};
151
158
  var __vue_staticRenderFns__ = [];
152
159
 
153
160
  /* style */
@@ -134,6 +134,15 @@ var script = {
134
134
  type: Boolean,
135
135
  required: false,
136
136
  default: false
137
+ },
138
+
139
+ /**
140
+ * Allows user to bulk delete tokens when enabled
141
+ */
142
+ allowClearAll: {
143
+ type: Boolean,
144
+ required: false,
145
+ default: false
137
146
  }
138
147
  },
139
148
 
@@ -189,8 +198,16 @@ var script = {
189
198
  return this.state ? 'is-valid gl-inset-border-1-gray-400!' : 'is-invalid gl-inset-border-1-red-500!';
190
199
  },
191
200
 
201
+ hasSelectedTokens() {
202
+ return this.selectedTokens.length > 0;
203
+ },
204
+
192
205
  showEmptyPlaceholder() {
193
- return this.selectedTokens.length === 0 && !this.inputFocused;
206
+ return !this.hasSelectedTokens && !this.inputFocused;
207
+ },
208
+
209
+ showClearAllButton() {
210
+ return this.hasSelectedTokens && this.allowClearAll;
194
211
  }
195
212
 
196
213
  },
@@ -384,6 +401,11 @@ var script = {
384
401
 
385
402
  registerFocusOnToken(focusOnToken) {
386
403
  this.focusOnToken = focusOnToken;
404
+ },
405
+
406
+ clearAll() {
407
+ this.$emit('input', []);
408
+ this.focusTextInput();
387
409
  }
388
410
 
389
411
  }
@@ -393,7 +415,7 @@ var script = {
393
415
  const __vue_script__ = script;
394
416
 
395
417
  /* template */
396
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',{ref:"container",staticClass:"gl-token-selector gl-form-input gl-display-flex gl-align-items-center form-control form-control-plaintext gl-cursor-text! gl-py-2! gl-px-3!",class:[_vm.inputFocused ? 'gl-token-selector-focus-glow' : '', _vm.containerClass, _vm.stateClass],on:{"click":_vm.handleContainerClick}},[(_vm.showEmptyPlaceholder)?_vm._t("empty-placeholder"):_vm._e(),_vm._v(" "),_c('gl-token-container',{attrs:{"tokens":_vm.selectedTokens,"state":_vm.state,"register-focus-on-token":_vm.registerFocusOnToken,"view-only":_vm.viewOnly},on:{"token-remove":_vm.removeToken,"cancel-focus":_vm.cancelTokenFocus},scopedSlots:_vm._u([{key:"token-content",fn:function(ref){
418
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',{ref:"container",staticClass:"gl-token-selector gl-form-input gl-display-flex gl-align-items-center form-control form-control-plaintext gl-cursor-text! gl-py-2! gl-px-3!",class:[_vm.inputFocused ? 'gl-token-selector-focus-glow' : '', _vm.containerClass, _vm.stateClass],on:{"click":_vm.handleContainerClick}},[(_vm.showEmptyPlaceholder)?_vm._t("empty-placeholder"):_vm._e(),_vm._v(" "),_c('gl-token-container',{attrs:{"tokens":_vm.selectedTokens,"state":_vm.state,"register-focus-on-token":_vm.registerFocusOnToken,"view-only":_vm.viewOnly,"show-clear-all-button":_vm.showClearAllButton},on:{"token-remove":_vm.removeToken,"cancel-focus":_vm.cancelTokenFocus,"clear-all":_vm.clearAll},scopedSlots:_vm._u([{key:"token-content",fn:function(ref){
397
419
  var token = ref.token;
398
420
  return [_vm._t("token-content",null,{"token":token})]}},{key:"text-input",fn:function(){return [_c('input',_vm._b({ref:"textInput",staticClass:"gl-token-selector-input gl-bg-none gl-font-regular gl-font-base gl-line-height-normal gl-px-1 gl-h-auto gl-text-gray-900 gl-border-none gl-outline-none gl-flex-grow-1",attrs:{"type":"text","autocomplete":_vm.autocomplete,"aria-labelledby":_vm.ariaLabelledby,"placeholder":_vm.placeholder,"disabled":_vm.viewOnly},domProps:{"value":_vm.inputText},on:{"input":function($event){_vm.inputText = $event.target.value;},"focus":_vm.handleFocus,"blur":_vm.handleBlur,"keydown":[function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"enter",13,$event.key,"Enter")){ return null; }return _vm.handleEnter($event)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"esc",27,$event.key,["Esc","Escape"])){ return null; }return _vm.handleEscape($event)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"delete",[8,46],$event.key,["Backspace","Delete","Del"])){ return null; }return _vm.handleBackspace($event)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"up",38,$event.key,["Up","ArrowUp"])){ return null; }$event.preventDefault();return _vm.dropdownEventHandlers.handleUpArrow($event)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"down",40,$event.key,["Down","ArrowDown"])){ return null; }$event.preventDefault();return _vm.dropdownEventHandlers.handleDownArrow($event)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"home",undefined,$event.key,undefined)){ return null; }return _vm.dropdownEventHandlers.handleHomeKey($event)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"end",undefined,$event.key,undefined)){ return null; }return _vm.dropdownEventHandlers.handleEndKey($event)},function($event){$event.stopPropagation();return _vm.$emit('keydown', $event)}],"click":_vm.handleInputClick}},'input',_vm.textInputAttrs,false))]},proxy:true}],null,true)})],2),_vm._v(" "),_c('gl-token-selector-dropdown',{attrs:{"menu-class":_vm.menuClass,"show":_vm.dropdownIsOpen,"loading":_vm.loading,"dropdown-items":_vm.filteredDropdownItems,"selected-tokens":_vm.selectedTokens,"input-text":_vm.inputText,"allow-user-defined-tokens":_vm.allowUserDefinedTokens,"component-id":_vm.$options.componentId,"register-dropdown-event-handlers":_vm.registerDropdownEventHandlers,"register-reset-focused-dropdown-item":_vm.registerResetFocusedDropdownItem},on:{"dropdown-item-click":_vm.addToken,"show":_vm.openDropdown},scopedSlots:_vm._u([{key:"loading-content",fn:function(){return [_vm._t("loading-content")]},proxy:true},{key:"user-defined-token-content",fn:function(){return [_vm._t("user-defined-token-content",null,{"inputText":_vm.inputText})]},proxy:true},{key:"no-results-content",fn:function(){return [_vm._t("no-results-content")]},proxy:true},{key:"dropdown-item-content",fn:function(ref){
399
421
  var dropdownItem = ref.dropdownItem;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "42.14.0",
3
+ "version": "42.15.0",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -83,7 +83,7 @@
83
83
  "@babel/preset-env": "^7.10.2",
84
84
  "@gitlab/eslint-plugin": "13.0.0",
85
85
  "@gitlab/stylelint-config": "4.1.0",
86
- "@gitlab/svgs": "2.25.0",
86
+ "@gitlab/svgs": "2.26.0",
87
87
  "@rollup/plugin-commonjs": "^11.1.0",
88
88
  "@rollup/plugin-node-resolve": "^7.1.3",
89
89
  "@rollup/plugin-replace": "^2.3.2",
@@ -71,6 +71,29 @@ describe('GlTokenContainer', () => {
71
71
  });
72
72
  });
73
73
 
74
+ describe('clearing all tokens', () => {
75
+ const findClearAllButton = () => wrapper.find('[data-testid="clear-all-button"]');
76
+
77
+ it('does not render `Clear all` button by default', () => {
78
+ createComponent();
79
+
80
+ expect(findClearAllButton().exists()).toBe(false);
81
+ });
82
+
83
+ it('renders `Clear all` button when `showClearAllButton` prop is true', () => {
84
+ createComponent({ propsData: { showClearAllButton: true } });
85
+
86
+ expect(findClearAllButton().exists()).toBe(true);
87
+ });
88
+
89
+ it('emits `clear-all` event when `Clear all` button is clicked', () => {
90
+ createComponent({ propsData: { showClearAllButton: true } });
91
+ findClearAllButton().vm.$emit('click', new MouseEvent('click'));
92
+
93
+ expect(wrapper.emitted('clear-all')).toEqual([[]]);
94
+ });
95
+ });
96
+
74
97
  describe('state', () => {
75
98
  describe('when `state` is `false`', () => {
76
99
  it('adds `aria-invalid="true"` attribute`', () => {
@@ -1,10 +1,11 @@
1
1
  <script>
2
2
  import GlToken from '../token/token.vue';
3
+ import GlButton from '../button/button.vue';
3
4
  import { tokensValidator } from './helpers';
4
5
 
5
6
  export default {
6
7
  name: 'GlTokenContainer',
7
- components: { GlToken },
8
+ components: { GlToken, GlButton },
8
9
  props: {
9
10
  tokens: {
10
11
  type: Array,
@@ -26,6 +27,11 @@ export default {
26
27
  required: false,
27
28
  default: false,
28
29
  },
30
+ showClearAllButton: {
31
+ type: Boolean,
32
+ required: false,
33
+ default: false,
34
+ },
29
35
  },
30
36
  data() {
31
37
  return {
@@ -124,7 +130,7 @@ export default {
124
130
  <template>
125
131
  <div
126
132
  ref="tokenContainer"
127
- class="gl-display-flex gl-flex-wrap gl-align-items-center gl-my-n1 gl-mx-n1"
133
+ class="gl-display-flex gl-flex-wrap gl-align-items-center gl-my-n1 gl-mx-n1 gl-w-full"
128
134
  role="listbox"
129
135
  aria-multiselectable="false"
130
136
  aria-orientation="horizontal"
@@ -162,5 +168,14 @@ export default {
162
168
  </gl-token>
163
169
  </div>
164
170
  <slot name="text-input"></slot>
171
+ <gl-button
172
+ v-if="showClearAllButton"
173
+ size="small"
174
+ aria-label="Clear all"
175
+ icon="clear"
176
+ category="tertiary"
177
+ data-testid="clear-all-button"
178
+ @click.stop="$emit('clear-all')"
179
+ />
165
180
  </div>
166
181
  </template>
@@ -86,6 +86,8 @@ describe('GlTokenSelector', () => {
86
86
 
87
87
  const findContainer = () => wrapper.findComponent({ ref: 'container' });
88
88
 
89
+ const findTokenContainer = () => wrapper.findComponent(GlTokenContainer);
90
+
89
91
  beforeAll(() => {
90
92
  if (!HTMLElement.prototype.scrollIntoView) {
91
93
  HTMLElement.prototype.scrollIntoView = jest.fn();
@@ -149,7 +151,7 @@ describe('GlTokenSelector', () => {
149
151
  });
150
152
 
151
153
  it('passes `viewOnly` prop to GlTokenContainer', () => {
152
- expect(wrapper.findComponent(GlTokenContainer).props('viewOnly')).toBe(true);
154
+ expect(findTokenContainer().props('viewOnly')).toBe(true);
153
155
  });
154
156
 
155
157
  it('disables input field if viewOnly is true', () => {
@@ -159,6 +161,16 @@ describe('GlTokenSelector', () => {
159
161
  });
160
162
  });
161
163
 
164
+ describe('when there are tokens and `allowClearAll` is true', () => {
165
+ beforeEach(() => {
166
+ createComponent({ propsData: { allowClearAll: true, selectedTokens: tokens } });
167
+ });
168
+
169
+ it('passes `showClearAllButton` prop as `true` to token-container', () => {
170
+ expect(findTokenContainer().props('showClearAllButton')).toBe(true);
171
+ });
172
+ });
173
+
162
174
  describe('containerClass', () => {
163
175
  it('renders passed CSS classes', () => {
164
176
  createComponent({
@@ -290,7 +302,7 @@ describe('GlTokenSelector', () => {
290
302
  },
291
303
  });
292
304
 
293
- expect(wrapper.findComponent(GlTokenContainer).props('state')).toBe(true);
305
+ expect(findTokenContainer().props('state')).toBe(true);
294
306
  });
295
307
  });
296
308
 
@@ -589,7 +601,7 @@ describe('GlTokenSelector', () => {
589
601
  propsData: { selectedTokens: [token] },
590
602
  });
591
603
 
592
- wrapper.findComponent(GlTokenContainer).vm.$emit('token-remove', token);
604
+ findTokenContainer().vm.$emit('token-remove', token);
593
605
 
594
606
  await nextTick();
595
607
  });
@@ -631,7 +643,7 @@ describe('GlTokenSelector', () => {
631
643
  selectedTokens: tokens,
632
644
  });
633
645
 
634
- wrapper.findComponent(GlTokenContainer).vm.$emit('cancel-focus');
646
+ findTokenContainer().vm.$emit('cancel-focus');
635
647
 
636
648
  await nextTick();
637
649
 
@@ -1,6 +1,5 @@
1
1
  <script>
2
2
  import { uniqueId } from 'lodash';
3
-
4
3
  import { tokensValidator } from './helpers';
5
4
  import GlTokenContainer from './token_container.vue';
6
5
  import GlTokenSelectorDropdown from './token_selector_dropdown.vue';
@@ -8,7 +7,10 @@ import GlTokenSelectorDropdown from './token_selector_dropdown.vue';
8
7
  export default {
9
8
  name: 'GlTokenSelector',
10
9
  componentId: uniqueId('token-selector'),
11
- components: { GlTokenContainer, GlTokenSelectorDropdown },
10
+ components: {
11
+ GlTokenContainer,
12
+ GlTokenSelectorDropdown,
13
+ },
12
14
  model: {
13
15
  prop: 'selectedTokens',
14
16
  event: 'input',
@@ -121,6 +123,14 @@ export default {
121
123
  required: false,
122
124
  default: false,
123
125
  },
126
+ /**
127
+ * Allows user to bulk delete tokens when enabled
128
+ */
129
+ allowClearAll: {
130
+ type: Boolean,
131
+ required: false,
132
+ default: false,
133
+ },
124
134
  },
125
135
  data() {
126
136
  return {
@@ -173,8 +183,14 @@ export default {
173
183
  ? 'is-valid gl-inset-border-1-gray-400!'
174
184
  : 'is-invalid gl-inset-border-1-red-500!';
175
185
  },
186
+ hasSelectedTokens() {
187
+ return this.selectedTokens.length > 0;
188
+ },
176
189
  showEmptyPlaceholder() {
177
- return this.selectedTokens.length === 0 && !this.inputFocused;
190
+ return !this.hasSelectedTokens && !this.inputFocused;
191
+ },
192
+ showClearAllButton() {
193
+ return this.hasSelectedTokens && this.allowClearAll;
178
194
  },
179
195
  },
180
196
  watch: {
@@ -357,6 +373,10 @@ export default {
357
373
  registerFocusOnToken(focusOnToken) {
358
374
  this.focusOnToken = focusOnToken;
359
375
  },
376
+ clearAll() {
377
+ this.$emit('input', []);
378
+ this.focusTextInput();
379
+ },
360
380
  },
361
381
  };
362
382
  </script>
@@ -377,8 +397,10 @@ export default {
377
397
  :state="state"
378
398
  :register-focus-on-token="registerFocusOnToken"
379
399
  :view-only="viewOnly"
400
+ :show-clear-all-button="showClearAllButton"
380
401
  @token-remove="removeToken"
381
402
  @cancel-focus="cancelTokenFocus"
403
+ @clear-all="clearAll"
382
404
  >
383
405
  <template #token-content="{ token }">
384
406
  <!-- @slot Content to pass to the token component slot. Can be used