/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ // This file contains the `_computeAriaAccessibleName` function, which computes what the *expected* // ARIA accessible name would be for a given element. Implements a subset of ARIA specification // [Accessible Name and Description Computation 1.2](https://www.w3.org/TR/accname-1.2/). // // Specification accname-1.2 can be summarized by returning the result of the first method // available. // // 1. `aria-labelledby` attribute // ``` // // // // ``` // 2. `aria-label` attribute (e.g. ``) // 3. Label with `for`/`id` // ``` // // // // ``` // 4. `placeholder` attribute (e.g. ``) // 5. `title` attribute (e.g. ``) // 6. text content // ``` // // // // ``` /** * Computes the *expected* ARIA accessible name for argument element based on [accname-1.2 * specification](https://www.w3.org/TR/accname-1.2/). Implements a subset of accname-1.2, * and should only be used for the Datepicker's specific use case. * * Intended use: * This is not a general use implementation. Only implements the parts of accname-1.2 that are * required for the Datepicker's specific use case. This function is not intended for any other * use. * * Limitations: * - Only covers the needs of `matStartDate` and `matEndDate`. Does not support other use cases. * - See NOTES's in implementation for specific details on what parts of the accname-1.2 * specification are not implemented. * * @param element {HTMLInputElement} native <input/> element of `matStartDate` or * `matEndDate` component. Corresponds to the 'Root Element' from accname-1.2 * * @return expected ARIA accessible name of argument <input/> */ export function _computeAriaAccessibleName(element) { return _computeAriaAccessibleNameInternal(element, true); } /** * Determine if argument node is an Element based on `nodeType` property. This function is safe to * use with server-side rendering. */ function ssrSafeIsElement(node) { return node.nodeType === Node.ELEMENT_NODE; } /** * Determine if argument node is an HTMLInputElement based on `nodeName` property. This funciton is * safe to use with server-side rendering. */ function ssrSafeIsHTMLInputElement(node) { return node.nodeName === 'INPUT'; } /** * Determine if argument node is an HTMLTextAreaElement based on `nodeName` property. This * funciton is safe to use with server-side rendering. */ function ssrSafeIsHTMLTextAreaElement(node) { return node.nodeName === 'TEXTAREA'; } /** * Calculate the expected ARIA accessible name for given DOM Node. Given DOM Node may be either the * "Root node" passed to `_computeAriaAccessibleName` or "Current node" as result of recursion. * * @return the accessible name of argument DOM Node * * @param currentNode node to determine accessible name of * @param isDirectlyReferenced true if `currentNode` is the root node to calculate ARIA accessible * name of. False if it is a result of recursion. */ function _computeAriaAccessibleNameInternal(currentNode, isDirectlyReferenced) { // NOTE: this differs from accname-1.2 specification. // - Does not implement Step 1. of accname-1.2: '''If `currentNode`'s role prohibits naming, // return the empty string ("")'''. // - Does not implement Step 2.A. of accname-1.2: '''if current node is hidden and not directly // referenced by aria-labelledby... return the empty string.''' // acc-name-1.2 Step 2.B.: aria-labelledby if (ssrSafeIsElement(currentNode) && isDirectlyReferenced) { const labelledbyIds = currentNode.getAttribute?.('aria-labelledby')?.split(/\s+/g) || []; const validIdRefs = labelledbyIds.reduce((validIds, id) => { const elem = document.getElementById(id); if (elem) { validIds.push(elem); } return validIds; }, []); if (validIdRefs.length) { return validIdRefs .map(idRef => { return _computeAriaAccessibleNameInternal(idRef, false); }) .join(' '); } } // acc-name-1.2 Step 2.C.: aria-label if (ssrSafeIsElement(currentNode)) { const ariaLabel = currentNode.getAttribute('aria-label')?.trim(); if (ariaLabel) { return ariaLabel; } } // acc-name-1.2 Step 2.D. attribute or element that defines a text alternative // // NOTE: this differs from accname-1.2 specification. // Only implements Step 2.D. for `