Add aria-label to all <Select /> instances
This commit is contained in:
parent
9ffcd44e6d
commit
b96b02593b
7 changed files with 112 additions and 17 deletions
|
@ -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"
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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) ||
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
)}
|
||||
|
|
|
@ -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}
|
||||
|
|
Loading…
Reference in a new issue