@gitlab/ui 64.20.1 → 64.21.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.
@@ -249,6 +249,7 @@ const formInputSizes = {
249
249
  const toggleLabelPosition = {
250
250
  hidden: 'hidden',
251
251
  left: 'left',
252
+ block: 'block',
252
253
  top: 'top'
253
254
  };
254
255
  const tooltipActionEvents = ['open', 'close', 'enable', 'disable'];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "64.20.1",
3
+ "version": "64.21.0",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -118,7 +118,7 @@
118
118
  "babel-loader": "^8.0.5",
119
119
  "babel-plugin-require-context-hook": "^1.0.0",
120
120
  "bootstrap": "4.6.2",
121
- "cypress": "12.17.0",
121
+ "cypress": "12.17.1",
122
122
  "emoji-regex": "^10.0.0",
123
123
  "eslint": "8.44.0",
124
124
  "eslint-import-resolver-jest": "3.0.2",
@@ -29,18 +29,18 @@
29
29
  .gl-toggle {
30
30
  @include gl-cursor-not-allowed;
31
31
  @include gl-bg-gray-200;
32
+ }
32
33
 
33
- .toggle-icon > svg {
34
- @include gl-fill-gray-200;
35
- }
34
+ .toggle-icon > svg {
35
+ @include gl-fill-gray-200;
36
36
  }
37
37
 
38
38
  .gl-toggle.is-checked {
39
39
  @include gl-bg-blue-200;
40
+ }
40
41
 
41
- .toggle-icon > svg {
42
- @include gl-fill-blue-200;
43
- }
42
+ .toggle-icon > svg {
43
+ @include gl-fill-blue-200;
44
44
  }
45
45
 
46
46
  .gl-toggle-label,
@@ -50,6 +50,35 @@
50
50
  }
51
51
  }
52
52
 
