Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(modal): support classNames #44934

Merged
merged 16 commits into from Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 7 additions & 1 deletion components/config-provider/context.ts
Expand Up @@ -5,6 +5,7 @@ import type { Options } from 'scroll-into-view-if-needed';
import type { WarningContextProps } from '../_util/warning';
import type { ShowWaveEffect } from '../_util/wave/interface';
import type { BadgeProps } from '../badge';
import type { ModalProps } from '../modal';
import type { ButtonProps } from '../button';
import type { FlexProps } from '../flex/interface';
import type { RequiredMark } from '../form/Form';
Expand Down Expand Up @@ -52,6 +53,11 @@ export interface ComponentStyleConfig {
style?: React.CSSProperties;
}

export interface ModalConfig extends ComponentStyleConfig {
classNames?: ModalProps['classNames'];
styles?: ModalProps['styles'];
}

export interface BadgeConfig extends ComponentStyleConfig {
classNames?: BadgeProps['classNames'];
styles?: BadgeProps['styles'];
Expand Down Expand Up @@ -132,7 +138,7 @@ export interface ConfigConsumerProps {
layout?: ComponentStyleConfig;
list?: ComponentStyleConfig;
mentions?: ComponentStyleConfig;
modal?: ComponentStyleConfig;
modal?: ModalConfig;
progress?: ComponentStyleConfig;
result?: ComponentStyleConfig;
slider?: ComponentStyleConfig;
Expand Down
2 changes: 1 addition & 1 deletion components/config-provider/index.en-US.md
Expand Up @@ -131,7 +131,7 @@ const {
| menu | Set Menu common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| mentions | Set Mentions common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| message | Set Message common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| modal | Set Modal common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| modal | Set Modal common props | { className?: string, style?: React.CSSProperties, classNames?: [ModalProps\["classNames"\]](/components/modal-cn#api), styles?: [ModalProps\["styles"\]](/components/modal-cn#api) } | - | 5.7.0, `classNames` and `styles`: 5.10.0 |
| notification | Set Notification common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| pagination | Set Pagination common props | { showSizeChanger?: boolean, className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| progress | Set Progress common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
Expand Down
6 changes: 3 additions & 3 deletions components/config-provider/index.tsx
Expand Up @@ -19,8 +19,7 @@ import LocaleContext from '../locale/context';
import defaultLocale from '../locale/en_US';
import type { SpaceProps } from '../space';
import type { TabsProps } from '../tabs';
import { defaultTheme } from '../theme/context';
import { DesignTokenContext } from '../theme/internal';
import { defaultTheme, DesignTokenContext } from '../theme/context';
import defaultSeedToken from '../theme/themes/seed';
import type {
BadgeConfig,
Expand All @@ -30,6 +29,7 @@ import type {
CSPConfig,
DirectionType,
FlexConfig,
ModalConfig,
PopupOverflow,
Theme,
ThemeConfig,
Expand Down Expand Up @@ -170,7 +170,7 @@ export interface ConfigProviderProps {
layout?: ComponentStyleConfig;
list?: ComponentStyleConfig;
mentions?: ComponentStyleConfig;
modal?: ComponentStyleConfig;
modal?: ModalConfig;
progress?: ComponentStyleConfig;
result?: ComponentStyleConfig;
slider?: ComponentStyleConfig;
Expand Down
2 changes: 1 addition & 1 deletion components/config-provider/index.zh-CN.md
Expand Up @@ -133,7 +133,7 @@ const {
| menu | 设置 Menu 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| mentions | 设置 Mentions 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| message | 设置 Message 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| modal | 设置 Modal 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| modal | 设置 Modal 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [ModalProps\["classNames"\]](/components/modal-cn#api), styles?: [ModalProps\["styles"\]](/components/modal-cn#api) } | - | 5.7.0, `classNames` 和 `styles`: 5.10.0 |
| notification | 设置 Notification 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| pagination | 设置 Pagination 组件的通用属性 | { showSizeChanger?: boolean, className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| progress | 设置 Progress 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
Expand Down
15 changes: 11 additions & 4 deletions components/modal/ConfirmDialog.tsx
Expand Up @@ -168,7 +168,6 @@ const ConfirmDialog: React.FC<ConfirmDialogProps> = (props) => {
close,
zIndex,
afterClose,
visible,
open,
keyboard,
centered,
Expand All @@ -191,7 +190,13 @@ const ConfirmDialog: React.FC<ConfirmDialogProps> = (props) => {
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning('Modal');

warning.deprecated(visible === undefined, 'visible', 'open');
[
['visible', 'open'],
['bodyStyle', 'styles.body'],
['maskStyle', 'styles.mask'],
].forEach(([deprecatedName, newName]) => {
warning.deprecated(!(deprecatedName in props), deprecatedName, newName);
});
}

const confirmPrefixCls = `${prefixCls}-confirm`;
Expand Down Expand Up @@ -238,9 +243,11 @@ const ConfirmDialog: React.FC<ConfirmDialogProps> = (props) => {
)}
mask={mask}
maskClosable={maskClosable}
maskStyle={maskStyle}
style={style}
bodyStyle={bodyStyle}
styles={{
body: bodyStyle,
mask: maskStyle,
}}
width={width}
zIndex={zIndex}
afterClose={afterClose}
Expand Down
20 changes: 18 additions & 2 deletions components/modal/Modal.tsx
Expand Up @@ -57,7 +57,13 @@ const Modal: React.FC<ModalProps> = (props) => {
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning('Modal');

warning.deprecated(!('visible' in props), 'visible', 'open');
[
['visible', 'open'],
['bodyStyle', 'styles.body'],
['maskStyle', 'styles.mask'],
].forEach(([deprecatedName, newName]) => {
warning.deprecated(!(deprecatedName in props), deprecatedName, newName);
});
}

const {
Expand All @@ -77,6 +83,8 @@ const Modal: React.FC<ModalProps> = (props) => {

width = 520,
footer,
classNames: modalClassNames,
styles: modalStyles,
...restProps
} = props;

Expand Down Expand Up @@ -115,7 +123,6 @@ const Modal: React.FC<ModalProps> = (props) => {
getContainer={getContainer === undefined ? getContextPopupContainer : getContainer}
prefixCls={prefixCls}
rootClassName={classNames(hashId, rootClassName)}
wrapClassName={wrapClassNameExtended}
footer={dialogFooter}
visible={open ?? visible}
mousePosition={restProps.mousePosition ?? mousePosition}
Expand All @@ -127,6 +134,15 @@ const Modal: React.FC<ModalProps> = (props) => {
maskTransitionName={getTransitionName(rootPrefixCls, 'fade', props.maskTransitionName)}
className={classNames(hashId, className, modal?.className)}
style={{ ...modal?.style, ...style }}
classNames={{
wrapper: wrapClassNameExtended,
...modal?.classNames,
...modalClassNames,
}}
styles={{
...modal?.styles,
...modalStyles,
}}
panelRef={panelRef}
/>
</NoFormStyle>
Expand Down
33 changes: 33 additions & 0 deletions components/modal/__tests__/__snapshots__/demo-extend.test.ts.snap
Expand Up @@ -39,6 +39,39 @@ exports[`renders components/modal/demo/button-props.tsx extend context correctly

exports[`renders components/modal/demo/button-props.tsx extend context correctly 2`] = `[]`;

exports[`renders components/modal/demo/classNames.tsx extend context correctly 1`] = `
<div
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"
>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Open Modal
</span>
</button>
</div>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
ConfigProvider
</span>
</button>
</div>
</div>
`;

exports[`renders components/modal/demo/classNames.tsx extend context correctly 2`] = `[]`;

exports[`renders components/modal/demo/component-token.tsx extend context correctly 1`] = `
<div
style="display: flex; flex-direction: column; row-gap: 16px;"
Expand Down
31 changes: 31 additions & 0 deletions components/modal/__tests__/__snapshots__/demo.test.tsx.snap
Expand Up @@ -33,6 +33,37 @@ exports[`renders components/modal/demo/button-props.tsx correctly 1`] = `
</button>
`;

exports[`renders components/modal/demo/classNames.tsx correctly 1`] = `
<div
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"
>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Open Modal
</span>
</button>
</div>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
ConfigProvider
</span>
</button>
</div>
</div>
`;

exports[`renders components/modal/demo/component-token.tsx correctly 1`] = `
<div
style="display:flex;flex-direction:column;row-gap:16px"
Expand Down
6 changes: 6 additions & 0 deletions components/modal/__tests__/confirm.test.tsx
Expand Up @@ -651,10 +651,16 @@ describe('Modal.confirm triggers callbacks correctly', () => {
});

it('bodyStyle', async () => {
resetWarned();
const spy = jest.spyOn(console, 'error').mockImplementation(() => {});
await open({ bodyStyle: { width: 500 } });

const { width } = $$('.ant-modal-body')[0].style;
expect(width).toBe('500px');
expect(spy).toHaveBeenCalledWith(
'Warning: [antd: Modal] `bodyStyle` is deprecated. Please use `styles.body` instead.',
);
spy.mockRestore();
});

describe('the callback close should be a method when onCancel has a close parameter', () => {
Expand Down
7 changes: 7 additions & 0 deletions components/modal/demo/classNames.md
@@ -0,0 +1,7 @@
## zh-CN

通过 `classNames` 属性设置弹窗内部区域(header、body、footer、mask、wrapper)的 `className`。

## en-US

Set the className of the build-in module (header, body, footer, mask, wrapper) of the modal through the classNames property.
110 changes: 110 additions & 0 deletions components/modal/demo/classNames.tsx
@@ -0,0 +1,110 @@
import React, { useState } from 'react';
import { Button, ConfigProvider, Modal, Space } from 'antd';
import { createStyles, useTheme } from 'antd-style';

const useStyle = createStyles(({ token }) => ({
'my-modal-body': {
background: token['blue-1'],
padding: token.paddingSM,
},
'my-modal-mask': {
boxShadow: `inset 0 0 15px #fff`,
},
'my-modal-header': {
borderBottom: `1px dotted ${token.colorPrimary}`,
},
'my-modal-footer': {
color: token.colorPrimary,
},
'my-modal-content': {
border: '1px solid #333',
},
}));

const App: React.FC = () => {
const [isModalOpen, setIsModalOpen] = useState([false, false]);
const { styles } = useStyle();
const token = useTheme();

const toggleModal = (idx: number, target: boolean) => {
setIsModalOpen((p) => {
p[idx] = target;
return [...p];
});
};

const classNames = {
body: styles['my-modal-body'],
mask: styles['my-modal-mask'],
header: styles['my-modal-header'],
footer: styles['my-modal-footer'],
content: styles['my-modal-content'],
};

const modalStyles = {
header: {
borderLeft: `5px solid ${token.colorPrimary}`,
borderRadius: 0,
paddingInlineStart: 5,
},
body: {
boxShadow: 'inset 0 0 5px #999',
borderRadius: 5,
},
mask: {
backdropFilter: 'blur(10px)',
},
footer: {
borderTop: '1px solid #333',
},
content: {
boxShadow: '0 0 30px #999',
},
};

return (
<>
<Space>
<Button type="primary" onClick={() => toggleModal(0, true)}>
Open Modal
</Button>
<Button type="primary" onClick={() => toggleModal(1, true)}>
ConfigProvider
</Button>
</Space>
<Modal
title="Basic Modal"
open={isModalOpen[0]}
onOk={() => toggleModal(0, false)}
onCancel={() => toggleModal(0, false)}
footer="Footer"
classNames={classNames}
styles={modalStyles}
>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</Modal>
<ConfigProvider
modal={{
classNames,
styles: modalStyles,
}}
>
<Modal
title="Basic Modal"
open={isModalOpen[1]}
onOk={() => toggleModal(1, false)}
onCancel={() => toggleModal(1, false)}
footer="Footer"
>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</Modal>
</ConfigProvider>
</>
);
};

export default App;