diff --git a/__snapshots__/divider/patternhub/divider-properties-should-match-screenshot.png b/__snapshots__/divider/patternhub/divider-properties-should-match-screenshot.png index 9e9b7a5eec8..458e3e1a7ee 100644 Binary files a/__snapshots__/divider/patternhub/divider-properties-should-match-screenshot.png and b/__snapshots__/divider/patternhub/divider-properties-should-match-screenshot.png differ diff --git a/package-lock.json b/package-lock.json index d48de3a8ab5..1d9d885df2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11376,11 +11376,11 @@ } }, "node_modules/@types/node": { - "version": "22.2.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.2.0.tgz", - "integrity": "sha512-bm6EG6/pCpkxDf/0gDNDdtDILMOHgaQBVOJGdwsqClnxA3xL6jtMv76rLBc006RVMWbmaf0xbmom4Z/5o2nRkQ==", + "version": "22.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.3.0.tgz", + "integrity": "sha512-nrWpWVaDZuaVc5X84xJ0vNrLvomM205oQyLsRt7OHNZbSHslcWsvgFR7O7hire2ZonjLrWBbedmotmIlJDVd6g==", "dependencies": { - "undici-types": "~6.13.0" + "undici-types": "~6.18.2" } }, "node_modules/@types/node-forge": { @@ -34705,9 +34705,9 @@ } }, "node_modules/undici-types": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", - "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==" + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.18.2.tgz", + "integrity": "sha512-5ruQbENj95yDYJNS3TvcaxPMshV7aizdv/hWYjGIKoANWKjhWNBsr2YEuYZKodQulB1b8l7ILOuDQep3afowQQ==" }, "node_modules/unenv": { "version": "1.10.0", @@ -38054,7 +38054,7 @@ "@angular/cli": "17.3.8", "@angular/compiler-cli": "17.3.12", "@types/express": "^4.17.21", - "@types/node": "^22.2.0", + "@types/node": "^22.3.0", "cpr": "3.0.1", "ng-packagr": "17.3.0", "typescript": "^5.4.5" @@ -38067,7 +38067,7 @@ "react-dom": "18.3.1" }, "devDependencies": { - "@types/node": "^22.2.0", + "@types/node": "^22.3.0", "@types/react": "^18.3.1", "npm-run-all": "^4.1.5", "open-cli": "^8.0.0", @@ -38103,7 +38103,7 @@ "@mdx-js/react": "^3.0.1", "@next/mdx": "^14.2.5", "@types/dompurify": "3.0.5", - "@types/node": "22.2.0", + "@types/node": "22.3.0", "@types/react": "18.3.3", "@types/react-dom": "18.3.0", "esbuild": "0.23.0", diff --git a/packages/components/src/components/checkbox/checkbox.lite.tsx b/packages/components/src/components/checkbox/checkbox.lite.tsx index 2990ddb59db..98b658e44b2 100644 --- a/packages/components/src/components/checkbox/checkbox.lite.tsx +++ b/packages/components/src/components/checkbox/checkbox.lite.tsx @@ -7,7 +7,7 @@ import { useStore } from '@builder.io/mitosis'; import { DBCheckboxProps, DBCheckboxState } from './model'; -import { cls, uuid } from '../../utils'; +import { cls, delay, hasVoiceOver, uuid } from '../../utils'; import { DEFAULT_INVALID_MESSAGE, DEFAULT_INVALID_MESSAGE_ID_SUFFIX, @@ -33,6 +33,7 @@ export default function DBCheckbox(props: DBCheckboxProps) { _validMessageId: this._id + DEFAULT_VALID_MESSAGE_ID_SUFFIX, _invalidMessageId: this._id + DEFAULT_INVALID_MESSAGE_ID_SUFFIX, _descByIds: '', + _voiceOverFallback: '', handleChange: (event: ChangeEvent) => { if (props.onChange) { props.onChange(event); @@ -46,11 +47,23 @@ export default function DBCheckbox(props: DBCheckboxProps) { /* For a11y reasons we need to map the correct message with the checkbox */ if (!ref?.validity.valid || props.customValidity === 'invalid') { state._descByIds = state._invalidMessageId; + if (hasVoiceOver()) { + state._voiceOverFallback = + props.invalidMessage ?? + ref?.validationMessage ?? + DEFAULT_INVALID_MESSAGE; + delay(() => (state._voiceOverFallback = ''), 1000); + } } else if ( props.customValidity === 'valid' || (ref?.validity.valid && props.required) ) { state._descByIds = state._validMessageId; + if (hasVoiceOver()) { + state._voiceOverFallback = + props.validMessage ?? DEFAULT_VALID_MESSAGE; + delay(() => (state._voiceOverFallback = ''), 1000); + } } else if (props.message) { state._descByIds = state._messageId; } else { @@ -179,6 +192,13 @@ export default function DBCheckbox(props: DBCheckboxProps) { ref?.validationMessage ?? DEFAULT_INVALID_MESSAGE} + + {/* * https://www.davidmacd.com/blog/test-aria-describedby-errormessage-aria-live.html + * Currently VoiceOver isn't supporting changes from aria-describedby. + * This is an internal Fallback */} + + {state._voiceOverFallback} + ); } diff --git a/packages/components/src/components/divider/divider.scss b/packages/components/src/components/divider/divider.scss index d22af4704d5..fdf3d726d14 100644 --- a/packages/components/src/components/divider/divider.scss +++ b/packages/components/src/components/divider/divider.scss @@ -2,10 +2,22 @@ @use "@db-ui/foundations/build/scss/helpers"; .db-divider { - &:not([data-margin="none"]) { + &:is(:not([data-margin]), [data-margin="small"]) { margin: variables.$db-spacing-fixed-sm 0; } + &[data-margin="medium"] { + margin: variables.$db-spacing-fixed-md 0; + } + + &[data-margin="large"] { + margin: variables.$db-spacing-fixed-lg 0; + } + + &[data-margin="none"] { + margin: 0; + } + &:not([data-variant="vertical"]) { @include helpers.divider(); @@ -21,10 +33,22 @@ inline-size: variables.$db-border-height-3xs; - &:not([data-margin="none"]) { + &:is(:not([data-margin]), [data-margin="small"]) { margin: 0 variables.$db-spacing-fixed-sm; } + &[data-margin="medium"] { + margin: 0 variables.$db-spacing-fixed-md; + } + + &[data-margin="large"] { + margin: 0 variables.$db-spacing-fixed-lg; + } + + &[data-margin="none"] { + margin: 0; + } + &[data-width="full"] { block-size: 100%; } diff --git a/packages/components/src/components/divider/model.ts b/packages/components/src/components/divider/model.ts index e0a832d9a6c..9aa5768824b 100644 --- a/packages/components/src/components/divider/model.ts +++ b/packages/components/src/components/divider/model.ts @@ -2,7 +2,8 @@ import { EmphasisProps, GlobalProps, GlobalState, - WidthProps + WidthProps, + MarginProps } from '../../shared/model'; export const DividerMarginList = ['none', '_'] as const; @@ -19,6 +20,7 @@ export type DBDividerDefaultProps = { export type DBDividerProps = DBDividerDefaultProps & GlobalProps & EmphasisProps & + MarginProps & WidthProps; export type DBDividerDefaultState = {}; diff --git a/packages/components/src/components/drawer/drawer.lite.tsx b/packages/components/src/components/drawer/drawer.lite.tsx index 0b3bab71b2d..ce6b302b0d1 100644 --- a/packages/components/src/components/drawer/drawer.lite.tsx +++ b/packages/components/src/components/drawer/drawer.lite.tsx @@ -10,7 +10,7 @@ import { import { DBDrawerProps, DBDrawerState } from './model'; import { DBButton } from '../button'; import { DEFAULT_CLOSE_BUTTON } from '../../shared/constants'; -import { cls } from '../../utils'; +import { cls, delay } from '../../utils'; useMetadata({ isAttachedToShadowDom: true @@ -54,7 +54,7 @@ export default function DBDrawer(props: DBDrawerProps) { if (dialogContainerRef) { dialogContainerRef.hidden = true; } - setTimeout(() => { + delay(() => { if (dialogContainerRef) { dialogContainerRef.hidden = false; } diff --git a/packages/components/src/components/input/input.lite.tsx b/packages/components/src/components/input/input.lite.tsx index 6f4918fdd29..494adde355c 100644 --- a/packages/components/src/components/input/input.lite.tsx +++ b/packages/components/src/components/input/input.lite.tsx @@ -7,7 +7,7 @@ import { useRef, useStore } from '@builder.io/mitosis'; -import { cls, isArrayOfStrings, uuid } from '../../utils'; +import { cls, delay, hasVoiceOver, isArrayOfStrings, uuid } from '../../utils'; import { DBInputProps, DBInputState } from './model'; import { DEFAULT_DATALIST_ID_SUFFIX, @@ -42,6 +42,7 @@ export default function DBInput(props: DBInputProps) { _dataListId: this._id + DEFAULT_DATALIST_ID_SUFFIX, _descByIds: '', _value: '', + _voiceOverFallback: '', defaultValues: { label: DEFAULT_LABEL, placeholder: ' ' @@ -69,6 +70,13 @@ export default function DBInput(props: DBInputProps) { /* For a11y reasons we need to map the correct message with the input */ if (!ref?.validity.valid || props.customValidity === 'invalid') { state._descByIds = state._invalidMessageId; + if (hasVoiceOver()) { + state._voiceOverFallback = + props.invalidMessage ?? + ref?.validationMessage ?? + DEFAULT_INVALID_MESSAGE; + delay(() => (state._voiceOverFallback = ''), 1000); + } } else if ( props.customValidity === 'valid' || (ref?.validity.valid && @@ -78,6 +86,11 @@ export default function DBInput(props: DBInputProps) { props.pattern)) ) { state._descByIds = state._validMessageId; + if (hasVoiceOver()) { + state._voiceOverFallback = + props.validMessage ?? DEFAULT_VALID_MESSAGE; + delay(() => (state._voiceOverFallback = ''), 1000); + } } else if (props.message) { state._descByIds = state._messageId; } else { @@ -230,6 +243,13 @@ export default function DBInput(props: DBInputProps) { ref?.validationMessage ?? DEFAULT_INVALID_MESSAGE} + + {/* * https://www.davidmacd.com/blog/test-aria-describedby-errormessage-aria-live.html + * Currently VoiceOver isn't supporting changes from aria-describedby. + * This is an internal Fallback */} + + {state._voiceOverFallback} + ); // jscpd:ignore-end diff --git a/packages/components/src/components/select/select.lite.tsx b/packages/components/src/components/select/select.lite.tsx index 6adb6394be2..78939a0ec37 100644 --- a/packages/components/src/components/select/select.lite.tsx +++ b/packages/components/src/components/select/select.lite.tsx @@ -8,7 +8,7 @@ import { useStore } from '@builder.io/mitosis'; import { DBSelectOptionType, DBSelectProps, DBSelectState } from './model'; -import { cls, uuid } from '../../utils'; +import { cls, delay, hasVoiceOver, uuid } from '../../utils'; import { DEFAULT_INVALID_MESSAGE, DEFAULT_INVALID_MESSAGE_ID_SUFFIX, @@ -47,6 +47,7 @@ export default function DBSelect(props: DBSelectProps) { _descByIds: '', _value: '', initialized: false, + _voiceOverFallback: '', handleClick: (event: ClickEvent) => { if (props.onClick) { props.onClick(event); @@ -75,11 +76,23 @@ export default function DBSelect(props: DBSelectProps) { /* For a11y reasons we need to map the correct message with the select */ if (!ref?.validity.valid || props.customValidity === 'invalid') { state._descByIds = state._invalidMessageId; + if (hasVoiceOver()) { + state._voiceOverFallback = + props.invalidMessage ?? + ref?.validationMessage ?? + DEFAULT_INVALID_MESSAGE; + delay(() => (state._voiceOverFallback = ''), 1000); + } } else if ( props.customValidity === 'valid' || (ref?.validity.valid && props.required) ) { state._descByIds = state._validMessageId; + if (hasVoiceOver()) { + state._voiceOverFallback = + props.validMessage ?? DEFAULT_VALID_MESSAGE; + delay(() => (state._voiceOverFallback = ''), 1000); + } } else if (props.message) { state._descByIds = state._messageId; } else { @@ -156,7 +169,7 @@ export default function DBSelect(props: DBSelectProps) { name={props.name} value={props.value ?? state._value} autocomplete={props.autocomplete} - onInput={(event: ChangeEvent) => + onInput={(event: ChangeEvent) => state.handleInput(event) } onClick={(event: ClickEvent) => @@ -243,6 +256,13 @@ export default function DBSelect(props: DBSelectProps) { ref?.validationMessage ?? DEFAULT_INVALID_MESSAGE} + + {/* * https://www.davidmacd.com/blog/test-aria-describedby-errormessage-aria-live.html + * Currently VoiceOver isn't supporting changes from aria-describedby. + * This is an internal Fallback */} + + {state._voiceOverFallback} + ); // jscpd:ignore-end diff --git a/packages/components/src/components/textarea/textarea.lite.tsx b/packages/components/src/components/textarea/textarea.lite.tsx index fd0dd74032d..fbf639c965a 100644 --- a/packages/components/src/components/textarea/textarea.lite.tsx +++ b/packages/components/src/components/textarea/textarea.lite.tsx @@ -8,7 +8,7 @@ import { } from '@builder.io/mitosis'; import { DBTextareaProps, DBTextareaState } from './model'; import { DBInfotext } from '../infotext'; -import { cls, uuid } from '../../utils'; +import { cls, delay, hasVoiceOver, uuid } from '../../utils'; import { DEFAULT_INVALID_MESSAGE, DEFAULT_INVALID_MESSAGE_ID_SUFFIX, @@ -40,6 +40,7 @@ export default function DBTextarea(props: DBTextareaProps) { placeholder: ' ', rows: '4' }, + _voiceOverFallback: '', handleInput: (event: InputEvent) => { if (props.onInput) { props.onInput(event); @@ -63,12 +64,24 @@ export default function DBTextarea(props: DBTextareaProps) { /* For a11y reasons we need to map the correct message with the textarea */ if (!ref?.validity.valid || props.customValidity === 'invalid') { state._descByIds = state._invalidMessageId; + if (hasVoiceOver()) { + state._voiceOverFallback = + props.invalidMessage ?? + ref?.validationMessage ?? + DEFAULT_INVALID_MESSAGE; + delay(() => (state._voiceOverFallback = ''), 1000); + } } else if ( props.customValidity === 'valid' || (ref?.validity.valid && (props.required || props.minLength || props.maxLength)) ) { state._descByIds = state._validMessageId; + if (hasVoiceOver()) { + state._voiceOverFallback = + props.validMessage ?? DEFAULT_VALID_MESSAGE; + delay(() => (state._voiceOverFallback = ''), 1000); + } } else if (props.message) { state._descByIds = state._messageId; } else { @@ -143,7 +156,7 @@ export default function DBTextarea(props: DBTextareaProps) { wrap={props.wrap} spellcheck={props.spellCheck} autocomplete={props.autocomplete} - onInput={(event: ChangeEvent) => + onInput={(event: ChangeEvent) => state.handleInput(event) } onChange={(event: ChangeEvent) => @@ -188,6 +201,13 @@ export default function DBTextarea(props: DBTextareaProps) { ref?.validationMessage ?? DEFAULT_INVALID_MESSAGE} + + {/* * https://www.davidmacd.com/blog/test-aria-describedby-errormessage-aria-live.html + * Currently VoiceOver isn't supporting changes from aria-describedby. + * This is an internal Fallback */} + + {state._voiceOverFallback} + ); // jscpd:ignore-end diff --git a/packages/components/src/shared/model.ts b/packages/components/src/shared/model.ts index 06d9dfaa316..07b83290fa4 100644 --- a/packages/components/src/shared/model.ts +++ b/packages/components/src/shared/model.ts @@ -74,6 +74,15 @@ export type SpacingProps = { */ spacing?: SpacingType; }; +export const MarginList = ['medium', 'small', 'large', 'none'] as const; +export type MarginType = (typeof MarginList)[number]; + +export type MarginProps = { + /** + * The margin attribute changes the margin of the component. + */ + margin?: MarginType; +}; export const PlacementList = [ 'left', @@ -355,6 +364,13 @@ export type FormState = { _invalidMessageId?: string; _descByIds?: string; _value?: string; + + /** + * https://www.davidmacd.com/blog/test-aria-describedby-errormessage-aria-live.html + * Currently VoiceOver isn't supporting changes from aria-describedby. + * This is an internal Fallback + */ + _voiceOverFallback?: string; }; export type InitializedState = { diff --git a/packages/components/src/styles/_form-components.scss b/packages/components/src/styles/_form-components.scss index d0ffa9b2db7..39a73547a53 100644 --- a/packages/components/src/styles/_form-components.scss +++ b/packages/components/src/styles/_form-components.scss @@ -7,6 +7,8 @@ @use "@db-ui/foundations/build/scss/helpers"; @use "component"; +@forward "visually-hidden"; + $dropdown-icon-transition: transform variables.$db-transition-straight-emotional; $dropdown-icon-transform: rotate(-180deg); diff --git a/packages/components/src/styles/visually-hidden.scss b/packages/components/src/styles/visually-hidden.scss new file mode 100644 index 00000000000..6a175b4c45b --- /dev/null +++ b/packages/components/src/styles/visually-hidden.scss @@ -0,0 +1,6 @@ +@use "@db-ui/foundations/build/scss/helpers"; + +.db-visually-hidden, +[data-visually-hidden="true"] { + @extend %a11y-visually-hidden; +} diff --git a/packages/components/src/utils/index.ts b/packages/components/src/utils/index.ts index 20195b4387b..f18e961eb65 100644 --- a/packages/components/src/utils/index.ts +++ b/packages/components/src/utils/index.ts @@ -194,6 +194,14 @@ export const handleDataOutside = (el: Element): DBDataOutsidePair => { export const isArrayOfStrings = (value: unknown): value is string[] => Array.isArray(value) && value.every((item) => typeof item === 'string'); +const appleOs = ['Mac', 'iPhone', 'iPad', 'iPod']; +export const hasVoiceOver = (): boolean => + typeof window !== 'undefined' && + appleOs.some((os) => window.navigator.userAgent.includes(os)); + +export const delay = (fn: () => void, ms: number) => + new Promise(() => setTimeout(fn, ms)); + export default { filterPassingProps, cls, @@ -203,5 +211,7 @@ export default { visibleInVY, isInView, handleDataOutside, - isArrayOfStrings + isArrayOfStrings, + hasVoiceOver, + delay }; diff --git a/packages/foundations/scss/helpers/_a11y.scss b/packages/foundations/scss/helpers/_a11y.scss index f382bbbf126..e7ff6b4d5ac 100644 --- a/packages/foundations/scss/helpers/_a11y.scss +++ b/packages/foundations/scss/helpers/_a11y.scss @@ -1,8 +1,17 @@ %a11y-visually-hidden { - clip: rect(0, 0, 0, 0); - block-size: 1px; - overflow: hidden; + clip: rect(0, 0, 0, 0) !important; + overflow: hidden !important; + white-space: nowrap !important; + font-size: 0 !important; + all: initial; + inset-block-start: 0 !important; + block-size: 1px !important; position: absolute !important; - white-space: nowrap; - inline-size: 1px; + inline-size: 1px !important; + border-width: 0 !important; + border-style: initial !important; + border-color: initial !important; + border-image: initial !important; + padding: 0 !important; + pointer-events: none !important; } diff --git a/showcases/angular-ssr-showcase/package.json b/showcases/angular-ssr-showcase/package.json index 93056230a47..c2c663c981b 100644 --- a/showcases/angular-ssr-showcase/package.json +++ b/showcases/angular-ssr-showcase/package.json @@ -32,7 +32,7 @@ "@angular/cli": "17.3.8", "@angular/compiler-cli": "17.3.12", "@types/express": "^4.17.21", - "@types/node": "^22.2.0", + "@types/node": "^22.3.0", "cpr": "3.0.1", "ng-packagr": "17.3.0", "typescript": "^5.4.5" diff --git a/showcases/next-showcase/package.json b/showcases/next-showcase/package.json index 84f23cf4c72..cc3dbc2bb88 100644 --- a/showcases/next-showcase/package.json +++ b/showcases/next-showcase/package.json @@ -17,7 +17,7 @@ "react-dom": "18.3.1" }, "devDependencies": { - "@types/node": "^22.2.0", + "@types/node": "^22.3.0", "@types/react": "^18.3.1", "npm-run-all": "^4.1.5", "open-cli": "^8.0.0", diff --git a/showcases/patternhub/package.json b/showcases/patternhub/package.json index f00ca07ab84..e389fe10a85 100644 --- a/showcases/patternhub/package.json +++ b/showcases/patternhub/package.json @@ -37,7 +37,7 @@ "@mdx-js/react": "^3.0.1", "@next/mdx": "^14.2.5", "@types/dompurify": "3.0.5", - "@types/node": "22.2.0", + "@types/node": "22.3.0", "@types/react": "18.3.3", "@types/react-dom": "18.3.0", "esbuild": "0.23.0", diff --git a/showcases/screen-reader/__snapshots__/macos/webkit/DBInput-required-1.txt b/showcases/screen-reader/__snapshots__/macos/webkit/DBInput-required-1.txt new file mode 100644 index 00000000000..28a6a206d61 --- /dev/null +++ b/showcases/screen-reader/__snapshots__/macos/webkit/DBInput-required-1.txt @@ -0,0 +1 @@ +["Label * Label* Required required edit text","TODO: Add a validMessage. Test","Test selected","Test. Fill out this field","TODO: Add a validMessage. Test"] \ No newline at end of file diff --git a/showcases/screen-reader/__snapshots__/windows/chromium/DBInput-required-1.txt b/showcases/screen-reader/__snapshots__/windows/chromium/DBInput-required-1.txt new file mode 100644 index 00000000000..72bb6bae613 --- /dev/null +++ b/showcases/screen-reader/__snapshots__/windows/chromium/DBInput-required-1.txt @@ -0,0 +1 @@ +["Label star, edit, required, Required, blank","T. e. s. t. TODO: Add a valid Message","Test selected","blank. Please fill out this field.. unselected","T. e. s. t. TODO: Add a valid Message"] \ No newline at end of file diff --git a/showcases/screen-reader/default.ts b/showcases/screen-reader/default.ts index 9c2a3348e88..51b0c3c1c98 100644 --- a/showcases/screen-reader/default.ts +++ b/showcases/screen-reader/default.ts @@ -13,20 +13,7 @@ import { type RunTestType, type ScreenReaderTestType } from './data'; - -const translations: Record = { - button: ['Schalter'], - edit: ['Eingabefeld'], - 'radio button': ['Auswahlschalter'], - blank: ['Leer'], - checked: ['aktiviert'], - ' of ': [' von '], - clickable: ['anklickbar'], - 'has auto complete': ['mit Auto Vervollständigung'], - unknown: ['Unbekannt'], - dialog: ['Dialogfeld'], - document: ['Dokument'] -}; +import { translations } from './translations'; const standardPhrases = [ 'You are currently', diff --git a/showcases/screen-reader/tests/input.spec.ts b/showcases/screen-reader/tests/input.spec.ts index 2adfcb07547..0c29cbccfd7 100644 --- a/showcases/screen-reader/tests/input.spec.ts +++ b/showcases/screen-reader/tests/input.spec.ts @@ -1,5 +1,5 @@ import { NVDAKeyCodeCommands } from '@guidepup/guidepup'; -import { getTest, testDefault } from '../default'; +import { generateSnapshot, getTest, testDefault } from '../default'; const test = getTest(); test.describe('DBInput', () => { @@ -22,7 +22,6 @@ test.describe('DBInput', () => { await voiceOver?.next(); } }); - // We don't test default "next" here because we will be locked inside the textarea testDefault({ test, title: 'tab', @@ -40,4 +39,41 @@ test.describe('DBInput', () => { await nvda?.press('Tab'); } }); + testDefault({ + test, + title: 'required', + description: 'should inform user for changes', + url: './#/03/input?page=requirement', + async testFn(voiceOver, nvda) { + if (voiceOver) { + /* Goto desired input */ + await voiceOver?.next(); + await voiceOver?.next(); + await voiceOver?.clearSpokenPhraseLog(); + await voiceOver?.next(); + await voiceOver?.type('Test'); + await voiceOver?.press('Command+A'); + await voiceOver?.press('Delete'); + await voiceOver?.type('Test'); + } else { + await nvda?.press('Tab'); + await nvda?.type('Test'); + await nvda?.press('Control+A'); + await nvda?.press('Delete'); + await nvda?.type('Test'); + } + }, + async postTestFn(voiceOver, nvda, retry) { + if (nvda) { + await generateSnapshot(nvda, retry); + } else if (voiceOver) { + /* + * There is a timing issue for macOS for typing in input we clean the result + */ + await generateSnapshot(nvda, retry, (phraseLog) => + phraseLog.map((log) => log.replace('t. ', '')) + ); + } + } + }); }); diff --git a/showcases/screen-reader/translations.ts b/showcases/screen-reader/translations.ts new file mode 100644 index 00000000000..4c3ab2b5c0e --- /dev/null +++ b/showcases/screen-reader/translations.ts @@ -0,0 +1,19 @@ +export const translations: Record = { + star: ['Stern'], + button: ['Schalter'], + edit: ['Eingabefeld'], + 'radio button': ['Auswahlschalter'], + blank: ['Leer'], + checked: ['aktiviert'], + ' of ': [' von '], + clickable: ['anklickbar'], + 'has auto complete': ['mit Auto Vervollständigung'], + required: ['erforderlich'], + 'Please fill out this field..': ['Fülle dieses Feld aus..'], + unselected: ['nicht ausgewählt'], + selected: ['ausgewählt'], + '': ['. Nummernblock eingeschaltet'], + unknown: ['Unbekannt'], + dialog: ['Dialogfeld'], + document: ['Dokument'] +};