Add aria-label to all <Select /> instances

This commit is contained in:
Josh Perez 2022-04-07 17:40:57 -04:00 committed by GitHub
parent 9ffcd44e6d
commit b96b02593b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 112 additions and 17 deletions

View File

@ -6332,6 +6332,14 @@
"message": "Custom time",
"description": "Text for an option in Conversation Details Disappearing Messages setting when user previously selected custom time"
},
"DisappearingTimeDialog__label--value": {
"message": "Number",
"description": "aria-label for the number select box"
},
"DisappearingTimeDialog__label--units": {
"message": "Unit of time",
"description": "aria-label for the units of time select box"
},
"DisappearingTimeDialog__title": {
"message": "Custom Time",
"description": "Title for the custom disappearing message timeout dialog"

View File

@ -1,4 +1,4 @@
// Copyright 2021 Signal Messenger, LLC
// Copyright 2021-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useState } from 'react';
@ -91,12 +91,14 @@ export function DisappearingTimeDialog(props: PropsType): JSX.Element {
<p>{i18n('DisappearingTimeDialog__body')}</p>
<section className={`${CSS_MODULE}__time-boxes`}>
<Select
ariaLabel={i18n('DisappearingTimeDialog__label--value')}
moduleClassName={`${CSS_MODULE}__time-boxes__value`}
value={unitValue}
onChange={newValue => setUnitValue(parseInt(newValue, 10))}
options={values.map(value => ({ value, text: value.toString() }))}
/>
<Select
ariaLabel={i18n('DisappearingTimeDialog__label--units')}
moduleClassName={`${CSS_MODULE}__time-boxes__units`}
value={unit}
onChange={newUnit => {

View File

@ -1,4 +1,4 @@
// Copyright 2021 Signal Messenger, LLC
// Copyright 2021-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { ReactNode } from 'react';
@ -36,6 +36,7 @@ import {
format as formatExpirationTimer,
} from '../util/expirationTimer';
import { useEscapeHandling } from '../hooks/useEscapeHandling';
import { useUniqueId } from '../hooks/useUniqueId';
type CheckboxChangeHandlerType = (value: boolean) => unknown;
type SelectChangeHandlerType<T = string | number> = (value: T) => unknown;
@ -263,6 +264,9 @@ export const Preferences = ({
whoCanSeeMe,
zoomFactor,
}: PropsType): JSX.Element => {
const themeSelectId = useUniqueId();
const zoomSelectId = useUniqueId();
const [confirmDelete, setConfirmDelete] = useState(false);
const [page, setPage] = useState<Page>(Page.General);
const [showSyncFailed, setShowSyncFailed] = useState(false);
@ -410,9 +414,14 @@ export const Preferences = ({
</div>
<SettingsRow>
<Control
left={i18n('Preferences--theme')}
left={
<label htmlFor={themeSelectId}>
{i18n('Preferences--theme')}
</label>
}
right={
<Select
id={themeSelectId}
onChange={onThemeChange}
options={[
{
@ -449,9 +458,12 @@ export const Preferences = ({
}
/>
<Control
left={i18n('Preferences--zoom')}
left={
<label htmlFor={zoomSelectId}>{i18n('Preferences--zoom')}</label>
}
right={
<Select
id={zoomSelectId}
onChange={onZoomSelectChange}
options={zoomFactors}
value={zoomFactor}
@ -576,6 +588,7 @@ export const Preferences = ({
{i18n('callingDeviceSelection__label--video')}
</label>
<Select
ariaLabel={i18n('callingDeviceSelection__label--video')}
disabled={!availableCameras.length}
moduleClassName="Preferences__select"
name="video"
@ -611,6 +624,7 @@ export const Preferences = ({
{i18n('callingDeviceSelection__label--audio-input')}
</label>
<Select
ariaLabel={i18n('callingDeviceSelection__label--audio-input')}
disabled={!availableMicrophones.length}
moduleClassName="Preferences__select"
name="audio-input"
@ -646,6 +660,9 @@ export const Preferences = ({
{i18n('callingDeviceSelection__label--audio-output')}
</label>
<Select
ariaLabel={i18n(
'callingDeviceSelection__label--audio-output'
)}
disabled={!availableSpeakers.length}
moduleClassName="Preferences__select"
name="audio-output"
@ -738,6 +755,7 @@ export const Preferences = ({
left={i18n('Preferences--notification-content')}
right={
<Select
ariaLabel={i18n('Preferences--notification-content')}
disabled={!hasNotifications}
onChange={onNotificationContentChange}
options={[
@ -792,6 +810,7 @@ export const Preferences = ({
left={i18n('Preferences--see-me')}
right={
<Select
ariaLabel={i18n('Preferences--see-me')}
disabled
onChange={noop}
options={[
@ -816,6 +835,7 @@ export const Preferences = ({
left={i18n('Preferences--find-me')}
right={
<Select
ariaLabel={i18n('Preferences--find-me')}
disabled
onChange={noop}
options={[
@ -884,6 +904,7 @@ export const Preferences = ({
}
right={
<Select
ariaLabel={i18n('settings__DisappearingMessages__timer__label')}
onChange={value => {
if (
value === String(universalExpireTimer) ||

View File

@ -1,4 +1,4 @@
// Copyright 2021 Signal Messenger, LLC
// Copyright 2021-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { ChangeEvent } from 'react';
@ -12,7 +12,9 @@ export type Option = Readonly<{
}>;
export type PropsType = Readonly<{
ariaLabel?: string;
disabled?: boolean;
id?: string;
moduleClassName?: string;
name?: string;
options: ReadonlyArray<Option>;
@ -22,7 +24,16 @@ export type PropsType = Readonly<{
export const Select = React.forwardRef(
(
{ disabled, moduleClassName, name, onChange, options, value }: PropsType,
{
ariaLabel,
disabled,
id,
moduleClassName,
name,
onChange,
options,
value,
}: PropsType,
ref: React.Ref<HTMLSelectElement>
): JSX.Element => {
const onSelectChange = (event: ChangeEvent<HTMLSelectElement>) => {
@ -32,7 +43,9 @@ export const Select = React.forwardRef(
return (
<div className={classNames(['module-select', moduleClassName])}>
<select
aria-label={ariaLabel}
disabled={disabled}
id={id}
name={name}
value={value}
onChange={onSelectChange}

View File

@ -1,4 +1,4 @@
// Copyright 2021 Signal Messenger, LLC
// Copyright 2021-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { FunctionComponent } from 'react';
@ -13,6 +13,7 @@ import { Select } from '../../Select';
import { isMuted } from '../../../util/isMuted';
import { getMuteOptions } from '../../../util/getMuteOptions';
import { parseIntOrThrow } from '../../../util/parseIntOrThrow';
import { useUniqueId } from '../../../hooks/useUniqueId';
type PropsType = {
conversationType: ConversationTypeType;
@ -35,6 +36,8 @@ export const ConversationNotificationsSettings: FunctionComponent<
setMuteExpiration,
setDontNotifyForMentionsIfMuted,
}) => {
const muteNotificationsSelectId = useUniqueId();
const mentionsSelectId = useUniqueId();
const muteOptions = useMemo(
() => [
...(isMuted(muteExpiresAt)
@ -79,9 +82,18 @@ export const ConversationNotificationsSettings: FunctionComponent<
icon={IconType.mute}
/>
}
label={i18n('muteNotificationsTitle')}
label={
<label htmlFor={muteNotificationsSelectId}>
{i18n('muteNotificationsTitle')}
</label>
}
right={
<Select options={muteOptions} onChange={onMuteChange} value={-1} />
<Select
id={muteNotificationsSelectId}
options={muteOptions}
onChange={onMuteChange}
value={-1}
/>
}
/>
{conversationType === 'group' && (
@ -94,10 +106,15 @@ export const ConversationNotificationsSettings: FunctionComponent<
icon={IconType.mention}
/>
}
label={i18n('ConversationNotificationsSettings__mentions__label')}
label={
<label htmlFor={mentionsSelectId}>
{i18n('ConversationNotificationsSettings__mentions__label')}
</label>
}
info={i18n('ConversationNotificationsSettings__mentions__info')}
right={
<Select
id={mentionsSelectId}
options={[
{
text: i18n(

View File

@ -1,4 +1,4 @@
// Copyright 2020 Signal Messenger, LLC
// Copyright 2020-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
@ -12,6 +12,7 @@ import { PanelSection } from './PanelSection';
import { Select } from '../../Select';
import { useDelayedRestoreFocus } from '../../../hooks/useRestoreFocus';
import { useUniqueId } from '../../../hooks/useUniqueId';
const AccessControlEnum = Proto.AccessControl.AccessRequired;
@ -34,6 +35,9 @@ export const GroupLinkManagement: React.ComponentType<PropsType> = ({
isAdmin,
setAccessControlAddFromInviteLinkSetting,
}) => {
const groupLinkSelectId = useUniqueId();
const approveSelectId = useUniqueId();
if (conversation === undefined) {
throw new Error('GroupLinkManagement rendered without a conversation');
}
@ -61,10 +65,15 @@ export const GroupLinkManagement: React.ComponentType<PropsType> = ({
<PanelSection>
<PanelRow
info={groupLinkInfo}
label={i18n('ConversationDetails--group-link')}
label={
<label htmlFor={groupLinkSelectId}>
{i18n('ConversationDetails--group-link')}
</label>
}
right={
isAdmin ? (
<Select
id={groupLinkSelectId}
onChange={createEventHandler(changeHasGroupLink)}
options={[
{
@ -120,9 +129,14 @@ export const GroupLinkManagement: React.ComponentType<PropsType> = ({
<PanelSection>
<PanelRow
info={i18n('GroupLinkManagement--approve-info')}
label={i18n('GroupLinkManagement--approve-label')}
label={
<label htmlFor={approveSelectId}>
{i18n('GroupLinkManagement--approve-label')}
</label>
}
right={
<Select
id={approveSelectId}
onChange={createEventHandler(
setAccessControlAddFromInviteLinkSetting
)}

View File

@ -1,4 +1,4 @@
// Copyright 2020 Signal Messenger, LLC
// Copyright 2020-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
@ -11,6 +11,7 @@ import { SignalService as Proto } from '../../../protobuf';
import { PanelRow } from './PanelRow';
import { PanelSection } from './PanelSection';
import { Select } from '../../Select';
import { useUniqueId } from '../../../hooks/useUniqueId';
export type PropsType = {
conversation?: ConversationType;
@ -27,6 +28,10 @@ export const GroupV2Permissions = ({
setAccessControlMembersSetting,
setAnnouncementsOnly,
}: PropsType): JSX.Element => {
const addMembersSelectId = useUniqueId();
const groupInfoSelectId = useUniqueId();
const announcementSelectId = useUniqueId();
if (conversation === undefined) {
throw new Error('GroupV2Permissions rendered without a conversation');
}
@ -55,10 +60,15 @@ export const GroupV2Permissions = ({
return (
<PanelSection>
<PanelRow
label={i18n('ConversationDetails--add-members-label')}
label={
<label htmlFor={addMembersSelectId}>
{i18n('ConversationDetails--add-members-label')}
</label>
}
info={i18n('ConversationDetails--add-members-info')}
right={
<Select
id={addMembersSelectId}
onChange={updateAccessControlMembers}
options={accessControlOptions}
value={String(conversation.accessControlMembers)}
@ -66,10 +76,15 @@ export const GroupV2Permissions = ({
}
/>
<PanelRow
label={i18n('ConversationDetails--group-info-label')}
label={
<label htmlFor={groupInfoSelectId}>
{i18n('ConversationDetails--group-info-label')}
</label>
}
info={i18n('ConversationDetails--group-info-info')}
right={
<Select
id={groupInfoSelectId}
onChange={updateAccessControlAttributes}
options={accessControlOptions}
value={String(conversation.accessControlAttributes)}
@ -78,10 +93,15 @@ export const GroupV2Permissions = ({
/>
{showAnnouncementsOnlyPermission && (
<PanelRow
label={i18n('ConversationDetails--announcement-label')}
label={
<label htmlFor={announcementSelectId}>
{i18n('ConversationDetails--announcement-label')}
</label>
}
info={i18n('ConversationDetails--announcement-info')}
right={
<Select
id={announcementSelectId}
onChange={updateAnnouncementsOnly}
options={accessControlOptions}
value={announcementsOnlyValue}