mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-02 12:47:41 +02:00
Mobile: Accessibility: Improve setting control accessibility (#11358)
This commit is contained in:
parent
1e21fc242b
commit
4d5b1ce5fd
@ -23,6 +23,7 @@ interface DropdownProps {
|
|||||||
headerStyle?: TextStyle;
|
headerStyle?: TextStyle;
|
||||||
itemStyle?: TextStyle;
|
itemStyle?: TextStyle;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
accessibilityHint?: string;
|
||||||
|
|
||||||
labelTransform?: 'trim';
|
labelTransform?: 'trim';
|
||||||
items: DropdownListItem[];
|
items: DropdownListItem[];
|
||||||
@ -169,6 +170,7 @@ class Dropdown extends Component<DropdownProps, DropdownState> {
|
|||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={itemWrapperStyle}
|
style={itemWrapperStyle}
|
||||||
accessibilityRole="menuitem"
|
accessibilityRole="menuitem"
|
||||||
|
accessibilityState={{ selected: item.value === this.props.selectedValue }}
|
||||||
key={key}
|
key={key}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
this.onCloseList();
|
this.onCloseList();
|
||||||
@ -209,12 +211,19 @@ class Dropdown extends Component<DropdownProps, DropdownState> {
|
|||||||
style={headerWrapperStyle}
|
style={headerWrapperStyle}
|
||||||
disabled={this.props.disabled}
|
disabled={this.props.disabled}
|
||||||
onPress={this.onOpenList}
|
onPress={this.onOpenList}
|
||||||
role='button'
|
accessibilityRole='button'
|
||||||
|
accessibilityHint={[this.props.accessibilityHint, _('Opens dropdown')].join(' ')}
|
||||||
>
|
>
|
||||||
<Text ellipsizeMode="tail" numberOfLines={1} style={headerStyle}>
|
<Text ellipsizeMode="tail" numberOfLines={1} style={headerStyle}>
|
||||||
{headerLabel}
|
{headerLabel}
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={headerArrowStyle}>{'▼'}</Text>
|
<Text
|
||||||
|
style={headerArrowStyle}
|
||||||
|
aria-hidden={true}
|
||||||
|
importantForAccessibility='no'
|
||||||
|
accessibilityElementsHidden={true}
|
||||||
|
accessibilityRole='image'
|
||||||
|
>{'▼'}</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
{this.state.listVisible ? null : this.props.coverableChildrenRight}
|
{this.state.listVisible ? null : this.props.coverableChildrenRight}
|
||||||
</View>
|
</View>
|
||||||
@ -237,11 +246,13 @@ class Dropdown extends Component<DropdownProps, DropdownState> {
|
|||||||
|
|
||||||
<View
|
<View
|
||||||
accessibilityRole='menu'
|
accessibilityRole='menu'
|
||||||
style={wrapperStyle}>
|
style={wrapperStyle}
|
||||||
|
>
|
||||||
<FlatList
|
<FlatList
|
||||||
ref={this.onListLoad}
|
ref={this.onListLoad}
|
||||||
style={itemListStyle}
|
style={itemListStyle}
|
||||||
data={this.props.items}
|
data={this.props.items}
|
||||||
|
extraData={this.props.selectedValue}
|
||||||
renderItem={itemRenderer}
|
renderItem={itemRenderer}
|
||||||
getItemLayout={(_data, index) => ({
|
getItemLayout={(_data, index) => ({
|
||||||
length: itemHeight,
|
length: itemHeight,
|
||||||
|
@ -10,6 +10,7 @@ import SettingsToggle from './SettingsToggle';
|
|||||||
import FileSystemPathSelector from './FileSystemPathSelector';
|
import FileSystemPathSelector from './FileSystemPathSelector';
|
||||||
import shim from '@joplin/lib/shim';
|
import shim from '@joplin/lib/shim';
|
||||||
import { themeStyle } from '../../global-style';
|
import { themeStyle } from '../../global-style';
|
||||||
|
import { useId } from 'react';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
settingId: string;
|
settingId: string;
|
||||||
@ -39,16 +40,19 @@ const SettingComponent: React.FunctionComponent<Props> = props => {
|
|||||||
const descriptionComp = !settingDescription ? null : <Text style={styleSheet.settingDescriptionText}>{settingDescription}</Text>;
|
const descriptionComp = !settingDescription ? null : <Text style={styleSheet.settingDescriptionText}>{settingDescription}</Text>;
|
||||||
const containerStyle = props.styles.getContainerStyle(!!settingDescription);
|
const containerStyle = props.styles.getContainerStyle(!!settingDescription);
|
||||||
|
|
||||||
|
const labelId = useId();
|
||||||
|
|
||||||
if (md.isEnum) {
|
if (md.isEnum) {
|
||||||
const value = props.value?.toString();
|
const value = props.value?.toString();
|
||||||
|
|
||||||
const items = Setting.enumOptionsToValueLabels(md.options(), md.optionsOrder ? md.optionsOrder() : []);
|
const items = Setting.enumOptionsToValueLabels(md.options(), md.optionsOrder ? md.optionsOrder() : []);
|
||||||
|
const label = md.label();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View key={props.settingId} style={{ flexDirection: 'column', borderBottomWidth: 1, borderBottomColor: theme.dividerColor }}>
|
<View key={props.settingId} style={{ flexDirection: 'column', borderBottomWidth: 1, borderBottomColor: theme.dividerColor }}>
|
||||||
<View style={containerStyle}>
|
<View style={containerStyle}>
|
||||||
<Text key="label" style={styleSheet.settingText}>
|
<Text key="label" style={styleSheet.settingText}>
|
||||||
{md.label()}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
key="control"
|
key="control"
|
||||||
@ -69,6 +73,7 @@ const SettingComponent: React.FunctionComponent<Props> = props => {
|
|||||||
onValueChange={(itemValue: string) => {
|
onValueChange={(itemValue: string) => {
|
||||||
void props.updateSettingValue(props.settingId, itemValue);
|
void props.updateSettingValue(props.settingId, itemValue);
|
||||||
}}
|
}}
|
||||||
|
accessibilityHint={label}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
{descriptionComp}
|
{descriptionComp}
|
||||||
@ -90,6 +95,7 @@ const SettingComponent: React.FunctionComponent<Props> = props => {
|
|||||||
const unitLabel = md.unitLabel ? md.unitLabel(props.value) : props.value;
|
const unitLabel = md.unitLabel ? md.unitLabel(props.value) : props.value;
|
||||||
const minimum = 'minimum' in md ? md.minimum : 0;
|
const minimum = 'minimum' in md ? md.minimum : 0;
|
||||||
const maximum = 'maximum' in md ? md.maximum : 10;
|
const maximum = 'maximum' in md ? md.maximum : 10;
|
||||||
|
const label = md.label();
|
||||||
|
|
||||||
// Note: Do NOT add the minimumTrackTintColor and maximumTrackTintColor props
|
// Note: Do NOT add the minimumTrackTintColor and maximumTrackTintColor props
|
||||||
// on the Slider as they are buggy and can crash the app on certain devices.
|
// on the Slider as they are buggy and can crash the app on certain devices.
|
||||||
@ -97,8 +103,8 @@ const SettingComponent: React.FunctionComponent<Props> = props => {
|
|||||||
// https://github.com/react-native-community/react-native-slider/issues/161
|
// https://github.com/react-native-community/react-native-slider/issues/161
|
||||||
return (
|
return (
|
||||||
<View key={props.settingId} style={styleSheet.settingContainer}>
|
<View key={props.settingId} style={styleSheet.settingContainer}>
|
||||||
<Text key="label" style={styleSheet.settingText}>
|
<Text key="label" style={styleSheet.settingText} nativeID={labelId}>
|
||||||
{md.label()}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
<View style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', flex: 1 }}>
|
<View style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', flex: 1 }}>
|
||||||
<Text style={styleSheet.sliderUnits}>{unitLabel}</Text>
|
<Text style={styleSheet.sliderUnits}>{unitLabel}</Text>
|
||||||
@ -110,6 +116,7 @@ const SettingComponent: React.FunctionComponent<Props> = props => {
|
|||||||
maximumValue={maximum}
|
maximumValue={maximum}
|
||||||
value={props.value}
|
value={props.value}
|
||||||
onValueChange={newValue => void props.updateSettingValue(props.settingId, newValue)}
|
onValueChange={newValue => void props.updateSettingValue(props.settingId, newValue)}
|
||||||
|
accessibilityHint={label}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@ -129,7 +136,7 @@ const SettingComponent: React.FunctionComponent<Props> = props => {
|
|||||||
return (
|
return (
|
||||||
<View key={props.settingId} style={{ flexDirection: 'column', borderBottomWidth: 1, borderBottomColor: theme.dividerColor }}>
|
<View key={props.settingId} style={{ flexDirection: 'column', borderBottomWidth: 1, borderBottomColor: theme.dividerColor }}>
|
||||||
<View key={props.settingId} style={containerStyle}>
|
<View key={props.settingId} style={containerStyle}>
|
||||||
<Text key="label" style={styleSheet.settingText}>
|
<Text key="label" style={styleSheet.settingText} nativeID={labelId}>
|
||||||
{md.label()}
|
{md.label()}
|
||||||
</Text>
|
</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
@ -143,6 +150,7 @@ const SettingComponent: React.FunctionComponent<Props> = props => {
|
|||||||
value={props.value}
|
value={props.value}
|
||||||
onChangeText={(newValue: string) => void props.updateSettingValue(props.settingId, newValue)}
|
onChangeText={(newValue: string) => void props.updateSettingValue(props.settingId, newValue)}
|
||||||
secureTextEntry={!!md.secure}
|
secureTextEntry={!!md.secure}
|
||||||
|
aria-labelledby={labelId}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
{descriptionComp}
|
{descriptionComp}
|
||||||
|
@ -36,6 +36,7 @@ const SettingsToggle: FunctionComponent<Props> = props => {
|
|||||||
trackColor={{ false: theme.dividerColor }}
|
trackColor={{ false: theme.dividerColor }}
|
||||||
value={props.value}
|
value={props.value}
|
||||||
onValueChange={(value: boolean) => void props.updateSettingValue(props.settingId, value)}
|
onValueChange={(value: boolean) => void props.updateSettingValue(props.settingId, value)}
|
||||||
|
accessibilityHint={props.label}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
{props.description}
|
{props.description}
|
||||||
|
Loading…
Reference in New Issue
Block a user