Skip to content

Commit ad2e305

Browse files
Carl Schwanbackportbot[bot]
authored andcommitted
feat(NcCheckboxRadioSwitch): Add support for a description field
Both as a slot and a prop Signed-off-by: Carl Schwan <carl.schwan@nextclound.com> [skip ci]
1 parent f12a347 commit ad2e305

File tree

3 files changed

+81
-4
lines changed

3 files changed

+81
-4
lines changed

src/components/NcCheckboxRadioSwitch/NcCheckboxContent.vue

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,22 @@ export default {
140140
type: Number,
141141
default: 24,
142142
},
143+
144+
/**
145+
* Label id attribute
146+
*/
147+
labelId: {
148+
type: String,
149+
required: true,
150+
},
151+
152+
/**
153+
* Description id attribute
154+
*/
155+
descriptionId: {
156+
type: String,
157+
required: true,
158+
},
143159
},
144160
145161
computed: {
@@ -196,8 +212,11 @@ export default {
196212
// but restrict to content so plain checkboxes / radio switches do not expand
197213
max-width: fit-content;
198214
199-
&__text {
215+
&__wrapper {
200216
flex: 1 0;
217+
}
218+
219+
&__text {
201220
202221
&:empty {
203222
// hide text if empty to ensure checkbox outline is a circle instead of oval
@@ -211,12 +230,26 @@ export default {
211230
margin-block: calc((var(--default-clickable-area) - 2 * var(--default-grid-baseline) - var(--icon-height)) / 2) auto;
212231
}
213232
233+
&-checkbox:not(&--button-variant) &__icon--has-description,
234+
&-radio:not(&--button-variant) &__icon--has-description,
235+
&-switch:not(&--button-variant) &__icon--has-description {
236+
display: flex;
237+
align-items: center;
238+
margin-block-end: 0;
239+
align-self: start;
240+
}
241+
214242
&__icon > * {
215243
width: var(--icon-size);
216244
height: var(--icon-height);
217245
color: var(--color-primary-element);
218246
}
219247
248+
&__description {
249+
display: block;
250+
color: var(--color-text-maxcontrast);
251+
}
252+
220253
&--button-variant {
221254
.checkbox-content__icon:not(.checkbox-content__icon--checked) > * {
222255
color: var(--color-primary-element);

src/components/NcCheckboxRadioSwitch/NcCheckboxRadioSwitch.vue

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ Note: All attributes on the element are passed to the inner input element - exce
2323
<NcCheckboxRadioSwitch v-model="sharingEnabled">
2424
Enable sharing. This can contain a long multiline text, that will be wrapped in a second row. It is generally not advised to have such long text inside of an element
2525
</NcCheckboxRadioSwitch>
26+
27+
<NcCheckboxRadioSwitch v-model="sharingEnabled" description="The description">
28+
Enable sharing with description
29+
</NcCheckboxRadioSwitch>
2630
<br>
2731
sharingEnabled: {{ sharingEnabled }}
2832
</div>
@@ -237,6 +241,16 @@ export default {
237241
<NcCheckboxRadioSwitch v-model="sharingEnabled" type="switch">
238242
Enable sharing. This can contain a long multiline text, that will be wrapped in a second row. It is generally not advised to have such long text inside of an element
239243
</NcCheckboxRadioSwitch>
244+
<NcCheckboxRadioSwitch v-model="sharingEnabled" type="switch" description="Instead you can use a description as a prop which can also be a long multiline text, that will be wrapped in a second row.">
245+
Enable sharing.
246+
</NcCheckboxRadioSwitch>
247+
248+
<NcCheckboxRadioSwitch v-model="sharingEnabled" type="switch">
249+
Enable sharing.
250+
<template #description>
251+
Or you can use a description as slot which can also be a <strong>long multiline text</strong>, that will be wrapped in a second row.
252+
</template>
253+
</NcCheckboxRadioSwitch>
240254
<br>
241255
sharingEnabled: {{ sharingEnabled }}
242256
</div>
@@ -275,7 +289,8 @@ export default {
275289
v-on="isButtonType ? listeners : null">
276290
<input v-if="!isButtonType"
277291
:id="id"
278-
:aria-labelledby="!isButtonType && !ariaLabel ? `${id}-label` : null"
292+
:aria-labelledby="!isButtonType && !ariaLabel ? labelId : null"
293+
:aria-describedby="!isButtonType && (description || $slots.description) ? descriptionId : null"
279294
:aria-label="ariaLabel || undefined"
280295
class="checkbox-radio-switch__input"
281296
:disabled="disabled"
@@ -302,6 +317,12 @@ export default {
302317
<!-- @slot The checkbox/radio icon, you can use it for adding an icon to the button variant -->
303318
<slot name="icon" />
304319
</template>
320+
<template v-if="$slots.description || description" #description>
321+
<!-- @slot The checkbox/radio/switch description, you can use it for adding a more complex description element as opposed to the description prop -->
322+
<slot name="description">
323+
{{ description }}
324+
</slot>
325+
</template>
305326

306327
<!-- @slot The checkbox/radio label -->
307328
<slot />
@@ -493,6 +514,13 @@ export default {
493514
}
494515
},
495516
517+
setup() {
518+
return {
519+
labelId: createElementId(),
520+
descriptionId: createElementId(),
521+
}
522+
},
523+
496524
computed: {
497525
dataAttrs() {
498526
// filter all data attributes

tests/unit/components/NcCheckboxRadioSwitch/checkbox.spec.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ describe('NcCheckboxRadioSwitch', () => {
4949
},
5050
})
5151

52-
expect(wrapper.find('input').attributes('aria-labelledby')).toBe('test-id-label')
53-
expect(wrapper.findComponent({ name: 'NcCheckboxContent' }).attributes('id')).toBe('test-id-label')
52+
const labelById = wrapper.find('input').attributes('aria-labelledby')
53+
expect(wrapper.findComponent({ name: 'NcCheckboxContent' }).find('#' + labelById).exists()).toBe(true)
5454
})
5555

5656
it('does not set id on button content', () => {
@@ -67,4 +67,20 @@ describe('NcCheckboxRadioSwitch', () => {
6767
expect(wrapper.find('input').exists()).toBe(false)
6868
expect(wrapper.findComponent({ name: 'NcCheckboxContent' }).attributes('id')).toBe(undefined)
6969
})
70+
71+
it('sets aria-describedby attribute correctly', () => {
72+
const wrapper = mount(NcCheckboxRadioSwitch, {
73+
props: {
74+
description: 'My description',
75+
},
76+
slots: {
77+
default: 'Test',
78+
},
79+
})
80+
81+
const describedById = wrapper.find('input').attributes('aria-describedby')
82+
const descriptionElement = wrapper.findComponent({ name: 'NcCheckboxContent' }).find('#' + describedById)
83+
expect(descriptionElement.exists()).toBe(true)
84+
expect(descriptionElement.text()).toContain('My description')
85+
})
7086
})

0 commit comments

Comments
 (0)