Skip to content

Commit 5ca4aba

Browse files
vursenclaude
andauthored
feat: add HasAriaDescription to field components (#9811)
This PR adds the `HasAriaDescription` mixin interface to all field components that implement `HasAriaLabel`, overriding `setAriaDescribedBy` / `getAriaDescribedBy` to map to the `accessibleDescriptionRef` property, which propagates the reference to the input element (the default implementation sets attributes on the host element instead). This follows the same pattern as `accessibleName` / `accessibleNameRef`. DateTimePicker is left out because it doesn't implement `HasAriaLabel`, `HasPrefix` or `HasSuffix`. Part of vaadin/web-components#11975 Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 9d32e25 commit 5ca4aba

26 files changed

Lines changed: 588 additions & 21 deletions

File tree

vaadin-checkbox-flow-parent/vaadin-checkbox-flow/src/main/java/com/vaadin/flow/component/checkbox/Checkbox.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.vaadin.flow.component.ClickNotifier;
2626
import com.vaadin.flow.component.Component;
2727
import com.vaadin.flow.component.Focusable;
28+
import com.vaadin.flow.component.HasAriaDescription;
2829
import com.vaadin.flow.component.HasAriaLabel;
2930
import com.vaadin.flow.component.Synchronize;
3031
import com.vaadin.flow.component.Tag;
@@ -85,8 +86,9 @@
8586
@NpmPackage(value = "@vaadin/checkbox", version = "25.3.0-alpha8")
8687
@JsModule("@vaadin/checkbox/src/vaadin-checkbox.js")
8788
public class Checkbox extends AbstractSinglePropertyField<Checkbox, Boolean>
88-
implements ClickNotifier<Checkbox>, Focusable<Checkbox>, HasAriaLabel,
89-
HasValidationProperties, HasValidator<Boolean>,
89+
implements ClickNotifier<Checkbox>, Focusable<Checkbox>,
90+
HasAriaDescription, HasAriaLabel, HasValidationProperties,
91+
HasValidator<Boolean>,
9092
InputField<AbstractField.ComponentValueChangeEvent<Checkbox, Boolean>, Boolean>,
9193
HasThemeVariant<CheckboxVariant> {
9294

@@ -322,6 +324,23 @@ public Optional<String> getAriaLabelledBy() {
322324
.ofNullable(getElement().getProperty("accessibleNameRef"));
323325
}
324326

327+
/**
328+
* {@inheritDoc}
329+
* <p>
330+
* The referenced elements are announced in addition to the helper text and
331+
* the error message.
332+
*/
333+
@Override
334+
public void setAriaDescribedBy(String ariaDescribedBy) {
335+
getElement().setProperty("accessibleDescriptionRef", ariaDescribedBy);
336+
}
337+
338+
@Override
339+
public Optional<String> getAriaDescribedBy() {
340+
return Optional.ofNullable(
341+
getElement().getProperty("accessibleDescriptionRef"));
342+
}
343+
325344
/**
326345
* Set the checkbox to be input focused when the page loads.
327346
*

vaadin-checkbox-flow-parent/vaadin-checkbox-flow/src/main/java/com/vaadin/flow/component/checkbox/CheckboxGroup.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import com.vaadin.flow.component.AbstractSinglePropertyField;
3434
import com.vaadin.flow.component.Component;
3535
import com.vaadin.flow.component.ComponentUtil;
36+
import com.vaadin.flow.component.HasAriaDescription;
3637
import com.vaadin.flow.component.HasAriaLabel;
3738
import com.vaadin.flow.component.ItemLabelGenerator;
3839
import com.vaadin.flow.component.Tag;
@@ -116,8 +117,8 @@
116117
@JsModule("@vaadin/checkbox-group/src/vaadin-checkbox-group.js")
117118
public class CheckboxGroup<T>
118119
extends AbstractSinglePropertyField<CheckboxGroup<T>, Set<T>>
119-
implements HasAriaLabel, HasDataView<T, Void, CheckboxGroupDataView<T>>,
120-
HasItemComponents<T>,
120+
implements HasAriaDescription, HasAriaLabel,
121+
HasDataView<T, Void, CheckboxGroupDataView<T>>, HasItemComponents<T>,
121122
InputField<AbstractField.ComponentValueChangeEvent<CheckboxGroup<T>, Set<T>>, Set<T>>,
122123
HasListDataView<T, CheckboxGroupListDataView<T>>,
123124
HasThemeVariant<CheckboxGroupVariant>, HasValidationProperties,
@@ -663,6 +664,23 @@ public Optional<String> getAriaLabelledBy() {
663664
.ofNullable(getElement().getProperty("accessibleNameRef"));
664665
}
665666

667+
/**
668+
* {@inheritDoc}
669+
* <p>
670+
* The referenced elements are announced in addition to the helper text and
671+
* the error message.
672+
*/
673+
@Override
674+
public void setAriaDescribedBy(String ariaDescribedBy) {
675+
getElement().setProperty("accessibleDescriptionRef", ariaDescribedBy);
676+
}
677+
678+
@Override
679+
public Optional<String> getAriaDescribedBy() {
680+
return Optional.ofNullable(
681+
getElement().getProperty("accessibleDescriptionRef"));
682+
}
683+
666684
/**
667685
* Sets whether the user is required to select at least one checkbox. When
668686
* required, an indicator appears next to the label and the field

vaadin-checkbox-flow-parent/vaadin-checkbox-flow/src/main/java/com/vaadin/flow/component/checkbox/Switch.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.vaadin.flow.component.ClickNotifier;
2828
import com.vaadin.flow.component.Component;
2929
import com.vaadin.flow.component.Focusable;
30+
import com.vaadin.flow.component.HasAriaDescription;
3031
import com.vaadin.flow.component.HasAriaLabel;
3132
import com.vaadin.flow.component.Tag;
3233
import com.vaadin.flow.component.UI;
@@ -83,8 +84,8 @@
8384
@NpmPackage(value = "@vaadin/switch", version = "25.3.0-alpha8")
8485
@JsModule("@vaadin/switch/src/vaadin-switch.js")
8586
public class Switch extends AbstractSinglePropertyField<Switch, Boolean>
86-
implements ClickNotifier<Switch>, Focusable<Switch>, HasAriaLabel,
87-
HasValidationProperties, HasValidator<Boolean>,
87+
implements ClickNotifier<Switch>, Focusable<Switch>, HasAriaDescription,
88+
HasAriaLabel, HasValidationProperties, HasValidator<Boolean>,
8889
InputField<AbstractField.ComponentValueChangeEvent<Switch, Boolean>, Boolean>,
8990
HasThemeVariant<SwitchVariant> {
9091

@@ -320,6 +321,23 @@ public Optional<String> getAriaLabelledBy() {
320321
.ofNullable(getElement().getProperty("accessibleNameRef"));
321322
}
322323

324+
/**
325+
* {@inheritDoc}
326+
* <p>
327+
* The referenced elements are announced in addition to the helper text and
328+
* the error message.
329+
*/
330+
@Override
331+
public void setAriaDescribedBy(String ariaDescribedBy) {
332+
getElement().setProperty("accessibleDescriptionRef", ariaDescribedBy);
333+
}
334+
335+
@Override
336+
public Optional<String> getAriaDescribedBy() {
337+
return Optional.ofNullable(
338+
getElement().getProperty("accessibleDescriptionRef"));
339+
}
340+
323341
/**
324342
* Set the switch to be input focused when the page loads.
325343
*

vaadin-checkbox-flow-parent/vaadin-checkbox-flow/src/test/java/com/vaadin/flow/component/checkbox/tests/CheckboxGroupTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
import com.vaadin.flow.component.AbstractField;
3434
import com.vaadin.flow.component.Component;
35+
import com.vaadin.flow.component.HasAriaDescription;
3536
import com.vaadin.flow.component.HasAriaLabel;
3637
import com.vaadin.flow.component.HasValue;
3738
import com.vaadin.flow.component.HasValue.ValueChangeEvent;
@@ -625,6 +626,28 @@ void setAriaLabelledBy() {
625626
Assertions.assertTrue(group.getAriaLabelledBy().isEmpty());
626627
}
627628

629+
@Test
630+
void implementHasAriaDescription() {
631+
Assertions.assertTrue(
632+
HasAriaDescription.class.isAssignableFrom(CheckboxGroup.class));
633+
}
634+
635+
@Test
636+
void setAriaDescribedBy() {
637+
CheckboxGroup<String> group = new CheckboxGroup<>();
638+
group.setAriaDescribedBy("description-id");
639+
640+
Assertions.assertEquals("description-id",
641+
group.getElement().getProperty("accessibleDescriptionRef"));
642+
Assertions.assertEquals("description-id",
643+
group.getAriaDescribedBy().get());
644+
645+
group.setAriaDescribedBy((String) null);
646+
Assertions.assertNull(
647+
group.getElement().getProperty("accessibleDescriptionRef"));
648+
Assertions.assertTrue(group.getAriaDescribedBy().isEmpty());
649+
}
650+
628651
@Test
629652
void implementsInputField() {
630653
CheckboxGroup<String> field = new CheckboxGroup<String>();

vaadin-checkbox-flow-parent/vaadin-checkbox-flow/src/test/java/com/vaadin/flow/component/checkbox/tests/CheckboxUnitTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import com.vaadin.flow.component.AbstractField;
2424
import com.vaadin.flow.component.Component;
25+
import com.vaadin.flow.component.HasAriaDescription;
2526
import com.vaadin.flow.component.HasAriaLabel;
2627
import com.vaadin.flow.component.checkbox.Checkbox;
2728
import com.vaadin.flow.component.shared.HasThemeVariant;
@@ -147,6 +148,28 @@ void setAriaLabelledBy() {
147148
Assertions.assertTrue(checkbox.getAriaLabelledBy().isEmpty());
148149
}
149150

151+
@Test
152+
void implementHasAriaDescription() {
153+
Checkbox checkbox = new Checkbox();
154+
Assertions.assertTrue(checkbox instanceof HasAriaDescription);
155+
}
156+
157+
@Test
158+
void setAriaDescribedBy() {
159+
Checkbox checkbox = new Checkbox();
160+
checkbox.setAriaDescribedBy("description-id");
161+
162+
Assertions.assertEquals("description-id",
163+
checkbox.getElement().getProperty("accessibleDescriptionRef"));
164+
Assertions.assertEquals("description-id",
165+
checkbox.getAriaDescribedBy().get());
166+
167+
checkbox.setAriaDescribedBy((String) null);
168+
Assertions.assertNull(
169+
checkbox.getElement().getProperty("accessibleDescriptionRef"));
170+
Assertions.assertTrue(checkbox.getAriaDescribedBy().isEmpty());
171+
}
172+
150173
@Test
151174
void implementsInputField() {
152175
Checkbox field = new Checkbox();

vaadin-checkbox-flow-parent/vaadin-checkbox-flow/src/test/java/com/vaadin/flow/component/checkbox/tests/SwitchTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.junit.jupiter.api.Test;
2020
import org.junit.jupiter.api.extension.RegisterExtension;
2121

22+
import com.vaadin.flow.component.HasAriaDescription;
2223
import com.vaadin.flow.component.checkbox.Switch;
2324
import com.vaadin.flow.component.shared.HasThemeVariant;
2425
import com.vaadin.tests.MockUIExtension;
@@ -73,4 +74,26 @@ void implementsHasThemeVariant() {
7374
Assertions.assertTrue(
7475
HasThemeVariant.class.isAssignableFrom(Switch.class));
7576
}
77+
78+
@Test
79+
void implementsHasAriaDescription() {
80+
Assertions.assertTrue(
81+
HasAriaDescription.class.isAssignableFrom(Switch.class));
82+
}
83+
84+
@Test
85+
void setAriaDescribedBy() {
86+
Switch field = new Switch();
87+
field.setAriaDescribedBy("description-id");
88+
89+
Assertions.assertEquals("description-id",
90+
field.getElement().getProperty("accessibleDescriptionRef"));
91+
Assertions.assertEquals("description-id",
92+
field.getAriaDescribedBy().get());
93+
94+
field.setAriaDescribedBy((String) null);
95+
Assertions.assertNull(
96+
field.getElement().getProperty("accessibleDescriptionRef"));
97+
Assertions.assertTrue(field.getAriaDescribedBy().isEmpty());
98+
}
7699
}

vaadin-combo-box-flow-parent/vaadin-combo-box-flow/src/main/java/com/vaadin/flow/component/combobox/ComboBoxBase.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import com.vaadin.flow.component.DomEvent;
3838
import com.vaadin.flow.component.EventData;
3939
import com.vaadin.flow.component.Focusable;
40+
import com.vaadin.flow.component.HasAriaDescription;
4041
import com.vaadin.flow.component.HasAriaLabel;
4142
import com.vaadin.flow.component.HasPlaceholder;
4243
import com.vaadin.flow.component.HasTheme;
@@ -91,9 +92,10 @@
9192
* @since 23.2
9293
*/
9394
public abstract class ComboBoxBase<TComponent extends ComboBoxBase<TComponent, TItem, TValue>, TItem, TValue>
94-
extends AbstractSinglePropertyField<TComponent, TValue> implements
95-
Focusable<TComponent>, HasAllowedCharPattern, HasAriaLabel, HasAutoOpen,
96-
HasClearButton, HasDataView<TItem, String, ComboBoxDataView<TItem>>,
95+
extends AbstractSinglePropertyField<TComponent, TValue>
96+
implements Focusable<TComponent>, HasAllowedCharPattern,
97+
HasAriaDescription, HasAriaLabel, HasAutoOpen, HasClearButton,
98+
HasDataView<TItem, String, ComboBoxDataView<TItem>>,
9799
InputField<AbstractField.ComponentValueChangeEvent<TComponent, TValue>, TValue>,
98100
HasLazyDataView<TItem, String, ComboBoxLazyDataView<TItem>>,
99101
HasListDataView<TItem, ComboBoxListDataView<TItem>>, HasTheme,
@@ -461,6 +463,23 @@ public Optional<String> getAriaLabelledBy() {
461463
.ofNullable(getElement().getProperty("accessibleNameRef"));
462464
}
463465

466+
/**
467+
* {@inheritDoc}
468+
* <p>
469+
* The referenced elements are announced in addition to the helper text and
470+
* the error message.
471+
*/
472+
@Override
473+
public void setAriaDescribedBy(String ariaDescribedBy) {
474+
getElement().setProperty("accessibleDescriptionRef", ariaDescribedBy);
475+
}
476+
477+
@Override
478+
public Optional<String> getAriaDescribedBy() {
479+
return Optional.ofNullable(
480+
getElement().getProperty("accessibleDescriptionRef"));
481+
}
482+
464483
/**
465484
* Sets the item label generator that is used to produce the strings shown
466485
* in the combo box for each item. By default,

vaadin-combo-box-flow-parent/vaadin-combo-box-flow/src/test/java/com/vaadin/flow/component/combobox/ComboBoxBaseTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.mockito.Mockito;
2626

2727
import com.vaadin.flow.component.Focusable;
28+
import com.vaadin.flow.component.HasAriaDescription;
2829
import com.vaadin.flow.component.HasAriaLabel;
2930
import com.vaadin.flow.component.HasLabel;
3031
import com.vaadin.flow.component.HasPlaceholder;
@@ -77,6 +78,14 @@ void implementsHasAriaLabel() {
7778
"ComboBox should support setting aria-label and aria-labelledby");
7879
}
7980

81+
@Test
82+
void implementsHasAriaDescription() {
83+
Assertions.assertTrue(
84+
HasAriaDescription.class.isAssignableFrom(
85+
createComboBox(String.class).getClass()),
86+
"ComboBox should support setting aria-describedby");
87+
}
88+
8089
@Test
8190
void implementsHasAllowedCharPattern() {
8291
Assertions.assertTrue(
@@ -375,4 +384,20 @@ void setAriaLabelledBy() {
375384
comboBox.setAriaLabelledBy((String) null);
376385
Assertions.assertTrue(comboBox.getAriaLabelledBy().isEmpty());
377386
}
387+
388+
@Test
389+
void setAriaDescribedBy() {
390+
ComboBoxBase<?, String, ?> comboBox = createComboBox(String.class);
391+
392+
comboBox.setAriaDescribedBy("description-id");
393+
Assertions.assertEquals("description-id",
394+
comboBox.getElement().getProperty("accessibleDescriptionRef"));
395+
Assertions.assertEquals("description-id",
396+
comboBox.getAriaDescribedBy().get());
397+
398+
comboBox.setAriaDescribedBy((String) null);
399+
Assertions.assertNull(
400+
comboBox.getElement().getProperty("accessibleDescriptionRef"));
401+
Assertions.assertTrue(comboBox.getAriaDescribedBy().isEmpty());
402+
}
378403
}

vaadin-date-picker-flow-parent/vaadin-date-picker-flow/src/main/java/com/vaadin/flow/component/datepicker/DatePicker.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import com.vaadin.flow.component.ComponentEvent;
3838
import com.vaadin.flow.component.ComponentEventListener;
3939
import com.vaadin.flow.component.Focusable;
40+
import com.vaadin.flow.component.HasAriaDescription;
4041
import com.vaadin.flow.component.HasAriaLabel;
4142
import com.vaadin.flow.component.HasPlaceholder;
4243
import com.vaadin.flow.component.HasValue;
@@ -134,8 +135,8 @@
134135
@NpmPackage(value = "date-fns", version = "4.1.0")
135136
public class DatePicker
136137
extends AbstractSinglePropertyField<DatePicker, LocalDate>
137-
implements Focusable<DatePicker>, HasAllowedCharPattern, HasAriaLabel,
138-
HasAutoOpen, HasClearButton,
138+
implements Focusable<DatePicker>, HasAllowedCharPattern,
139+
HasAriaDescription, HasAriaLabel, HasAutoOpen, HasClearButton,
139140
InputField<AbstractField.ComponentValueChangeEvent<DatePicker, LocalDate>, LocalDate>,
140141
HasPrefix, HasThemeVariant<DatePickerVariant>, HasValidationProperties,
141142
HasValidator<LocalDate>, HasPlaceholder {
@@ -572,6 +573,23 @@ public Optional<String> getAriaLabelledBy() {
572573
.ofNullable(getElement().getProperty("accessibleNameRef"));
573574
}
574575

576+
/**
577+
* {@inheritDoc}
578+
* <p>
579+
* The referenced elements are announced in addition to the helper text and
580+
* the error message.
581+
*/
582+
@Override
583+
public void setAriaDescribedBy(String ariaDescribedBy) {
584+
getElement().setProperty("accessibleDescriptionRef", ariaDescribedBy);
585+
}
586+
587+
@Override
588+
public Optional<String> getAriaDescribedBy() {
589+
return Optional.ofNullable(
590+
getElement().getProperty("accessibleDescriptionRef"));
591+
}
592+
575593
@Override
576594
protected void onAttach(AttachEvent attachEvent) {
577595
super.onAttach(attachEvent);

0 commit comments

Comments
 (0)