Skip to content

Commit 227c6ef

Browse files
marker-daomarker dao ®
andauthored
SpeechToText: Support Accessibility (#31143)
Co-authored-by: marker dao ® <youdontknow@marker-dao.eth>
1 parent 1a887c5 commit 227c6ef

File tree

35 files changed

+401
-53
lines changed

35 files changed

+401
-53
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Properties } from 'devextreme/ui/speech_to_text.d';
2+
import url from '../../../helpers/getPageUrl';
3+
import { testAccessibility, Configuration } from '../../../helpers/accessibility/test';
4+
import { Options } from '../../../helpers/generateOptionMatrix';
5+
6+
fixture.disablePageReloads`Accessibility`
7+
.page(url(__dirname, '../../container.html'));
8+
9+
const options: Options<Properties> = {
10+
startText: ['', 'custom text'],
11+
stopIcon: ['', 'user'],
12+
};
13+
14+
const configuration: Configuration = {
15+
component: 'dxSpeechToText',
16+
options,
17+
};
18+
19+
testAccessibility(configuration);

packages/devextreme/js/__internal/ui/speech_to_text/speech_to_text.ts

Lines changed: 71 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1+
import messageLocalization from '@js/common/core/localization/message';
12
import registerComponent from '@js/core/component_registrator';
3+
import devices from '@js/core/devices';
4+
import type { DefaultOptionsRule } from '@js/core/options/utils';
25
import { noop } from '@js/core/utils/common';
36
import type { DxEvent } from '@js/events';
4-
import type { ClickEvent, Properties as ButtonProperties } from '@js/ui/button';
7+
import type { ClickEvent } from '@js/ui/button';
58
import Button from '@js/ui/button';
69
import type {
710
Properties as SpeechToTextProperties,
811
} from '@js/ui/speech_to_text';
12+
import { current, isMaterial } from '@js/ui/themes';
913
import { SpeechRecognitionAdapter } from '@ts/core/speech_recognition_adapter';
1014
import type { OptionChanged } from '@ts/core/widget/types';
1115
import Widget from '@ts/core/widget/widget';
16+
import type { ButtonProps as ButtonProperties } from '@ts/ui/button/button';
1217

1318
export const SPEECH_TO_TEXT_CLASS = 'dx-speech-to-text';
1419
export const SPEECH_TO_TEXT_LISTENING_CLASS = 'dx-speech-to-text-listening';
@@ -32,7 +37,11 @@ const ACTIONS: (keyof SpeechToTextActions)[] = [
3237
'onError',
3338
];
3439

