diff --git a/examples/puppeteer/yarn.lock b/examples/puppeteer/yarn.lock index 3abf7f0..233bf57 100644 --- a/examples/puppeteer/yarn.lock +++ b/examples/puppeteer/yarn.lock @@ -1090,9 +1090,9 @@ create-jest@^29.7.0: prompts "^2.0.1" cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" diff --git a/examples/typescript-legacy/yarn.lock b/examples/typescript-legacy/yarn.lock index 375bbe3..7d5e16b 100644 --- a/examples/typescript-legacy/yarn.lock +++ b/examples/typescript-legacy/yarn.lock @@ -988,9 +988,9 @@ create-jest@^29.7.0: prompts "^2.0.1" cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" diff --git a/examples/typescript-nodenext/yarn.lock b/examples/typescript-nodenext/yarn.lock index 375bbe3..7d5e16b 100644 --- a/examples/typescript-nodenext/yarn.lock +++ b/examples/typescript-nodenext/yarn.lock @@ -988,9 +988,9 @@ create-jest@^29.7.0: prompts "^2.0.1" cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" diff --git a/examples/vue/yarn.lock b/examples/vue/yarn.lock index 7b09f57..cfef6dc 100644 --- a/examples/vue/yarn.lock +++ b/examples/vue/yarn.lock @@ -1522,9 +1522,9 @@ convert-source-map@^2.0.0: integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" diff --git a/src/Virtual.ts b/src/Virtual.ts index 682cffc..564ff2f 100644 --- a/src/Virtual.ts +++ b/src/Virtual.ts @@ -7,7 +7,7 @@ import { ERR_VIRTUAL_MISSING_CONTAINER, ERR_VIRTUAL_NOT_STARTED, } from "./errors"; -import { getLiveSpokenPhrase, Live } from "./getLiveSpokenPhrase"; +import { getLiveSpokenPhrase, LIVE } from "./getLiveSpokenPhrase"; import { flattenTree } from "./flattenTree"; import { getElementNode } from "./commands/getElementNode"; import { getItemText } from "./getItemText"; @@ -180,10 +180,8 @@ const defaultUserEventOptions = { * "heading, Section Heading, level 1", * "Section Text", * "article", - * "banner", * "heading, Article Header Heading, level 1", * "Article Header Text", - * "end of banner", * "Article Text", * "end of article", * "end of region", @@ -360,8 +358,8 @@ export class Virtual { #spokenPhraseLogWithoutLiveRegions() { return this.#spokenPhraseLog.filter( (spokenPhrase) => - !spokenPhrase.startsWith(Live.ASSERTIVE) && - !spokenPhrase.startsWith(Live.POLITE) + !spokenPhrase.startsWith(LIVE.ASSERTIVE) && + !spokenPhrase.startsWith(LIVE.POLITE) ); } diff --git a/src/getLiveSpokenPhrase.ts b/src/getLiveSpokenPhrase.ts index 9edeff3..bc3e564 100644 --- a/src/getLiveSpokenPhrase.ts +++ b/src/getLiveSpokenPhrase.ts @@ -5,6 +5,8 @@ import { getRole } from "./getNodeAccessibilityData/getRole"; import { isElement } from "./isElement"; import { sanitizeString } from "./sanitizeString"; +type ValueOf = T[keyof T]; + /** * Live region roles: * @@ -36,23 +38,23 @@ import { sanitizeString } from "./sanitizeString"; * - https://www.w3.org/TR/wai-aria-1.2/#aria-live */ -export enum Live { - ASSERTIVE = "assertive", - OFF = "off", - POLITE = "polite", -} +export const LIVE = { + ASSERTIVE: "assertive", + OFF: "off", + POLITE: "polite", +}; -enum Relevant { - ADDITIONS = "additions", - ALL = "all", - REMOVALS = "removals", - TEXT = "text", -} +const RELEVANT = { + ADDITIONS: "additions", + ALL: "all", + REMOVALS: "removals", + TEXT: "text", +}; -const RELEVANT_VALUES = new Set(Object.values(Relevant)); +const RELEVANT_VALUES = new Set(Object.values(RELEVANT)); const DEFAULT_ATOMIC = false; -const DEFAULT_LIVE = Live.OFF; -const DEFAULT_RELEVANT = [Relevant.ADDITIONS, Relevant.TEXT]; +const DEFAULT_LIVE = LIVE.OFF; +const DEFAULT_RELEVANT = [RELEVANT.ADDITIONS, RELEVANT.TEXT]; function getSpokenPhraseForNode(node: Node) { return ( @@ -60,7 +62,6 @@ function getSpokenPhraseForNode(node: Node) { getAccessibleValue(node) || // `node.textContent` is only `null` if the `node` is a `document` or a // `doctype`. We don't consider either. - sanitizeString(node.textContent!) ); } @@ -141,36 +142,36 @@ function getTextSpokenPhrase({ } const relevantToSpokenPhraseMap = { - [Relevant.ADDITIONS]: getAdditionsSpokenPhrase, - [Relevant.ALL]: getAllSpokenPhrase, - [Relevant.REMOVALS]: getRemovalsSpokenPhrase, - [Relevant.TEXT]: getTextSpokenPhrase, + [RELEVANT.ADDITIONS]: getAdditionsSpokenPhrase, + [RELEVANT.ALL]: getAllSpokenPhrase, + [RELEVANT.REMOVALS]: getRemovalsSpokenPhrase, + [RELEVANT.TEXT]: getTextSpokenPhrase, }; const roleToImplicitLiveRegionStatesAndPropertiesMap: Record< string, - { atomic?: boolean; live: Live } + { atomic?: boolean; live: ValueOf } > = { alert: { atomic: true, - live: Live.ASSERTIVE, + live: LIVE.ASSERTIVE, }, log: { - live: Live.POLITE, + live: LIVE.POLITE, }, marquee: { - live: Live.OFF, + live: LIVE.OFF, }, status: { atomic: true, - live: Live.POLITE, + live: LIVE.POLITE, }, timer: { - live: Live.OFF, + live: LIVE.OFF, }, alertdialog: { atomic: true, - live: Live.ASSERTIVE, + live: LIVE.ASSERTIVE, }, }; @@ -189,11 +190,16 @@ function getLiveRegionAttributes( relevant, }: { atomic?: boolean; - live?: Live; + live?: ValueOf; liveTarget?: Element; - relevant?: Relevant[]; + relevant?: ValueOf[]; } = {} -): { atomic: boolean; live: Live; liveTarget?: Element; relevant: Relevant[] } { +): { + atomic: boolean; + live: ValueOf; + liveTarget?: Element; + relevant: ValueOf[]; +} { // TODO: it would be far better if worked with the accessibility tree rather // than reconstructing here and making assumptions (though probable) about // the allowed roles or inherited presentational roles. @@ -213,7 +219,7 @@ function getLiveRegionAttributes( } if (typeof live === "undefined" && target.hasAttribute("aria-live")) { - live = target.getAttribute("aria-live") as Live; + live = target.getAttribute("aria-live") as ValueOf; liveTarget = target; } @@ -234,11 +240,11 @@ function getLiveRegionAttributes( .getAttribute("aria-relevant")! .split(" ") .filter( - (token) => !!RELEVANT_VALUES.has(token as Relevant) - ) as Relevant[]; + (token) => !!RELEVANT_VALUES.has(token as ValueOf) + ) as ValueOf[]; - if (relevant.includes(Relevant.ALL)) { - relevant = [Relevant.ALL]; + if (relevant.includes(RELEVANT.ALL)) { + relevant = [RELEVANT.ALL]; } } @@ -289,7 +295,7 @@ export function getLiveSpokenPhrase({ target: getElementFromNode(target), }); - if (live === Live.OFF || !liveTarget) { + if (live === LIVE.OFF || !liveTarget) { return ""; } diff --git a/src/getNodeAccessibilityData/getAccessibleAttributeLabels/mapAttributeNameAndValueToLabel.ts b/src/getNodeAccessibilityData/getAccessibleAttributeLabels/mapAttributeNameAndValueToLabel.ts index 9a616f4..ba39720 100644 --- a/src/getNodeAccessibilityData/getAccessibleAttributeLabels/mapAttributeNameAndValueToLabel.ts +++ b/src/getNodeAccessibilityData/getAccessibleAttributeLabels/mapAttributeNameAndValueToLabel.ts @@ -3,22 +3,24 @@ import { getAccessibleValue } from "../getAccessibleValue"; import { getItemText } from "../../getItemText"; import { getNodeByIdRef } from "../../getNodeByIdRef"; -enum State { - BUSY = "busy", - CHECKED = "checked", - CURRENT = "current item", - DISABLED = "disabled", - EXPANDED = "expanded", - INVALID = "invalid", - MODAL = "modal", - MULTI_SELECTABLE = "multi-selectable", - PARTIALLY_CHECKED = "partially checked", - PARTIALLY_PRESSED = "partially pressed", - PRESSED = "pressed", - READ_ONLY = "read only", - REQUIRED = "required", - SELECTED = "selected", -} +type ValueOf = T[keyof T]; + +const STATE = { + BUSY: "busy", + CHECKED: "checked", + CURRENT: "current item", + DISABLED: "disabled", + EXPANDED: "expanded", + INVALID: "invalid", + MODAL: "modal", + MULTI_SELECTABLE: "multi-selectable", + PARTIALLY_CHECKED: "partially checked", + PARTIALLY_PRESSED: "partially pressed", + PRESSED: "pressed", + READ_ONLY: "read only", + REQUIRED: "required", + SELECTED: "selected", +}; // https://www.w3.org/TR/wai-aria-1.2/#state_prop_def const ariaPropertyToVirtualLabelMap: Record< @@ -35,8 +37,8 @@ const ariaPropertyToVirtualLabelMap: Record< }), "aria-braillelabel": null, // Currently won't do - not implementing a braille screen reader "aria-brailleroledescription": null, // Currently won't do - not implementing a braille screen reader - "aria-busy": state(State.BUSY), - "aria-checked": tristate(State.CHECKED, State.PARTIALLY_CHECKED), + "aria-busy": state(STATE.BUSY), + "aria-checked": tristate(STATE.CHECKED, STATE.PARTIALLY_CHECKED), "aria-colcount": integer("column count"), "aria-colindex": integer("column index"), "aria-colindextext": string("column index"), @@ -48,16 +50,16 @@ const ariaPropertyToVirtualLabelMap: Record< location: "current location", date: "current date", time: "current time", - true: State.CURRENT, - false: `not ${State.CURRENT}`, + true: STATE.CURRENT, + false: `not ${STATE.CURRENT}`, }), "aria-describedby": null, // Handled by accessible description "aria-description": null, // Handled by accessible description "aria-details": idRefs("linked details", "linked details", false), - "aria-disabled": state(State.DISABLED), + "aria-disabled": state(STATE.DISABLED), "aria-dropeffect": null, // Deprecated in WAI-ARIA 1.1 "aria-errormessage": errorMessageIdRefs("error message", "error messages"), - "aria-expanded": state(State.EXPANDED), + "aria-expanded": state(STATE.EXPANDED), "aria-flowto": idRefs("alternate reading order", "alternate reading orders"), // Handled by virtual.perform() "aria-grabbed": null, // Deprecated in WAI-ARIA 1.1 "aria-haspopup": token({ @@ -78,17 +80,17 @@ const ariaPropertyToVirtualLabelMap: Record< "aria-hidden": null, // Excluded from accessibility tree "aria-invalid": token({ grammar: "grammatical error detected", - false: `not ${State.INVALID}`, + false: `not ${STATE.INVALID}`, spelling: "spelling error detected", - true: State.INVALID, + true: STATE.INVALID, }), "aria-keyshortcuts": string("key shortcuts"), "aria-label": null, // Handled by accessible name "aria-labelledby": null, // Handled by accessible name "aria-level": integer("level"), "aria-live": null, // Handled by live region logic - "aria-modal": state(State.MODAL), - "aria-multiselectable": state(State.MULTI_SELECTABLE), + "aria-modal": state(STATE.MODAL), + "aria-multiselectable": state(STATE.MULTI_SELECTABLE), "aria-orientation": token({ horizontal: "orientated horizontally", vertical: "orientated vertically", @@ -96,16 +98,16 @@ const ariaPropertyToVirtualLabelMap: Record< "aria-owns": null, // Handled by accessibility tree construction "aria-placeholder": string("placeholder"), "aria-posinset": integer("position"), - "aria-pressed": tristate(State.PRESSED, State.PARTIALLY_PRESSED), - "aria-readonly": state(State.READ_ONLY), + "aria-pressed": tristate(STATE.PRESSED, STATE.PARTIALLY_PRESSED), + "aria-readonly": state(STATE.READ_ONLY), "aria-relevant": null, // Handled by live region logic - "aria-required": state(State.REQUIRED), + "aria-required": state(STATE.REQUIRED), "aria-roledescription": null, // Handled by accessible description "aria-rowcount": integer("row count"), "aria-rowindex": integer("row index"), "aria-rowindextext": string("row index"), "aria-rowspan": integer("row span"), - "aria-selected": state(State.SELECTED), + "aria-selected": state(STATE.SELECTED), "aria-setsize": integer("set size"), "aria-sort": token({ ascending: "sorted in ascending order", @@ -126,7 +128,7 @@ interface MapperArgs { node?: HTMLElement; } -function state(stateValue: State) { +function state(stateValue: ValueOf) { return function stateMapper({ attributeValue, negative }: MapperArgs) { if (negative) { return attributeValue !== "false" ? `not ${stateValue}` : stateValue; @@ -198,7 +200,10 @@ function idRef(propertyName: string) { }; } -function tristate(stateValue: State, mixedValue: State) { +function tristate( + stateValue: ValueOf, + mixedValue: ValueOf +) { return function stateMapper({ attributeValue }: MapperArgs) { if (attributeValue === "mixed") { return mixedValue; diff --git a/src/index.ts b/src/index.ts index 697248c..95a5eac 100644 --- a/src/index.ts +++ b/src/index.ts @@ -53,10 +53,8 @@ import { StartOptions, Virtual } from "./Virtual"; * "heading, Section Heading, level 1", * "Section Text", * "article", - * "banner", * "heading, Article Header Heading, level 1", * "Article Header Text", - * "end of banner", * "Article Text", * "end of article", * "end of region",