feat: meta添加app、notification组件
This commit is contained in:
parent
43741393f7
commit
e03338ab02
4
global.d.ts
vendored
4
global.d.ts
vendored
@ -1,4 +0,0 @@
|
||||
declare module '@zhst/func';
|
||||
declare module '@zhst/hooks';
|
||||
declare module '@zhst/meta';
|
||||
declare module '@zhst/request';
|
@ -38,6 +38,7 @@
|
||||
"@types/zhst": "workspace:^"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^5.2.6",
|
||||
"@zhst/func": "workspace:^",
|
||||
"@zhst/hooks": "workspace:^",
|
||||
"@zhst/meta": "workspace:^",
|
||||
|
@ -16,7 +16,11 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
||||
import React, { useEffect, useState, useCallback, useRef, useImperativeHandle } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { useLatest } from '@zhst/hooks';
|
||||
import { get, pick, isNull, generateImg, dataURLToBlob,
|
||||
import { get, pick, isNull,
|
||||
// @ts-ignore
|
||||
generateImg,
|
||||
// @ts-ignore
|
||||
dataURLToBlob,
|
||||
// @ts-ignore
|
||||
getOdRect,
|
||||
// @ts-ignore
|
||||
@ -24,7 +28,9 @@ getExtendRect,
|
||||
// @ts-ignore
|
||||
getTransformRect,
|
||||
// @ts-ignore
|
||||
getRotateImg, getTransforms, addEventListenerWrapper, getFileByRect
|
||||
getRotateImg, getTransforms, addEventListenerWrapper,
|
||||
// @ts-ignore
|
||||
getFileByRect
|
||||
// @ts-ignore
|
||||
} from '@zhst/func';
|
||||
import Align from 'rc-align';
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Button, Tooltip } from 'antd';
|
||||
// @ts-ignore
|
||||
import { Button, Tooltip } from "../../..";
|
||||
import Icon from "../../../iconfont";
|
||||
import "./index.less";
|
||||
var componentName = "zhst-image__btn-group";
|
||||
|
@ -14,7 +14,9 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
|
||||
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
||||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
||||
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
||||
import { noop, get, addEventListenerWrapper, dataURLToBlob, nextTick, toRealNumber, getTransforms, formatDurationTime
|
||||
import { noop, get, addEventListenerWrapper,
|
||||
// @ts-ignore
|
||||
dataURLToBlob, nextTick, toRealNumber, getTransforms, formatDurationTime
|
||||
// @ts-ignore
|
||||
} from '@zhst/func';
|
||||
import Align from 'rc-align';
|
||||
|
@ -41,4 +41,6 @@ export { default as Card } from "./card";
|
||||
export { default as Skeleton } from "./skeleton";
|
||||
export { default as Tooltip } from "./tooltip";
|
||||
export { default as Tour } from "./tour";
|
||||
export { default as Segmented } from "./segmented";
|
||||
export { default as Segmented } from "./segmented";
|
||||
export { default as App } from "./app";
|
||||
export { default as notification } from "./notification";
|
@ -5,7 +5,9 @@ import {
|
||||
get,
|
||||
pick,
|
||||
isNull,
|
||||
// @ts-ignore
|
||||
generateImg,
|
||||
// @ts-ignore
|
||||
dataURLToBlob,
|
||||
// @ts-ignore
|
||||
getOdRect,
|
||||
@ -17,6 +19,7 @@ import {
|
||||
getRotateImg,
|
||||
getTransforms,
|
||||
addEventListenerWrapper,
|
||||
// @ts-ignore
|
||||
getFileByRect
|
||||
// @ts-ignore
|
||||
} from '@zhst/func';
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React, { MouseEvent } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Button, Tooltip, TooltipProps } from 'antd';
|
||||
// @ts-ignore
|
||||
import { Button, Tooltip, TooltipProps } from '../../..';
|
||||
import Icon from '../../../iconfont';
|
||||
import './index.less';
|
||||
|
||||
@ -38,7 +39,7 @@ export const BtnGroup: React.FC<BtnGroupProps> = (props) => {
|
||||
>
|
||||
<Button
|
||||
type="text"
|
||||
onClick={(e) => {
|
||||
onClick={(e: React.MouseEvent<HTMLElement, globalThis.MouseEvent>) => {
|
||||
onClick(key, e);
|
||||
}}
|
||||
>
|
||||
|
@ -1,6 +1,6 @@
|
||||
|
||||
import React, { useRef } from 'react';
|
||||
import { Button, Space } from 'antd'
|
||||
import { Button, Space } from '@zhst/meta'
|
||||
import { BigImagePreview } from '@zhst/meta'
|
||||
|
||||
|
||||
|
@ -3,6 +3,7 @@ import {
|
||||
noop,
|
||||
get,
|
||||
addEventListenerWrapper,
|
||||
// @ts-ignore
|
||||
dataURLToBlob,
|
||||
nextTick,
|
||||
toRealNumber,
|
||||
|
@ -0,0 +1,87 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders components/app/demo/basic.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-app"
|
||||
>
|
||||
<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 message
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<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>
|
||||
Open notification
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/app/demo/basic.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/app/demo/config.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-app"
|
||||
>
|
||||
<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>
|
||||
Message for only one
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Notification for bottomLeft
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/app/demo/config.tsx extend context correctly 2`] = `[]`;
|
@ -0,0 +1,83 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders components/app/demo/basic.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-app"
|
||||
>
|
||||
<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 message
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<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>
|
||||
Open notification
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/app/demo/config.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-app"
|
||||
>
|
||||
<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>
|
||||
Message for only one
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Notification for bottomLeft
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -0,0 +1,17 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`App rtl render component should be rendered correctly in RTL direction 1`] = `
|
||||
<div
|
||||
class="ant-app"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`App single 1`] = `
|
||||
<div
|
||||
class="ant-app"
|
||||
>
|
||||
<div>
|
||||
Hello World
|
||||
</div>
|
||||
</div>
|
||||
`;
|
3
packages/meta/src/app/__tests__/demo-extend.test.ts
Normal file
3
packages/meta/src/app/__tests__/demo-extend.test.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { extendTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
extendTest('app');
|
3
packages/meta/src/app/__tests__/demo.test.ts
Normal file
3
packages/meta/src/app/__tests__/demo.test.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('app');
|
5
packages/meta/src/app/__tests__/image.test.ts
Normal file
5
packages/meta/src/app/__tests__/image.test.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { imageDemoTest } from '../../../tests/shared/imageTest';
|
||||
|
||||
describe('app', () => {
|
||||
imageDemoTest('app');
|
||||
});
|
236
packages/meta/src/app/__tests__/index.test.tsx
Normal file
236
packages/meta/src/app/__tests__/index.test.tsx
Normal file
@ -0,0 +1,236 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { SmileOutlined } from '@ant-design/icons';
|
||||
import type { NotificationConfig } from 'antd/es/notification/interface';
|
||||
|
||||
import App from '..';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { render, waitFakeTimer } from '../../../tests/utils';
|
||||
import type { AppConfig } from '../context';
|
||||
import { AppConfigContext } from '../context';
|
||||
|
||||
describe('App', () => {
|
||||
mountTest(App);
|
||||
rtlTest(App);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllTimers();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('single', () => {
|
||||
// Sub page
|
||||
const MyPage: React.FC = () => {
|
||||
const { message } = App.useApp();
|
||||
React.useEffect(() => {
|
||||
message.success('Good!');
|
||||
}, [message]);
|
||||
|
||||
return <div>Hello World</div>;
|
||||
};
|
||||
|
||||
// Entry component
|
||||
const MyApp: React.FC = () => (
|
||||
<App>
|
||||
<MyPage />
|
||||
</App>
|
||||
);
|
||||
|
||||
const { getByText, container } = render(<MyApp />);
|
||||
expect(getByText('Hello World')).toBeTruthy();
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should work as message and notification config configured in app', async () => {
|
||||
let consumedConfig: AppConfig | undefined;
|
||||
const Consumer = () => {
|
||||
const { message, notification } = App.useApp();
|
||||
consumedConfig = React.useContext(AppConfigContext);
|
||||
|
||||
useEffect(() => {
|
||||
message.success('Message 1');
|
||||
message.success('Message 2');
|
||||
notification.success({ message: 'Notification 1' });
|
||||
notification.success({ message: 'Notification 2' });
|
||||
notification.success({ message: 'Notification 3' });
|
||||
}, [message, notification]);
|
||||
|
||||
return <div />;
|
||||
};
|
||||
const Wrapper = () => (
|
||||
<App message={{ maxCount: 1 }} notification={{ maxCount: 2 }}>
|
||||
<Consumer />
|
||||
</App>
|
||||
);
|
||||
|
||||
render(<Wrapper />);
|
||||
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(consumedConfig?.message).toStrictEqual({ maxCount: 1 });
|
||||
expect(consumedConfig?.notification).toStrictEqual({ maxCount: 2 });
|
||||
|
||||
expect(document.querySelectorAll('.ant-message-notice')).toHaveLength(1);
|
||||
expect(document.querySelectorAll('.ant-notification-notice')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('should be a merged config configured in nested app', async () => {
|
||||
let offsetConsumedConfig: AppConfig | undefined;
|
||||
let maxCountConsumedConfig: AppConfig | undefined;
|
||||
const OffsetConsumer = () => {
|
||||
offsetConsumedConfig = React.useContext(AppConfigContext);
|
||||
return <div />;
|
||||
};
|
||||
const MaxCountConsumer = () => {
|
||||
maxCountConsumedConfig = React.useContext(AppConfigContext);
|
||||
return <div />;
|
||||
};
|
||||
const Wrapper = () => (
|
||||
<App message={{ maxCount: 1 }} notification={{ maxCount: 2 }}>
|
||||
<App message={{ top: 32 }} notification={{ top: 96 }}>
|
||||
<OffsetConsumer />
|
||||
</App>
|
||||
<MaxCountConsumer />
|
||||
</App>
|
||||
);
|
||||
|
||||
render(<Wrapper />);
|
||||
|
||||
expect(offsetConsumedConfig?.message).toStrictEqual({ maxCount: 1, top: 32 });
|
||||
expect(offsetConsumedConfig?.notification).toStrictEqual({ maxCount: 2, top: 96 });
|
||||
expect(maxCountConsumedConfig?.message).toStrictEqual({ maxCount: 1 });
|
||||
expect(maxCountConsumedConfig?.notification).toStrictEqual({ maxCount: 2 });
|
||||
});
|
||||
|
||||
it('should respect config from props in priority', async () => {
|
||||
let config: AppConfig | undefined;
|
||||
const Consumer = () => {
|
||||
config = React.useContext(AppConfigContext);
|
||||
return <div />;
|
||||
};
|
||||
const Wrapper = () => (
|
||||
<App message={{ maxCount: 10, top: 20 }} notification={{ maxCount: 30, bottom: 40 }}>
|
||||
<App message={{ maxCount: 11 }} notification={{ bottom: 41 }}>
|
||||
<Consumer />
|
||||
</App>
|
||||
</App>
|
||||
);
|
||||
|
||||
render(<Wrapper />);
|
||||
|
||||
expect(config?.message).toStrictEqual({ maxCount: 11, top: 20 });
|
||||
expect(config?.notification).toStrictEqual({ maxCount: 30, bottom: 41 });
|
||||
});
|
||||
|
||||
it('should respect notification placement config from props in priority', async () => {
|
||||
let consumedConfig: AppConfig | undefined;
|
||||
|
||||
const Consumer = () => {
|
||||
const { notification } = App.useApp();
|
||||
consumedConfig = React.useContext(AppConfigContext);
|
||||
|
||||
useEffect(() => {
|
||||
notification.success({ message: 'Notification 1' });
|
||||
notification.success({ message: 'Notification 2' });
|
||||
notification.success({ message: 'Notification 3' });
|
||||
}, [notification]);
|
||||
|
||||
return <div />;
|
||||
};
|
||||
|
||||
const config: NotificationConfig = {
|
||||
placement: 'bottomLeft',
|
||||
top: 100,
|
||||
bottom: 50,
|
||||
};
|
||||
|
||||
const Wrapper = () => (
|
||||
<App notification={config}>
|
||||
<Consumer />
|
||||
</App>
|
||||
);
|
||||
|
||||
render(<Wrapper />);
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(consumedConfig?.notification).toStrictEqual(config);
|
||||
expect(document.querySelector('.ant-notification-topRight')).not.toBeInTheDocument();
|
||||
expect(document.querySelector('.ant-notification-bottomLeft')).toHaveStyle({
|
||||
top: '',
|
||||
left: '0px',
|
||||
bottom: '50px',
|
||||
});
|
||||
});
|
||||
|
||||
it('support className', () => {
|
||||
const { container } = render(
|
||||
<App className="test-class">
|
||||
<div>test</div>
|
||||
</App>,
|
||||
);
|
||||
expect(container.querySelector<HTMLDivElement>('.ant-app')).toHaveClass('test-class');
|
||||
});
|
||||
|
||||
it('support style', () => {
|
||||
const { container } = render(
|
||||
<App style={{ color: 'blue' }}>
|
||||
<div>test</div>
|
||||
</App>,
|
||||
);
|
||||
expect(container.querySelector<HTMLDivElement>('.ant-app')).toHaveStyle('color: blue;');
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/41197#issuecomment-1465803061
|
||||
describe('restIcon style', () => {
|
||||
beforeEach(() => {
|
||||
Array.from(document.querySelectorAll('style')).forEach((style) => {
|
||||
style.parentNode?.removeChild(style);
|
||||
});
|
||||
});
|
||||
|
||||
it('should work by default', () => {
|
||||
const { container } = render(
|
||||
<App>
|
||||
<SmileOutlined />
|
||||
</App>,
|
||||
);
|
||||
|
||||
expect(container.querySelector('.anticon')).toBeTruthy();
|
||||
const dynamicStyles = Array.from(document.querySelectorAll('style[data-css-hash]'));
|
||||
expect(
|
||||
dynamicStyles.some((style) => {
|
||||
const { innerHTML } = style;
|
||||
return innerHTML.startsWith('.anticon');
|
||||
}),
|
||||
).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('component', () => {
|
||||
it('replace', () => {
|
||||
const { container } = render(
|
||||
<App component="section">
|
||||
<p />
|
||||
</App>,
|
||||
);
|
||||
|
||||
expect(container.querySelector('section.ant-app')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('to false', () => {
|
||||
const warnSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
const { container } = render(
|
||||
<App component={false}>
|
||||
<p />
|
||||
</App>,
|
||||
);
|
||||
expect(warnSpy).not.toHaveBeenCalled();
|
||||
expect(container.querySelector('.ant-app')).toBeFalsy();
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
});
|
26
packages/meta/src/app/context.ts
Normal file
26
packages/meta/src/app/context.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
|
||||
import type { ConfigOptions as MessageConfig, MessageInstance } from '../message/interface';
|
||||
import type { HookAPI as ModalHookAPI } from '../modal/useModal';
|
||||
import type { NotificationConfig, NotificationInstance } from '../notification/interface';
|
||||
|
||||
export interface AppConfig {
|
||||
message?: MessageConfig;
|
||||
notification?: NotificationConfig;
|
||||
}
|
||||
|
||||
export const AppConfigContext = React.createContext<AppConfig>({});
|
||||
|
||||
export interface useAppProps {
|
||||
message: MessageInstance;
|
||||
notification: NotificationInstance;
|
||||
modal: ModalHookAPI;
|
||||
}
|
||||
|
||||
const AppContext = React.createContext<useAppProps>({
|
||||
message: {},
|
||||
notification: {},
|
||||
modal: {},
|
||||
} as useAppProps);
|
||||
|
||||
export default AppContext;
|
7
packages/meta/src/app/demo/basic.md
Normal file
7
packages/meta/src/app/demo/basic.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
获取 `message`、`notification`、`modal` 实例。
|
||||
|
||||
## en-US
|
||||
|
||||
Get instance for `message`, `notification`, `modal`.
|
47
packages/meta/src/app/demo/basic.tsx
Normal file
47
packages/meta/src/app/demo/basic.tsx
Normal file
@ -0,0 +1,47 @@
|
||||
import React from 'react';
|
||||
import { App, Button, Space } from 'antd';
|
||||
|
||||
// Sub page
|
||||
const MyPage = () => {
|
||||
const { message, modal, notification } = App.useApp();
|
||||
|
||||
const showMessage = () => {
|
||||
message.success('Success!');
|
||||
};
|
||||
|
||||
const showModal = () => {
|
||||
modal.warning({
|
||||
title: 'This is a warning message',
|
||||
content: 'some messages...some messages...',
|
||||
});
|
||||
};
|
||||
|
||||
const showNotification = () => {
|
||||
notification.info({
|
||||
message: `Notification topLeft`,
|
||||
description: 'Hello, Ant Design!!',
|
||||
placement: 'topLeft',
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Space>
|
||||
<Button type="primary" onClick={showMessage}>
|
||||
Open message
|
||||
</Button>
|
||||
<Button type="primary" onClick={showModal}>
|
||||
Open modal
|
||||
</Button>
|
||||
<Button type="primary" onClick={showNotification}>
|
||||
Open notification
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
// Entry component
|
||||
export default () => (
|
||||
<App>
|
||||
<MyPage />
|
||||
</App>
|
||||
);
|
7
packages/meta/src/app/demo/config.md
Normal file
7
packages/meta/src/app/demo/config.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
对 `message`、`notification` 进行配置。
|
||||
|
||||
## en-US
|
||||
|
||||
Config for `message`, `notification`.
|
36
packages/meta/src/app/demo/config.tsx
Normal file
36
packages/meta/src/app/demo/config.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import React from 'react';
|
||||
import { App, Button, Space } from 'antd';
|
||||
|
||||
// Sub page
|
||||
const MyPage = () => {
|
||||
const { message, notification } = App.useApp();
|
||||
|
||||
const showMessage = () => {
|
||||
message.success('Success!');
|
||||
};
|
||||
|
||||
const showNotification = () => {
|
||||
notification.info({
|
||||
message: `Notification`,
|
||||
description: 'Hello, Ant Design!!',
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Space>
|
||||
<Button type="primary" onClick={showMessage}>
|
||||
Message for only one
|
||||
</Button>
|
||||
<Button type="primary" onClick={showNotification}>
|
||||
Notification for bottomLeft
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
// Entry component
|
||||
export default () => (
|
||||
<App message={{ maxCount: 1 }} notification={{ placement: 'bottomLeft' }}>
|
||||
<MyPage />
|
||||
</App>
|
||||
);
|
94
packages/meta/src/app/index.tsx
Normal file
94
packages/meta/src/app/index.tsx
Normal file
@ -0,0 +1,94 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import type { AnyObject, CustomComponent } from '../_util/type';
|
||||
import type { ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import useMessage from '../message/useMessage';
|
||||
import useModal from '../modal/useModal';
|
||||
import useNotification from '../notification/useNotification';
|
||||
import type { AppConfig, useAppProps } from './context';
|
||||
import AppContext, { AppConfigContext } from './context';
|
||||
import useStyle from './style';
|
||||
|
||||
export interface AppProps<P = AnyObject> extends AppConfig {
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
prefixCls?: string;
|
||||
children?: ReactNode;
|
||||
component?: CustomComponent<P> | false;
|
||||
}
|
||||
|
||||
const useApp = () => React.useContext<useAppProps>(AppContext);
|
||||
|
||||
const App: React.FC<AppProps> & { useApp: () => useAppProps } = (props) => {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
children,
|
||||
className,
|
||||
rootClassName,
|
||||
message,
|
||||
notification,
|
||||
style,
|
||||
component = 'div',
|
||||
} = props;
|
||||
const { getPrefixCls } = useContext<ConfigConsumerProps>(ConfigContext);
|
||||
const prefixCls = getPrefixCls('app', customizePrefixCls);
|
||||
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls);
|
||||
const customClassName = classNames(hashId, prefixCls, className, rootClassName, cssVarCls);
|
||||
|
||||
const appConfig = useContext<AppConfig>(AppConfigContext);
|
||||
|
||||
const mergedAppConfig = React.useMemo<AppConfig>(
|
||||
() => ({
|
||||
message: { ...appConfig.message, ...message },
|
||||
notification: { ...appConfig.notification, ...notification },
|
||||
}),
|
||||
[message, notification, appConfig.message, appConfig.notification],
|
||||
);
|
||||
|
||||
const [messageApi, messageContextHolder] = useMessage(mergedAppConfig.message);
|
||||
const [notificationApi, notificationContextHolder] = useNotification(
|
||||
mergedAppConfig.notification,
|
||||
);
|
||||
const [ModalApi, ModalContextHolder] = useModal();
|
||||
|
||||
const memoizedContextValue = React.useMemo<useAppProps>(
|
||||
() => ({
|
||||
message: messageApi,
|
||||
notification: notificationApi,
|
||||
modal: ModalApi,
|
||||
}),
|
||||
[messageApi, notificationApi, ModalApi],
|
||||
);
|
||||
|
||||
// ============================ Render ============================
|
||||
const Component = component === false ? React.Fragment : component;
|
||||
const rootProps: AppProps = {
|
||||
className: customClassName,
|
||||
style,
|
||||
};
|
||||
|
||||
return wrapCSSVar(
|
||||
<AppContext.Provider value={memoizedContextValue}>
|
||||
<AppConfigContext.Provider value={mergedAppConfig}>
|
||||
<Component {...(component === false ? undefined : rootProps)}>
|
||||
{ModalContextHolder}
|
||||
{messageContextHolder}
|
||||
{notificationContextHolder}
|
||||
{children}
|
||||
</Component>
|
||||
</AppConfigContext.Provider>
|
||||
</AppContext.Provider>,
|
||||
);
|
||||
};
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
App.displayName = 'App';
|
||||
}
|
||||
|
||||
App.useApp = useApp;
|
||||
|
||||
export default App;
|
133
packages/meta/src/app/index.zh-CN.md
Normal file
133
packages/meta/src/app/index.zh-CN.md
Normal file
@ -0,0 +1,133 @@
|
||||
---
|
||||
category: Components
|
||||
subtitle: 包裹组件
|
||||
group: 其他
|
||||
title: App 包裹组件
|
||||
demo:
|
||||
cols: 2
|
||||
---
|
||||
|
||||
新的包裹组件,提供重置样式和提供消费上下文的默认环境。
|
||||
|
||||
## 何时使用
|
||||
|
||||
- 提供可消费 React context 的 `message.xxx`、`Modal.xxx`、`notification.xxx` 的静态方法,可以简化 useMessage 等方法需要手动植入 `contextHolder` 的问题。
|
||||
- 提供基于 `.ant-app` 的默认重置样式,解决原生元素没有 antd 规范样式的问题。
|
||||
|
||||
## 代码演示
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<code src="./demo/basic.tsx">基本用法</code>
|
||||
<code src="./demo/config.tsx">Hooks 配置</code>
|
||||
|
||||
## 如何使用
|
||||
|
||||
### 基础用法
|
||||
|
||||
App 组件通过 `Context` 提供上下文方法调用,因而 useApp 需要作为子组件才能使用,我们推荐在应用中顶层包裹 App。
|
||||
|
||||
```ts
|
||||
import React from 'react';
|
||||
import { App } from 'antd';
|
||||
|
||||
const MyPage: React.FC = () => {
|
||||
const { message, notification, modal } = App.useApp();
|
||||
message.success('Good!');
|
||||
notification.info({ message: 'Good' });
|
||||
modal.warning({ title: 'Good' });
|
||||
// ....
|
||||
// other message, notification, modal static function
|
||||
return <div>Hello word</div>;
|
||||
};
|
||||
|
||||
const MyApp: React.FC = () => (
|
||||
<App>
|
||||
<MyPage />
|
||||
</App>
|
||||
);
|
||||
|
||||
export default MyApp;
|
||||
```
|
||||
|
||||
注意:App.useApp 必须在 App 之下方可使用。
|
||||
|
||||
### 与 ConfigProvider 先后顺序
|
||||
|
||||
App 组件只能在 `ConfigProvider` 之下才能使用 Design Token, 如果需要使用其样式重置能力,则 ConfigProvider 与 App 组件必须成对出现。
|
||||
|
||||
```ts
|
||||
<ConfigProvider theme={{ ... }}>
|
||||
<App>
|
||||
...
|
||||
</App>
|
||||
</ConfigProvider>
|
||||
```
|
||||
|
||||
### 内嵌使用场景(如无必要,尽量不做嵌套)
|
||||
|
||||
```ts
|
||||
<App>
|
||||
<Space>
|
||||
...
|
||||
<App>...</App>
|
||||
</Space>
|
||||
</App>
|
||||
```
|
||||
|
||||
### 全局场景(redux 场景)
|
||||
|
||||
```ts
|
||||
// Entry component
|
||||
import { App } from 'antd';
|
||||
import type { MessageInstance } from 'antd/es/message/interface';
|
||||
import type { ModalStaticFunctions } from 'antd/es/modal/confirm';
|
||||
import type { NotificationInstance } from 'antd/es/notification/interface';
|
||||
|
||||
let message: MessageInstance;
|
||||
let notification: NotificationInstance;
|
||||
let modal: Omit<ModalStaticFunctions, 'warn'>;
|
||||
|
||||
export default () => {
|
||||
const staticFunction = App.useApp();
|
||||
message = staticFunction.message;
|
||||
modal = staticFunction.modal;
|
||||
notification = staticFunction.notification;
|
||||
return null;
|
||||
};
|
||||
|
||||
export { message, notification, modal };
|
||||
```
|
||||
|
||||
```ts
|
||||
// sub page
|
||||
import React from 'react';
|
||||
import { Button, Space } from 'antd';
|
||||
|
||||
import { message } from './store';
|
||||
|
||||
export default () => {
|
||||
const showMessage = () => {
|
||||
message.success('Success!');
|
||||
};
|
||||
|
||||
return (
|
||||
<Space>
|
||||
<Button type="primary" onClick={showMessage}>
|
||||
Open message
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
通用属性参考:[通用属性](/docs/react/common-props)
|
||||
|
||||
### App
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| component | 设置渲染元素,为 `false` 则不创建 DOM 节点 | ComponentType | div | 5.11.0 |
|
||||
| message | App 内 Message 的全局配置 | [MessageConfig](/components/message-cn/#messageconfig) | - | 5.3.0 |
|
||||
| notification | App 内 Notification 的全局配置 | [NotificationConfig](/components/notification-cn/#notificationconfig) | - | 5.3.0 |
|
25
packages/meta/src/app/style/index.ts
Normal file
25
packages/meta/src/app/style/index.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import type { FullToken, GenerateStyle, GetDefaultToken } from '../../theme/internal';
|
||||
import { genStyleHooks } from '../../theme/internal';
|
||||
|
||||
export type ComponentToken = {};
|
||||
// @ts-ignore
|
||||
interface AppToken extends FullToken<'App'> {}
|
||||
|
||||
// =============================== Base ===============================
|
||||
const genBaseStyle: GenerateStyle<AppToken> = (token) => {
|
||||
const { componentCls, colorText, fontSize, lineHeight, fontFamily } = token;
|
||||
return {
|
||||
[componentCls]: {
|
||||
color: colorText,
|
||||
fontSize,
|
||||
lineHeight,
|
||||
fontFamily,
|
||||
},
|
||||
};
|
||||
};
|
||||
// @ts-ignore
|
||||
export const prepareComponentToken: GetDefaultToken<'App'> = () => ({});
|
||||
|
||||
// ============================== Export ==============================
|
||||
// @ts-ignore
|
||||
export default genStyleHooks('App', genBaseStyle, prepareComponentToken);
|
@ -81,3 +81,7 @@ export { default as Tour } from './tour'
|
||||
export type { TourLocale, TourProps, TourStepProps } from './tour/interface'
|
||||
export { default as Segmented } from './segmented'
|
||||
export type { SegmentedLabeledOption, SegmentedProps, SegmentedValue } from './segmented'
|
||||
export { default as App } from './app';
|
||||
export type { AppProps } from './app';
|
||||
export { default as notification } from './notification';
|
||||
export type { ArgsProps as NotificationArgsProps } from './notification';
|
||||
|
135
packages/meta/src/notification/PurePanel.tsx
Normal file
135
packages/meta/src/notification/PurePanel.tsx
Normal file
@ -0,0 +1,135 @@
|
||||
import * as React from 'react';
|
||||
import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
|
||||
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
|
||||
import CloseOutlined from '@ant-design/icons/CloseOutlined';
|
||||
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
|
||||
import InfoCircleFilled from '@ant-design/icons/InfoCircleFilled';
|
||||
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
|
||||
import classNames from 'classnames';
|
||||
import { Notice } from 'rc-notification';
|
||||
import type { NoticeProps } from 'rc-notification/lib/Notice';
|
||||
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
|
||||
import type { IconType } from './interface';
|
||||
import useStyle from './style';
|
||||
import PurePanelStyle from './style/pure-panel';
|
||||
|
||||
export const TypeIcon = {
|
||||
info: <InfoCircleFilled />,
|
||||
success: <CheckCircleFilled />,
|
||||
error: <CloseCircleFilled />,
|
||||
warning: <ExclamationCircleFilled />,
|
||||
loading: <LoadingOutlined />,
|
||||
};
|
||||
|
||||
export function getCloseIcon(prefixCls: string, closeIcon?: React.ReactNode): React.ReactNode {
|
||||
if (closeIcon === null || closeIcon === false) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
closeIcon || (
|
||||
<span className={`${prefixCls}-close-x`}>
|
||||
<CloseOutlined className={`${prefixCls}-close-icon`} />
|
||||
</span>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export interface PureContentProps {
|
||||
prefixCls: string;
|
||||
icon?: React.ReactNode;
|
||||
message?: React.ReactNode;
|
||||
description?: React.ReactNode;
|
||||
btn?: React.ReactNode;
|
||||
type?: IconType;
|
||||
role?: 'alert' | 'status';
|
||||
}
|
||||
|
||||
const typeToIcon = {
|
||||
success: CheckCircleFilled,
|
||||
info: InfoCircleFilled,
|
||||
error: CloseCircleFilled,
|
||||
warning: ExclamationCircleFilled,
|
||||
};
|
||||
|
||||
export const PureContent: React.FC<PureContentProps> = (props) => {
|
||||
const { prefixCls, icon, type, message, description, btn, role = 'alert' } = props;
|
||||
let iconNode: React.ReactNode = null;
|
||||
if (icon) {
|
||||
iconNode = <span className={`${prefixCls}-icon`}>{icon}</span>;
|
||||
} else if (type) {
|
||||
iconNode = React.createElement(typeToIcon[type] || null, {
|
||||
className: classNames(`${prefixCls}-icon`, `${prefixCls}-icon-${type}`),
|
||||
});
|
||||
}
|
||||
return (
|
||||
<div className={classNames({ [`${prefixCls}-with-icon`]: iconNode })} role={role}>
|
||||
{iconNode}
|
||||
<div className={`${prefixCls}-message`}>{message}</div>
|
||||
<div className={`${prefixCls}-description`}>{description}</div>
|
||||
{btn && <div className={`${prefixCls}-btn`}>{btn}</div>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export interface PurePanelProps
|
||||
extends Omit<NoticeProps, 'prefixCls' | 'eventKey'>,
|
||||
Omit<PureContentProps, 'prefixCls' | 'children'> {
|
||||
prefixCls?: string;
|
||||
}
|
||||
|
||||
/** @private Internal Component. Do not use in your production. */
|
||||
const PurePanel: React.FC<PurePanelProps> = (props) => {
|
||||
const {
|
||||
prefixCls: staticPrefixCls,
|
||||
className,
|
||||
icon,
|
||||
type,
|
||||
message,
|
||||
description,
|
||||
btn,
|
||||
closable = true,
|
||||
closeIcon,
|
||||
className: notificationClassName,
|
||||
...restProps
|
||||
} = props;
|
||||
const { getPrefixCls } = React.useContext(ConfigContext);
|
||||
|
||||
const prefixCls = staticPrefixCls || getPrefixCls('notification');
|
||||
const noticePrefixCls = `${prefixCls}-notice`;
|
||||
|
||||
const rootCls = useCSSVarCls(prefixCls);
|
||||
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls);
|
||||
|
||||
return wrapCSSVar(
|
||||
<div
|
||||
className={classNames(`${noticePrefixCls}-pure-panel`, hashId, className, cssVarCls, rootCls)}
|
||||
>
|
||||
<PurePanelStyle prefixCls={prefixCls} />
|
||||
<Notice
|
||||
{...restProps}
|
||||
prefixCls={prefixCls}
|
||||
eventKey="pure"
|
||||
duration={null}
|
||||
closable={closable}
|
||||
className={classNames({
|
||||
notificationClassName,
|
||||
})}
|
||||
closeIcon={getCloseIcon(prefixCls, closeIcon)}
|
||||
content={
|
||||
<PureContent
|
||||
prefixCls={noticePrefixCls}
|
||||
icon={icon}
|
||||
type={type}
|
||||
message={message}
|
||||
description={description}
|
||||
btn={btn}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>,
|
||||
);
|
||||
};
|
||||
|
||||
export default PurePanel;
|
@ -0,0 +1,783 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders components/notification/demo/basic.tsx extend context correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open the notification box
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`renders components/notification/demo/basic.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/notification/demo/custom-icon.tsx extend context correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open the notification box
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`renders components/notification/demo/custom-icon.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/notification/demo/custom-style.tsx extend context correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open the notification box
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`renders components/notification/demo/custom-style.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/notification/demo/duration.tsx extend context correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open the notification box
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`renders components/notification/demo/duration.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/notification/demo/hooks.tsx extend context correctly 1`] = `
|
||||
Array [
|
||||
<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
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="radius-upleft"
|
||||
class="anticon anticon-radius-upleft"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="radius-upleft"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M656 200h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm58 624h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM192 650h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm696-696h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-348 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-174 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm174-696H358c-127 0-230 103-230 230v182c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V358c0-87.3 70.7-158 158-158h182c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
topLeft
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="radius-upright"
|
||||
class="anticon anticon-radius-upright"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="radius-upright"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M368 128h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-2 696h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm522-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM192 128h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm348 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm174 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-48-696H484c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h182c87.3 0 158 70.7 158 158v182c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V358c0-127-103-230-230-230z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
topRight
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal"
|
||||
role="separator"
|
||||
/>,
|
||||
<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
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="radius-bottomleft"
|
||||
class="anticon anticon-radius-bottomleft"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="radius-bottomleft"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M712 824h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm2-696h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM136 374h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm0-174h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm752 624h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-348 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-230 72h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm230 624H358c-87.3 0-158-70.7-158-158V484c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v182c0 127 103 230 230 230h182c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
bottomLeft
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="radius-bottomright"
|
||||
class="anticon anticon-radius-bottomright"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="radius-bottomright"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M368 824h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-58-624h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm578 102h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM192 824h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm292 72h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm174 0h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm230 276h-56c-4.4 0-8 3.6-8 8v182c0 87.3-70.7 158-158 158H484c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h182c127 0 230-103 230-230V484c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
bottomRight
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/notification/demo/hooks.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/notification/demo/placement.tsx extend context correctly 1`] = `
|
||||
Array [
|
||||
<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
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="border-top"
|
||||
class="anticon anticon-border-top"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="border-top"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M872 144H152c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h720c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM208 310h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 498h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-332h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 166h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm166-166h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 332h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm332 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-332h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm166 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-332 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm332 332h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-332 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm332-498h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-332 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm332 332h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-332 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
top
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="border-bottom"
|
||||
class="anticon anticon-border-bottom"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="border-bottom"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M872 808H152c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h720c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-720-94h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm0-498h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm0 332h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm0-166h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm166 166h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm0-332h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm332 0h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm0 332h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm222-72h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-388 72h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm388-404h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-388 72h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm388 426h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-388 72h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm388-404h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-388 72h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
bottom
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal"
|
||||
role="separator"
|
||||
/>,
|
||||
<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
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="radius-upleft"
|
||||
class="anticon anticon-radius-upleft"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="radius-upleft"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M656 200h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm58 624h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM192 650h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm696-696h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-348 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-174 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm174-696H358c-127 0-230 103-230 230v182c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V358c0-87.3 70.7-158 158-158h182c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
topLeft
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="radius-upright"
|
||||
class="anticon anticon-radius-upright"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="radius-upright"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M368 128h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-2 696h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm522-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM192 128h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm348 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm174 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-48-696H484c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h182c87.3 0 158 70.7 158 158v182c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V358c0-127-103-230-230-230z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
topRight
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal"
|
||||
role="separator"
|
||||
/>,
|
||||
<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
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="radius-bottomleft"
|
||||
class="anticon anticon-radius-bottomleft"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="radius-bottomleft"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M712 824h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm2-696h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM136 374h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm0-174h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm752 624h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-348 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-230 72h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm230 624H358c-87.3 0-158-70.7-158-158V484c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v182c0 127 103 230 230 230h182c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
bottomLeft
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="radius-bottomright"
|
||||
class="anticon anticon-radius-bottomright"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="radius-bottomright"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M368 824h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-58-624h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm578 102h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM192 824h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm292 72h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm174 0h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm230 276h-56c-4.4 0-8 3.6-8 8v182c0 87.3-70.7 158-158 158H484c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h182c127 0 230-103 230-230V484c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
bottomRight
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/notification/demo/placement.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/notification/demo/render-panel.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-notification-notice-pure-panel"
|
||||
>
|
||||
<div
|
||||
class="ant-notification-notice ant-notification-notice-closable"
|
||||
>
|
||||
<div
|
||||
class="ant-notification-notice-content"
|
||||
>
|
||||
<div
|
||||
class="ant-notification-notice-with-icon"
|
||||
role="alert"
|
||||
>
|
||||
<span
|
||||
aria-label="check-circle"
|
||||
class="anticon anticon-check-circle ant-notification-notice-icon ant-notification-notice-icon-success"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="check-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 01-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<div
|
||||
class="ant-notification-notice-message"
|
||||
>
|
||||
Hello World!
|
||||
</div>
|
||||
<div
|
||||
class="ant-notification-notice-description"
|
||||
>
|
||||
Hello World?
|
||||
</div>
|
||||
<div
|
||||
class="ant-notification-notice-btn"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary ant-btn-sm"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
My Button
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a
|
||||
class="ant-notification-notice-close"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
class="ant-notification-close-x"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close ant-notification-close-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close"
|
||||
fill="currentColor"
|
||||
fill-rule="evenodd"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M799.86 166.31c.02 0 .04.02.08.06l57.69 57.7c.04.03.05.05.06.08a.12.12 0 010 .06c0 .03-.02.05-.06.09L569.93 512l287.7 287.7c.04.04.05.06.06.09a.12.12 0 010 .07c0 .02-.02.04-.06.08l-57.7 57.69c-.03.04-.05.05-.07.06a.12.12 0 01-.07 0c-.03 0-.05-.02-.09-.06L512 569.93l-287.7 287.7c-.04.04-.06.05-.09.06a.12.12 0 01-.07 0c-.02 0-.04-.02-.08-.06l-57.69-57.7c-.04-.03-.05-.05-.06-.07a.12.12 0 010-.07c0-.03.02-.05.06-.09L454.07 512l-287.7-287.7c-.04-.04-.05-.06-.06-.09a.12.12 0 010-.07c0-.02.02-.04.06-.08l57.7-57.69c.03-.04.05-.05.07-.06a.12.12 0 01.07 0c.03 0 .05.02.09.06L512 454.07l287.7-287.7c.04-.04.06-.05.09-.06a.12.12 0 01.07 0z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/notification/demo/render-panel.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/notification/demo/stack.tsx extend context correctly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-large ant-space-gap-col-large"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"
|
||||
style="width: 100%;"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<span>
|
||||
Enabled:
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
aria-checked="true"
|
||||
class="ant-switch ant-switch-checked"
|
||||
role="switch"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="ant-switch-handle"
|
||||
/>
|
||||
<span
|
||||
class="ant-switch-inner"
|
||||
>
|
||||
<span
|
||||
class="ant-switch-inner-checked"
|
||||
/>
|
||||
<span
|
||||
class="ant-switch-inner-unchecked"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"
|
||||
style="width: 100%;"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<span>
|
||||
Threshold:
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-handler-wrap"
|
||||
>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Increase Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-up"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="up"
|
||||
class="anticon anticon-up ant-input-number-handler-up-inner"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="up"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Decrease Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-down"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-input-number-handler-down-inner"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-input-number-input-wrap"
|
||||
>
|
||||
<input
|
||||
aria-valuemax="10"
|
||||
aria-valuemin="1"
|
||||
aria-valuenow="3"
|
||||
autocomplete="off"
|
||||
class="ant-input-number-input"
|
||||
role="spinbutton"
|
||||
step="1"
|
||||
value="3"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal"
|
||||
role="separator"
|
||||
/>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open the notification box
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/notification/demo/stack.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/notification/demo/update.tsx extend context correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open the notification box
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`renders components/notification/demo/update.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/notification/demo/with-btn.tsx extend context correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open the notification box
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`renders components/notification/demo/with-btn.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/notification/demo/with-icon.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-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Success
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Info
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Warning
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Error
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/notification/demo/with-icon.tsx extend context correctly 2`] = `[]`;
|
@ -0,0 +1,761 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders components/notification/demo/basic.tsx correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open the notification box
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`renders components/notification/demo/custom-icon.tsx correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open the notification box
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`renders components/notification/demo/custom-style.tsx correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open the notification box
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`renders components/notification/demo/duration.tsx correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open the notification box
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`renders components/notification/demo/hooks.tsx correctly 1`] = `
|
||||
Array [
|
||||
<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
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="radius-upleft"
|
||||
class="anticon anticon-radius-upleft"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="radius-upleft"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M656 200h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm58 624h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM192 650h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm696-696h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-348 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-174 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm174-696H358c-127 0-230 103-230 230v182c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V358c0-87.3 70.7-158 158-158h182c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
topLeft
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="radius-upright"
|
||||
class="anticon anticon-radius-upright"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="radius-upright"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M368 128h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-2 696h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm522-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM192 128h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm348 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm174 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-48-696H484c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h182c87.3 0 158 70.7 158 158v182c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V358c0-127-103-230-230-230z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
topRight
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal"
|
||||
role="separator"
|
||||
/>,
|
||||
<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
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="radius-bottomleft"
|
||||
class="anticon anticon-radius-bottomleft"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="radius-bottomleft"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M712 824h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm2-696h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM136 374h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm0-174h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm752 624h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-348 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-230 72h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm230 624H358c-87.3 0-158-70.7-158-158V484c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v182c0 127 103 230 230 230h182c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
bottomLeft
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="radius-bottomright"
|
||||
class="anticon anticon-radius-bottomright"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="radius-bottomright"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M368 824h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-58-624h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm578 102h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM192 824h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm292 72h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm174 0h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm230 276h-56c-4.4 0-8 3.6-8 8v182c0 87.3-70.7 158-158 158H484c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h182c127 0 230-103 230-230V484c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
bottomRight
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/notification/demo/placement.tsx correctly 1`] = `
|
||||
Array [
|
||||
<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
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="border-top"
|
||||
class="anticon anticon-border-top"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="border-top"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M872 144H152c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h720c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM208 310h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 498h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-332h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 166h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm166-166h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 332h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm332 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-332h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm166 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-332 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm332 332h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-332 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm332-498h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-332 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm332 332h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-332 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
top
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="border-bottom"
|
||||
class="anticon anticon-border-bottom"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="border-bottom"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M872 808H152c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h720c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-720-94h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm0-498h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm0 332h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm0-166h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm166 166h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm0-332h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm332 0h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm0 332h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm222-72h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-388 72h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm388-404h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-388 72h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm388 426h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-388 72h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm388-404h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-388 72h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
bottom
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal"
|
||||
role="separator"
|
||||
/>,
|
||||
<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
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="radius-upleft"
|
||||
class="anticon anticon-radius-upleft"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="radius-upleft"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M656 200h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm58 624h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM192 650h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm696-696h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-348 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-174 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm174-696H358c-127 0-230 103-230 230v182c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V358c0-87.3 70.7-158 158-158h182c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
topLeft
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="radius-upright"
|
||||
class="anticon anticon-radius-upright"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="radius-upright"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M368 128h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-2 696h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm522-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM192 128h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm348 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm174 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-48-696H484c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h182c87.3 0 158 70.7 158 158v182c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V358c0-127-103-230-230-230z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
topRight
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal"
|
||||
role="separator"
|
||||
/>,
|
||||
<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
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="radius-bottomleft"
|
||||
class="anticon anticon-radius-bottomleft"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="radius-bottomleft"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M712 824h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm2-696h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM136 374h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm0-174h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm752 624h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-348 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-230 72h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm230 624H358c-87.3 0-158-70.7-158-158V484c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v182c0 127 103 230 230 230h182c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
bottomLeft
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="radius-bottomright"
|
||||
class="anticon anticon-radius-bottomright"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="radius-bottomright"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M368 824h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-58-624h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm578 102h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM192 824h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-174h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm292 72h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm174 0h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm230 276h-56c-4.4 0-8 3.6-8 8v182c0 87.3-70.7 158-158 158H484c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h182c127 0 230-103 230-230V484c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
bottomRight
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/notification/demo/render-panel.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-notification-notice-pure-panel"
|
||||
>
|
||||
<div
|
||||
class="ant-notification-notice ant-notification-notice-closable"
|
||||
>
|
||||
<div
|
||||
class="ant-notification-notice-content"
|
||||
>
|
||||
<div
|
||||
class="ant-notification-notice-with-icon"
|
||||
role="alert"
|
||||
>
|
||||
<span
|
||||
aria-label="check-circle"
|
||||
class="anticon anticon-check-circle ant-notification-notice-icon ant-notification-notice-icon-success"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="check-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 01-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<div
|
||||
class="ant-notification-notice-message"
|
||||
>
|
||||
Hello World!
|
||||
</div>
|
||||
<div
|
||||
class="ant-notification-notice-description"
|
||||
>
|
||||
Hello World?
|
||||
</div>
|
||||
<div
|
||||
class="ant-notification-notice-btn"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary ant-btn-sm"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
My Button
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a
|
||||
class="ant-notification-notice-close"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
class="ant-notification-close-x"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close ant-notification-close-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close"
|
||||
fill="currentColor"
|
||||
fill-rule="evenodd"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M799.86 166.31c.02 0 .04.02.08.06l57.69 57.7c.04.03.05.05.06.08a.12.12 0 010 .06c0 .03-.02.05-.06.09L569.93 512l287.7 287.7c.04.04.05.06.06.09a.12.12 0 010 .07c0 .02-.02.04-.06.08l-57.7 57.69c-.03.04-.05.05-.07.06a.12.12 0 01-.07 0c-.03 0-.05-.02-.09-.06L512 569.93l-287.7 287.7c-.04.04-.06.05-.09.06a.12.12 0 01-.07 0c-.02 0-.04-.02-.08-.06l-57.69-57.7c-.04-.03-.05-.05-.06-.07a.12.12 0 010-.07c0-.03.02-.05.06-.09L454.07 512l-287.7-287.7c-.04-.04-.05-.06-.06-.09a.12.12 0 010-.07c0-.02.02-.04.06-.08l57.7-57.69c.03-.04.05-.05.07-.06a.12.12 0 01.07 0c.03 0 .05.02.09.06L512 454.07l287.7-287.7c.04-.04.06-.05.09-.06a.12.12 0 01.07 0z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/notification/demo/stack.tsx correctly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-large ant-space-gap-col-large"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<span>
|
||||
Enabled:
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
aria-checked="true"
|
||||
class="ant-switch ant-switch-checked"
|
||||
role="switch"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="ant-switch-handle"
|
||||
/>
|
||||
<span
|
||||
class="ant-switch-inner"
|
||||
>
|
||||
<span
|
||||
class="ant-switch-inner-checked"
|
||||
/>
|
||||
<span
|
||||
class="ant-switch-inner-unchecked"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<span>
|
||||
Threshold:
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-handler-wrap"
|
||||
>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Increase Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-up"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="up"
|
||||
class="anticon anticon-up ant-input-number-handler-up-inner"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="up"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Decrease Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-down"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-input-number-handler-down-inner"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-input-number-input-wrap"
|
||||
>
|
||||
<input
|
||||
aria-valuemax="10"
|
||||
aria-valuemin="1"
|
||||
aria-valuenow="3"
|
||||
autocomplete="off"
|
||||
class="ant-input-number-input"
|
||||
role="spinbutton"
|
||||
step="1"
|
||||
value="3"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal"
|
||||
role="separator"
|
||||
/>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open the notification box
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/notification/demo/update.tsx correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open the notification box
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`renders components/notification/demo/with-btn.tsx correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open the notification box
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`renders components/notification/demo/with-icon.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-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Success
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Info
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Warning
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Error
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
87
packages/meta/src/notification/__tests__/config.test.tsx
Normal file
87
packages/meta/src/notification/__tests__/config.test.tsx
Normal file
@ -0,0 +1,87 @@
|
||||
import notification, { actWrapper } from '..';
|
||||
import { act } from '../../../tests/utils';
|
||||
import { awaitPromise, triggerMotionEnd } from './util';
|
||||
|
||||
describe('notification.config', () => {
|
||||
beforeAll(() => {
|
||||
actWrapper(act);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
// Clean up
|
||||
notification.destroy();
|
||||
await triggerMotionEnd();
|
||||
|
||||
notification.config({
|
||||
prefixCls: undefined,
|
||||
getContainer: undefined,
|
||||
});
|
||||
|
||||
jest.useRealTimers();
|
||||
|
||||
await awaitPromise();
|
||||
});
|
||||
|
||||
it('should be able to config maxCount', async () => {
|
||||
notification.config({
|
||||
maxCount: 5,
|
||||
duration: 0.5,
|
||||
});
|
||||
|
||||
for (let i = 0; i < 10; i += 1) {
|
||||
notification.open({
|
||||
message: 'Notification message',
|
||||
key: i,
|
||||
duration: 999,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await awaitPromise();
|
||||
|
||||
act(() => {
|
||||
// One frame is 16ms
|
||||
jest.advanceTimersByTime(100);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await triggerMotionEnd(false);
|
||||
|
||||
const count = document.querySelectorAll('.ant-notification-notice').length;
|
||||
expect(count).toBeLessThanOrEqual(5);
|
||||
}
|
||||
|
||||
act(() => {
|
||||
notification.open({
|
||||
message: 'Notification last',
|
||||
key: '11',
|
||||
duration: 999,
|
||||
});
|
||||
});
|
||||
|
||||
act(() => {
|
||||
// One frame is 16ms
|
||||
jest.advanceTimersByTime(100);
|
||||
});
|
||||
await triggerMotionEnd(false);
|
||||
|
||||
expect(document.querySelectorAll('.ant-notification-notice')).toHaveLength(5);
|
||||
expect(document.querySelectorAll('.ant-notification-notice')[4].textContent).toBe(
|
||||
'Notification last',
|
||||
);
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
await triggerMotionEnd(false);
|
||||
|
||||
expect(document.querySelectorAll('.ant-notification-notice')).toHaveLength(0);
|
||||
});
|
||||
});
|
@ -0,0 +1,3 @@
|
||||
import { extendTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
extendTest('notification');
|
6
packages/meta/src/notification/__tests__/demo.test.ts
Normal file
6
packages/meta/src/notification/__tests__/demo.test.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('notification', {
|
||||
testRootProps: false,
|
||||
nameCheckPathOnly: true,
|
||||
});
|
192
packages/meta/src/notification/__tests__/hooks.test.tsx
Normal file
192
packages/meta/src/notification/__tests__/hooks.test.tsx
Normal file
@ -0,0 +1,192 @@
|
||||
import React from 'react';
|
||||
import { StyleProvider, createCache, extractStyle } from '@ant-design/cssinjs';
|
||||
import notification from '..';
|
||||
import { fireEvent, pureRender, render } from '../../../tests/utils';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
|
||||
describe('notification.hooks', () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('should work', () => {
|
||||
const Context = React.createContext('light');
|
||||
|
||||
const Demo: React.FC = () => {
|
||||
const [api, holder] = notification.useNotification();
|
||||
|
||||
return (
|
||||
<ConfigProvider prefixCls="my-test">
|
||||
<Context.Provider value="bamboo">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
api.open({
|
||||
message: null,
|
||||
description: (
|
||||
<Context.Consumer>
|
||||
{(name) => <span className="hook-test-result">{name}</span>}
|
||||
</Context.Consumer>
|
||||
),
|
||||
duration: 0,
|
||||
});
|
||||
}}
|
||||
>
|
||||
test
|
||||
</button>
|
||||
{holder}
|
||||
</Context.Provider>
|
||||
</ConfigProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const { container } = render(<Demo />);
|
||||
|
||||
fireEvent.click(container.querySelector('button')!);
|
||||
expect(document.querySelectorAll('.my-test-notification-notice')).toHaveLength(1);
|
||||
expect(document.querySelector('.hook-test-result')!.textContent).toEqual('bamboo');
|
||||
});
|
||||
|
||||
it('should work with success', () => {
|
||||
const Context = React.createContext('light');
|
||||
|
||||
const Demo: React.FC = () => {
|
||||
const [api, holder] = notification.useNotification();
|
||||
|
||||
return (
|
||||
<ConfigProvider prefixCls="my-test">
|
||||
<Context.Provider value="bamboo">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
api.success({
|
||||
message: null,
|
||||
description: (
|
||||
<Context.Consumer>
|
||||
{(name) => <span className="hook-test-result">{name}</span>}
|
||||
</Context.Consumer>
|
||||
),
|
||||
duration: 0,
|
||||
});
|
||||
}}
|
||||
>
|
||||
test
|
||||
</button>
|
||||
{holder}
|
||||
</Context.Provider>
|
||||
</ConfigProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const { container } = render(<Demo />);
|
||||
fireEvent.click(container.querySelector('button')!);
|
||||
expect(document.querySelectorAll('.my-test-notification-notice')).toHaveLength(1);
|
||||
expect(document.querySelectorAll('.anticon-check-circle')).toHaveLength(1);
|
||||
expect(document.querySelector('.hook-test-result')!.textContent).toEqual('bamboo');
|
||||
});
|
||||
|
||||
it('should be same hook', () => {
|
||||
let count = 0;
|
||||
|
||||
const Demo: React.FC = () => {
|
||||
const [, forceUpdate] = React.useState([]);
|
||||
const [api] = notification.useNotification();
|
||||
React.useEffect(() => {
|
||||
count += 1;
|
||||
expect(count).toEqual(1);
|
||||
forceUpdate([]);
|
||||
}, [api]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
pureRender(<Demo />);
|
||||
});
|
||||
|
||||
describe('not break in effect', () => {
|
||||
it('basic', () => {
|
||||
const Demo = () => {
|
||||
const [api, holder] = notification.useNotification();
|
||||
|
||||
React.useEffect(() => {
|
||||
api.info({
|
||||
message: null,
|
||||
description: <div className="bamboo" />,
|
||||
});
|
||||
}, []);
|
||||
|
||||
return holder;
|
||||
};
|
||||
|
||||
render(<Demo />);
|
||||
|
||||
expect(document.querySelector('.bamboo')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('warning if user call update in render', () => {
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
const Demo = () => {
|
||||
const [api, holder] = notification.useNotification();
|
||||
const calledRef = React.useRef(false);
|
||||
|
||||
if (!calledRef.current) {
|
||||
api.info({
|
||||
message: null,
|
||||
description: <div className="bamboo" />,
|
||||
});
|
||||
calledRef.current = true;
|
||||
}
|
||||
|
||||
return holder;
|
||||
};
|
||||
|
||||
render(<Demo />);
|
||||
|
||||
expect(document.querySelector('.bamboo')).toBeFalsy();
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Notification] You are calling notice in render which will break in React 18 concurrent mode. Please trigger in effect instead.',
|
||||
);
|
||||
|
||||
errorSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
it('not export style in SSR', () => {
|
||||
const cache = createCache();
|
||||
|
||||
const Demo = () => {
|
||||
const [, holder] = notification.useNotification();
|
||||
|
||||
return <StyleProvider cache={cache}>{holder}</StyleProvider>;
|
||||
};
|
||||
|
||||
render(<Demo />);
|
||||
|
||||
const styleText = extractStyle(cache, true);
|
||||
expect(styleText).not.toContain('.ant-notification');
|
||||
});
|
||||
|
||||
it('disable stack', () => {
|
||||
const Demo = () => {
|
||||
const [api, holder] = notification.useNotification({ stack: false });
|
||||
|
||||
React.useEffect(() => {
|
||||
api.info({
|
||||
message: null,
|
||||
description: 'test',
|
||||
});
|
||||
}, []);
|
||||
|
||||
return holder;
|
||||
};
|
||||
|
||||
render(<Demo />);
|
||||
|
||||
expect(document.querySelector('.ant-notification-stack')).toBeFalsy();
|
||||
});
|
||||
});
|
5
packages/meta/src/notification/__tests__/image.test.ts
Normal file
5
packages/meta/src/notification/__tests__/image.test.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { imageDemoTest } from '../../../tests/shared/imageTest';
|
||||
|
||||
describe('Notification image', () => {
|
||||
imageDemoTest('notification');
|
||||
});
|
370
packages/meta/src/notification/__tests__/index.test.tsx
Normal file
370
packages/meta/src/notification/__tests__/index.test.tsx
Normal file
@ -0,0 +1,370 @@
|
||||
import { UserOutlined } from '@ant-design/icons';
|
||||
import React from 'react';
|
||||
import notification, { actWrapper } from '..';
|
||||
import { act, fireEvent } from '../../../tests/utils';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
import { awaitPromise, triggerMotionEnd } from './util';
|
||||
|
||||
describe('notification', () => {
|
||||
beforeAll(() => {
|
||||
actWrapper(act);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
// Clean up
|
||||
notification.destroy();
|
||||
await triggerMotionEnd();
|
||||
|
||||
notification.config({
|
||||
prefixCls: undefined,
|
||||
getContainer: undefined,
|
||||
});
|
||||
|
||||
jest.useRealTimers();
|
||||
|
||||
await awaitPromise();
|
||||
});
|
||||
|
||||
it('not duplicate create holder', async () => {
|
||||
notification.config({
|
||||
prefixCls: 'additional-holder',
|
||||
});
|
||||
|
||||
for (let i = 0; i < 5; i += 1) {
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
duration: 0,
|
||||
});
|
||||
}
|
||||
|
||||
await awaitPromise();
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
expect(document.querySelectorAll('.additional-holder')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should be able to hide manually', async () => {
|
||||
notification.open({
|
||||
message: 'Notification Title 1',
|
||||
duration: 0,
|
||||
key: '1',
|
||||
});
|
||||
await awaitPromise();
|
||||
|
||||
notification.open({
|
||||
message: 'Notification Title 2',
|
||||
duration: 0,
|
||||
key: '2',
|
||||
});
|
||||
|
||||
expect(document.querySelectorAll('.ant-notification-notice')).toHaveLength(2);
|
||||
|
||||
// Close 1
|
||||
notification.destroy('1');
|
||||
|
||||
await triggerMotionEnd();
|
||||
|
||||
expect(document.querySelectorAll('.ant-notification-notice')).toHaveLength(1);
|
||||
|
||||
// Close 2
|
||||
notification.destroy('2');
|
||||
|
||||
await triggerMotionEnd();
|
||||
|
||||
expect(document.querySelectorAll('.ant-notification-notice')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should be able to destroy globally', async () => {
|
||||
notification.open({
|
||||
message: 'Notification Title 1',
|
||||
duration: 0,
|
||||
});
|
||||
await awaitPromise();
|
||||
|
||||
notification.open({
|
||||
message: 'Notification Title 2',
|
||||
duration: 0,
|
||||
});
|
||||
|
||||
expect(document.querySelectorAll('.ant-notification')).toHaveLength(1);
|
||||
expect(document.querySelectorAll('.ant-notification-notice')).toHaveLength(2);
|
||||
|
||||
notification.destroy();
|
||||
|
||||
await triggerMotionEnd();
|
||||
|
||||
expect(document.querySelectorAll('.ant-notification')).toHaveLength(0);
|
||||
expect(document.querySelectorAll('.ant-notification-notice')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should be able to destroy after config', () => {
|
||||
notification.config({
|
||||
bottom: 100,
|
||||
});
|
||||
|
||||
notification.destroy();
|
||||
});
|
||||
|
||||
it('should be able to config rtl', async () => {
|
||||
notification.config({
|
||||
rtl: true,
|
||||
});
|
||||
|
||||
notification.open({
|
||||
message: 'whatever',
|
||||
});
|
||||
await awaitPromise();
|
||||
|
||||
expect(document.querySelectorAll('.ant-notification-rtl')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should be able to global config rootPrefixCls', async () => {
|
||||
ConfigProvider.config({ prefixCls: 'prefix-test', iconPrefixCls: 'bamboo' });
|
||||
|
||||
notification.success({ message: 'Notification Title', duration: 0 });
|
||||
await awaitPromise();
|
||||
|
||||
expect(document.querySelectorAll('.ant-notification-notice')).toHaveLength(0);
|
||||
expect(document.querySelectorAll('.prefix-test-notification-notice')).toHaveLength(1);
|
||||
expect(document.querySelectorAll('.bamboo-check-circle')).toHaveLength(1);
|
||||
|
||||
ConfigProvider.config({ prefixCls: 'ant', iconPrefixCls: null! });
|
||||
});
|
||||
|
||||
it('should be able to config prefixCls', async () => {
|
||||
notification.config({
|
||||
prefixCls: 'prefix-test',
|
||||
});
|
||||
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
duration: 0,
|
||||
});
|
||||
await awaitPromise();
|
||||
|
||||
expect(document.querySelectorAll('.ant-notification-notice')).toHaveLength(0);
|
||||
expect(document.querySelectorAll('.prefix-test-notice')).toHaveLength(1);
|
||||
|
||||
notification.config({
|
||||
prefixCls: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to open with icon', async () => {
|
||||
const iconPrefix = '.ant-notification-notice-icon';
|
||||
|
||||
const list = ['success', 'info', 'warning', 'error'] as const;
|
||||
|
||||
list.forEach((type) => {
|
||||
notification[type]({
|
||||
message: 'Notification Title',
|
||||
duration: 0,
|
||||
description: 'This is the content of the notification.',
|
||||
});
|
||||
});
|
||||
|
||||
await awaitPromise();
|
||||
|
||||
list.forEach((type) => {
|
||||
expect(document.querySelectorAll(`${iconPrefix}-${type}`)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to add parent class for different notification types', async () => {
|
||||
const list = ['success', 'info', 'warning', 'error'] as const;
|
||||
list.forEach((type) => {
|
||||
notification[type]({
|
||||
message: 'Notification Title',
|
||||
duration: 0,
|
||||
description: 'This is the content of the notification.',
|
||||
});
|
||||
});
|
||||
|
||||
await awaitPromise();
|
||||
|
||||
list.forEach((type) => {
|
||||
expect(document.querySelectorAll(`.ant-notification-notice-${type}`)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('trigger onClick', async () => {
|
||||
const onClick = jest.fn();
|
||||
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
duration: 0,
|
||||
onClick,
|
||||
});
|
||||
await awaitPromise();
|
||||
|
||||
expect(document.querySelectorAll('.ant-notification')).toHaveLength(1);
|
||||
|
||||
fireEvent.click(document.querySelector('.ant-notification-notice')!);
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('support closeIcon', async () => {
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
duration: 0,
|
||||
closeIcon: <span className="test-customize-icon" />,
|
||||
});
|
||||
await awaitPromise();
|
||||
|
||||
expect(document.querySelectorAll('.test-customize-icon')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('support config closeIcon', async () => {
|
||||
notification.config({
|
||||
closeIcon: <span className="test-customize-icon" />,
|
||||
});
|
||||
|
||||
// Global Icon
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
duration: 0,
|
||||
});
|
||||
await awaitPromise();
|
||||
|
||||
expect(document.querySelector('.test-customize-icon')).toBeTruthy();
|
||||
|
||||
// Notice Icon
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
duration: 0,
|
||||
closeIcon: <span className="replace-icon" />,
|
||||
});
|
||||
|
||||
expect(document.querySelector('.replace-icon')).toBeTruthy();
|
||||
|
||||
notification.config({
|
||||
closeIcon: null,
|
||||
});
|
||||
});
|
||||
|
||||
it('closeIcon should be update', async () => {
|
||||
const list = ['1', '2'];
|
||||
list.forEach((type) => {
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
closeIcon: <span className={`test-customize-icon-${type}`} />,
|
||||
duration: 0,
|
||||
});
|
||||
});
|
||||
|
||||
await awaitPromise();
|
||||
|
||||
list.forEach((type) => {
|
||||
expect(document.querySelector(`.test-customize-icon-${type}`)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('support config duration', async () => {
|
||||
notification.config({
|
||||
duration: 0,
|
||||
});
|
||||
|
||||
notification.open({
|
||||
message: 'whatever',
|
||||
});
|
||||
await awaitPromise();
|
||||
|
||||
expect(document.querySelector('.ant-notification')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('support icon', async () => {
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
duration: 0,
|
||||
icon: <UserOutlined />,
|
||||
});
|
||||
await awaitPromise();
|
||||
|
||||
expect(document.querySelector('.anticon-user')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('support props', () => {
|
||||
act(() => {
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
duration: 0,
|
||||
props: { 'data-testid': 'test-notification' },
|
||||
});
|
||||
});
|
||||
|
||||
expect(document.querySelectorAll("[data-testid='test-notification']").length).toBe(1);
|
||||
});
|
||||
|
||||
it('support role', async () => {
|
||||
act(() => {
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
duration: 0,
|
||||
role: 'status',
|
||||
});
|
||||
});
|
||||
|
||||
expect(document.querySelectorAll('[role="status"]').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should hide close btn when closeIcon setting to null or false', async () => {
|
||||
notification.config({
|
||||
closeIcon: undefined,
|
||||
});
|
||||
act(() => {
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
duration: 0,
|
||||
className: 'normal',
|
||||
});
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
duration: 0,
|
||||
className: 'custom',
|
||||
closeIcon: <span className="custom-close-icon">Close</span>,
|
||||
});
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
duration: 0,
|
||||
closeIcon: null,
|
||||
className: 'with-null',
|
||||
});
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
duration: 0,
|
||||
closeIcon: false,
|
||||
className: 'with-false',
|
||||
});
|
||||
});
|
||||
await awaitPromise();
|
||||
expect(document.querySelectorAll('.normal .ant-notification-notice-close').length).toBe(1);
|
||||
expect(document.querySelectorAll('.custom .custom-close-icon').length).toBe(1);
|
||||
expect(document.querySelectorAll('.with-null .ant-notification-notice-close').length).toBe(0);
|
||||
expect(document.querySelectorAll('.with-false .ant-notification-notice-close').length).toBe(0);
|
||||
});
|
||||
|
||||
it('style.width could be overrided', async () => {
|
||||
act(() => {
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
duration: 0,
|
||||
style: {
|
||||
width: 600,
|
||||
},
|
||||
className: 'with-style',
|
||||
});
|
||||
});
|
||||
await awaitPromise();
|
||||
expect(document.querySelector('.with-style')).toHaveStyle({ width: '600px' });
|
||||
expect(
|
||||
document.querySelector('.ant-notification-notice-wrapper:has(.width-style)'),
|
||||
).toHaveStyle({ width: '' });
|
||||
});
|
||||
});
|
167
packages/meta/src/notification/__tests__/placement.test.tsx
Normal file
167
packages/meta/src/notification/__tests__/placement.test.tsx
Normal file
@ -0,0 +1,167 @@
|
||||
import notification, { actWrapper } from '..';
|
||||
import { act, fireEvent } from '../../../tests/utils';
|
||||
import type { ArgsProps, GlobalConfigProps } from '../interface';
|
||||
import { awaitPromise, triggerMotionEnd } from './util';
|
||||
|
||||
describe('Notification.placement', () => {
|
||||
function open(args?: Partial<ArgsProps>) {
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
description: 'This is the content of the notification.',
|
||||
...args,
|
||||
});
|
||||
}
|
||||
|
||||
function config(args: Partial<GlobalConfigProps>) {
|
||||
notification.config({
|
||||
...args,
|
||||
});
|
||||
|
||||
act(() => {
|
||||
open();
|
||||
});
|
||||
}
|
||||
|
||||
beforeAll(() => {
|
||||
actWrapper(act);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
// Clean up
|
||||
notification.destroy();
|
||||
await triggerMotionEnd();
|
||||
|
||||
notification.config({
|
||||
prefixCls: undefined,
|
||||
getContainer: undefined,
|
||||
});
|
||||
|
||||
jest.useRealTimers();
|
||||
|
||||
await awaitPromise();
|
||||
});
|
||||
|
||||
describe('placement', () => {
|
||||
it('can be configured globally using the `config` method', async () => {
|
||||
// topLeft
|
||||
config({
|
||||
placement: 'topLeft',
|
||||
top: 50,
|
||||
bottom: 50,
|
||||
});
|
||||
await awaitPromise();
|
||||
|
||||
expect(document.querySelector('.ant-notification-topLeft')).toHaveStyle({
|
||||
top: '50px',
|
||||
left: '0px',
|
||||
bottom: '',
|
||||
});
|
||||
|
||||
// topRight
|
||||
config({
|
||||
placement: 'topRight',
|
||||
top: 100,
|
||||
bottom: 50,
|
||||
});
|
||||
|
||||
expect(document.querySelector('.ant-notification-topRight')).toHaveStyle({
|
||||
top: '100px',
|
||||
right: '0px',
|
||||
bottom: '',
|
||||
});
|
||||
|
||||
// bottomRight
|
||||
config({
|
||||
placement: 'bottomRight',
|
||||
top: 50,
|
||||
bottom: 100,
|
||||
});
|
||||
|
||||
expect(document.querySelector('.ant-notification-bottomRight')).toHaveStyle({
|
||||
top: '',
|
||||
right: '0px',
|
||||
bottom: '100px',
|
||||
});
|
||||
|
||||
// bottomLeft
|
||||
config({
|
||||
placement: 'bottomLeft',
|
||||
top: 100,
|
||||
bottom: 50,
|
||||
});
|
||||
|
||||
expect(document.querySelector('.ant-notification-bottomLeft')).toHaveStyle({
|
||||
top: '',
|
||||
left: '0px',
|
||||
bottom: '50px',
|
||||
});
|
||||
|
||||
// top
|
||||
config({
|
||||
placement: 'top',
|
||||
top: 50,
|
||||
bottom: 60,
|
||||
});
|
||||
await awaitPromise();
|
||||
|
||||
expect(document.querySelector('.ant-notification-top')).toHaveStyle({
|
||||
top: '50px',
|
||||
left: '50%',
|
||||
bottom: '',
|
||||
});
|
||||
|
||||
// bottom
|
||||
config({
|
||||
placement: 'bottom',
|
||||
top: 50,
|
||||
bottom: 60,
|
||||
});
|
||||
await awaitPromise();
|
||||
|
||||
expect(document.querySelector('.ant-notification-bottom')).toHaveStyle({
|
||||
top: '',
|
||||
left: '50%',
|
||||
bottom: '60px',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('mountNode', () => {
|
||||
const $container = document.createElement('div');
|
||||
beforeEach(() => {
|
||||
document.body.appendChild($container);
|
||||
});
|
||||
afterEach(() => {
|
||||
$container.remove();
|
||||
});
|
||||
|
||||
it('can be configured globally using the `config` method', async () => {
|
||||
config({
|
||||
getContainer: () => $container,
|
||||
});
|
||||
await awaitPromise();
|
||||
|
||||
expect($container.querySelector('.ant-notification')).toBeTruthy();
|
||||
notification.destroy();
|
||||
|
||||
// Leave motion
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
document.querySelectorAll('.ant-notification-notice-wrapper').forEach((ele) => {
|
||||
fireEvent.animationEnd(ele);
|
||||
});
|
||||
expect($container.querySelector('.ant-notification')).toBeFalsy();
|
||||
|
||||
// Upcoming notifications are mounted in $container
|
||||
act(() => {
|
||||
open();
|
||||
});
|
||||
expect($container.querySelector('.ant-notification')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,57 @@
|
||||
import React from 'react';
|
||||
import notification, { actWrapper } from '..';
|
||||
import { act, render, waitFakeTimer } from '../../../tests/utils';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
import { awaitPromise, triggerMotionEnd } from './util';
|
||||
|
||||
describe('notification static warning', () => {
|
||||
beforeAll(() => {
|
||||
actWrapper(act);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
// Clean up
|
||||
notification.destroy();
|
||||
await triggerMotionEnd();
|
||||
|
||||
jest.useRealTimers();
|
||||
|
||||
await awaitPromise();
|
||||
});
|
||||
|
||||
// Follow test need keep order
|
||||
it('no warning', async () => {
|
||||
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
notification.open({
|
||||
message: <div className="bamboo" />,
|
||||
duration: 0,
|
||||
});
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(document.querySelector('.bamboo')).toBeTruthy();
|
||||
|
||||
expect(errSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('warning if use theme', async () => {
|
||||
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
render(<ConfigProvider theme={{}} />);
|
||||
|
||||
notification.open({
|
||||
message: <div className="light" />,
|
||||
duration: 0,
|
||||
});
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(document.querySelector('.light')).toBeTruthy();
|
||||
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
"Warning: [antd: notification] Static function can not consume context like dynamic theme. Please use 'App' component instead.",
|
||||
);
|
||||
});
|
||||
});
|
31
packages/meta/src/notification/__tests__/util.ts
Normal file
31
packages/meta/src/notification/__tests__/util.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { act, fireEvent } from '../../../tests/utils';
|
||||
|
||||
export async function awaitPromise() {
|
||||
for (let i = 0; i < 10; i += 1) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
export async function triggerMotionEnd(runAllTimers: boolean = true) {
|
||||
await awaitPromise();
|
||||
|
||||
if (runAllTimers) {
|
||||
// Flush css motion state update
|
||||
for (let i = 0; i < 5; i += 1) {
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// document.querySelectorAll('.ant-notification-fade-leave').forEach(ele => {
|
||||
// fireEvent.animationEnd(ele);
|
||||
// });
|
||||
document.querySelectorAll('[role="alert"]').forEach((ele) => {
|
||||
// close > notice > notice-wrapper
|
||||
fireEvent.animationEnd(ele.parentNode?.parentNode?.parentNode!);
|
||||
});
|
||||
|
||||
await awaitPromise();
|
||||
}
|
7
packages/meta/src/notification/demo/basic.md
Normal file
7
packages/meta/src/notification/demo/basic.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
静态方法无法消费 Context,推荐优先使用 Hooks 版本。
|
||||
|
||||
## en-US
|
||||
|
||||
Static methods cannot consume Context. Please use hooks first.
|
20
packages/meta/src/notification/demo/basic.tsx
Normal file
20
packages/meta/src/notification/demo/basic.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { Button, notification } from 'antd';
|
||||
|
||||
const openNotification = () => {
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
description:
|
||||
'This is the content of the notification. This is the content of the notification. This is the content of the notification.',
|
||||
onClick: () => {
|
||||
console.log('Notification Clicked!');
|
||||
},
|
||||
});
|
||||
};
|
||||
const App: React.FC = () => (
|
||||
<Button type="primary" onClick={openNotification}>
|
||||
Open the notification box
|
||||
</Button>
|
||||
);
|
||||
|
||||
export default App;
|
7
packages/meta/src/notification/demo/custom-icon.md
Normal file
7
packages/meta/src/notification/demo/custom-icon.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
图标可以被自定义。
|
||||
|
||||
## en-US
|
||||
|
||||
The icon can be customized to any react node.
|
27
packages/meta/src/notification/demo/custom-icon.tsx
Normal file
27
packages/meta/src/notification/demo/custom-icon.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
import { SmileOutlined } from '@ant-design/icons';
|
||||
import { Button, notification } from 'antd';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [api, contextHolder] = notification.useNotification();
|
||||
|
||||
const openNotification = () => {
|
||||
api.open({
|
||||
message: 'Notification Title',
|
||||
description:
|
||||
'This is the content of the notification. This is the content of the notification. This is the content of the notification.',
|
||||
icon: <SmileOutlined style={{ color: '#108ee9' }} />,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{contextHolder}
|
||||
<Button type="primary" onClick={openNotification}>
|
||||
Open the notification box
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
7
packages/meta/src/notification/demo/custom-style.md
Normal file
7
packages/meta/src/notification/demo/custom-style.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
使用 style 和 className 来定义样式。
|
||||
|
||||
## en-US
|
||||
|
||||
The style and className are available to customize Notification.
|
28
packages/meta/src/notification/demo/custom-style.tsx
Normal file
28
packages/meta/src/notification/demo/custom-style.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import { Button, notification } from 'antd';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [api, contextHolder] = notification.useNotification();
|
||||
|
||||
const openNotification = () => {
|
||||
api.open({
|
||||
message: 'Notification Title',
|
||||
description:
|
||||
'This is the content of the notification. This is the content of the notification. This is the content of the notification.',
|
||||
className: 'custom-class',
|
||||
style: {
|
||||
width: 600,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{contextHolder}
|
||||
<Button type="primary" onClick={openNotification}>
|
||||
Open the notification box
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default App;
|
7
packages/meta/src/notification/demo/duration.md
Normal file
7
packages/meta/src/notification/demo/duration.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
自定义通知框自动关闭的延时,默认 `4.5s`,取消自动关闭只要将该值设为 `0` 即可。
|
||||
|
||||
## en-US
|
||||
|
||||
`Duration` can be used to specify how long the notification stays open. After the duration time elapses, the notification closes automatically. If not specified, default value is 4.5 seconds. If you set the value to 0, the notification box will never close automatically.
|
26
packages/meta/src/notification/demo/duration.tsx
Normal file
26
packages/meta/src/notification/demo/duration.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import { Button, notification } from 'antd';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [api, contextHolder] = notification.useNotification();
|
||||
|
||||
const openNotification = () => {
|
||||
api.open({
|
||||
message: 'Notification Title',
|
||||
description:
|
||||
'I will never close automatically. This is a purposely very very long description that has many many characters and words.',
|
||||
duration: 0,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{contextHolder}
|
||||
<Button type="primary" onClick={openNotification}>
|
||||
Open the notification box
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
7
packages/meta/src/notification/demo/hooks.md
Normal file
7
packages/meta/src/notification/demo/hooks.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
通过 `notification.useNotification` 创建支持读取 context 的 `contextHolder`。请注意,我们推荐通过顶层注册的方式代替 `message` 静态方法,因为静态方法无法消费上下文,因而 ConfigProvider 的数据也不会生效。
|
||||
|
||||
## en-US
|
||||
|
||||
Use `notification.useNotification` to get `contextHolder` with context accessible issue. Please note that, we recommend to use top level registration instead of `notification` static method, because static method cannot consume context, and ConfigProvider data will not work.
|
66
packages/meta/src/notification/demo/hooks.tsx
Normal file
66
packages/meta/src/notification/demo/hooks.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
import {
|
||||
RadiusBottomleftOutlined,
|
||||
RadiusBottomrightOutlined,
|
||||
RadiusUpleftOutlined,
|
||||
RadiusUprightOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Button, Divider, Space, notification } from 'antd';
|
||||
import type { NotificationPlacement } from 'antd/es/notification/interface';
|
||||
|
||||
const Context = React.createContext({ name: 'Default' });
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [api, contextHolder] = notification.useNotification();
|
||||
|
||||
const openNotification = (placement: NotificationPlacement) => {
|
||||
api.info({
|
||||
message: `Notification ${placement}`,
|
||||
description: <Context.Consumer>{({ name }) => `Hello, ${name}!`}</Context.Consumer>,
|
||||
placement,
|
||||
});
|
||||
};
|
||||
|
||||
const contextValue = useMemo(() => ({ name: 'Ant Design' }), []);
|
||||
|
||||
return (
|
||||
<Context.Provider value={contextValue}>
|
||||
{contextHolder}
|
||||
<Space>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => openNotification('topLeft')}
|
||||
icon={<RadiusUpleftOutlined />}
|
||||
>
|
||||
topLeft
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => openNotification('topRight')}
|
||||
icon={<RadiusUprightOutlined />}
|
||||
>
|
||||
topRight
|
||||
</Button>
|
||||
</Space>
|
||||
<Divider />
|
||||
<Space>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => openNotification('bottomLeft')}
|
||||
icon={<RadiusBottomleftOutlined />}
|
||||
>
|
||||
bottomLeft
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => openNotification('bottomRight')}
|
||||
icon={<RadiusBottomrightOutlined />}
|
||||
>
|
||||
bottomRight
|
||||
</Button>
|
||||
</Space>
|
||||
</Context.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
7
packages/meta/src/notification/demo/placement.md
Normal file
7
packages/meta/src/notification/demo/placement.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
使用 `placement` 可以配置通知从右上角、右下角、左下角、左上角弹出。
|
||||
|
||||
## en-US
|
||||
|
||||
A notification box can appear from the `topRight`, `bottomRight`, `bottomLeft` or `topLeft` of the viewport via `placement`.
|
78
packages/meta/src/notification/demo/placement.tsx
Normal file
78
packages/meta/src/notification/demo/placement.tsx
Normal file
@ -0,0 +1,78 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
BorderBottomOutlined,
|
||||
BorderTopOutlined,
|
||||
RadiusBottomleftOutlined,
|
||||
RadiusBottomrightOutlined,
|
||||
RadiusUpleftOutlined,
|
||||
RadiusUprightOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { Button, Divider, notification, Space } from 'antd';
|
||||
import type { NotificationPlacement } from 'antd/es/notification/interface';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [api, contextHolder] = notification.useNotification();
|
||||
|
||||
const openNotification = (placement: NotificationPlacement) => {
|
||||
api.info({
|
||||
message: `Notification ${placement}`,
|
||||
description:
|
||||
'This is the content of the notification. This is the content of the notification. This is the content of the notification.',
|
||||
placement,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{contextHolder}
|
||||
<Space>
|
||||
<Button type="primary" onClick={() => openNotification('top')} icon={<BorderTopOutlined />}>
|
||||
top
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => openNotification('bottom')}
|
||||
icon={<BorderBottomOutlined />}
|
||||
>
|
||||
bottom
|
||||
</Button>
|
||||
</Space>
|
||||
<Divider />
|
||||
<Space>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => openNotification('topLeft')}
|
||||
icon={<RadiusUpleftOutlined />}
|
||||
>
|
||||
topLeft
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => openNotification('topRight')}
|
||||
icon={<RadiusUprightOutlined />}
|
||||
>
|
||||
topRight
|
||||
</Button>
|
||||
</Space>
|
||||
<Divider />
|
||||
<Space>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => openNotification('bottomLeft')}
|
||||
icon={<RadiusBottomleftOutlined />}
|
||||
>
|
||||
bottomLeft
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => openNotification('bottomRight')}
|
||||
icon={<RadiusBottomrightOutlined />}
|
||||
>
|
||||
bottomRight
|
||||
</Button>
|
||||
</Space>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
7
packages/meta/src/notification/demo/render-panel.md
Normal file
7
packages/meta/src/notification/demo/render-panel.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
调试用组件,请勿直接使用。
|
||||
|
||||
## en-US
|
||||
|
||||
Debug usage. Do not use in your production.
|
18
packages/meta/src/notification/demo/render-panel.tsx
Normal file
18
packages/meta/src/notification/demo/render-panel.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import { Button, notification } from 'antd';
|
||||
|
||||
/** Test usage. Do not use in your production. */
|
||||
const { _InternalPanelDoNotUseOrYouWillBeFired: InternalPanel } = notification;
|
||||
|
||||
export default () => (
|
||||
<InternalPanel
|
||||
message="Hello World!"
|
||||
description="Hello World?"
|
||||
type="success"
|
||||
btn={
|
||||
<Button type="primary" size="small">
|
||||
My Button
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
);
|
7
packages/meta/src/notification/demo/stack.md
Normal file
7
packages/meta/src/notification/demo/stack.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
堆叠配置,默认开启。超过 3 个以上的消息会被自动收起,可以通过 `threshold` 来设置不会被收起的最大数量。
|
||||
|
||||
## en-US
|
||||
|
||||
Stack configuration, enabled by default. More than 3 notifications will be automatically stacked, and could be changed by `threshold`.
|
59
packages/meta/src/notification/demo/stack.tsx
Normal file
59
packages/meta/src/notification/demo/stack.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Button, Divider, InputNumber, notification, Space, Switch } from 'antd';
|
||||
|
||||
const Context = React.createContext({ name: 'Default' });
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [enabled, setEnabled] = React.useState(true);
|
||||
const [threshold, setThreshold] = React.useState(3);
|
||||
const [api, contextHolder] = notification.useNotification({
|
||||
stack: enabled
|
||||
? {
|
||||
threshold,
|
||||
}
|
||||
: false,
|
||||
});
|
||||
|
||||
const openNotification = () => {
|
||||
api.open({
|
||||
message: 'Notification Title',
|
||||
description: `${Array(Math.round(Math.random() * 5) + 1)
|
||||
.fill('This is the content of the notification.')
|
||||
.join('\n')}`,
|
||||
duration: null,
|
||||
});
|
||||
};
|
||||
|
||||
const contextValue = useMemo(() => ({ name: 'Ant Design' }), []);
|
||||
|
||||
return (
|
||||
<Context.Provider value={contextValue}>
|
||||
{contextHolder}
|
||||
<div>
|
||||
<Space size="large">
|
||||
<Space style={{ width: '100%' }}>
|
||||
<span>Enabled: </span>
|
||||
<Switch checked={enabled} onChange={(v) => setEnabled(v)} />
|
||||
</Space>
|
||||
<Space style={{ width: '100%' }}>
|
||||
<span>Threshold: </span>
|
||||
<InputNumber
|
||||
disabled={!enabled}
|
||||
value={threshold}
|
||||
step={1}
|
||||
min={1}
|
||||
max={10}
|
||||
onChange={(v) => setThreshold(v || 0)}
|
||||
/>
|
||||
</Space>
|
||||
</Space>
|
||||
<Divider />
|
||||
<Button type="primary" onClick={openNotification}>
|
||||
Open the notification box
|
||||
</Button>
|
||||
</div>
|
||||
</Context.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
7
packages/meta/src/notification/demo/update.md
Normal file
7
packages/meta/src/notification/demo/update.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
可以通过唯一的 key 来更新内容。
|
||||
|
||||
## en-US
|
||||
|
||||
Update content with unique key.
|
34
packages/meta/src/notification/demo/update.tsx
Normal file
34
packages/meta/src/notification/demo/update.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
import { Button, notification } from 'antd';
|
||||
|
||||
const key = 'updatable';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [api, contextHolder] = notification.useNotification();
|
||||
const openNotification = () => {
|
||||
api.open({
|
||||
key,
|
||||
message: 'Notification Title',
|
||||
description: 'description.',
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
api.open({
|
||||
key,
|
||||
message: 'New Title',
|
||||
description: 'New description.',
|
||||
});
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{contextHolder}
|
||||
<Button type="primary" onClick={openNotification}>
|
||||
Open the notification box
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
7
packages/meta/src/notification/demo/with-btn.md
Normal file
7
packages/meta/src/notification/demo/with-btn.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
自定义关闭按钮的样式和文字。
|
||||
|
||||
## en-US
|
||||
|
||||
To customize the style or font of the close button.
|
45
packages/meta/src/notification/demo/with-btn.tsx
Normal file
45
packages/meta/src/notification/demo/with-btn.tsx
Normal file
@ -0,0 +1,45 @@
|
||||
import React from 'react';
|
||||
import { Button, notification, Space } from 'antd';
|
||||
|
||||
const close = () => {
|
||||
console.log(
|
||||
'Notification was closed. Either the close button was clicked or duration time elapsed.',
|
||||
);
|
||||
};
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [api, contextHolder] = notification.useNotification();
|
||||
|
||||
const openNotification = () => {
|
||||
const key = `open${Date.now()}`;
|
||||
const btn = (
|
||||
<Space>
|
||||
<Button type="link" size="small" onClick={() => api.destroy()}>
|
||||
Destroy All
|
||||
</Button>
|
||||
<Button type="primary" size="small" onClick={() => api.destroy(key)}>
|
||||
Confirm
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
api.open({
|
||||
message: 'Notification Title',
|
||||
description:
|
||||
'A function will be be called after the notification is closed (automatically after the "duration" time of manually).',
|
||||
btn,
|
||||
key,
|
||||
onClose: close,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{contextHolder}
|
||||
<Button type="primary" onClick={openNotification}>
|
||||
Open the notification box
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
7
packages/meta/src/notification/demo/with-icon.md
Normal file
7
packages/meta/src/notification/demo/with-icon.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
通知提醒框左侧有图标。
|
||||
|
||||
## en-US
|
||||
|
||||
A notification box with a icon at the left side.
|
30
packages/meta/src/notification/demo/with-icon.tsx
Normal file
30
packages/meta/src/notification/demo/with-icon.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import { Button, notification, Space } from 'antd';
|
||||
|
||||
type NotificationType = 'success' | 'info' | 'warning' | 'error';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [api, contextHolder] = notification.useNotification();
|
||||
|
||||
const openNotificationWithIcon = (type: NotificationType) => {
|
||||
api[type]({
|
||||
message: 'Notification Title',
|
||||
description:
|
||||
'This is the content of the notification. This is the content of the notification. This is the content of the notification.',
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{contextHolder}
|
||||
<Space>
|
||||
<Button onClick={() => openNotificationWithIcon('success')}>Success</Button>
|
||||
<Button onClick={() => openNotificationWithIcon('info')}>Info</Button>
|
||||
<Button onClick={() => openNotificationWithIcon('warning')}>Warning</Button>
|
||||
<Button onClick={() => openNotificationWithIcon('error')}>Error</Button>
|
||||
</Space>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
253
packages/meta/src/notification/index.tsx
Normal file
253
packages/meta/src/notification/index.tsx
Normal file
@ -0,0 +1,253 @@
|
||||
import * as React from 'react';
|
||||
import { render } from 'rc-util/lib/React/render';
|
||||
|
||||
import ConfigProvider, { globalConfig, warnContext } from '../config-provider';
|
||||
import type { ArgsProps, GlobalConfigProps, NotificationInstance } from './interface';
|
||||
import PurePanel from './PurePanel';
|
||||
import useNotification, { useInternalNotification } from './useNotification';
|
||||
|
||||
export type { ArgsProps };
|
||||
|
||||
let notification: GlobalNotification | null = null;
|
||||
|
||||
let act: (callback: VoidFunction) => Promise<void> | void = (callback: VoidFunction) => callback();
|
||||
|
||||
interface GlobalNotification {
|
||||
fragment: DocumentFragment;
|
||||
instance?: NotificationInstance | null;
|
||||
sync?: VoidFunction;
|
||||
}
|
||||
|
||||
type Task =
|
||||
| {
|
||||
type: 'open';
|
||||
config: ArgsProps;
|
||||
}
|
||||
| {
|
||||
type: 'destroy';
|
||||
key: React.Key;
|
||||
};
|
||||
|
||||
let taskQueue: Task[] = [];
|
||||
|
||||
let defaultGlobalConfig: GlobalConfigProps = {};
|
||||
|
||||
function getGlobalContext() {
|
||||
const {
|
||||
prefixCls: globalPrefixCls,
|
||||
getContainer: globalGetContainer,
|
||||
rtl,
|
||||
maxCount,
|
||||
top,
|
||||
bottom,
|
||||
} = defaultGlobalConfig;
|
||||
const mergedPrefixCls = globalPrefixCls ?? globalConfig().getPrefixCls('notification');
|
||||
const mergedContainer = globalGetContainer?.() || document.body;
|
||||
|
||||
return {
|
||||
prefixCls: mergedPrefixCls,
|
||||
getContainer: () => mergedContainer!,
|
||||
rtl,
|
||||
maxCount,
|
||||
top,
|
||||
bottom,
|
||||
};
|
||||
}
|
||||
|
||||
interface GlobalHolderRef {
|
||||
instance: NotificationInstance;
|
||||
sync: () => void;
|
||||
}
|
||||
|
||||
const GlobalHolder = React.forwardRef<GlobalHolderRef, {}>((_, ref) => {
|
||||
const [notificationConfig, setNotificationConfig] =
|
||||
React.useState<GlobalConfigProps>(getGlobalContext);
|
||||
|
||||
const [api, holder] = useInternalNotification(notificationConfig);
|
||||
|
||||
const global = globalConfig();
|
||||
const rootPrefixCls = global.getRootPrefixCls();
|
||||
const rootIconPrefixCls = global.getIconPrefixCls();
|
||||
const theme = global.getTheme();
|
||||
|
||||
const sync = () => {
|
||||
setNotificationConfig(getGlobalContext);
|
||||
};
|
||||
|
||||
React.useEffect(sync, []);
|
||||
|
||||
React.useImperativeHandle(ref, () => {
|
||||
const instance: NotificationInstance = { ...api };
|
||||
// @ts-ignore
|
||||
Object.keys(instance).forEach((method: keyof NotificationInstance) => {
|
||||
instance[method] = (...args: any[]) => {
|
||||
sync();
|
||||
return (api as any)[method](...args);
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
instance,
|
||||
sync,
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<ConfigProvider prefixCls={rootPrefixCls} iconPrefixCls={rootIconPrefixCls} theme={theme}>
|
||||
{holder}
|
||||
</ConfigProvider>
|
||||
);
|
||||
});
|
||||
|
||||
function flushNotice() {
|
||||
if (!notification) {
|
||||
const holderFragment = document.createDocumentFragment();
|
||||
|
||||
const newNotification: GlobalNotification = {
|
||||
fragment: holderFragment,
|
||||
};
|
||||
|
||||
notification = newNotification;
|
||||
|
||||
// Delay render to avoid sync issue
|
||||
act(() => {
|
||||
render(
|
||||
<GlobalHolder
|
||||
ref={(node) => {
|
||||
const { instance, sync } = node || {};
|
||||
|
||||
Promise.resolve().then(() => {
|
||||
if (!newNotification.instance && instance) {
|
||||
newNotification.instance = instance;
|
||||
newNotification.sync = sync;
|
||||
flushNotice();
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>,
|
||||
holderFragment,
|
||||
);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Notification not ready
|
||||
if (!notification.instance) {
|
||||
return;
|
||||
}
|
||||
|
||||
// >>> Execute task
|
||||
taskQueue.forEach((task) => {
|
||||
// eslint-disable-next-line default-case
|
||||
switch (task.type) {
|
||||
case 'open': {
|
||||
act(() => {
|
||||
notification!.instance!.open({
|
||||
...defaultGlobalConfig,
|
||||
...task.config,
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case 'destroy':
|
||||
act(() => {
|
||||
notification?.instance!.destroy(task.key);
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Clean up
|
||||
taskQueue = [];
|
||||
}
|
||||
|
||||
// ==============================================================================
|
||||
// == Export ==
|
||||
// ==============================================================================
|
||||
|
||||
function setNotificationGlobalConfig(config: GlobalConfigProps) {
|
||||
defaultGlobalConfig = {
|
||||
...defaultGlobalConfig,
|
||||
...config,
|
||||
};
|
||||
|
||||
// Trigger sync for it
|
||||
act(() => {
|
||||
notification?.sync?.();
|
||||
});
|
||||
}
|
||||
|
||||
function open(config: ArgsProps) {
|
||||
// Warning if exist theme
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
warnContext('notification');
|
||||
}
|
||||
|
||||
taskQueue.push({
|
||||
type: 'open',
|
||||
config,
|
||||
});
|
||||
flushNotice();
|
||||
}
|
||||
|
||||
function destroy(key: React.Key) {
|
||||
taskQueue.push({
|
||||
type: 'destroy',
|
||||
key,
|
||||
});
|
||||
flushNotice();
|
||||
}
|
||||
|
||||
interface BaseMethods {
|
||||
open: (config: ArgsProps) => void;
|
||||
destroy: (key?: React.Key) => void;
|
||||
config: (config: GlobalConfigProps) => void;
|
||||
useNotification: typeof useNotification;
|
||||
/** @private Internal Component. Do not use in your production. */
|
||||
_InternalPanelDoNotUseOrYouWillBeFired: typeof PurePanel;
|
||||
}
|
||||
|
||||
type StaticFn = (config: ArgsProps) => void;
|
||||
|
||||
interface NoticeMethods {
|
||||
success: StaticFn;
|
||||
info: StaticFn;
|
||||
warning: StaticFn;
|
||||
error: StaticFn;
|
||||
}
|
||||
|
||||
const methods: (keyof NoticeMethods)[] = ['success', 'info', 'warning', 'error'];
|
||||
|
||||
const baseStaticMethods: BaseMethods = {
|
||||
open,
|
||||
// @ts-ignore
|
||||
destroy,
|
||||
config: setNotificationGlobalConfig,
|
||||
useNotification,
|
||||
_InternalPanelDoNotUseOrYouWillBeFired: PurePanel,
|
||||
};
|
||||
|
||||
const staticMethods = baseStaticMethods as NoticeMethods & BaseMethods;
|
||||
|
||||
methods.forEach((type: keyof NoticeMethods) => {
|
||||
staticMethods[type] = (config) => open({ ...config, type });
|
||||
});
|
||||
|
||||
// ==============================================================================
|
||||
// == Test ==
|
||||
// ==============================================================================
|
||||
const noop = () => {};
|
||||
|
||||
/** @internal Only Work in test env */
|
||||
// eslint-disable-next-line import/no-mutable-exports
|
||||
export let actWrapper: (wrapper: any) => void = noop;
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
actWrapper = (wrapper) => {
|
||||
act = wrapper;
|
||||
};
|
||||
}
|
||||
|
||||
export default staticMethods;
|
142
packages/meta/src/notification/index.zh-CN.md
Normal file
142
packages/meta/src/notification/index.zh-CN.md
Normal file
@ -0,0 +1,142 @@
|
||||
---
|
||||
category: Components
|
||||
group: 反馈
|
||||
noinstant: true
|
||||
title: Notification
|
||||
subtitle: 通知提醒框
|
||||
demo:
|
||||
cols: 2
|
||||
---
|
||||
|
||||
全局展示通知提醒信息。
|
||||
|
||||
## 何时使用
|
||||
|
||||
在系统四个角显示通知提醒信息。经常用于以下情况:
|
||||
|
||||
- 较为复杂的通知内容。
|
||||
- 带有交互的通知,给出用户下一步的行动点。
|
||||
- 系统主动推送。
|
||||
|
||||
## 代码演示
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<code src="./demo/hooks.tsx">Hooks 调用(推荐)</code>
|
||||
<code src="./demo/duration.tsx">自动关闭的延时</code>
|
||||
<code src="./demo/with-icon.tsx">带有图标的通知提醒框</code>
|
||||
<code src="./demo/with-btn.tsx">自定义按钮</code>
|
||||
<code src="./demo/custom-icon.tsx">自定义图标</code>
|
||||
<code src="./demo/placement.tsx">位置</code>
|
||||
<code src="./demo/custom-style.tsx">自定义样式</code>
|
||||
<code src="./demo/update.tsx">更新消息内容</code>
|
||||
<code src="./demo/stack.tsx" version="5.10.0">堆叠</code>
|
||||
<code src="./demo/basic.tsx">静态方法(不推荐)</code>
|
||||
<code src="./demo/render-panel.tsx" debug>_InternalPanelDoNotUseOrYouWillBeFired</code>
|
||||
|
||||
## API
|
||||
|
||||
通用属性参考:[通用属性](/docs/react/common-props)
|
||||
|
||||
- `notification.success(config)`
|
||||
- `notification.error(config)`
|
||||
- `notification.info(config)`
|
||||
- `notification.warning(config)`
|
||||
- `notification.open(config)`
|
||||
- `notification.destroy(key?: String)`
|
||||
|
||||
config 参数如下:
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| btn | 自定义关闭按钮 | ReactNode | - | - |
|
||||
| className | 自定义 CSS class | string | - | - |
|
||||
| closeIcon | 自定义关闭图标 | boolean \| ReactNode | true | 5.7.0:设置为 null 或 false 时隐藏关闭按钮 |
|
||||
| description | 通知提醒内容,必选 | ReactNode | - | - |
|
||||
| duration | 默认 4.5 秒后自动关闭,配置为 null 则不自动关闭 | number | 4.5 | - |
|
||||
| icon | 自定义图标 | ReactNode | - | - |
|
||||
| key | 当前通知唯一标志 | string | - | - |
|
||||
| message | 通知提醒标题,必选 | ReactNode | - | - |
|
||||
| placement | 弹出位置,可选 `topLeft` `topRight` `bottomLeft` `bottomRight` | string | `topRight` | - |
|
||||
| style | 自定义内联样式 | [CSSProperties](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/e434515761b36830c3e58a970abf5186f005adac/types/react/index.d.ts#L794) | - | - |
|
||||
| role | 供屏幕阅读器识别的通知内容语义,默认为 `alert`。此情况下屏幕阅读器会立即打断当前正在阅读的其他内容,转而阅读通知内容 | `alert \| status` | `alert` | 5.6.0 |
|
||||
| onClick | 点击通知时触发的回调函数 | function | - | - |
|
||||
| onClose | 当通知关闭时触发 | function | - | - |
|
||||
| props | 透传至通知 `div` 上的 props 对象,支持传入 `data-*` `aria-*` 或 `role` 作为对象的属性。需要注意的是,虽然在 TypeScript 类型中声明的类型支持传入 `data-*` 作为对象的属性,但目前只允许传入 `data-testid` 作为对象的属性。 详见 https://github.com/microsoft/TypeScript/issues/28960 | Object | - | - |
|
||||
|
||||
- `notification.useNotification(config)`
|
||||
|
||||
config 参数如下:
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| bottom | 消息从底部弹出时,距离底部的位置,单位像素 | number | 24 | |
|
||||
| closeIcon | 自定义关闭图标 | boolean \| ReactNode | true | 5.7.0:设置为 null 或 false 时隐藏关闭按钮 |
|
||||
| getContainer | 配置渲染节点的输出位置 | () => HTMLNode | () => document.body | |
|
||||
| placement | 弹出位置,可选 `top` `topLeft` `topRight` `bottom` `bottomLeft` `bottomRight` | string | `topRight` | |
|
||||
| rtl | 是否开启 RTL 模式 | boolean | false | |
|
||||
| stack | 堆叠模式,超过阈值时会将所有消息收起 | boolean \| `{ threshold: number }` | `{ threshold: 3 }` | 5.10.0 |
|
||||
| top | 消息从顶部弹出时,距离顶部的位置,单位像素 | number | 24 | |
|
||||
| maxCount | 最大显示数,超过限制时,最早的消息会被自动关闭 | number | - | 4.17.0 |
|
||||
|
||||
### 全局配置
|
||||
|
||||
还提供了一个全局配置方法,在调用前提前配置,全局一次生效。
|
||||
|
||||
`notification.config(options)`
|
||||
|
||||
> 当你使用 `ConfigProvider` 进行全局化配置时,系统会默认自动开启 RTL 模式。(4.3.0+)
|
||||
>
|
||||
> 当你想单独使用,可通过如下设置开启 RTL 模式。
|
||||
|
||||
```js
|
||||
notification.config({
|
||||
placement: 'bottomRight',
|
||||
bottom: 50,
|
||||
duration: 3,
|
||||
rtl: true,
|
||||
});
|
||||
```
|
||||
|
||||
#### notification.config
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| bottom | 消息从底部弹出时,距离底部的位置,单位像素 | number | 24 | |
|
||||
| closeIcon | 自定义关闭图标 | boolean \| ReactNode | true | 5.7.0:设置为 null 或 false 时隐藏关闭按钮 |
|
||||
| duration | 默认自动关闭延时,单位秒 | number | 4.5 | |
|
||||
| getContainer | 配置渲染节点的输出位置,但依旧为全屏展示 | () => HTMLNode | () => document.body | |
|
||||
| placement | 弹出位置,可选 `top` `topLeft` `topRight` `bottom` `bottomLeft` `bottomRight` | string | `topRight` | |
|
||||
| rtl | 是否开启 RTL 模式 | boolean | false | |
|
||||
| top | 消息从顶部弹出时,距离顶部的位置,单位像素 | number | 24 | |
|
||||
| maxCount | 最大显示数,超过限制时,最早的消息会被自动关闭 | number | - | 4.17.0 |
|
||||
|
||||
|
||||
## FAQ
|
||||
|
||||
### 为什么 notification 不能获取 context、redux 的内容和 ConfigProvider 的 `locale/prefixCls/theme` 等配置?
|
||||
|
||||
直接调用 notification 方法,antd 会通过 `ReactDOM.render` 动态创建新的 React 实体。其 context 与当前代码所在 context 并不相同,因而无法获取 context 信息。
|
||||
|
||||
当你需要 context 信息(例如 ConfigProvider 配置的内容)时,可以通过 `notification.useNotification` 方法会返回 `api` 实体以及 `contextHolder` 节点。将其插入到你需要获取 context 位置即可:
|
||||
|
||||
```ts
|
||||
const [api, contextHolder] = notification.useNotification();
|
||||
|
||||
return (
|
||||
<Context1.Provider value="Ant">
|
||||
{/* contextHolder 在 Context1 内,它可以获得 Context1 的 context */}
|
||||
{contextHolder}
|
||||
<Context2.Provider value="Design">
|
||||
{/* contextHolder 在 Context2 外,因而不会获得 Context2 的 context */}
|
||||
</Context2.Provider>
|
||||
</Context1.Provider>
|
||||
);
|
||||
```
|
||||
|
||||
**异同**:通过 hooks 创建的 `contextHolder` 必须插入到子元素节点中才会生效,当你不需要上下文信息时请直接调用。
|
||||
|
||||
> 可通过 [App 包裹组件](/components/app-cn) 简化 `useNotification` 等方法需要手动植入 contextHolder 的问题。
|
||||
|
||||
### 静态方法如何设置 prefixCls ?
|
||||
|
||||
你可以通过 [`ConfigProvider.config`](/components/config-provider-cn#configproviderconfig-4130) 进行设置。
|
70
packages/meta/src/notification/interface.ts
Normal file
70
packages/meta/src/notification/interface.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import type * as React from 'react';
|
||||
|
||||
interface DivProps extends React.HTMLProps<HTMLDivElement> {
|
||||
'data-testid'?: string;
|
||||
}
|
||||
|
||||
export const NotificationPlacements = [
|
||||
'top',
|
||||
'topLeft',
|
||||
'topRight',
|
||||
'bottom',
|
||||
'bottomLeft',
|
||||
'bottomRight',
|
||||
] as const;
|
||||
export type NotificationPlacement = typeof NotificationPlacements[number];
|
||||
|
||||
export type IconType = 'success' | 'info' | 'error' | 'warning';
|
||||
|
||||
export interface ArgsProps {
|
||||
message: React.ReactNode;
|
||||
description?: React.ReactNode;
|
||||
btn?: React.ReactNode;
|
||||
key?: React.Key;
|
||||
onClose?: () => void;
|
||||
duration?: number | null;
|
||||
icon?: React.ReactNode;
|
||||
placement?: NotificationPlacement;
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
readonly type?: IconType;
|
||||
onClick?: () => void;
|
||||
closeIcon?: boolean | React.ReactNode;
|
||||
props?: DivProps;
|
||||
role?: 'alert' | 'status';
|
||||
}
|
||||
|
||||
type StaticFn = (args: ArgsProps) => void;
|
||||
|
||||
export interface NotificationInstance {
|
||||
success: StaticFn;
|
||||
error: StaticFn;
|
||||
info: StaticFn;
|
||||
warning: StaticFn;
|
||||
open: StaticFn;
|
||||
destroy(key?: React.Key): void;
|
||||
}
|
||||
|
||||
export interface GlobalConfigProps {
|
||||
top?: number;
|
||||
bottom?: number;
|
||||
duration?: number;
|
||||
prefixCls?: string;
|
||||
getContainer?: () => HTMLElement | ShadowRoot;
|
||||
placement?: NotificationPlacement;
|
||||
closeIcon?: React.ReactNode;
|
||||
rtl?: boolean;
|
||||
maxCount?: number;
|
||||
props?: DivProps;
|
||||
}
|
||||
|
||||
export interface NotificationConfig {
|
||||
top?: number;
|
||||
bottom?: number;
|
||||
prefixCls?: string;
|
||||
getContainer?: () => HTMLElement | ShadowRoot;
|
||||
placement?: NotificationPlacement;
|
||||
maxCount?: number;
|
||||
rtl?: boolean;
|
||||
stack?: boolean | { threshold?: number };
|
||||
}
|
307
packages/meta/src/notification/style/index.ts
Normal file
307
packages/meta/src/notification/style/index.ts
Normal file
@ -0,0 +1,307 @@
|
||||
import type { CSSObject } from '@ant-design/cssinjs';
|
||||
import { Keyframes, unit } from '@ant-design/cssinjs';
|
||||
|
||||
import { CONTAINER_MAX_OFFSET } from '../../_util/hooks/useZIndex';
|
||||
import { resetComponent } from '../../style';
|
||||
import type { AliasToken, FullToken, GenerateStyle } from '../../theme/internal';
|
||||
import { genStyleHooks, mergeToken } from '../../theme/internal';
|
||||
import genNotificationPlacementStyle from './placement';
|
||||
import genStackStyle from './stack';
|
||||
import type { GenStyleFn } from '../../theme/util/genComponentStyleHook';
|
||||
|
||||
/** Component only token. Which will handle additional calculation of alias token */
|
||||
export interface ComponentToken {
|
||||
/**
|
||||
* @desc 提醒框 z-index
|
||||
* @descEN z-index of Notification
|
||||
*/
|
||||
zIndexPopup: number;
|
||||
/**
|
||||
* @desc 提醒框宽度
|
||||
* @descEN Width of Notification
|
||||
*/
|
||||
width: number;
|
||||
/** @internal */
|
||||
closeBtnHoverBg: string;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
export interface NotificationToken extends FullToken<'Notification'> {
|
||||
animationMaxHeight: number;
|
||||
notificationBg: string;
|
||||
notificationPadding: string;
|
||||
notificationPaddingVertical: number;
|
||||
notificationPaddingHorizontal: number;
|
||||
notificationIconSize: number | string;
|
||||
notificationCloseButtonSize: number | string;
|
||||
notificationMarginBottom: number;
|
||||
notificationMarginEdge: number;
|
||||
notificationStackLayer: number;
|
||||
}
|
||||
|
||||
export const genNoticeStyle = (token: NotificationToken): CSSObject => {
|
||||
const {
|
||||
iconCls,
|
||||
componentCls, // .ant-notification
|
||||
boxShadow,
|
||||
fontSizeLG,
|
||||
notificationMarginBottom,
|
||||
borderRadiusLG,
|
||||
colorSuccess,
|
||||
colorInfo,
|
||||
colorWarning,
|
||||
colorError,
|
||||
colorTextHeading,
|
||||
notificationBg,
|
||||
notificationPadding,
|
||||
notificationMarginEdge,
|
||||
fontSize,
|
||||
lineHeight,
|
||||
// @ts-ignore
|
||||
width,
|
||||
notificationIconSize,
|
||||
colorText,
|
||||
} = token;
|
||||
|
||||
const noticeCls = `${componentCls}-notice`;
|
||||
|
||||
return {
|
||||
position: 'relative',
|
||||
marginBottom: notificationMarginBottom,
|
||||
marginInlineStart: 'auto',
|
||||
background: notificationBg,
|
||||
borderRadius: borderRadiusLG,
|
||||
boxShadow,
|
||||
|
||||
[noticeCls]: {
|
||||
padding: notificationPadding,
|
||||
width,
|
||||
maxWidth: `calc(100vw - ${unit(token.calc(notificationMarginEdge).mul(2).equal())})`,
|
||||
overflow: 'hidden',
|
||||
lineHeight,
|
||||
wordWrap: 'break-word',
|
||||
},
|
||||
|
||||
[`${componentCls}-close-icon`]: {
|
||||
fontSize,
|
||||
cursor: 'pointer',
|
||||
},
|
||||
|
||||
[`${noticeCls}-message`]: {
|
||||
marginBottom: token.marginXS,
|
||||
color: colorTextHeading,
|
||||
fontSize: fontSizeLG,
|
||||
lineHeight: token.lineHeightLG,
|
||||
},
|
||||
|
||||
[`${noticeCls}-description`]: {
|
||||
fontSize,
|
||||
color: colorText,
|
||||
},
|
||||
|
||||
[`${noticeCls}-closable ${noticeCls}-message`]: {
|
||||
paddingInlineEnd: token.paddingLG,
|
||||
},
|
||||
|
||||
[`${noticeCls}-with-icon ${noticeCls}-message`]: {
|
||||
marginBottom: token.marginXS,
|
||||
marginInlineStart: token.calc(token.marginSM).add(notificationIconSize).equal(),
|
||||
fontSize: fontSizeLG,
|
||||
},
|
||||
|
||||
[`${noticeCls}-with-icon ${noticeCls}-description`]: {
|
||||
marginInlineStart: token.calc(token.marginSM).add(notificationIconSize).equal(),
|
||||
fontSize,
|
||||
},
|
||||
|
||||
// Icon & color style in different selector level
|
||||
// https://github.com/ant-design/ant-design/issues/16503
|
||||
// https://github.com/ant-design/ant-design/issues/15512
|
||||
[`${noticeCls}-icon`]: {
|
||||
position: 'absolute',
|
||||
fontSize: notificationIconSize,
|
||||
lineHeight: 1,
|
||||
|
||||
// icon-font
|
||||
[`&-success${iconCls}`]: {
|
||||
color: colorSuccess,
|
||||
},
|
||||
[`&-info${iconCls}`]: {
|
||||
color: colorInfo,
|
||||
},
|
||||
[`&-warning${iconCls}`]: {
|
||||
color: colorWarning,
|
||||
},
|
||||
[`&-error${iconCls}`]: {
|
||||
color: colorError,
|
||||
},
|
||||
},
|
||||
|
||||
[`${noticeCls}-close`]: {
|
||||
position: 'absolute',
|
||||
top: token.notificationPaddingVertical,
|
||||
insetInlineEnd: token.notificationPaddingHorizontal,
|
||||
color: token.colorIcon,
|
||||
outline: 'none',
|
||||
width: token.notificationCloseButtonSize,
|
||||
height: token.notificationCloseButtonSize,
|
||||
borderRadius: token.borderRadiusSM,
|
||||
transition: `background-color ${token.motionDurationMid}, color ${token.motionDurationMid}`,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
|
||||
'&:hover': {
|
||||
color: token.colorIconHover,
|
||||
// @ts-ignore
|
||||
backgroundColor: token.closeBtnHoverBg,
|
||||
},
|
||||
},
|
||||
|
||||
[`${noticeCls}-btn`]: {
|
||||
float: 'right',
|
||||
marginTop: token.marginSM,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const genNotificationStyle: GenerateStyle<NotificationToken> = (token) => {
|
||||
const {
|
||||
componentCls, // .ant-notification
|
||||
notificationMarginBottom,
|
||||
notificationMarginEdge,
|
||||
motionDurationMid,
|
||||
motionEaseInOut,
|
||||
} = token;
|
||||
|
||||
const noticeCls = `${componentCls}-notice`;
|
||||
|
||||
const fadeOut = new Keyframes('antNotificationFadeOut', {
|
||||
'0%': {
|
||||
maxHeight: token.animationMaxHeight,
|
||||
marginBottom: notificationMarginBottom,
|
||||
},
|
||||
|
||||
'100%': {
|
||||
maxHeight: 0,
|
||||
marginBottom: 0,
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0,
|
||||
opacity: 0,
|
||||
},
|
||||
});
|
||||
|
||||
return [
|
||||
// ============================ Holder ============================
|
||||
{
|
||||
[componentCls]: {
|
||||
...resetComponent(token),
|
||||
|
||||
position: 'fixed',
|
||||
// @ts-ignore
|
||||
zIndex: token.zIndexPopup,
|
||||
marginRight: {
|
||||
value: notificationMarginEdge,
|
||||
_skip_check_: true,
|
||||
},
|
||||
|
||||
[`${componentCls}-hook-holder`]: {
|
||||
position: 'relative',
|
||||
},
|
||||
|
||||
// animation
|
||||
[`${componentCls}-fade-appear-prepare`]: {
|
||||
opacity: '0 !important',
|
||||
},
|
||||
|
||||
[`${componentCls}-fade-enter, ${componentCls}-fade-appear`]: {
|
||||
animationDuration: token.motionDurationMid,
|
||||
animationTimingFunction: motionEaseInOut,
|
||||
animationFillMode: 'both',
|
||||
opacity: 0,
|
||||
animationPlayState: 'paused',
|
||||
},
|
||||
|
||||
[`${componentCls}-fade-leave`]: {
|
||||
animationTimingFunction: motionEaseInOut,
|
||||
animationFillMode: 'both',
|
||||
|
||||
animationDuration: motionDurationMid,
|
||||
animationPlayState: 'paused',
|
||||
},
|
||||
|
||||
[`${componentCls}-fade-enter${componentCls}-fade-enter-active, ${componentCls}-fade-appear${componentCls}-fade-appear-active`]:
|
||||
{
|
||||
animationPlayState: 'running',
|
||||
},
|
||||
|
||||
[`${componentCls}-fade-leave${componentCls}-fade-leave-active`]: {
|
||||
animationName: fadeOut,
|
||||
animationPlayState: 'running',
|
||||
},
|
||||
|
||||
// RTL
|
||||
'&-rtl': {
|
||||
direction: 'rtl',
|
||||
|
||||
[`${noticeCls}-btn`]: {
|
||||
float: 'left',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// ============================ Notice ============================
|
||||
{
|
||||
[componentCls]: {
|
||||
[`${noticeCls}-wrapper`]: {
|
||||
...genNoticeStyle(token),
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
// ============================== Export ==============================
|
||||
export const prepareComponentToken = (token: AliasToken) => ({
|
||||
zIndexPopup: token.zIndexPopupBase + CONTAINER_MAX_OFFSET + 50,
|
||||
width: 384,
|
||||
closeBtnHoverBg: token.wireframe ? 'transparent' : token.colorFillContent,
|
||||
});
|
||||
|
||||
export const prepareNotificationToken: (
|
||||
// @ts-ignore
|
||||
token: Parameters<GenStyleFn<'Notification'>>[0],
|
||||
) => NotificationToken = (token) => {
|
||||
const notificationPaddingVertical = token.paddingMD;
|
||||
const notificationPaddingHorizontal = token.paddingLG;
|
||||
const notificationToken = mergeToken<NotificationToken>(token, {
|
||||
notificationBg: token.colorBgElevated,
|
||||
notificationPaddingVertical,
|
||||
notificationPaddingHorizontal,
|
||||
notificationIconSize: token.calc(token.fontSizeLG).mul(token.lineHeightLG).equal(),
|
||||
notificationCloseButtonSize: token.calc(token.controlHeightLG).mul(0.55).equal(),
|
||||
notificationMarginBottom: token.margin,
|
||||
notificationPadding: `${unit(token.paddingMD)} ${unit(token.paddingContentHorizontalLG)}`,
|
||||
notificationMarginEdge: token.marginLG,
|
||||
animationMaxHeight: 150,
|
||||
notificationStackLayer: 3,
|
||||
});
|
||||
|
||||
return notificationToken;
|
||||
};
|
||||
|
||||
export default genStyleHooks(
|
||||
// @ts-ignore
|
||||
'Notification',
|
||||
(token) => {
|
||||
const notificationToken = prepareNotificationToken(token);
|
||||
|
||||
return [
|
||||
genNotificationStyle(notificationToken),
|
||||
genNotificationPlacementStyle(notificationToken),
|
||||
genStackStyle(notificationToken),
|
||||
];
|
||||
},
|
||||
prepareComponentToken,
|
||||
);
|
113
packages/meta/src/notification/style/placement.ts
Normal file
113
packages/meta/src/notification/style/placement.ts
Normal file
@ -0,0 +1,113 @@
|
||||
import type { CSSObject } from '@ant-design/cssinjs';
|
||||
import { Keyframes } from '@ant-design/cssinjs';
|
||||
import type { NotificationToken } from '.';
|
||||
import type { GenerateStyle } from '../../theme/internal';
|
||||
|
||||
const genNotificationPlacementStyle: GenerateStyle<NotificationToken, CSSObject> = (token) => {
|
||||
const { componentCls, notificationMarginEdge, animationMaxHeight } = token;
|
||||
|
||||
const noticeCls = `${componentCls}-notice`;
|
||||
|
||||
const rightFadeIn = new Keyframes('antNotificationFadeIn', {
|
||||
'0%': {
|
||||
transform: `translate3d(100%, 0, 0)`,
|
||||
opacity: 0,
|
||||
},
|
||||
|
||||
'100%': {
|
||||
transform: `translate3d(0, 0, 0)`,
|
||||
opacity: 1,
|
||||
},
|
||||
});
|
||||
|
||||
const topFadeIn = new Keyframes('antNotificationTopFadeIn', {
|
||||
'0%': {
|
||||
top: -animationMaxHeight,
|
||||
opacity: 0,
|
||||
},
|
||||
|
||||
'100%': {
|
||||
top: 0,
|
||||
opacity: 1,
|
||||
},
|
||||
});
|
||||
|
||||
const bottomFadeIn = new Keyframes('antNotificationBottomFadeIn', {
|
||||
'0%': {
|
||||
bottom: token.calc(animationMaxHeight).mul(-1).equal(),
|
||||
opacity: 0,
|
||||
},
|
||||
|
||||
'100%': {
|
||||
bottom: 0,
|
||||
opacity: 1,
|
||||
},
|
||||
});
|
||||
|
||||
const leftFadeIn = new Keyframes('antNotificationLeftFadeIn', {
|
||||
'0%': {
|
||||
transform: `translate3d(-100%, 0, 0)`,
|
||||
opacity: 0,
|
||||
},
|
||||
|
||||
'100%': {
|
||||
transform: `translate3d(0, 0, 0)`,
|
||||
opacity: 1,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
[componentCls]: {
|
||||
[`&${componentCls}-top, &${componentCls}-bottom`]: {
|
||||
marginInline: 0,
|
||||
[noticeCls]: {
|
||||
marginInline: 'auto auto',
|
||||
},
|
||||
},
|
||||
|
||||
[`&${componentCls}-top`]: {
|
||||
[`${componentCls}-fade-enter${componentCls}-fade-enter-active, ${componentCls}-fade-appear${componentCls}-fade-appear-active`]:
|
||||
{
|
||||
animationName: topFadeIn,
|
||||
},
|
||||
},
|
||||
|
||||
[`&${componentCls}-bottom`]: {
|
||||
[`${componentCls}-fade-enter${componentCls}-fade-enter-active, ${componentCls}-fade-appear${componentCls}-fade-appear-active`]:
|
||||
{
|
||||
animationName: bottomFadeIn,
|
||||
},
|
||||
},
|
||||
|
||||
[`&${componentCls}-topRight, &${componentCls}-bottomRight`]: {
|
||||
[`${componentCls}-fade-enter${componentCls}-fade-enter-active, ${componentCls}-fade-appear${componentCls}-fade-appear-active`]:
|
||||
{
|
||||
animationName: rightFadeIn,
|
||||
},
|
||||
},
|
||||
|
||||
[`&${componentCls}-topLeft, &${componentCls}-bottomLeft`]: {
|
||||
marginRight: {
|
||||
value: 0,
|
||||
_skip_check_: true,
|
||||
},
|
||||
marginLeft: {
|
||||
value: notificationMarginEdge,
|
||||
_skip_check_: true,
|
||||
},
|
||||
|
||||
[noticeCls]: {
|
||||
marginInlineEnd: 'auto',
|
||||
marginInlineStart: 0,
|
||||
},
|
||||
|
||||
[`${componentCls}-fade-enter${componentCls}-fade-enter-active, ${componentCls}-fade-appear${componentCls}-fade-appear-active`]:
|
||||
{
|
||||
animationName: leftFadeIn,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default genNotificationPlacementStyle;
|
25
packages/meta/src/notification/style/pure-panel.ts
Normal file
25
packages/meta/src/notification/style/pure-panel.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { genSubStyleComponent } from '../../theme/internal';
|
||||
import { prepareComponentToken, genNoticeStyle, prepareNotificationToken } from '.';
|
||||
import { unit } from '@ant-design/cssinjs';
|
||||
|
||||
export default genSubStyleComponent(
|
||||
// @ts-ignore
|
||||
['Notification', 'PurePanel'],
|
||||
(token) => {
|
||||
const noticeCls = `${token.componentCls}-notice`;
|
||||
const notificationToken = prepareNotificationToken(token);
|
||||
|
||||
return {
|
||||
[`${noticeCls}-pure-panel`]: {
|
||||
...genNoticeStyle(notificationToken),
|
||||
// @ts-ignore
|
||||
width: notificationToken.width,
|
||||
maxWidth: `calc(100vw - ${unit(
|
||||
token.calc(notificationToken.notificationMarginEdge).mul(2).equal(),
|
||||
)})`,
|
||||
margin: 0,
|
||||
},
|
||||
};
|
||||
},
|
||||
prepareComponentToken,
|
||||
);
|
119
packages/meta/src/notification/style/stack.ts
Normal file
119
packages/meta/src/notification/style/stack.ts
Normal file
@ -0,0 +1,119 @@
|
||||
import type { GenerateStyle } from '../../theme/internal';
|
||||
import type { NotificationToken } from '.';
|
||||
import type { CSSObject } from '@ant-design/cssinjs';
|
||||
import type { NotificationPlacement } from '../interface';
|
||||
import { NotificationPlacements } from '../interface';
|
||||
|
||||
const placementAlignProperty: Record<NotificationPlacement, 'left' | 'right'> = {
|
||||
topLeft: 'left',
|
||||
topRight: 'right',
|
||||
bottomLeft: 'left',
|
||||
bottomRight: 'right',
|
||||
top: 'left',
|
||||
bottom: 'left',
|
||||
};
|
||||
|
||||
const genPlacementStackStyle = (
|
||||
token: NotificationToken,
|
||||
placement: NotificationPlacement,
|
||||
): CSSObject => {
|
||||
const { componentCls } = token;
|
||||
|
||||
return {
|
||||
[`${componentCls}-${placement}`]: {
|
||||
[`&${componentCls}-stack > ${componentCls}-notice-wrapper`]: {
|
||||
[placement.startsWith('top') ? 'top' : 'bottom']: 0,
|
||||
[placementAlignProperty[placement]]: { value: 0, _skip_check_: true },
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const genStackChildrenStyle = (token: NotificationToken): CSSObject => {
|
||||
const childrenStyle: CSSObject = {};
|
||||
for (let i = 1; i < token.notificationStackLayer; i++) {
|
||||
childrenStyle[`&:nth-last-child(${i + 1})`] = {
|
||||
overflow: 'hidden',
|
||||
|
||||
[`& > ${token.componentCls}-notice`]: {
|
||||
opacity: 0,
|
||||
transition: `opacity ${token.motionDurationMid}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
[`&:not(:nth-last-child(-n+${token.notificationStackLayer}))`]: {
|
||||
opacity: 0,
|
||||
overflow: 'hidden',
|
||||
color: 'transparent',
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
...childrenStyle,
|
||||
};
|
||||
};
|
||||
|
||||
const genStackedNoticeStyle = (token: NotificationToken): CSSObject => {
|
||||
const childrenStyle: CSSObject = {};
|
||||
for (let i = 1; i < token.notificationStackLayer; i++) {
|
||||
childrenStyle[`&:nth-last-child(${i + 1})`] = {
|
||||
background: token.colorBgBlur,
|
||||
backdropFilter: 'blur(10px)',
|
||||
'-webkit-backdrop-filter': 'blur(10px)',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...childrenStyle,
|
||||
};
|
||||
};
|
||||
|
||||
const genStackStyle: GenerateStyle<NotificationToken> = (token) => {
|
||||
const { componentCls } = token;
|
||||
return {
|
||||
[`${componentCls}-stack`]: {
|
||||
[`& > ${componentCls}-notice-wrapper`]: {
|
||||
transition: `all ${token.motionDurationSlow}, backdrop-filter 0s`,
|
||||
position: 'absolute',
|
||||
|
||||
...genStackChildrenStyle(token),
|
||||
},
|
||||
},
|
||||
[`${componentCls}-stack:not(${componentCls}-stack-expanded)`]: {
|
||||
[`& > ${componentCls}-notice-wrapper`]: {
|
||||
...genStackedNoticeStyle(token),
|
||||
},
|
||||
},
|
||||
[`${componentCls}-stack${componentCls}-stack-expanded`]: {
|
||||
[`& > ${componentCls}-notice-wrapper`]: {
|
||||
'&:not(:nth-last-child(-n + 1))': {
|
||||
opacity: 1,
|
||||
overflow: 'unset',
|
||||
color: 'inherit',
|
||||
pointerEvents: 'auto',
|
||||
|
||||
[`& > ${token.componentCls}-notice`]: {
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
|
||||
'&:after': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
height: token.margin,
|
||||
width: '100%',
|
||||
insetInline: 0,
|
||||
bottom: token.calc(token.margin).mul(-1).equal(),
|
||||
background: 'transparent',
|
||||
pointerEvents: 'auto',
|
||||
},
|
||||
},
|
||||
},
|
||||
...NotificationPlacements.map((placement) => genPlacementStackStyle(token, placement)).reduce(
|
||||
(acc, cur) => ({ ...acc, ...cur }),
|
||||
{},
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
export default genStackStyle;
|
220
packages/meta/src/notification/useNotification.tsx
Normal file
220
packages/meta/src/notification/useNotification.tsx
Normal file
@ -0,0 +1,220 @@
|
||||
import * as React from 'react';
|
||||
import type { FC, PropsWithChildren } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { NotificationProvider, useNotification as useRcNotification } from 'rc-notification';
|
||||
import type { NotificationAPI, NotificationConfig as RcNotificationConfig } from 'rc-notification';
|
||||
|
||||
import { devUseWarning } from '../_util/warning';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import type { ComponentStyleConfig } from '../config-provider/context';
|
||||
import type {
|
||||
ArgsProps,
|
||||
NotificationConfig,
|
||||
NotificationInstance,
|
||||
NotificationPlacement,
|
||||
} from './interface';
|
||||
import { getCloseIcon, PureContent } from './PurePanel';
|
||||
import useStyle from './style';
|
||||
import { getMotion, getPlacementStyle } from './util';
|
||||
import { useToken } from '../theme/internal';
|
||||
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
|
||||
|
||||
const DEFAULT_OFFSET = 24;
|
||||
const DEFAULT_DURATION = 4.5;
|
||||
const DEFAULT_PLACEMENT: NotificationPlacement = 'topRight';
|
||||
|
||||
// ==============================================================================
|
||||
// == Holder ==
|
||||
// ==============================================================================
|
||||
type HolderProps = NotificationConfig & {
|
||||
onAllRemoved?: VoidFunction;
|
||||
};
|
||||
|
||||
interface HolderRef extends NotificationAPI {
|
||||
prefixCls: string;
|
||||
notification?: ComponentStyleConfig;
|
||||
}
|
||||
|
||||
const Wrapper: FC<PropsWithChildren<{ prefixCls: string }>> = ({ children, prefixCls }) => {
|
||||
const rootCls = useCSSVarCls(prefixCls);
|
||||
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls);
|
||||
return wrapCSSVar(
|
||||
<NotificationProvider classNames={{ list: classNames(hashId, cssVarCls, rootCls) }}>
|
||||
{children}
|
||||
</NotificationProvider>,
|
||||
);
|
||||
};
|
||||
|
||||
const renderNotifications: RcNotificationConfig['renderNotifications'] = (
|
||||
node,
|
||||
{ prefixCls, key },
|
||||
) => (
|
||||
<Wrapper prefixCls={prefixCls} key={key}>
|
||||
{node}
|
||||
</Wrapper>
|
||||
);
|
||||
|
||||
const Holder = React.forwardRef<HolderRef, HolderProps>((props, ref) => {
|
||||
const {
|
||||
top,
|
||||
bottom,
|
||||
prefixCls: staticPrefixCls,
|
||||
getContainer: staticGetContainer,
|
||||
maxCount,
|
||||
rtl,
|
||||
onAllRemoved,
|
||||
stack,
|
||||
} = props;
|
||||
const { getPrefixCls, getPopupContainer, notification } = React.useContext(ConfigContext);
|
||||
const [, token] = useToken();
|
||||
|
||||
const prefixCls = staticPrefixCls || getPrefixCls('notification');
|
||||
|
||||
// =============================== Style ===============================
|
||||
const getStyle = (placement: NotificationPlacement): React.CSSProperties =>
|
||||
getPlacementStyle(placement, top ?? DEFAULT_OFFSET, bottom ?? DEFAULT_OFFSET);
|
||||
|
||||
const getClassName = () => classNames({ [`${prefixCls}-rtl`]: rtl });
|
||||
|
||||
// ============================== Motion ===============================
|
||||
const getNotificationMotion = () => getMotion(prefixCls);
|
||||
|
||||
// ============================== Origin ===============================
|
||||
const [api, holder] = useRcNotification({
|
||||
prefixCls,
|
||||
style: getStyle,
|
||||
className: getClassName,
|
||||
motion: getNotificationMotion,
|
||||
closable: true,
|
||||
closeIcon: getCloseIcon(prefixCls),
|
||||
duration: DEFAULT_DURATION,
|
||||
getContainer: () => staticGetContainer?.() || getPopupContainer?.() || document.body,
|
||||
maxCount,
|
||||
onAllRemoved,
|
||||
renderNotifications,
|
||||
stack:
|
||||
stack === false
|
||||
? false
|
||||
: {
|
||||
threshold: typeof stack === 'object' ? stack?.threshold : undefined,
|
||||
offset: 8,
|
||||
gap: token.margin,
|
||||
},
|
||||
});
|
||||
|
||||
// ================================ Ref ================================
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
...api,
|
||||
prefixCls,
|
||||
notification,
|
||||
}));
|
||||
|
||||
return holder;
|
||||
});
|
||||
|
||||
// ==============================================================================
|
||||
// == Hook ==
|
||||
// ==============================================================================
|
||||
export function useInternalNotification(
|
||||
notificationConfig?: HolderProps,
|
||||
): readonly [NotificationInstance, React.ReactElement] {
|
||||
const holderRef = React.useRef<HolderRef>(null);
|
||||
|
||||
const warning = devUseWarning('Notification');
|
||||
|
||||
// ================================ API ================================
|
||||
const wrapAPI = React.useMemo<NotificationInstance>(() => {
|
||||
// Wrap with notification content
|
||||
|
||||
// >>> Open
|
||||
const open = (config: ArgsProps) => {
|
||||
if (!holderRef.current) {
|
||||
warning(
|
||||
false,
|
||||
'usage',
|
||||
'You are calling notice in render which will break in React 18 concurrent mode. Please trigger in effect instead.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const { open: originOpen, prefixCls, notification } = holderRef.current;
|
||||
|
||||
const noticePrefixCls = `${prefixCls}-notice`;
|
||||
|
||||
const {
|
||||
message,
|
||||
description,
|
||||
icon,
|
||||
type,
|
||||
btn,
|
||||
className,
|
||||
style,
|
||||
role = 'alert',
|
||||
closeIcon,
|
||||
...restConfig
|
||||
} = config;
|
||||
|
||||
const realCloseIcon = getCloseIcon(noticePrefixCls, closeIcon);
|
||||
|
||||
return originOpen({
|
||||
// use placement from props instead of hard-coding "topRight"
|
||||
placement: notificationConfig?.placement ?? DEFAULT_PLACEMENT,
|
||||
...restConfig,
|
||||
content: (
|
||||
<PureContent
|
||||
prefixCls={noticePrefixCls}
|
||||
icon={icon}
|
||||
type={type}
|
||||
message={message}
|
||||
description={description}
|
||||
btn={btn}
|
||||
role={role}
|
||||
/>
|
||||
),
|
||||
className: classNames(
|
||||
type && `${noticePrefixCls}-${type}`,
|
||||
className,
|
||||
notification?.className,
|
||||
),
|
||||
style: { ...notification?.style, ...style },
|
||||
closeIcon: realCloseIcon,
|
||||
closable: !!realCloseIcon,
|
||||
});
|
||||
};
|
||||
|
||||
// >>> destroy
|
||||
const destroy = (key?: React.Key) => {
|
||||
if (key !== undefined) {
|
||||
holderRef.current?.close(key);
|
||||
} else {
|
||||
holderRef.current?.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
const clone = {
|
||||
open,
|
||||
destroy,
|
||||
} as NotificationInstance;
|
||||
|
||||
const keys = ['success', 'info', 'warning', 'error'] as const;
|
||||
keys.forEach((type) => {
|
||||
clone[type] = (config) =>
|
||||
open({
|
||||
...config,
|
||||
type,
|
||||
});
|
||||
});
|
||||
|
||||
return clone;
|
||||
}, []);
|
||||
|
||||
// ============================== Return ===============================
|
||||
return [
|
||||
wrapAPI,
|
||||
<Holder key="notification-holder" {...notificationConfig} ref={holderRef} />,
|
||||
] as const;
|
||||
}
|
||||
|
||||
export default function useNotification(notificationConfig?: NotificationConfig) {
|
||||
return useInternalNotification(notificationConfig);
|
||||
}
|
68
packages/meta/src/notification/util.ts
Normal file
68
packages/meta/src/notification/util.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import type * as React from 'react';
|
||||
import type { CSSMotionProps } from 'rc-motion';
|
||||
import type { NotificationPlacement } from './interface';
|
||||
|
||||
export function getPlacementStyle(placement: NotificationPlacement, top: number, bottom: number) {
|
||||
let style: React.CSSProperties;
|
||||
|
||||
switch (placement) {
|
||||
case 'top':
|
||||
style = {
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
right: 'auto',
|
||||
top,
|
||||
bottom: 'auto',
|
||||
};
|
||||
break;
|
||||
|
||||
case 'topLeft':
|
||||
style = {
|
||||
left: 0,
|
||||
top,
|
||||
bottom: 'auto',
|
||||
};
|
||||
break;
|
||||
|
||||
case 'topRight':
|
||||
style = {
|
||||
right: 0,
|
||||
top,
|
||||
bottom: 'auto',
|
||||
};
|
||||
break;
|
||||
|
||||
case 'bottom':
|
||||
style = {
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
right: 'auto',
|
||||
top: 'auto',
|
||||
bottom,
|
||||
};
|
||||
break;
|
||||
|
||||
case 'bottomLeft':
|
||||
style = {
|
||||
left: 0,
|
||||
top: 'auto',
|
||||
bottom,
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
style = {
|
||||
right: 0,
|
||||
top: 'auto',
|
||||
bottom,
|
||||
};
|
||||
break;
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
export function getMotion(prefixCls: string): CSSMotionProps {
|
||||
return {
|
||||
motionName: `${prefixCls}-fade`,
|
||||
};
|
||||
}
|
@ -68,6 +68,9 @@ importers:
|
||||
|
||||
packages/biz:
|
||||
dependencies:
|
||||
'@ant-design/icons':
|
||||
specifier: ^5.2.6
|
||||
version: 5.2.6(react-dom@18.2.0)(react@18.2.0)
|
||||
'@zhst/func':
|
||||
specifier: workspace:^
|
||||
version: link:../func
|
||||
|
Loading…
Reference in New Issue
Block a user