mirror of
https://github.com/ant-design/ant-design.git
synced 2026-02-09 02:49:18 +08:00
fix(drawer): improve accessibility (aria-labelledby) (#55697)
Co-authored-by: 遇见同学 <1875694521@qq.com> Co-authored-by: thinkasany <480968828@qq.com>
This commit is contained in:
@@ -21,7 +21,7 @@ export interface DrawerStyles extends NonNullable<RCDrawerProps['styles']> {
|
||||
|
||||
export interface DrawerPanelProps {
|
||||
prefixCls: string;
|
||||
|
||||
ariaId?: string;
|
||||
title?: React.ReactNode;
|
||||
footer?: React.ReactNode;
|
||||
extra?: React.ReactNode;
|
||||
@@ -62,6 +62,7 @@ export interface DrawerPanelProps {
|
||||
const DrawerPanel: React.FC<DrawerPanelProps> = (props) => {
|
||||
const {
|
||||
prefixCls,
|
||||
ariaId,
|
||||
title,
|
||||
footer,
|
||||
extra,
|
||||
@@ -128,7 +129,11 @@ const DrawerPanel: React.FC<DrawerPanelProps> = (props) => {
|
||||
>
|
||||
<div className={`${prefixCls}-header-title`}>
|
||||
{closablePlacement === 'start' && mergedCloseIcon}
|
||||
{title && <div className={`${prefixCls}-title`}>{title}</div>}
|
||||
{title && (
|
||||
<div className={`${prefixCls}-title`} id={ariaId}>
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{extra && <div className={`${prefixCls}-extra`}>{extra}</div>}
|
||||
{closablePlacement === 'end' && mergedCloseIcon}
|
||||
|
||||
@@ -460,4 +460,25 @@ describe('Drawer', () => {
|
||||
// 添加快照断言
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should have aria-labelledby on drawer content when title is provided', () => {
|
||||
const { baseElement, rerender } = render(<Drawer open>Here is content of Drawer</Drawer>);
|
||||
const content = baseElement.querySelector('.ant-drawer-content');
|
||||
expect(content).not.toHaveAttribute('aria-labelledby');
|
||||
|
||||
rerender(
|
||||
<Drawer open title="Test Title">
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
const title = baseElement.querySelector('.ant-drawer-title');
|
||||
expect(content).toHaveAttribute('aria-labelledby', title?.getAttribute('id'));
|
||||
|
||||
rerender(
|
||||
<Drawer open title="Test Title" aria-labelledby="custom-id">
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
expect(content).toHaveAttribute('aria-labelledby', 'custom-id');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -379,6 +379,7 @@ exports[`Drawer have a title 1`] = `
|
||||
style="width: 378px;"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="test-id"
|
||||
aria-modal="true"
|
||||
class="ant-drawer-content"
|
||||
role="dialog"
|
||||
@@ -417,6 +418,7 @@ exports[`Drawer have a title 1`] = `
|
||||
</button>
|
||||
<div
|
||||
class="ant-drawer-title"
|
||||
id="test-id"
|
||||
>
|
||||
Test Title
|
||||
</div>
|
||||
|
||||
@@ -28,6 +28,7 @@ Array [
|
||||
style="width: 378px;"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="test-id"
|
||||
aria-modal="true"
|
||||
class="ant-drawer-content"
|
||||
role="dialog"
|
||||
@@ -66,6 +67,7 @@ Array [
|
||||
</button>
|
||||
<div
|
||||
class="ant-drawer-title"
|
||||
id="test-id"
|
||||
>
|
||||
Basic Drawer
|
||||
</div>
|
||||
@@ -146,6 +148,7 @@ Array [
|
||||
style="width: 378px;"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="test-id"
|
||||
aria-modal="true"
|
||||
class="ant-drawer-content acss-10412ne"
|
||||
role="dialog"
|
||||
@@ -186,6 +189,7 @@ Array [
|
||||
</button>
|
||||
<div
|
||||
class="ant-drawer-title"
|
||||
id="test-id"
|
||||
>
|
||||
Basic Drawer
|
||||
</div>
|
||||
@@ -238,6 +242,7 @@ Array [
|
||||
style="width: 378px;"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="test-id"
|
||||
aria-modal="true"
|
||||
class="ant-drawer-content acss-10412ne"
|
||||
role="dialog"
|
||||
@@ -278,6 +283,7 @@ Array [
|
||||
</button>
|
||||
<div
|
||||
class="ant-drawer-title"
|
||||
id="test-id"
|
||||
>
|
||||
Basic Drawer
|
||||
</div>
|
||||
@@ -345,6 +351,7 @@ Array [
|
||||
style="width: 378px;"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="test-id"
|
||||
aria-modal="true"
|
||||
class="ant-drawer-content"
|
||||
role="dialog"
|
||||
@@ -357,6 +364,7 @@ Array [
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-title"
|
||||
id="test-id"
|
||||
>
|
||||
Drawer Closable Placement
|
||||
</div>
|
||||
@@ -509,6 +517,7 @@ exports[`renders components/drawer/demo/config-provider.tsx extend context corre
|
||||
style="width: 378px;"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="test-id"
|
||||
aria-modal="true"
|
||||
class="ant-drawer-content"
|
||||
role="dialog"
|
||||
@@ -547,6 +556,7 @@ exports[`renders components/drawer/demo/config-provider.tsx extend context corre
|
||||
</button>
|
||||
<div
|
||||
class="ant-drawer-title"
|
||||
id="test-id"
|
||||
>
|
||||
ConfigProvider
|
||||
</div>
|
||||
@@ -712,6 +722,7 @@ Array [
|
||||
style="width: 500px;"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="test-id"
|
||||
aria-modal="true"
|
||||
class="ant-drawer-content"
|
||||
role="dialog"
|
||||
@@ -750,6 +761,7 @@ Array [
|
||||
</button>
|
||||
<div
|
||||
class="ant-drawer-title"
|
||||
id="test-id"
|
||||
>
|
||||
Drawer with extra actions
|
||||
</div>
|
||||
@@ -868,6 +880,7 @@ Array [
|
||||
style="width: 720px;"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="test-id"
|
||||
aria-modal="true"
|
||||
class="ant-drawer-content"
|
||||
role="dialog"
|
||||
@@ -906,6 +919,7 @@ Array [
|
||||
</button>
|
||||
<div
|
||||
class="ant-drawer-title"
|
||||
id="test-id"
|
||||
>
|
||||
Create a new account
|
||||
</div>
|
||||
@@ -2948,6 +2962,7 @@ Array [
|
||||
style="width: 378px;"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="test-id"
|
||||
aria-modal="true"
|
||||
class="ant-drawer-content"
|
||||
role="dialog"
|
||||
@@ -2986,6 +3001,7 @@ Array [
|
||||
</button>
|
||||
<div
|
||||
class="ant-drawer-title"
|
||||
id="test-id"
|
||||
>
|
||||
<p>
|
||||
Loading Drawer
|
||||
@@ -3058,6 +3074,7 @@ Array [
|
||||
style="width: 520px; transform: translateX(-180px);"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="test-id"
|
||||
aria-modal="true"
|
||||
class="ant-drawer-content"
|
||||
role="dialog"
|
||||
@@ -3070,6 +3087,7 @@ Array [
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-title"
|
||||
id="test-id"
|
||||
>
|
||||
Multi-level drawer
|
||||
</div>
|
||||
@@ -3105,6 +3123,7 @@ Array [
|
||||
style="width: 320px;"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="test-id"
|
||||
aria-modal="true"
|
||||
class="ant-drawer-content"
|
||||
role="dialog"
|
||||
@@ -3117,6 +3136,7 @@ Array [
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-title"
|
||||
id="test-id"
|
||||
>
|
||||
Two-level Drawer
|
||||
</div>
|
||||
@@ -3176,6 +3196,7 @@ Array [
|
||||
style="width: 378px;"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="test-id"
|
||||
aria-modal="true"
|
||||
class="ant-drawer-content"
|
||||
role="dialog"
|
||||
@@ -3214,6 +3235,7 @@ Array [
|
||||
</button>
|
||||
<div
|
||||
class="ant-drawer-title"
|
||||
id="test-id"
|
||||
>
|
||||
Drawer without mask
|
||||
</div>
|
||||
@@ -3379,6 +3401,7 @@ Array [
|
||||
style="width: 378px;"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="test-id"
|
||||
aria-modal="true"
|
||||
class="ant-drawer-content"
|
||||
role="dialog"
|
||||
@@ -3391,6 +3414,7 @@ Array [
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-title"
|
||||
id="test-id"
|
||||
>
|
||||
Basic Drawer
|
||||
</div>
|
||||
@@ -3458,6 +3482,7 @@ exports[`renders components/drawer/demo/render-in-current.tsx extend context cor
|
||||
style="width: 378px;"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="test-id"
|
||||
aria-modal="true"
|
||||
class="ant-drawer-content"
|
||||
role="dialog"
|
||||
@@ -3470,6 +3495,7 @@ exports[`renders components/drawer/demo/render-in-current.tsx extend context cor
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-title"
|
||||
id="test-id"
|
||||
>
|
||||
Basic Drawer
|
||||
</div>
|
||||
@@ -3697,6 +3723,7 @@ exports[`renders components/drawer/demo/scroll-debug.tsx extend context correctl
|
||||
style="width: 378px; transform: translateX(-180px);"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="test-id"
|
||||
aria-modal="true"
|
||||
class="ant-drawer-content"
|
||||
role="dialog"
|
||||
@@ -3735,6 +3762,7 @@ exports[`renders components/drawer/demo/scroll-debug.tsx extend context correctl
|
||||
</button>
|
||||
<div
|
||||
class="ant-drawer-title"
|
||||
id="test-id"
|
||||
>
|
||||
Drawer
|
||||
</div>
|
||||
@@ -3763,6 +3791,7 @@ exports[`renders components/drawer/demo/scroll-debug.tsx extend context correctl
|
||||
style="width: 378px;"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="test-id"
|
||||
aria-modal="true"
|
||||
class="ant-drawer-content"
|
||||
role="dialog"
|
||||
@@ -3801,6 +3830,7 @@ exports[`renders components/drawer/demo/scroll-debug.tsx extend context correctl
|
||||
</button>
|
||||
<div
|
||||
class="ant-drawer-title"
|
||||
id="test-id"
|
||||
>
|
||||
Drawer Sub
|
||||
</div>
|
||||
@@ -3848,6 +3878,7 @@ exports[`renders components/drawer/demo/scroll-debug.tsx extend context correctl
|
||||
style="width: 378px;"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="test-id"
|
||||
aria-modal="true"
|
||||
class="ant-drawer-content"
|
||||
role="dialog"
|
||||
@@ -3886,6 +3917,7 @@ exports[`renders components/drawer/demo/scroll-debug.tsx extend context correctl
|
||||
</button>
|
||||
<div
|
||||
class="ant-drawer-title"
|
||||
id="test-id"
|
||||
>
|
||||
Drawer2
|
||||
</div>
|
||||
@@ -3958,6 +3990,7 @@ Array [
|
||||
style="width: 378px;"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="test-id"
|
||||
aria-modal="true"
|
||||
class="ant-drawer-content"
|
||||
role="dialog"
|
||||
@@ -3996,6 +4029,7 @@ Array [
|
||||
</button>
|
||||
<div
|
||||
class="ant-drawer-title"
|
||||
id="test-id"
|
||||
>
|
||||
undefined Drawer
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { DrawerProps as RcDrawerProps } from 'rc-drawer';
|
||||
import RcDrawer from 'rc-drawer';
|
||||
import type { Placement } from 'rc-drawer/lib/Drawer';
|
||||
import type { CSSMotionProps } from 'rc-motion';
|
||||
import useId from 'rc-util/lib/hooks/useId';
|
||||
import { composeRef } from 'rc-util/lib/ref';
|
||||
|
||||
import ContextIsolator from '../_util/ContextIsolator';
|
||||
@@ -28,7 +29,7 @@ export interface PushState {
|
||||
// Drawer diff props: 'open' | 'motion' | 'maskMotion' | 'wrapperClassName'
|
||||
export interface DrawerProps
|
||||
extends Omit<RcDrawerProps, 'maskStyle' | 'destroyOnClose'>,
|
||||
Omit<DrawerPanelProps, 'prefixCls'> {
|
||||
Omit<DrawerPanelProps, 'prefixCls' | 'ariaId'> {
|
||||
size?: sizeType;
|
||||
|
||||
open?: boolean;
|
||||
@@ -70,6 +71,7 @@ const Drawer: React.FC<DrawerProps> & {
|
||||
panelRef = null,
|
||||
style,
|
||||
className,
|
||||
'aria-labelledby': ariaLabelledby,
|
||||
|
||||
// Deprecated
|
||||
visible,
|
||||
@@ -82,6 +84,9 @@ const Drawer: React.FC<DrawerProps> & {
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
let ariaId: string | undefined = useId();
|
||||
ariaId = rest.title ? ariaId : undefined;
|
||||
|
||||
const {
|
||||
getPopupContainer,
|
||||
getPrefixCls,
|
||||
@@ -221,10 +226,11 @@ const Drawer: React.FC<DrawerProps> & {
|
||||
afterOpenChange={afterOpenChange ?? afterVisibleChange}
|
||||
panelRef={mergedPanelRef}
|
||||
zIndex={zIndex}
|
||||
aria-labelledby={ariaLabelledby ?? ariaId}
|
||||
// TODO: In the future, destroyOnClose in rc-drawer needs to be upgrade to destroyOnHidden
|
||||
destroyOnClose={destroyOnHidden ?? destroyOnClose}
|
||||
>
|
||||
<DrawerPanel prefixCls={prefixCls} {...rest} onClose={onClose} />
|
||||
<DrawerPanel prefixCls={prefixCls} {...rest} ariaId={ariaId} onClose={onClose} />
|
||||
</RcDrawer>
|
||||
</zIndexContext.Provider>
|
||||
</ContextIsolator>,
|
||||
|
||||
Reference in New Issue
Block a user