@gitlab/ui 41.9.1 → 41.10.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,10 @@
1
+ # [41.10.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v41.9.1...v41.10.0) (2022-06-15)
2
+
3
+
4
+ ### Features
5
+
6
+ * **GlTokenSelector:** add empty and readonly states ([e0b60a7](https://gitlab.com/gitlab-org/gitlab-ui/commit/e0b60a7cb24875e62e57c76963135e324b6bfa11))
7
+
1
8
  ## [41.9.1](https://gitlab.com/gitlab-org/gitlab-ui/compare/v41.9.0...v41.9.1) (2022-06-15)
2
9
 
3
10
 
@@ -22,6 +22,11 @@ var script = {
22
22
  registerFocusOnToken: {
23
23
  type: Function,
24
24
  required: true
25
+ },
26
+ viewOnly: {
27
+ type: Boolean,
28
+ required: false,
29
+ default: false
25
30
  }
26
31
  },
27
32
 
@@ -138,7 +143,7 @@ var script = {
138
143
  const __vue_script__ = script;
139
144
 
140
145
  /* template */
141
- 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){$event.preventDefault();}]}},[_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),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)};
146
+ 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){$event.preventDefault();}]}},[_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)};
142
147
  var __vue_staticRenderFns__ = [];
143
148
 
144
149
  /* style */
@@ -125,6 +125,15 @@ var script = {
125
125
  // All tokens need to have an `id` key
126
126
  validator: tokensValidator,
127
127
  required: true
128
+ },
129
+
130
+ /**
131
+ * Controls the `view-only` mode for the tokens
132
+ */
133
+ viewOnly: {
134
+ type: Boolean,
135
+ required: false,
136
+ default: false
128
137
  }
129
138
  },
130
139
 
@@ -178,6 +187,10 @@ var script = {
178
187
  }
179
188
 
180
189
  return this.state ? 'is-valid gl-inset-border-1-gray-400!' : 'is-invalid gl-inset-border-1-red-500!';
190
+ },
191
+
192
+ showEmptyPlaceholder() {
193
+ return this.selectedTokens.length === 0 && !this.inputFocused;
181
194
  }
182
195
 
183
196
  },
@@ -380,9 +393,9 @@ var script = {
380
393
  const __vue_script__ = script;
381
394
 
382
395
  /* template */
383
- 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 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}},[_c('gl-token-container',{attrs:{"tokens":_vm.selectedTokens,"state":_vm.state,"register-focus-on-token":_vm.registerFocusOnToken},on:{"token-remove":_vm.removeToken,"cancel-focus":_vm.cancelTokenFocus},scopedSlots:_vm._u([{key:"token-content",fn:function(ref){
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){
384
397
  var token = ref.token;
385
- 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},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)})],1),_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){
398
+ 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){
386
399
  var dropdownItem = ref.dropdownItem;
387
400
  return [_vm._t("dropdown-item-content",null,{"dropdownItem":dropdownItem})]}},{key:"dropdown-footer",fn:function(){return [_vm._t("dropdown-footer")]},proxy:true}],null,true),model:{value:(_vm.focusedDropdownItem),callback:function ($$v) {_vm.focusedDropdownItem=$$v;},expression:"focusedDropdownItem"}})],1)};
388
401
  var __vue_staticRenderFns__ = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "41.9.1",
3
+ "version": "41.10.0",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -62,6 +62,15 @@ describe('GlTokenContainer', () => {
62
62
  });
63
63
  });
64
64
 
