Skip to content

Commit

Permalink
feat: add aria-labelledby API to HasAriaLabel (#16081)
Browse files Browse the repository at this point in the history
  • Loading branch information
DiegoCardoso committed Mar 2, 2023
1 parent 157a507 commit eb4e41d
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 15 deletions.
Expand Up @@ -21,38 +21,56 @@

/**
* A generic interface for components and other user interface objects that may
* have an aria-label property to set the accessible name of the component.
* have an aria-label and an aria-labelledby DOM attributes to set the
* accessible name of the component.
* <p>
* The default implementations set the aria-label of the component to the given
* {@link #getElement()}. Override all methods in this interface if the
* aria-label should be added to some other element.
* The default implementation set the aria-label and aria-labelledby of the
* component to the given {@link #getElement()}. Override all methods in this
* interface if the aria-label and aria-labelledby should be added to some other
* element.
* <p>
* The purpose of aria-label is to provide the user with a recognizable name of
* the component. If the label text is visible on screen, aria-label should not
* be used. There may be instances where the name of an element cannot be
* determined programmatically from the content of the element, and there are
* cases where providing a visible label is not the desired user experience. In
* the cases where a visible label or visible tooltip is undesirable, aria-label
* may be used to set the accessible name of the component.
* the component. If the label text is visible on screen, aria-labelledby
* <b>should</b> be used and aria-label <b>should not</b> be used. There may be
* instances where the name of an element cannot be determined programmatically
* from the content of the element, and there are cases where providing a
* visible label is not the desired user experience. In the cases where a
* visible label or visible tooltip is undesirable, aria-label may be used to
* set the accessible name of the component.
* <p>
* <b>Don't include both</b>. If both are present on the same element,
* aria-labelledby will take precedence over aria-label.
* <p>
* See: https://www.w3.org/TR/wai-aria/#aria-label
* <p>
* Note: The aria-label property is not valid on every component, see
* https://www.w3.org/TR/using-aria/#label-support for more details.
* See: https://www.w3.org/TR/wai-aria/#aria-labelledby
* <p>
* Note: The aria-label and aria-labelledby attributes are not valid on every
* component, see https://www.w3.org/TR/using-aria/#label-support for more
* details.
*
* @author Vaadin Ltd
* @since
*/
public interface HasAriaLabel extends HasElement {
/**
* Set the aria-label of the component to the given text.
* <p>
* This method should not be used if {@link #setAriaLabelledBy(String)} is
* also used. If both attributes are present, aria-labelledby will take
* precedence over aria-label.
*
* @param ariaLabel
* the aria-label text to set or {@code null} to clear
*/
default void setAriaLabel(String ariaLabel) {
getElement().setProperty(ElementConstants.ARIA_LABEL_PROPERTY_NAME,
ariaLabel);
if (ariaLabel != null) {
getElement().setAttribute(
ElementConstants.ARIA_LABEL_ATTRIBUTE_NAME, ariaLabel);
} else {
getElement().removeAttribute(
ElementConstants.ARIA_LABEL_ATTRIBUTE_NAME);
}
}

/**
Expand All @@ -63,6 +81,42 @@ default void setAriaLabel(String ariaLabel) {
*/
default Optional<String> getAriaLabel() {
return Optional.ofNullable(getElement()
.getProperty(ElementConstants.ARIA_LABEL_PROPERTY_NAME, null));
.getAttribute(ElementConstants.ARIA_LABEL_ATTRIBUTE_NAME));
}

/**
* Set the aria-labelledby of the component. The value must be a valid id
* attribute of another element that labels the component. The label element
* <b>must</b> be in the same DOM scope of the component, otherwise screen
* readers may fail to announce the label content properly.
* <p>
* This method should not be used if {@link #setAriaLabel(String)} is also
* used. If both attributes are present, aria-labelledby will take
* precedence over aria-label.
*
* @param ariaLabelledBy
* the string with the id of the element that will be used as
* label or {@code null} to clear
*/
default void setAriaLabelledBy(String ariaLabelledBy) {
if (ariaLabelledBy != null) {
getElement().setAttribute(
ElementConstants.ARIA_LABELLEDBY_ATTRIBUTE_NAME,
ariaLabelledBy);
} else {
getElement().removeAttribute(
ElementConstants.ARIA_LABELLEDBY_ATTRIBUTE_NAME);
}
}

/**
* Gets the aria-labelledby of the component
*
* @return an optional aria-labelledby of the component if no
* aria-labelledby has been set
*/
default Optional<String> getAriaLabelledBy() {
return Optional.ofNullable(getElement()
.getAttribute(ElementConstants.ARIA_LABELLEDBY_ATTRIBUTE_NAME));
}
}
Expand Up @@ -57,8 +57,18 @@ public class ElementConstants {
public static final String LABEL_PROPERTY_NAME = "label";
/**
* The aria-label property.
*
* @deprecated use {@link #ARIA_LABEL_ATTRIBUTE_NAME} instead
*/
public static final String ARIA_LABEL_PROPERTY_NAME = "aria-label";
/**
* The aria-label attribute.
*/
public static final String ARIA_LABEL_ATTRIBUTE_NAME = "aria-label";
/**
* The aria-labelledby attribute.
*/
public static final String ARIA_LABELLEDBY_ATTRIBUTE_NAME = "aria-labelledby";

private ElementConstants() {
// Constants only
Expand Down
Expand Up @@ -65,4 +65,43 @@ public void setAriaLabel() {

assertEquals("test AriaLabel", component.getAriaLabel().get());
}

@Test
public void withoutAriaLabelledByComponent_getAriaLabelledByReturnsEmptyOptional() {
TestComponent component = new TestComponent();

assertFalse(component.getAriaLabelledBy().isPresent());
}

@Test
public void withNullAriaLabelledBy_getAriaLabelledByReturnsEmptyOptional() {
TestComponent component = new TestComponent();
component.setAriaLabelledBy(null);
assertFalse(component.getAriaLabelledBy().isPresent());
}

@Test
public void withEmptyAriaLabelledBy_getAriaLabelledByReturnsEmptyString() {
TestComponent component = new TestComponent();
component.setAriaLabelledBy("");
assertEquals("", component.getAriaLabelledBy().get());
}

@Test
public void withAriaLabelledBy_setAriaLabelledByToNullClearsAriaLabelledBy() {
TestComponent component = new TestComponent();
component.setAriaLabelledBy("test AriaLabelledBy");

component.setAriaLabelledBy(null);
assertFalse(component.getAriaLabelledBy().isPresent());
}

@Test
public void setAriaLabelledBy() {
TestComponent component = new TestComponent();
component.setAriaLabelledBy("test AriaLabelledBy");

assertEquals("test AriaLabelledBy",
component.getAriaLabelledBy().get());
}
}

0 comments on commit eb4e41d

Please sign in to comment.