@gitlab/ui 123.4.0 → 123.5.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.
@@ -7,6 +7,7 @@ import { isEvent } from '../../../vendor/bootstrap-vue/src/utils/inspect';
7
7
  import GlIcon from '../icon/icon';
8
8
  import GlLoadingIcon from '../loading_icon/loading_icon';
9
9
  import { SPACE, ENTER } from '../new_dropdowns/constants';
10
+ import { glButtonConfig } from '../../../config';
10
11
  import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
11
12
 
12
13
  //
@@ -78,6 +79,14 @@ var script = {
78
79
  required: false,
79
80
  default: false
80
81
  },
82
+ /**
83
+ * Keep the button accessible when `loading` is `true`.
84
+ */
85
+ accessibleLoading: {
86
+ type: Boolean,
87
+ required: false,
88
+ default: () => glButtonConfig.accessibleLoadingButton
89
+ },
81
90
  /**
82
91
  * CSS classes to add to the button text.
83
92
  */
@@ -237,6 +246,9 @@ var script = {
237
246
  isButtonDisabled() {
238
247
  return this.disabled || this.loading;
239
248
  },
249
+ isButtonAriaDisabled() {
250
+ return this.accessibleLoading && this.isButton && this.loading;
251
+ },
240
252
  buttonClasses() {
241
253
  const classes = ['btn', 'gl-button', `btn-${this.variant}`, `btn-${this.buttonSize}`];
242
254
  if (this.category !== buttonCategoryOptions.primary) {
@@ -247,7 +259,7 @@ var script = {
247
259
  'button-ellipsis-horizontal': this.hasIconOnly && this.icon === 'ellipsis_h',
248
260
  selected: this.selected,
249
261
  'btn-block': this.displayBlock,
250
- disabled: this.disabled
262
+ disabled: this.disabled || this.isButtonAriaDisabled
251
263
  });
252
264
  if (this.label) {
253
265
  classes.push('btn', 'btn-label');
@@ -297,7 +309,12 @@ var script = {
297
309
  ...(this.isNonStandardTag ? {
298
310
  'aria-disabled': String(this.disabled)
299
311
  } : {}),
300
- tabindex: this.tabindex
312
+ tabindex: this.tabindex,
313
+ // We set the `aria-disabled` state for buttons while loading
314
+ ...(this.isButtonAriaDisabled ? {
315
+ 'aria-disabled': 'true',
316
+ disabled: null
317
+ } : {})
301
318
  };
302
319
  if (this.isLink) {
303
320
  return {
@@ -323,11 +340,13 @@ var script = {
323
340
  };
324
341
  },
325
342
  computedListeners() {
326
- return {
327
- click: this.onClick,
328
- keydown: this.onKeydown,
343
+ const listeners = {
329
344
  ...this.$listeners
330
345
  };
346
+ if (this.isButtonAriaDisabled) {
347
+ delete listeners.click;
348
+ }
349
+ return listeners;
331
350
  },
332
351
  componentIs() {
333
352
  if (this.label) {
@@ -366,8 +385,8 @@ var script = {
366
385
  }
367
386
  }
368
387
  },
369
- onClick(event) {
370
- if (this.disabled && isEvent(event)) {
388
+ maybeStopEvent(event) {
389
+ if (this.isButtonAriaDisabled && isEvent(event)) {
371
390
  stopEvent(event);
372
391
  }
373
392
  }
@@ -378,7 +397,7 @@ var script = {
378
397
  const __vue_script__ = script;
379
398
 
380
399
  /* template */
381
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c(_vm.componentIs,_vm._g(_vm._b({directives:[{name:"safe-link",rawName:"v-safe-link:[safeLinkConfig]",arg:_vm.safeLinkConfig}],tag:"component",class:_vm.buttonClasses},'component',_vm.computedPropsAndAttributes,false),_vm.computedListeners),[(_vm.loading)?_c('gl-loading-icon',{staticClass:"gl-button-icon gl-button-loading-indicator",attrs:{"inline":""}}):_vm._e(),_vm._v(" "),(_vm.hasIcon && !(_vm.hasIconOnly && _vm.loading))?_c('gl-icon',{staticClass:"gl-button-icon",attrs:{"name":_vm.icon}}):_vm._e(),_vm._v(" "),_vm._t("emoji"),_vm._v(" "),(!_vm.hasIconOnly)?_c('span',{staticClass:"gl-button-text",class:_vm.buttonTextClasses},[_vm._t("default"),_vm._v(" "),(_vm.hasCount)?_c('span',{staticClass:"gl-button-count"},[_vm._v("\n "+_vm._s(_vm.count)+"\n "),(_vm.countSrText)?_c('span',{staticClass:"gl-sr-only"},[_vm._v(_vm._s(_vm.countSrText))]):_vm._e()]):_vm._e()],2):_vm._e()],2)};
400
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c(_vm.componentIs,_vm._g(_vm._b({directives:[{name:"safe-link",rawName:"v-safe-link:[safeLinkConfig]",arg:_vm.safeLinkConfig}],tag:"component",class:_vm.buttonClasses,on:{"click":_vm.maybeStopEvent,"keydown":_vm.onKeydown}},'component',_vm.computedPropsAndAttributes,false),_vm.computedListeners),[(_vm.loading)?_c('gl-loading-icon',{staticClass:"gl-button-icon gl-button-loading-indicator",attrs:{"inline":""}}):_vm._e(),_vm._v(" "),(_vm.hasIcon && !(_vm.hasIconOnly && _vm.loading))?_c('gl-icon',{staticClass:"gl-button-icon",attrs:{"name":_vm.icon}}):_vm._e(),_vm._v(" "),_vm._t("emoji"),_vm._v(" "),(!_vm.hasIconOnly)?_c('span',{staticClass:"gl-button-text",class:_vm.buttonTextClasses},[_vm._t("default"),_vm._v(" "),(_vm.hasCount)?_c('span',{staticClass:"gl-button-count"},[_vm._v("\n "+_vm._s(_vm.count)+"\n "),(_vm.countSrText)?_c('span',{staticClass:"gl-sr-only"},[_vm._v(_vm._s(_vm.countSrText))]):_vm._e()]):_vm._e()],2):_vm._e()],2)};
382
401
  var __vue_staticRenderFns__ = [];
383
402
 
384
403
  /* style */
package/dist/config.js CHANGED
@@ -35,6 +35,7 @@ const i18n = translationKeys;
35
35
  const defaultConfig = {
36
36
  firstDayOfWeek: 0 // Defaults to 0 (Sunday)
37
37
  };
38
+ const glButtonConfig = {};
38
39
  let configured = false;
39
40
 
40
41
  /**
@@ -44,11 +45,14 @@ let configured = false;
44
45
  * @template TValue=string
45
46
  * @property {undefined | Object} translations Generic translations for component labels to fall back to.
46
47
  * @property {undefined | Number} firstDayOfWeek Configured first day of the week, from 0 (Sunday) to 6 (Saturday).
48
+ * @property {boolean} [accessibleLoadingButton] Temporary flag to enable accessible loading button.
49
+ *
47
50
  */
48
51
  const setConfigs = function () {
49
52
  let {
50
53
  translations,
51
- firstDayOfWeek
54
+ firstDayOfWeek,
55
+ accessibleLoadingButton = false
52
56
  } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
53
57
  if (configured) {
54
58
  if (process.env.NODE_ENV === 'development') {
@@ -83,6 +87,23 @@ const setConfigs = function () {
83
87
  }
84
88
  Object.assign(i18n, translations);
85
89
  }
90
+
91
+ // Temporary flag to enable the accessible loading button feature.
92
+ // This flag allows the feature to be opt-in during the rollout phase,
93
+ // giving us the flexibility to test and validate its impact on user experience.
94
+
95
+ // The global variable `accessibleLoadingButton` is set to a boolean value
96
+ // to indicate whether the button should be disabled while loading.
97
+
98
+ // Future Plan:
99
+ // Once the accessible loading button feature is validated and stable,
100
+ // we will remove this temporary flag and make the feature the default behavior.
101
+ // At that point, there will be no need for opt-in or opt-out mechanisms for this feature.
102
+ if (typeof accessibleLoadingButton === 'boolean') {
103
+ Object.assign(glButtonConfig, {
104
+ accessibleLoadingButton
105
+ });
106
+ }
86
107
  };
87
108
 
88
- export { setConfigs as default, defaultConfig, i18n };
109
+ export { setConfigs as default, defaultConfig, glButtonConfig, i18n };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "123.4.0",
3
+ "version": "123.5.0",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -102,7 +102,7 @@
102
102
  "@cypress/grep": "^4.1.1",
103
103
  "@gitlab/fonts": "^1.3.1",
104
104
  "@gitlab/svgs": "3.147.0",
105
- "@jest/test-sequencer": "30.1.3",
105
+ "@jest/test-sequencer": "30.2.0",
106
106
  "@rollup/plugin-commonjs": "^28.0.6",
107
107
  "@rollup/plugin-node-resolve": "^16.0.1",
108
108
  "@rollup/plugin-replace": "^6.0.2",
@@ -14,6 +14,7 @@ import { isEvent } from '../../../vendor/bootstrap-vue/src/utils/inspect';
14
14
  import GlIcon from '../icon/icon.vue';
15
15
  import GlLoadingIcon from '../loading_icon/loading_icon.vue';
16
16
  import { ENTER, SPACE } from '../new_dropdowns/constants';
17
+ import { glButtonConfig } from '../../../config';
17
18
 
18
19
  export default {
19
20
  name: 'GlButton',
@@ -82,6 +83,14 @@ export default {
82
83
  required: false,
83
84
  default: false,
84
85
  },
86
+ /**
87
+ * Keep the button accessible when `loading` is `true`.
88
+ */
89
+ accessibleLoading: {
90
+ type: Boolean,
91
+ required: false,
92
+ default: () => glButtonConfig.accessibleLoadingButton,
93
+ },
85
94
  /**
86
95
  * CSS classes to add to the button text.
87
96
  */
@@ -241,6 +250,9 @@ export default {
241
250
  isButtonDisabled() {
242
251
  return this.disabled || this.loading;
243
252
  },
253
+ isButtonAriaDisabled() {
254
+ return this.accessibleLoading && this.isButton && this.loading;
255
+ },
244
256
  buttonClasses() {
245
257
  const classes = ['btn', 'gl-button', `btn-${this.variant}`, `btn-${this.buttonSize}`];
246
258
 
@@ -253,7 +265,7 @@ export default {
253
265
  'button-ellipsis-horizontal': this.hasIconOnly && this.icon === 'ellipsis_h',
254
266
  selected: this.selected,
255
267
  'btn-block': this.displayBlock,
256
- disabled: this.disabled,
268
+ disabled: this.disabled || this.isButtonAriaDisabled,
257
269
  });
258
270
 
259
271
  if (this.label) {
@@ -304,6 +316,8 @@ export default {
304
316
  // We set the `aria-disabled` state for non-standard tags
305
317
  ...(this.isNonStandardTag ? { 'aria-disabled': String(this.disabled) } : {}),
306
318
  tabindex: this.tabindex,
319
+ // We set the `aria-disabled` state for buttons while loading
320
+ ...(this.isButtonAriaDisabled ? { 'aria-disabled': 'true', disabled: null } : {}),
307
321
  };
308
322
 
309
323
  if (this.isLink) {
@@ -328,11 +342,14 @@ export default {
328
342
  return { ...this.$attrs, ...base };
329
343
  },
330
344
  computedListeners() {
331
- return {
332
- click: this.onClick,
333
- keydown: this.onKeydown,
345
+ const listeners = {
334
346
  ...this.$listeners,
335
347
  };
348
+
349
+ if (this.isButtonAriaDisabled) {
350
+ delete listeners.click;
351
+ }
352
+ return listeners;
336
353
  },
337
354
  componentIs() {
338
355
  if (this.label) {
@@ -373,8 +390,8 @@ export default {
373
390
  }
374
391
  }
375
392
  },
376
- onClick(event) {
377
- if (this.disabled && isEvent(event)) {
393
+ maybeStopEvent(event) {
394
+ if (this.isButtonAriaDisabled && isEvent(event)) {
378
395
  stopEvent(event);
379
396
  }
380
397
  },
@@ -387,6 +404,8 @@ export default {
387
404
  v-bind="computedPropsAndAttributes"
388
405
  v-safe-link:[safeLinkConfig]
389
406
  :class="buttonClasses"
407
+ @click="maybeStopEvent"
408
+ @keydown="onKeydown"
390
409
  v-on="computedListeners"
391
410
  >
392
411
  <gl-loading-icon v-if="loading" inline class="gl-button-icon gl-button-loading-indicator" />
package/src/config.js CHANGED
@@ -39,6 +39,8 @@ export const defaultConfig = {
39
39
  firstDayOfWeek: 0, // Defaults to 0 (Sunday)
40
40
  };
41
41
 
42
+ export const glButtonConfig = {};
43
+
42
44
  let configured = false;
43
45
 
44
46
  /**
@@ -48,8 +50,10 @@ let configured = false;
48
50
  * @template TValue=string
49
51
  * @property {undefined | Object} translations Generic translations for component labels to fall back to.
50
52
  * @property {undefined | Number} firstDayOfWeek Configured first day of the week, from 0 (Sunday) to 6 (Saturday).
53
+ * @property {boolean} [accessibleLoadingButton] Temporary flag to enable accessible loading button.
54
+ *
51
55
  */
52
- const setConfigs = ({ translations, firstDayOfWeek } = {}) => {
56
+ const setConfigs = ({ translations, firstDayOfWeek, accessibleLoadingButton = false } = {}) => {
53
57
  if (configured) {
54
58
  if (process.env.NODE_ENV === 'development') {
55
59
  throw new Error('GitLab UI can only be configured once!');
@@ -90,6 +94,23 @@ const setConfigs = ({ translations, firstDayOfWeek } = {}) => {
90
94
 
91
95
  Object.assign(i18n, translations);
92
96
  }
97
+
98
+ // Temporary flag to enable the accessible loading button feature.
99
+ // This flag allows the feature to be opt-in during the rollout phase,
100
+ // giving us the flexibility to test and validate its impact on user experience.
101
+
102
+ // The global variable `accessibleLoadingButton` is set to a boolean value
103
+ // to indicate whether the button should be disabled while loading.
104
+
105
+ // Future Plan:
106
+ // Once the accessible loading button feature is validated and stable,
107
+ // we will remove this temporary flag and make the feature the default behavior.
108
+ // At that point, there will be no need for opt-in or opt-out mechanisms for this feature.
109
+ if (typeof accessibleLoadingButton === 'boolean') {
110
+ Object.assign(glButtonConfig, {
111
+ accessibleLoadingButton,
112
+ });
113
+ }
93
114
  };
94
115
 
95
116
  export default setConfigs;