65
+ describe('viewOnly', () => {
66
+ it('passes viewOnly prop to tokens correctly', () => {
67
+ createComponent({ propsData: { viewOnly: true } });
68
+ const tokenWrappers = wrapper.findAllComponents(GlToken);
69
+
70
+ expect(tokenWrappers.wrappers.every((token) => token.props('viewOnly'))).toBe(true);
71
+ });
72
+ });
73
+
65
74
  describe('state', () => {
66
75
  describe('when `state` is `false`', () => {
67
76
  it('adds `aria-invalid="true"` attribute`', () => {
@@ -21,6 +21,11 @@ export default {
21
21
  type: Function,
22
22
  required: true,
23
23
  },
24
+ viewOnly: {
25
+ type: Boolean,
26
+ required: false,
27
+ default: false,
28
+ },
24
29
  },
25
30
  data() {
26
31
  return {
@@ -143,6 +148,7 @@ export default {
143
148
  class="gl-cursor-default"
144
149
  :class="token.class"
145
150
  :style="token.style"
151
+ :view-only="viewOnly"
146
152
  @close="handleClose(token)"
147
153
  >
148
154
  <slot name="token-content" :token="token">
@@ -46,6 +46,8 @@ describe('GlTokenSelector', () => {
46
46
  },
47
47
  ];
48
48
 
49
+ const placeholderText = 'Test placeholder';
50
+
49
51
  let wrapper;
50
52
 
51
53
  const defaultProps = { selectedTokens: [] };
@@ -141,6 +143,22 @@ describe('GlTokenSelector', () => {
141
143
  });
142
144
  });
143
145
 
146
+ describe('viewOnly', () => {
147
+ beforeEach(() => {
148
+ createComponent({ propsData: { viewOnly: true } });
149
+ });
150
+
151
+ it('passes `viewOnly` prop to GlTokenContainer', () => {
152
+ expect(wrapper.findComponent(GlTokenContainer).props('viewOnly')).toBe(true);
153
+ });
154
+
155
+ it('disables input field if viewOnly is true', () => {
156
+ findTextInput().trigger('focus');
157
+
158
+ expect(findTextInput().attributes('disabled')).toBe('disabled');
159
+ });
160
+ });
161
+
144
162
  describe('containerClass', () => {
145
163
  it('renders passed CSS classes', () => {
146
164
  createComponent({
@@ -302,6 +320,48 @@ describe('GlTokenSelector', () => {
302
320
 
303
321
  expect(wrapper.findComponent(component).vm.$scopedSlots).toHaveProperty(slot);
304
322
  });
323
+
324
+ it('renders empty-placeholder slot if tokens list is empty and input is not focused', () => {
325
+ createComponent({
326
+ propsData: {
327
+ selectedTokens: [],
328
+ },
329
+ slots: {
330
+ 'empty-placeholder': placeholderText,
331
+ },
332
+ });
333
+
334
+ expect(wrapper.text()).toContain(placeholderText);
335
+ });
336
+
337
+ it('does not render empty-placeholder slot if token list is not empty', () => {
338
+ createComponent({
339
+ propsData: {
340
+ selectedTokens: tokens,
341
+ },
342
+ slots: {
343
+ 'empty-placeholder': placeholderText,
344
+ },
345
+ });
346
+
347
+ expect(wrapper.text()).not.toContain(placeholderText);
348
+ });
349
+
350
+ it('hides empty-placeholder slot if input is focused', async () => {
351
+ createComponent({
352
+ propsData: {
353
+ selectedTokens: [],
354
+ },
355
+ slots: {
356
+ 'empty-placeholder': placeholderText,
357
+ },
358
+ });
359
+
360
+ expect(wrapper.text()).toContain(placeholderText);
361
+
362
+ await findTextInput().trigger('focus');
363
+ expect(wrapper.text()).not.toContain(placeholderText);
364
+ });
305
365
  });
306
366
 
307
367
  describe('text input events', () => {
@@ -113,6 +113,14 @@ export default {
113
113
  validator: tokensValidator,
114
114
  required: true,
115
115
  },
116
+ /**
117
+ * Controls the `view-only` mode for the tokens
118
+ */
119
+ viewOnly: {
120
+ type: Boolean,
121
+ required: false,
122
+ default: false,
123
+ },
116
124
  },
117
125
  data() {
118
126
  return {
@@ -165,6 +173,9 @@ export default {
165
173
  ? 'is-valid gl-inset-border-1-gray-400!'
166
174
  : 'is-invalid gl-inset-border-1-red-500!';
167
175
  },
176
+ showEmptyPlaceholder() {
177
+ return this.selectedTokens.length === 0 && !this.inputFocused;
178
+ },
168
179
  },
169
180
  watch: {
170
181
  inputText(newValue, oldValue) {
@@ -354,14 +365,18 @@ export default {
354
365
  <div>
355
366
  <div
356
367
  ref="container"
357
- class="gl-token-selector gl-form-input form-control form-control-plaintext gl-cursor-text! gl-py-2! gl-px-3!"
368
+ class="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!"
358
369
  :class="[inputFocused ? 'gl-token-selector-focus-glow' : '', containerClass, stateClass]"
359
370
  @click="handleContainerClick"
360
371
  >
372
+ <!-- @slot Optional content to display a placeholder when tokens list is empty
373
+ and user doesn't edit tokens -->
374
+ <slot v-if="showEmptyPlaceholder" name="empty-placeholder"></slot>
361
375
  <gl-token-container
362
376
  :tokens="selectedTokens"
363
377
  :state="state"
364
378
  :register-focus-on-token="registerFocusOnToken"
379
+ :view-only="viewOnly"
365
380
  @token-remove="removeToken"
366
381
  @cancel-focus="cancelTokenFocus"
367
382
  >
@@ -383,6 +398,7 @@ export default {
383
398
  :autocomplete="autocomplete"
384
399
  :aria-labelledby="ariaLabelledby"
385
400
  :placeholder="placeholder"
401
+ :disabled="viewOnly"
386
402
  v-bind="textInputAttrs"
387
403
  @input="inputText = $event.target.value"
388
404
  @focus="handleFocus"