53
+ .gl-toggle-label-container {
54
+ @include gl-flex-shrink-0;
55
+ @include gl-display-flex;
56
+ @include gl-flex-direction-column;
57
+ }
58
+
59
+ .gl-toggle-switch-container {
60
+ @include gl-display-flex;
61
+ @include gl-flex-direction-column;
62
+ }
63
+
64
+ .gl-toggle-label-position-block {
65
+ @include gl-justify-content-space-between;
66
+ @include gl-align-items-center;
67
+ @include gl-w-full;
68
+ @include gl-gap-3;
69
+
70
+ .gl-toggle-label-container {
71
+ @include gl-gap-2;
72
+ @include gl-flex-shrink-1;
73
+ }
74
+
75
+ .gl-toggle-switch-container {
76
+ @include gl-align-items-flex-end;
77
+ @include gl-gap-2;
78
+ @include gl-flex-shrink-1;
79
+ }
80
+ }
81
+
53
82
  .gl-help-label {
54
83
  @include gl-mt-3;
55
84
  @include gl-text-gray-500;
@@ -138,31 +138,43 @@ describe('toggle', () => {
138
138
 
139
139
  describe('label position', () => {
140
140
  describe.each`
141
- state | labelPosition | hasGlSrOnlyClass | flexDirection
142
- ${'top'} | ${toggleLabelPosition.top} | ${false} | ${'gl-flex-direction-column'}
143
- ${'left'} | ${toggleLabelPosition.left} | ${false} | ${'gl-toggle-label-inline'}
144
- ${'hidden'} | ${toggleLabelPosition.hidden} | ${true} | ${'gl-flex-direction-column'}
145
- `('when $state', ({ labelPosition, hasGlSrOnlyClass, flexDirection }) => {
146
- beforeEach(() => {
147
- createWrapper({ labelPosition });
148
- });
149
-
150
- it(`${flexDirection} class is added to the label`, () => {
151
- const cssClasses = wrapper.find('[data-testid="toggle-wrapper"]').classes();
152
-
153
- return expect(cssClasses).toContain(flexDirection);
154
- });
155
-
156
- it(`${hasGlSrOnlyClass ? 'adds' : 'does not add'} 'gl-sr-only' class to the label`, () => {
157
- const cssClasses = wrapper.find('[data-testid="toggle-label"]').classes();
158
- return hasGlSrOnlyClass
159
- ? expect(cssClasses).toContain('gl-sr-only')
160
- : expect(cssClasses).not.toContain('gl-sr-only');
161
- });
162
-
163
- it('has accessible name for the button', () => {
164
- expect(findButton().attributes('aria-labelledby')).toBeDefined();
165
- });
166
- });
141
+ state | labelPosition | hasGlSrOnlyClass | flexDirection | showsHelpText | showsDescription
142
+ ${'top'} | ${toggleLabelPosition.top} | ${false} | ${'gl-flex-direction-column'} | ${true} | ${true}
143
+ ${'left'} | ${toggleLabelPosition.left} | ${false} | ${'gl-toggle-label-inline'} | ${false} | ${false}
144
+ ${'hidden'} | ${toggleLabelPosition.hidden} | ${true} | ${'gl-flex-direction-column'} | ${true} | ${true}
145
+ ${'block'} | ${toggleLabelPosition.block} | ${false} | ${'gl-toggle-label-inline'} | ${true} | ${true}
146
+ `(
147
+ 'when $state',
148
+ ({ labelPosition, hasGlSrOnlyClass, flexDirection, showsHelpText, showsDescription }) => {
149
+ beforeEach(() => {
150
+ createWrapper({ labelPosition, help: helpText, description: descriptionText });
151
+ });
152
+
153
+ it(`${flexDirection} class is added to the label`, () => {
154
+ const cssClasses = wrapper.find('[data-testid="toggle-wrapper"]').classes();
155
+
156
+ expect(cssClasses).toContain(flexDirection);
157
+ });
158
+
159
+ it(`${hasGlSrOnlyClass ? 'adds' : 'does not add'} 'gl-sr-only' class to the label`, () => {
160
+ const cssClasses = wrapper.find('[data-testid="toggle-label"]').classes();
161
+ return hasGlSrOnlyClass
162
+ ? expect(cssClasses).toContain('gl-sr-only')
163
+ : expect(cssClasses).not.toContain('gl-sr-only');
164
+ });
165
+
166
+ it('has accessible name for the button', () => {
167
+ expect(findButton().attributes('aria-labelledby')).toBeDefined();
168
+ });
169
+
170
+ it(`${showsHelpText ? 'shows' : 'does not show'} the help text`, () => {
171
+ expect(findHelpElement().exists()).toBe(showsHelpText);
172
+ });
173
+
174
+ it(`${showsDescription ? 'shows' : 'does not show'} the description`, () => {
175
+ expect(findDescriptionElement().exists()).toBe(showsDescription);
176
+ });
177
+ }
178
+ );
167
179
  });
168
180
  });
@@ -67,6 +67,12 @@ LabelPositionLeft.args = generateProps({
67
67
  labelPosition: 'left',
68
68
  });
69
69
 
70
+ export const LabelPositionBlock = Template.bind({});
71
+ LabelPositionBlock.args = generateProps({
72
+ labelPosition: 'block',
73
+ description: withDescription,
74
+ });
75
+
70
76
  export default {
71
77
  title: 'base/toggle',
72
78
  component: GlToggle,
@@ -90,19 +90,42 @@ export default {
90
90
  };
91
91
  },
92
92
  computed: {
93
+ layoutAllowsDescription() {
94
+ return this.isVerticalLayout || this.isBlockLayout;
95
+ },
93
96
  shouldRenderDescription() {
94
- // eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots
95
- return Boolean(this.$scopedSlots.description || this.description) && this.isVerticalLayout;
97
+ return (
98
+ Boolean(this.$scopedSlots.description || this.description) && this.layoutAllowsDescription
99
+ );
96
100
  },
97
- shouldRenderHelp() {
98
- // eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots
99
- return Boolean(this.$slots.help || this.help) && this.isVerticalLayout;
101
+ labelIsSrOnly() {
102
+ return this.labelPosition === 'hidden';
100
103
  },
101
- toggleClasses() {
102
- return [
103
- { 'gl-sr-only': this.labelPosition === 'hidden' },
104
- this.shouldRenderDescription ? 'gl-mb-2' : 'gl-mb-3',
105
- ];
104
+ layoutAllowsHelp() {
105
+ return this.isVerticalLayout || this.isBlockLayout;
106
+ },
107
+ shouldRenderHelp() {
108
+ return Boolean(this.$scopedSlots.help || this.help) && this.layoutAllowsHelp;
109
+ },
110
+ labelContainerClasses() {
111
+ return {
112
+ 'gl-mb-3': this.isVerticalLayout && !this.labelIsSrOnly,
113
+ };
114
+ },
115
+ labelClasses() {
116
+ if (this.labelIsSrOnly) return 'gl-sr-only';
117
+ return {
118
+ 'gl-mb-2': this.shouldRenderDescription,
119
+ 'gl-mb-3': !this.shouldRenderDescription && !this.isVerticalLayout,
120
+ };
121
+ },
122
+ wrapperClasses() {
123
+ return {
124
+ 'gl-flex-direction-column': this.isVerticalLayout,
125
+ 'gl-toggle-label-inline': !this.isVerticalLayout,
126
+ 'is-disabled': this.disabled,
127
+ 'gl-toggle-label-position-block': this.isBlockLayout,
128
+ };
106
129
  },
107
130
  icon() {
108
131
  return this.value ? 'mobile-issue-close' : 'close';
@@ -116,6 +139,9 @@ export default {
116
139
  isVerticalLayout() {
117
140
  return this.labelPosition === 'top' || this.labelPosition === 'hidden';
118
141
  },
142
+ isBlockLayout() {
143
+ return this.labelPosition === 'block';
144
+ },
119
145
  },
120
146
 
121
147
  beforeCreate() {
@@ -141,55 +167,50 @@ export default {
141
167
 
142
168
  <template>
143
169
  <div
144
- class="gl-toggle-wrapper gl-display-flex gl-mb-0"
145
- :class="{
146
- 'gl-flex-direction-column': isVerticalLayout,
147
- 'gl-toggle-label-inline': !isVerticalLayout,
148
- 'is-disabled': disabled,
149
- }"
170
+ class="gl-toggle-wrapper gl-display-flex gl-mb-0 flex-grow-1"
171
+ :class="wrapperClasses"
150
172
  data-testid="toggle-wrapper"
151
173
  >
152
- <span
153
- :id="labelId"
154
- :class="toggleClasses"
155
- class="gl-toggle-label gl-flex-shrink-0"
156
- data-testid="toggle-label"
157
- >
158
- <!-- @slot The toggle's label. -->
159
- <slot name="label">{{ label }}</slot>
160
- </span>
161
- <span
162
- v-if="shouldRenderDescription"
163
- class="gl-description-label gl-mb-3"
164
- data-testid="toggle-description"
165
- >
166
- <!-- @slot A description text to be shown below the label. -->
167
- <slot name="description">{{ description }}</slot>
174
+ <span :class="labelContainerClasses" class="gl-toggle-label-container">
175
+ <span :id="labelId" :class="labelClasses" class="gl-toggle-label" data-testid="toggle-label">
176
+ <!-- @slot The toggle's label. -->
177
+ <slot name="label">{{ label }}</slot>
178
+ </span>
179
+ <span
180
+ v-if="shouldRenderDescription"
181
+ class="gl-description-label"
182
+ data-testid="toggle-description"
183
+ >
184
+ <!-- @slot A description text to be shown below the label. -->
185
+ <slot name="description">{{ description }}</slot>
186
+ </span>
168
187
  </span>
169
- <input v-if="name" :name="name" :value="value" type="hidden" />
170
- <button
171
- role="switch"
172
- :aria-checked="isChecked"
173
- :aria-labelledby="labelId"
174
- :aria-describedby="helpId"
175
- :aria-disabled="disabled"
176
- :class="{
177
- 'gl-toggle': true,
178
- 'is-checked': value,
179
- 'is-disabled': disabled,
180
- }"
181
- class="gl-flex-shrink-0"
182
- type="button"
183
- @click.prevent="toggleFeature"
184
- >
185
- <gl-loading-icon v-if="isLoading" color="light" class="toggle-loading" />
186
- <span v-else :class="{ 'toggle-icon': true, disabled: disabled }">
187
- <gl-icon :name="icon" :size="16" />
188
+ <span class="gl-toggle-switch-container">
189
+ <input v-if="name" :name="name" :value="value" type="hidden" />
190
+ <button
191
+ role="switch"
192
+ :aria-checked="isChecked"
193
+ :aria-labelledby="labelId"
194
+ :aria-describedby="helpId"
195
+ :aria-disabled="disabled"
196
+ :class="{
197
+ 'gl-toggle': true,
198
+ 'is-checked': value,
199
+ 'is-disabled': disabled,
200
+ }"
201
+ class="gl-flex-shrink-0"
202
+ type="button"
203
+ @click.prevent="toggleFeature"
204
+ >
205
+ <gl-loading-icon v-if="isLoading" color="light" class="toggle-loading" />
206
+ <span v-else :class="{ 'toggle-icon': true, disabled: disabled }">
207
+ <gl-icon :name="icon" :size="16" />
208
+ </span>
209
+ </button>
210
+ <span v-if="shouldRenderHelp" :id="helpId" class="gl-help-label" data-testid="toggle-help">
211
+ <!-- @slot A help text to be shown below the toggle. -->
212
+ <slot name="help">{{ help }}</slot>
188
213
  </span>
189
- </button>
190
- <span v-if="shouldRenderHelp" :id="helpId" class="gl-help-label" data-testid="toggle-help">
191
- <!-- @slot A help text to be shown below the toggle. -->
192
- <slot name="help">{{ help }}</slot>
193
214
  </span>
194
215
  </div>
195
216
  </template>
@@ -3571,6 +3571,14 @@
3571
3571
  flex-shrink: 0 !important;
3572
3572
  }
3573
3573
 
3574
+ .gl-flex-shrink-1 {
3575
+ flex-shrink: 1;
3576
+ }
3577
+
3578
+ .gl-flex-shrink-1\! {
3579
+ flex-shrink: 1 !important;
3580
+ }
3581
+
3574
3582
  .gl-flex-grow-0 {
3575
3583
  flex-grow: 0;
3576
3584
  }
@@ -183,6 +183,10 @@
183
183
  flex-shrink: 0;
184
184
  }
185
185
 
186
+ @mixin gl-flex-shrink-1 {
187
+ flex-shrink: 1;
188
+ }
189
+
186
190
  @mixin gl-flex-grow-0 {
187
191
  flex-grow: 0;
188
192
  }
@@ -297,6 +297,7 @@ export const formInputSizes = {
297
297
  export const toggleLabelPosition = {
298
298
  hidden: 'hidden',
299
299
  left: 'left',
300
+ block: 'block',
300
301
  top: 'top',
301
302
  };
302
303