mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-12 08:05:00 +03:00
fix(frontend): make the panel usable with screen readers (#6907)
* fix(frontend): make the panel usable with screen readers * fix(frontend): keep mobile tab names and the inbound expiry hint Review of #6907 found two gaps in the screen-reader pass: - On phone-width screens catTabLabel renders only the icon and names the tab by putting aria-label on it, but callers now pass that icon with aria-hidden for the desktop label, so every Settings, Xray and Host category tab lost its name under TalkBack. catTabLabel now clears aria-hidden on the icon it uses as the name. - DateTimePicker forwarded id and aria-label but not aria-describedby, so the inbound expiry hint ("leave blank to never expire") was never attached. It now forwards it on both the Gregorian and Jalali input.
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
import { PlusOutlined, WarningOutlined } from '@ant-design/icons';
|
||||
import { Button } from 'antd';
|
||||
|
||||
declare const rest: Record<string, unknown>;
|
||||
|
||||
export const Decorative = () => <Button icon={<PlusOutlined aria-hidden />}>Add</Button>;
|
||||
export const Named = () => <WarningOutlined aria-label="Disk almost full" />;
|
||||
export const LabelledBy = () => <WarningOutlined aria-labelledby="warning-text" />;
|
||||
export const Forwarded = () => <WarningOutlined {...rest} />;
|
||||
@@ -0,0 +1,8 @@
|
||||
import { PlusOutlined, WarningOutlined } from '@ant-design/icons';
|
||||
import { Button } from 'antd';
|
||||
|
||||
declare function CustomOutlined(): null;
|
||||
|
||||
export const InButton = () => <Button icon={<PlusOutlined />}>Add</Button>;
|
||||
export const Standalone = () => <WarningOutlined style={{ color: 'red' }} />;
|
||||
export const NotAnAntdIcon = () => <CustomOutlined />;
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"plugins": [],
|
||||
"categories": {},
|
||||
"jsPlugins": ["../icon-a11y-guard.mjs"],
|
||||
"rules": { "antd-icon/explicit-a11y": "error" }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"plugins": [],
|
||||
"categories": {},
|
||||
"jsPlugins": ["../control-label-guard.mjs"],
|
||||
"rules": { "control-label/has-name": "error" }
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Form, Input, Space, Switch } from 'antd';
|
||||
|
||||
declare function FormField(props: { label?: string; children?: unknown }): null;
|
||||
declare function SettingListItem(props: { title?: string; control?: unknown; children?: unknown }): null;
|
||||
declare const rest: Record<string, unknown>;
|
||||
|
||||
export const AriaLabel = () => <Switch aria-label="Enabled" />;
|
||||
export const LabelledBy = () => <Switch aria-labelledby="heading" />;
|
||||
export const Forwarded = () => <Input {...rest} />;
|
||||
export const Wrapped = () => (
|
||||
// eslint-disable-next-line jsx-a11y/label-has-associated-control -- antd Input is opaque to it
|
||||
<label>
|
||||
Email <Input />
|
||||
</label>
|
||||
);
|
||||
|
||||
export const HtmlForAndId = () => (
|
||||
<Form.Item label="Email" htmlFor="email">
|
||||
<Space.Compact>
|
||||
<Input id="email" />
|
||||
</Space.Compact>
|
||||
</Form.Item>
|
||||
);
|
||||
|
||||
export const NamedItemDirect = () => (
|
||||
<Form.Item label="Email" name="email">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
);
|
||||
|
||||
export const ViaFormField = () => (
|
||||
<FormField label="Email">
|
||||
<Input />
|
||||
</FormField>
|
||||
);
|
||||
|
||||
export const SettingChild = () => (
|
||||
<SettingListItem title="Enabled">
|
||||
<Switch />
|
||||
</SettingListItem>
|
||||
);
|
||||
|
||||
export const SettingControlProp = () => <SettingListItem title="Enabled" control={<Switch />} />;
|
||||
@@ -0,0 +1,39 @@
|
||||
import { Form, Input, Space, Switch } from 'antd';
|
||||
|
||||
declare function SettingListItem(props: { title?: string; children?: unknown }): null;
|
||||
|
||||
export const TableCell = () => <Switch checked />;
|
||||
|
||||
export const LabelOnlyItem = () => (
|
||||
<Form.Item label="Email">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
);
|
||||
|
||||
export const IdWithoutHtmlFor = () => (
|
||||
<Form.Item label="Email">
|
||||
<Input id="email" />
|
||||
</Form.Item>
|
||||
);
|
||||
|
||||
export const HtmlForWithoutId = () => (
|
||||
<Form.Item label="Email" htmlFor="email">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
);
|
||||
|
||||
export const NamedItemNested = () => (
|
||||
<Form.Item label="Email" name="email">
|
||||
<Space.Compact>
|
||||
<Input />
|
||||
</Space.Compact>
|
||||
</Form.Item>
|
||||
);
|
||||
|
||||
export const SettingNested = () => (
|
||||
<SettingListItem title="Enabled">
|
||||
<Space>
|
||||
<Switch />
|
||||
</Space>
|
||||
</SettingListItem>
|
||||
);
|
||||
Reference in New Issue
Block a user