35-
class SpeechToText extends Widget<SpeechToTextProperties> {
40+
type Properties = SpeechToTextProperties & {
41+
useInkRipple: boolean;
42+
};
43+
44+
class SpeechToText extends Widget<Properties> {
3645
private _button?: Button;
3746

3847
private _speechRecognitionAdapter?: SpeechRecognitionAdapter | null;
@@ -41,17 +50,20 @@ class SpeechToText extends Widget<SpeechToTextProperties> {
4150

4251
private _actions!: SpeechToTextActions;
4352

44-
_getDefaultOptions(): SpeechToTextProperties {
53+
_getDefaultOptions(): Properties {
4554
return {
4655
...super._getDefaultOptions(),
47-
startIcon: DEFAULT_INITIAL_ICON,
48-
stopIcon: DEFAULT_LISTENING_ICON,
49-
startText: '',
50-
stopText: '',
56+
activeStateEnabled: true,
5157
customSpeechRecognizer: {
5258
enabled: false,
5359
isListening: false,
5460
},
61+
hoverStateEnabled: true,
62+
startIcon: DEFAULT_INITIAL_ICON,
63+
stopIcon: DEFAULT_LISTENING_ICON,
64+
startText: '',
65+
stopText: '',
66+
useInkRipple: false,
5567
onStartClick: undefined,
5668
onStopClick: undefined,
5769
onResult: undefined,
@@ -106,6 +118,8 @@ class SpeechToText extends Widget<SpeechToTextProperties> {
106118
}) || noop;
107119
}
108120

121+
_attachFeedbackEvents(): void {}
122+
109123
private _renderButton(): void {
110124
this._button = this._createComponent<Button, ButtonProperties>(
111125
this.$element(),
@@ -117,26 +131,28 @@ class SpeechToText extends Widget<SpeechToTextProperties> {
117131
private _getButtonOptions(): ButtonProperties {
118132
const {
119133
activeStateEnabled,
134+
disabled,
120135
focusStateEnabled,
136+
height,
137+
hint,
121138
hoverStateEnabled,
122139
stylingMode,
123140
type,
124-
disabled,
141+
useInkRipple,
125142
width,
126-
height,
127-
hint,
128143
} = this.option();
129144

130145
return {
131-
stylingMode,
132-
type,
133-
disabled,
134-
width,
135-
height,
136146
activeStateEnabled,
147+
disabled,
137148
focusStateEnabled,
138-
hoverStateEnabled,
149+
height,
139150
hint,
151+
hoverStateEnabled,
152+
stylingMode,
153+
type,
154+
useInkRipple,
155+
width,
140156
icon: this._getCurrentIcon(),
141157
text: this._getCurrentText(),
142158
onClick: (e: ClickEvent): void => {
@@ -145,12 +161,41 @@ class SpeechToText extends Widget<SpeechToTextProperties> {
145161
};
146162
}
147163

164+
_defaultOptionsRules(): DefaultOptionsRule<Properties>[] {
165+
const rules = [
166+
...super._defaultOptionsRules(),
167+
{
168+
device: (): boolean => devices.real().deviceType === 'desktop' && !devices.isSimulator(),
169+
options: {
170+
focusStateEnabled: true,
171+
},
172+
}, {
173+
device: (): boolean => isMaterial(current()),
174+
options: {
175+
useInkRipple: true,
176+
},
177+
},
178+
];
179+
180+
return rules;
181+
}
182+
148183
private _getCurrentIcon(): string | undefined {
149184
const { startIcon, stopIcon } = this.option();
150185

151186
return this._isListening() ? stopIcon : startIcon;
152187
}
153188

189+
private _getCurrentAriaLabel(): string {
190+
return this._isListening()
191+
? messageLocalization.format('dxSpeechToText-ariaLabelStop')
192+
: messageLocalization.format('dxSpeechToText-ariaLabelStart');
193+
}
194+
195+
private _getCurrentAriaPressed(): boolean {
196+
return this._isListening();
197+
}
198+
154199
private _getCurrentText(): string {
155200
const { startText, stopText } = this.option();
156201

@@ -227,14 +272,18 @@ class SpeechToText extends Widget<SpeechToTextProperties> {
227272
this._button?.option({
228273
icon: this._getCurrentIcon(),
229274
text: this._getCurrentText(),
275+
elementAttr: {
276+
'aria-label': this._getCurrentAriaLabel(),
277+
'aria-pressed': this._getCurrentAriaPressed(),
278+
},
230279
});
231280
}
232281

233282
private _updateCssClasses(): void {
234283
this.$element().toggleClass(SPEECH_TO_TEXT_LISTENING_CLASS, this._isListening());
235284
}
236285

237-
_optionChanged(args: OptionChanged<SpeechToTextProperties>): void {
286+
_optionChanged(args: OptionChanged<Properties>): void {
238287
const { name, value } = args;
239288

240289
switch (name) {
@@ -246,11 +295,14 @@ class SpeechToText extends Widget<SpeechToTextProperties> {
246295
this._speechRecognitionAdapter?.applyConfig(value);
247296
break;
248297

298+
case 'activeStateEnabled':
299+
case 'focusStateEnabled':
300+
case 'height':
301+
case 'hint':
302+
case 'hoverStateEnabled':
249303
case 'stylingMode':
250304
case 'type':
251305
case 'width':
252-
case 'height':
253-
case 'hint':
254306
this._button?.option(name, value);
255307
break;
256308

packages/devextreme/js/localization/messages/ar.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,9 @@
851851
"dxSplitter-resizeHandleAriaLabel": "Split bar",
852852
"dxSplitter-resizeHandleAriaRoleDescription": "Separator",
853853

854-
"dxStepper-optionalMark": "(Optional)"
854+
"dxStepper-optionalMark": "(Optional)",
855+
856+
"dxSpeechToText-ariaLabelStart": "Press to start voice transcription",
857+
"dxSpeechToText-ariaLabelStop": "Press to stop voice transcription"
855858
}
856859
}

packages/devextreme/js/localization/messages/bg.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,9 @@
851851
"dxSplitter-resizeHandleAriaLabel": "Split bar",
852852
"dxSplitter-resizeHandleAriaRoleDescription": "Separator",
853853

854-
"dxStepper-optionalMark": "(Optional)"
854+
"dxStepper-optionalMark": "(Optional)",
855+
856+
"dxSpeechToText-ariaLabelStart": "Press to start voice transcription",
857+
"dxSpeechToText-ariaLabelStop": "Press to stop voice transcription"
855858
}
856859
}

packages/devextreme/js/localization/messages/ca.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,9 @@
851851
"dxSplitter-resizeHandleAriaLabel": "Split bar",
852852
"dxSplitter-resizeHandleAriaRoleDescription": "Separator",
853853

854-
"dxStepper-optionalMark": "(Optional)"
854+
"dxStepper-optionalMark": "(Optional)",
855+
856+
"dxSpeechToText-ariaLabelStart": "Press to start voice transcription",
857+
"dxSpeechToText-ariaLabelStop": "Press to stop voice transcription"
855858
}
856859
}

packages/devextreme/js/localization/messages/cs.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,9 @@
851851
"dxSplitter-resizeHandleAriaLabel": "Split bar",
852852
"dxSplitter-resizeHandleAriaRoleDescription": "Separator",
853853

854-
"dxStepper-optionalMark": "(Optional)"
854+
"dxStepper-optionalMark": "(Optional)",
855+
856+
"dxSpeechToText-ariaLabelStart": "Press to start voice transcription",
857+
"dxSpeechToText-ariaLabelStop": "Press to stop voice transcription"
855858
}
856859
}

packages/devextreme/js/localization/messages/da.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,9 @@
851851
"dxSplitter-resizeHandleAriaLabel": "Split bar",
852852
"dxSplitter-resizeHandleAriaRoleDescription": "Separator",
853853

854-
"dxStepper-optionalMark": "(Optional)"
854+
"dxStepper-optionalMark": "(Optional)",
855+
856+
"dxSpeechToText-ariaLabelStart": "Press to start voice transcription",
857+
"dxSpeechToText-ariaLabelStop": "Press to stop voice transcription"
855858
}
856859
}

packages/devextreme/js/localization/messages/de.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,9 @@
851851
"dxSplitter-resizeHandleAriaLabel": "Geteilter Balken",
852852
"dxSplitter-resizeHandleAriaRoleDescription": "Separator",
853853

854-
"dxStepper-optionalMark": "(Optional)"
854+
"dxStepper-optionalMark": "(Optional)",
855+
856+
"dxSpeechToText-ariaLabelStart": "Press to start voice transcription",
857+
"dxSpeechToText-ariaLabelStop": "Press to stop voice transcription"
855858
}
856859
}

packages/devextreme/js/localization/messages/el.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,9 @@
851851
"dxSplitter-resizeHandleAriaLabel": "Split bar",
852852
"dxSplitter-resizeHandleAriaRoleDescription": "Separator",
853853

854-
"dxStepper-optionalMark": "(Optional)"
854+
"dxStepper-optionalMark": "(Optional)",
855+
856+
"dxSpeechToText-ariaLabelStart": "Press to start voice transcription",
857+
"dxSpeechToText-ariaLabelStop": "Press to stop voice transcription"
855858
}
856859
}

packages/devextreme/js/localization/messages/en.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,9 @@
851851
"dxSplitter-resizeHandleAriaLabel": "Split bar",
852852
"dxSplitter-resizeHandleAriaRoleDescription": "Separator",
853853

854-
"dxStepper-optionalMark": "(Optional)"
854+
"dxStepper-optionalMark": "(Optional)",
855+
856+
"dxSpeechToText-ariaLabelStart": "Press to start voice transcription",
857+
"dxSpeechToText-ariaLabelStop": "Press to stop voice transcription"
855858
}
856859
}

0 commit comments

Comments
 (0)