Skip to content

Commit 2c82605

Browse files
authored
fix(slidepane): add two types and close ways (DTStack#505)
* fix(slidepane): add two types and close ways * docs(drawer): change drawer docs
1 parent cebafe6 commit 2c82605

File tree

7 files changed

+160
-64
lines changed

7 files changed

+160
-64
lines changed

src/drawer/__tests/index.test.tsx

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { fireEvent, render } from '@testing-library/react';
33
import { alert } from 'ant-design-testing';
44
import '@testing-library/jest-dom/extend-expect';
55

6-
import Drawer from '../index';
6+
import Drawer, { DrawerType } from '../index';
77

88
describe('test Drawer ', () => {
99
test('snapshot match', () => {
@@ -27,18 +27,23 @@ describe('test Drawer ', () => {
2727
const domLoading = document.querySelector('.ant-spin-spinning');
2828
expect(domLoading).not.toBe(null);
2929
});
30-
test('should render mask correct ', () => {
30+
test('should render mask/maskClosable correct', () => {
3131
const { unmount } = render(<Drawer open>Hello World</Drawer>);
3232
const dom = document.querySelector('.dtc-drawer-mask');
3333
expect(dom).toBe(null);
3434
unmount();
35+
const fn = jest.fn();
3536
render(
36-
<Drawer open mask>
37+
<Drawer open mask maskClosable onClose={fn}>
3738
Hello World
3839
</Drawer>
3940
);
4041
const domMask = document.querySelector('.dtc-drawer-mask');
42+
const domIcon = document.querySelector('.dtc-drawer-header--icon');
4143
expect(domMask).not.toBe(null);
44+
expect(domIcon).toBe(null);
45+
fireEvent.click(domMask as Element);
46+
expect(fn).toHaveBeenCalledTimes(1);
4247
});
4348
test('should render width correct', () => {
4449
render(
@@ -171,4 +176,19 @@ describe('test Drawer ', () => {
171176
fireEvent.click(oImg);
172177
expect(fn).toHaveBeenCalledTimes(1);
173178
});
179+
180+
test('should render from type', () => {
181+
const fn = jest.fn();
182+
render(
183+
<Drawer open type={DrawerType.Form} title="title" onClose={fn}>
184+
Hello World
185+
</Drawer>
186+
);
187+
const mask = document.querySelector('.dtc-drawer-mask');
188+
expect(mask).not.toBe(null);
189+
const domIcon = document.querySelector('.dtc-drawer-header--icon');
190+
expect(domIcon).not.toBe(null);
191+
fireEvent.click(mask as Element);
192+
expect(fn).toHaveBeenCalledTimes(0);
193+
});
174194
});

src/drawer/demos/basic_mask.tsx

Lines changed: 0 additions & 36 deletions
This file was deleted.

src/drawer/demos/basic_two.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import React, { useState } from 'react';
2+
import { Button } from 'antd';
3+
import { Drawer } from 'dt-react-component';
4+
import { DrawerType } from 'dt-react-component/drawer';
5+
6+
export default () => {
7+
const [firstVisible, setFirstVisible] = useState(false);
8+
const [secondVisible, setSecondVisible] = useState(false);
9+
10+
return (
11+
<>
12+
<Button
13+
style={{ margin: '10px' }}
14+
onClick={() => {
15+
setFirstVisible(true);
16+
}}
17+
>
18+
click me
19+
</Button>
20+
<Drawer open={firstVisible} onClose={() => setFirstVisible(false)} title="一级弹窗">
21+
<div>一级弹窗</div>
22+
<Button onClick={() => setSecondVisible(true)}>打开二级弹窗</Button>
23+
<Drawer
24+
open={secondVisible}
25+
onClose={() => setSecondVisible(false)}
26+
title="二级弹窗"
27+
type={DrawerType.Form}
28+
>
29+
<div>一级弹窗</div>
30+
</Drawer>
31+
</Drawer>
32+
</>
33+
);
34+
};

src/drawer/demos/basic_type.tsx

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import React, { useState } from 'react';
2+
import { Button, Space, Switch } from 'antd';
3+
import { Drawer } from 'dt-react-component';
4+
import { DrawerType } from 'dt-react-component/drawer';
5+
6+
export default () => {
7+
const [visible, setVisible] = useState(false);
8+
const [mask, setMask] = useState(false);
9+
const [maskClosable, setMaskClosable] = useState(false);
10+
const [type, setType] = useState(DrawerType.Normal);
11+
12+
return (
13+
<>
14+
<Space>
15+
<Button
16+
type="primary"
17+
onClick={() => {
18+
setVisible(true);
19+
setType(DrawerType.Form);
20+
}}
21+
>
22+
Form 类型
23+
</Button>
24+
<Button
25+
type="primary"
26+
onClick={() => {
27+
setVisible(true);
28+
setType(DrawerType.Normal);
29+
}}
30+
>
31+
正常类型
32+
</Button>
33+
</Space>
34+
<Space>
35+
mask:
36+
<Switch onChange={(checked) => setMask(checked)} />
37+
maskClosable:
38+
<Switch onChange={(checked) => setMaskClosable(checked)} />
39+
</Space>
40+
<Drawer
41+
open={visible}
42+
onClose={() => setVisible(false)}
43+
title="title"
44+
mask={mask}
45+
maskClosable={maskClosable}
46+
type={type}
47+
>
48+
<div>hello world</div>
49+
</Drawer>
50+
</>
51+
);
52+
};

src/drawer/index.md

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ demo:
1616
## 示例
1717

1818
<code src="./demos/basic.tsx" title="基础使用"></code>
19-
<code src="./demos/basic_mask.tsx" title="基础 mask 使用"></code>
19+
<code src="./demos/basic_type.tsx" title="两种类型的 SlidePane" description="表单类型默认展示`mask`且不可点击关闭,且不可配置;正常类型默认不展示`mask`,`mask | maskClosable`可以配置"></code>
2020
<code src="./demos/basicSize.tsx" title="尺寸"></code>
2121
<code src="./demos/basic_top.tsx" title="抽屉距顶部高度"></code>
2222
<code src="./demos/customTitle.tsx" title="自定义 Title"></code>
@@ -25,26 +25,28 @@ demo:
2525
<code src="./demos/footer.tsx" title="展示 footer"></code>
2626
<code src="./demos/basicBanner.tsx" title="支持 banner"></code>
2727
<code src="./demos/basicBannerProps.tsx" title="支持传 banner 的 Props 属性"></code>
28+
<code src="./demos/basic_two.tsx" title="二级弹窗"></code>
2829

2930
## API
3031

3132
### AlertProps
3233

3334
[AlertProps](https://4x-ant-design.antgroup.com/components/alert-cn/#API)
3435

35-
| 参数 | 说明 | 类型 | 默认值 |
36-
| ------------- | ---------------------------------- | ----------------------------------------------------- | --------- |
37-
| activeKey | 右侧面板的内容的 Tabs 的选中项 | `string` | - |
38-
| banner | 提示 | `React.ReactNode \| AlertProps` | - |
39-
| bodyClassName | 内容容器的类名 | `string` | - |
40-
| bodyStyle | 内容容器的样式 | `CSSProperties` | - |
41-
| children | 右侧面板展示内容 | `(key: string) => React.ReactNode \| React.ReactNode` | - |
42-
| defaultKey | 右侧面板的内容的 Tabs 的默认选中项 | `string` | - |
43-
| footer | 右侧面板的底部内容 | `React.ReactNode` | - |
44-
| size | 尺寸 | `small \| default \| large` | `default` |
45-
| tabs | 右侧面板的内容的 Tabs | `{ key: string; title: React.ReactNode }[]` | - |
46-
| title | 右侧面板的 title | `React.ReactNode` | - |
47-
| onChange | 右侧面板的 Tabs 切换回调 | `(key: string) => void` | - |
36+
| 参数 | 说明 | 类型 | 默认值 |
37+
| ------------- | ---------------------------------- | ----------------------------------------------------- | ---------------------- |
38+
| activeKey | 右侧面板的内容的 Tabs 的选中项 | `string` | - |
39+
| banner | 提示 | `React.ReactNode \| AlertProps` | - |
40+
| bodyClassName | 内容容器的类名 | `string` | - |
41+
| bodyStyle | 内容容器的样式 | `CSSProperties` | - |
42+
| children | 右侧面板展示内容 | `(key: string) => React.ReactNode \| React.ReactNode` | - |
43+
| defaultKey | 右侧面板的内容的 Tabs 的默认选中项 | `string` | - |
44+
| footer | 右侧面板的底部内容 | `React.ReactNode` | - |
45+
| size | 尺寸 | `small \| default \| large` | `default` |
46+
| tabs | 右侧面板的内容的 Tabs | `{ key: string; title: React.ReactNode }[]` | - |
47+
| title | 右侧面板的 title | `React.ReactNode` | - |
48+
| type | 右侧面板的类型 | `SlidePaneType.Form \| SlidePaneType.Normal` | `SlidePaneType.Normal` |
49+
| onChange | 右侧面板的 Tabs 切换回调 | `(key: string) => void` | - |
4850

4951
:::info
5052
其余属性继承 [antd4.x 的 Drawer](https://4x.ant.design/components/drawer-cn/#API)

src/drawer/index.tsx

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { CSSProperties, useEffect, useState } from 'react';
2+
import { CloseOutlined } from '@ant-design/icons';
23
import { Alert, AlertProps, Spin, Tabs } from 'antd';
34
import classNames from 'classnames';
45
import { omit } from 'lodash';
@@ -16,16 +17,20 @@ type readOnlyTab = readonly Tab[];
1617

1718
type TabKey<T extends readOnlyTab> = T[number]['key'];
1819

20+
export enum DrawerType {
21+
Form = 'form',
22+
Normal = 'normal',
23+
}
24+
1925
interface NormalDrawerProps extends Omit<AntdDrawerProps, 'placement'> {
20-
/** @deprecated */
21-
visible?: boolean;
2226
size?: 'small' | 'default' | 'large';
2327
loading?: boolean;
2428
bodyClassName?: string;
2529
title?: React.ReactNode;
2630
bodyStyle?: CSSProperties;
2731
footer?: React.ReactNode;
2832
banner?: AlertProps['message'] | Omit<AlertProps, 'banner'>;
33+
type?: DrawerType;
2934
}
3035

3136
interface TabsDrawerProps<T extends readOnlyTab> extends Omit<NormalDrawerProps, 'children'> {
@@ -65,31 +70,32 @@ const Drawer = <T extends readOnlyTab>(props: DrawerProps<T>) => {
6570
const slidePrefixCls = 'dtc-drawer';
6671

6772
const {
68-
visible,
6973
open,
7074
loading = false,
7175
bodyClassName,
7276
mask = false,
77+
maskClosable = true,
7378
bodyStyle,
7479
title,
7580
width,
81+
type = DrawerType.Normal,
7682
size = 'default',
7783
footer,
7884
banner,
7985
onClose,
8086
...rest
8187
} = props;
8288

83-
const composeOpen = open || visible;
8489
const finalWidth = width ?? getWidthFromSize(size);
90+
const isFormType = type === DrawerType.Form;
8591

8692
const [internalTabKey, setInternalTabKey] = useState('');
8793

8894
useEffect(() => {
89-
composeOpen &&
95+
open &&
9096
isTabMode(props) &&
9197
setInternalTabKey(props.defaultKey ?? props.tabs?.[0]?.key ?? '');
92-
}, [composeOpen]);
98+
}, [open]);
9399

94100
const currentKey = isControlled(props) ? props.activeKey : internalTabKey;
95101

@@ -111,21 +117,32 @@ const Drawer = <T extends readOnlyTab>(props: DrawerProps<T>) => {
111117

112118
return (
113119
<RcDrawer
114-
open={composeOpen}
120+
open={open}
115121
placement="right"
116-
mask={mask}
122+
mask={isFormType ? true : mask}
123+
maskClosable={isFormType ? false : maskClosable}
117124
width={finalWidth}
118125
prefixCls={slidePrefixCls}
119126
onClose={onClose}
120127
{...rest}
121128
{...motionProps}
122129
>
123-
{!mask && renderButton()}
130+
{!isFormType && renderButton()}
124131
<Spin wrapperClassName={`${slidePrefixCls}-nested-loading`} spinning={loading}>
125-
{title && <div className={`${slidePrefixCls}-header`}>{title}</div>}
132+
{title && (
133+
<div className={`${slidePrefixCls}-header`}>
134+
{title}
135+
{isFormType && (
136+
<CloseOutlined
137+
className={`${slidePrefixCls}-header--icon`}
138+
onClick={onClose}
139+
/>
140+
)}
141+
</div>
142+
)}
126143
{banner && (
127144
<Alert
128-
message={isValidBanner(banner) ? banner : banner.message}
145+
message={isValidBanner(banner) ? banner : (banner as any).message}
129146
banner
130147
{...(isValidBanner(banner) ? {} : omit(banner, 'message'))}
131148
/>

src/drawer/style.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,19 @@ $prefix: "dtc-drawer";
4040
pointer-events: auto;
4141
}
4242
&-header {
43+
display: flex;
44+
align-items: center;
45+
justify-content: space-between;
4346
padding: 16px;
4447
background-color: #F9F9FA;
4548
color: #3D446E;
4649
font-weight: 500;
4750
font-size: 14px;
4851
line-height: 22px;
52+
&--icon {
53+
color: #64698B;
54+
font-size: 16px;
55+
}
4956
}
5057
&-tabs {
5158
&.ant-tabs {

0 commit comments

Comments
 (0)