@gitlab/ui 48.3.1 → 49.0.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ ## [49.0.1](https://gitlab.com/gitlab-org/gitlab-ui/compare/v49.0.0...v49.0.1) (2022-10-24)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **GlListbox:** fix dropdown positioning ([18d061f](https://gitlab.com/gitlab-org/gitlab-ui/commit/18d061fc905ac906a6c88f080b08b9e085eb372e))
7
+
8
+ # [49.0.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v48.3.1...v49.0.0) (2022-10-24)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **GlToggle:** Fix help text visibility ([1bd4137](https://gitlab.com/gitlab-org/gitlab-ui/commit/1bd4137fca78152966869b0b6781fe44c5f9a69e))
14
+
15
+
16
+ ### BREAKING CHANGES
17
+
18
+ * **GlToggle:** Hide help text when `labelPosition: left`
19
+
1
20
  ## [48.3.1](https://gitlab.com/gitlab-org/gitlab-ui/compare/v48.3.0...v48.3.1) (2022-10-24)
2
21
 
3
22
 
@@ -147,14 +147,6 @@ var script = {
147
147
 
148
148
  },
149
149
 
150
- updated() {
151
- if (this.visible) {
152
- var _this$popper;
153
-
154
- (_this$popper = this.popper) === null || _this$popper === void 0 ? void 0 : _this$popper.update();
155
- }
156
- },
157
-
158
150
  mounted() {
159
151
  this.$nextTick(() => {
160
152
  this.popper = createPopper(this.$refs.toggle.$el, this.$refs.content, this.popperConfig);
@@ -166,11 +158,24 @@ var script = {
166
158
  },
167
159
 
168
160
  methods: {
169
- toggle() {
161
+ async toggle() {
170
162
  this.visible = !this.visible;
171
163
 
172
164
  if (this.visible) {
173
- this.popper.update();
165
+ var _this$popper;
166
+
167
+ /* Initially dropdown is hidden with `display="none"`.
168
+ When `visible` prop is toggled ON, with the `nextTick` we wait for the DOM update -
169
+ dropdown's `display="block"` is set (adding CSS class `show`).
170
+ After that we can recalculate its position (calling `popper.update()`).
171
+ https://github.com/floating-ui/floating-ui/issues/630:
172
+ "Unfortunately there's not any way to compute the position of an element not rendered in the document".
173
+ Then we `await` while the new dropdown position is calculated and DOM updated accordingly.
174
+ After we can emit the `GL_DROPDOWN_SHOWN` event to the parent which might interact with updated dropdown,
175
+ e.g. set focus..
176
+ */
177
+ await this.$nextTick();
178
+ await ((_this$popper = this.popper) === null || _this$popper === void 0 ? void 0 : _this$popper.update());
174
179
  this.$emit(GL_DROPDOWN_SHOWN);
175
180
  } else {
176
181
  this.$emit(GL_DROPDOWN_HIDDEN);
@@ -326,23 +326,21 @@ var script = {
326
326
  },
327
327
 
328
328
  onShow() {
329
- this.$nextTick(() => {
330
- if (this.searchable) {
331
- this.focusSearchInput();
332
- } else {
333
- var _this$selectedIndices;
329
+ if (this.searchable) {
330
+ this.focusSearchInput();
331
+ } else {
332
+ var _this$selectedIndices;
334
333
 
335
- this.focusItem((_this$selectedIndices = this.selectedIndices[0]) !== null && _this$selectedIndices !== void 0 ? _this$selectedIndices : 0, this.getFocusableListItemElements());
336
- }
337
- /**
338
- * Emitted when dropdown is shown
339
- *
340
- * @event shown
341
- */
334
+ this.focusItem((_this$selectedIndices = this.selectedIndices[0]) !== null && _this$selectedIndices !== void 0 ? _this$selectedIndices : 0, this.getFocusableListItemElements());
335
+ }
336
+ /**
337
+ * Emitted when dropdown is shown
338
+ *
339
+ * @event shown
340
+ */
342
341
 
343
342
 
344
- this.$emit(GL_DROPDOWN_SHOWN);
345
- });
343
+ this.$emit(GL_DROPDOWN_SHOWN);
346
344
  },
347
345
 
348
346
  onHide() {
@@ -413,12 +411,10 @@ var script = {
413
411
  },
414
412
 
415
413
  focusItem(index, elements) {
416
- this.nextFocusedItemIndex = index;
417
- this.$nextTick(() => {
418
- var _elements$index;
414
+ var _elements$index;
419
415
 
420
- (_elements$index = elements[index]) === null || _elements$index === void 0 ? void 0 : _elements$index.focus();
421
- });
416
+ this.nextFocusedItemIndex = index;
417
+ (_elements$index = elements[index]) === null || _elements$index === void 0 ? void 0 : _elements$index.focus();
422
418
  },
423
419
 
424
420
  focusSearchInput() {
@@ -90,7 +90,7 @@ var script = {
90
90
  computed: {
91
91
  shouldRenderHelp() {
92
92
  // eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots
93
- return Boolean(this.$slots.help || this.help);
93
+ return Boolean(this.$slots.help || this.help) && this.isVerticalLayout;
94
94
  },
95
95
 
96
96
  icon() {
@@ -103,6 +103,10 @@ var script = {
103
103
 
104
104
  isChecked() {
105
105
  return this.value ? 'true' : 'false';
106
+ },
107
+
108
+ isVerticalLayout() {
109
+ return this.labelPosition === 'top' || this.labelPosition === 'hidden';
106
110
  }
107
111
 
108
112
  },
@@ -133,8 +137,8 @@ const __vue_script__ = script;
133
137
 
134
138
  /* template */
135
139
  var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"gl-toggle-wrapper gl-display-flex gl-mb-0",class:{
136
- 'gl-flex-direction-column': _vm.labelPosition === 'top' || _vm.labelPosition === 'hidden',
137
- 'gl-toggle-label-inline': _vm.labelPosition === 'left',
140
+ 'gl-flex-direction-column': _vm.isVerticalLayout,
141
+ 'gl-toggle-label-inline': !_vm.isVerticalLayout,
138
142
  'is-disabled': _vm.disabled,
139
143
  },attrs:{"data-testid":"toggle-wrapper"}},[_c('span',{staticClass:"gl-toggle-label gl-flex-shrink-0",class:{ 'gl-sr-only': _vm.labelPosition === 'hidden' },attrs:{"id":_vm.labelId,"data-testid":"toggle-label"}},[_vm._t("label",function(){return [_vm._v(_vm._s(_vm.label))]})],2),_vm._v(" "),(_vm.name)?_c('input',{attrs:{"name":_vm.name,"type":"hidden"},domProps:{"value":_vm.value}}):_vm._e(),_vm._v(" "),_c('button',{staticClass:"gl-flex-shrink-0",class:{
140
144
  'gl-toggle': true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "48.3.1",
3
+ "version": "49.0.1",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -144,6 +144,7 @@ describe('base dropdown', () => {
144
144
  await toggle.trigger('click');
145
145
  expect(menu.classes('show')).toBe(true);
146
146
  expect(toggle.attributes('aria-expanded')).toBe('true');
147
+ await nextTick();
147
148
  expect(wrapper.emitted(GL_DROPDOWN_SHOWN).length).toBe(1);
148
149
 
149
150
  // close menu clicking toggle btn again
@@ -138,11 +138,6 @@ export default {
138
138
  };
139
139
  },
140
140
  },
141
- updated() {
142
- if (this.visible) {
143
- this.popper?.update();
144
- }
145
- },
146
141
  mounted() {
147
142
  this.$nextTick(() => {
148
143
  this.popper = createPopper(this.$refs.toggle.$el, this.$refs.content, this.popperConfig);
@@ -152,11 +147,22 @@ export default {
152
147
  this.popper.destroy();
153
148
  },
154
149
  methods: {
155
- toggle() {
150
+ async toggle() {
156
151
  this.visible = !this.visible;
157
152
 
158
153
  if (this.visible) {
159
- this.popper.update();
154
+ /* Initially dropdown is hidden with `display="none"`.
155
+ When `visible` prop is toggled ON, with the `nextTick` we wait for the DOM update -
156
+ dropdown's `display="block"` is set (adding CSS class `show`).
157
+ After that we can recalculate its position (calling `popper.update()`).
158
+ https://github.com/floating-ui/floating-ui/issues/630:
159
+ "Unfortunately there's not any way to compute the position of an element not rendered in the document".
160
+ Then we `await` while the new dropdown position is calculated and DOM updated accordingly.
161
+ After we can emit the `GL_DROPDOWN_SHOWN` event to the parent which might interact with updated dropdown,
162
+ e.g. set focus..
163
+ */
164
+ await this.$nextTick();
165
+ await this.popper?.update();
160
166
  this.$emit(GL_DROPDOWN_SHOWN);
161
167
  } else {
162
168
  this.$emit(GL_DROPDOWN_HIDDEN);
@@ -247,19 +247,25 @@ describe('GlListbox', () => {
247
247
  });
248
248
 
249
249
  describe('when `searchable` is enabled', () => {
250
- it('should move focus to the first item on search input `ARROW_DOWN`', async () => {
250
+ let searchbox;
251
+
252
+ beforeEach(() => {
251
253
  buildWrapper({ items: mockOptions, searchable: true });
252
254
  findBaseDropdown().vm.$emit(GL_DROPDOWN_SHOWN);
253
- findSearchBox().trigger('keydown', { code: ARROW_DOWN });
255
+ firstItem = findListItem(0);
256
+ searchbox = findSearchBox();
257
+ });
258
+
259
+ it('should move focus to the first item on search input `ARROW_DOWN`', async () => {
260
+ expect(searchbox.element).toHaveFocus();
261
+ searchbox.trigger('keydown', { code: ARROW_DOWN });
254
262
  expect(firstItem.element).toHaveFocus();
255
263
  });
256
264
 
257
265
  it('should move focus to the search input on first item `ARROW_UP', async () => {
258
- buildWrapper({ items: mockOptions, searchable: true });
259
- findBaseDropdown().vm.$emit(GL_DROPDOWN_SHOWN);
260
- const focusSpy = jest.spyOn(wrapper.vm.$refs.searchBox, 'focusInput');
261
- await firstItem.trigger('keydown', { code: ARROW_UP });
262
- expect(focusSpy).toHaveBeenCalled();
266
+ searchbox.trigger('keydown', { code: ARROW_DOWN });
267
+ firstItem.trigger('keydown', { code: ARROW_UP });
268
+ expect(searchbox.element).toHaveFocus();
263
269
  });
264
270
  });
265
271
  });
@@ -293,19 +293,17 @@ export default {
293
293
  return index === 0 ? null : GROUP_TOP_BORDER_CLASSES;
294
294
  },
295
295
  onShow() {
296
- this.$nextTick(() => {
297
- if (this.searchable) {
298
- this.focusSearchInput();
299
- } else {
300
- this.focusItem(this.selectedIndices[0] ?? 0, this.getFocusableListItemElements());
301
- }
302
- /**
303
- * Emitted when dropdown is shown
304
- *
305
- * @event shown
306
- */
307
- this.$emit(GL_DROPDOWN_SHOWN);
308
- });
296
+ if (this.searchable) {
297
+ this.focusSearchInput();
298
+ } else {
299
+ this.focusItem(this.selectedIndices[0] ?? 0, this.getFocusableListItemElements());
300
+ }
301
+ /**
302
+ * Emitted when dropdown is shown
303
+ *
304
+ * @event shown
305
+ */
306
+ this.$emit(GL_DROPDOWN_SHOWN);
309
307
  },
310
308
  onHide() {
311
309
  /**
@@ -366,9 +364,7 @@ export default {
366
364
  focusItem(index, elements) {
367
365
  this.nextFocusedItemIndex = index;
368
366
 
369
- this.$nextTick(() => {
370
- elements[index]?.focus();
371
- });
367
+ elements[index]?.focus();
372
368
  },
373
369
  focusSearchInput() {
374
370
  this.$refs.searchBox.focusInput();
@@ -86,10 +86,11 @@ describe('toggle', () => {
86
86
  });
87
87
 
88
88
  describe.each`
89
- state | help | props | options | getAriaDescribedBy
90
- ${'with help'} | ${helpText} | ${{ help: helpText }} | ${undefined} | ${() => findHelpElement().attributes('id')}
91
- ${'with help in slot'} | ${helpText} | ${undefined} | ${{ slots: { help: helpText } }} | ${() => findHelpElement().attributes('id')}
92
- ${'without help'} | ${undefined} | ${undefined} | ${undefined} | ${() => undefined}
89
+ state | help | props | options | getAriaDescribedBy
90
+ ${'with help'} | ${helpText} | ${{ help: helpText }} | ${undefined} | ${() => findHelpElement().attributes('id')}
91
+ ${'with help in slot'} | ${helpText} | ${undefined} | ${{ slots: { help: helpText } }} | ${() => findHelpElement().attributes('id')}
92
+ ${'without help'} | ${undefined} | ${undefined} | ${undefined} | ${() => undefined}
93
+ ${'with help and labelPosition left'} | ${undefined} | ${{ help: helpText, labelPosition: toggleLabelPosition.left }} | ${undefined} | ${() => undefined}
93
94
  `('$state', ({ help, props, options, getAriaDescribedBy }) => {
94
95
  beforeEach(() => {
95
96
  createWrapper(props, options);
@@ -83,7 +83,7 @@ export default {
83
83
  computed: {
84
84
  shouldRenderHelp() {
85
85
  // eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots
86
- return Boolean(this.$slots.help || this.help);
86
+ return Boolean(this.$slots.help || this.help) && this.isVerticalLayout;
87
87
  },
88
88
  icon() {
89
89
  return this.value ? 'mobile-issue-close' : 'close';
@@ -94,6 +94,9 @@ export default {
94
94
  isChecked() {
95
95
  return this.value ? 'true' : 'false';
96
96
  },
97
+ isVerticalLayout() {
98
+ return this.labelPosition === 'top' || this.labelPosition === 'hidden';
99
+ },
97
100
  },
98
101
 
99
102
  beforeCreate() {
@@ -121,8 +124,8 @@ export default {
121
124
  <div
122
125
  class="gl-toggle-wrapper gl-display-flex gl-mb-0"
123
126
  :class="{
124
- 'gl-flex-direction-column': labelPosition === 'top' || labelPosition === 'hidden',
125
- 'gl-toggle-label-inline': labelPosition === 'left',
127
+ 'gl-flex-direction-column': isVerticalLayout,
128
+ 'gl-toggle-label-inline': !isVerticalLayout,
126
129
  'is-disabled': disabled,
127
130
  }"
128
131
  data-testid="toggle-wrapper"