mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-12-02 03:59:01 +08:00
feat : new components app (#39046)
* feat : new components app * feat: update app * feat: update app * feat: update app * feat: update app * feat: update app * feat: update app * feat: update app * feat: update style * feat: update style * feat: update style * feat: add style * chore: code clean * feat: add prefixCls * chore: update snapshot * chore: update snapshot * chore: update snapshot * test: image test * Update components/app/index.zh-CN.md Co-authored-by: afc163 <afc163@gmail.com> * feat : update for reviewer * feat: update * feat: update * feat: update snap Co-authored-by: MadCcc <1075746765@qq.com> Co-authored-by: afc163 <afc163@gmail.com>
This commit is contained in:
parent
cd8840bf56
commit
db4ee53ffa
@ -5,6 +5,7 @@ exports[`antd exports modules correctly 1`] = `
|
||||
"Affix",
|
||||
"Alert",
|
||||
"Anchor",
|
||||
"App",
|
||||
"AutoComplete",
|
||||
"Avatar",
|
||||
"BackTop",
|
||||
|
@ -0,0 +1,46 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders ./components/app/demo/message.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-app"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open message
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/app/demo/modal.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-app"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open modal
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/app/demo/notification.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-app"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open notification
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
46
components/app/__tests__/__snapshots__/demo.test.ts.snap
Normal file
46
components/app/__tests__/__snapshots__/demo.test.ts.snap
Normal file
@ -0,0 +1,46 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders ./components/app/demo/message.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-app"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open message
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/app/demo/modal.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-app"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open modal
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/app/demo/notification.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-app"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Open notification
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
17
components/app/__tests__/__snapshots__/index.test.tsx.snap
Normal file
17
components/app/__tests__/__snapshots__/index.test.tsx.snap
Normal file
@ -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
components/app/__tests__/demo-extend.test.ts
Normal file
3
components/app/__tests__/demo-extend.test.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { extendTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
extendTest('app');
|
3
components/app/__tests__/demo.test.ts
Normal file
3
components/app/__tests__/demo.test.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('app');
|
5
components/app/__tests__/image.test.ts
Normal file
5
components/app/__tests__/image.test.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { imageDemoTest } from '../../../tests/shared/imageTest';
|
||||
|
||||
describe('app', () => {
|
||||
imageDemoTest('app');
|
||||
});
|
33
components/app/__tests__/index.test.tsx
Normal file
33
components/app/__tests__/index.test.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import App from '..';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { render } from '../../../tests/utils';
|
||||
|
||||
describe('App', () => {
|
||||
mountTest(App);
|
||||
rtlTest(App);
|
||||
|
||||
it('single', () => {
|
||||
// Sub page
|
||||
const MyPage = () => {
|
||||
const { message } = App.useApp();
|
||||
React.useEffect(() => {
|
||||
message.success('Good!');
|
||||
}, [message]);
|
||||
|
||||
return <div>Hello World</div>;
|
||||
};
|
||||
|
||||
// Entry component
|
||||
const MyApp = () => (
|
||||
<App>
|
||||
<MyPage />
|
||||
</App>
|
||||
);
|
||||
|
||||
const { getByText, container } = render(<MyApp />);
|
||||
expect(getByText('Hello World')).toBeTruthy();
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
19
components/app/context.ts
Normal file
19
components/app/context.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import type { MessageInstance } from '../message/interface';
|
||||
import type { NotificationInstance } from '../notification/interface';
|
||||
import type { ModalStaticFunctions } from '../modal/confirm';
|
||||
|
||||
type ModalType = Omit<ModalStaticFunctions, 'warn'>;
|
||||
export interface useAppProps {
|
||||
message: MessageInstance;
|
||||
notification: NotificationInstance;
|
||||
modal: ModalType;
|
||||
}
|
||||
|
||||
const AppContext = React.createContext<useAppProps>({
|
||||
message: {} as MessageInstance,
|
||||
notification: {} as NotificationInstance,
|
||||
modal: {} as ModalType,
|
||||
});
|
||||
|
||||
export default AppContext;
|
7
components/app/demo/message.md
Normal file
7
components/app/demo/message.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
获取 `message` 静态方法.
|
||||
|
||||
## en-US
|
||||
|
||||
Static method for `message`.
|
24
components/app/demo/message.tsx
Normal file
24
components/app/demo/message.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
import { App, Button } from 'antd';
|
||||
|
||||
// Sub page
|
||||
const MyPage = () => {
|
||||
const { message } = App.useApp();
|
||||
|
||||
const showMessage = () => {
|
||||
message.success('Success!');
|
||||
};
|
||||
|
||||
return (
|
||||
<Button type="primary" onClick={showMessage}>
|
||||
Open message
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
// Entry component
|
||||
export default () => (
|
||||
<App>
|
||||
<MyPage />
|
||||
</App>
|
||||
);
|
7
components/app/demo/modal.md
Normal file
7
components/app/demo/modal.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
获取 `modal` 静态方法.
|
||||
|
||||
## en-US
|
||||
|
||||
Static method for `modal`.
|
27
components/app/demo/modal.tsx
Normal file
27
components/app/demo/modal.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
import { App, Button } from 'antd';
|
||||
|
||||
// Sub page
|
||||
const MyPage = () => {
|
||||
const { modal } = App.useApp();
|
||||
|
||||
const showModal = () => {
|
||||
modal.warning({
|
||||
title: 'This is a warning message',
|
||||
content: 'some messages...some messages...',
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Button type="primary" onClick={showModal}>
|
||||
Open modal
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
// Entry component
|
||||
export default () => (
|
||||
<App>
|
||||
<MyPage />
|
||||
</App>
|
||||
);
|
7
components/app/demo/notification.md
Normal file
7
components/app/demo/notification.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
获取 `notification` 静态方法.
|
||||
|
||||
## en-US
|
||||
|
||||
Static method for `notification`.
|
28
components/app/demo/notification.tsx
Normal file
28
components/app/demo/notification.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import { App, Button } from 'antd';
|
||||
|
||||
// Sub page
|
||||
const MyPage = () => {
|
||||
const { notification } = App.useApp();
|
||||
|
||||
const showNotification = () => {
|
||||
notification.info({
|
||||
message: `Notification topLeft`,
|
||||
description: 'Hello, Ant Design!!',
|
||||
placement: 'topLeft',
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Button type="primary" onClick={showNotification}>
|
||||
Open notification
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
// Entry component
|
||||
export default () => (
|
||||
<App>
|
||||
<MyPage />
|
||||
</App>
|
||||
);
|
43
components/app/index.en-US.md
Normal file
43
components/app/index.en-US.md
Normal file
@ -0,0 +1,43 @@
|
||||
---
|
||||
category: Components
|
||||
group: Other
|
||||
title: App
|
||||
cover: https://gw.alipayobjects.com/zos/bmw-prod/cc3fcbfa-bf5b-4c8c-8a3d-c3f8388c75e8.svg
|
||||
demo:
|
||||
cols: 2
|
||||
---
|
||||
|
||||
New App Component which provide global style & static function replacement.
|
||||
|
||||
## When To Use
|
||||
|
||||
Static function in React 18 concurrent mode will not well support. In v5, we recommend to use hooks for the static replacement. But it will make user manual work on define this.
|
||||
|
||||
## Examples
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<code src="./demo/message.tsx">message</code>
|
||||
<code src="./demo/notification.tsx">notification</code>
|
||||
<code src="./demo/modal.tsx">modal</code>
|
||||
|
||||
## How to use
|
||||
|
||||
```javascript
|
||||
import React from 'react';
|
||||
import { App } from 'antd';
|
||||
const MyPage = () => {
|
||||
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 = () => (
|
||||
<App>
|
||||
<MyPage />
|
||||
</App>
|
||||
);
|
||||
```
|
60
components/app/index.tsx
Normal file
60
components/app/index.tsx
Normal file
@ -0,0 +1,60 @@
|
||||
import React, { useContext } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import type { ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import useStyle from './style';
|
||||
import useMessage from '../message/useMessage';
|
||||
import useNotification from '../notification/useNotification';
|
||||
import useModal from '../modal/useModal';
|
||||
import AppContext from './context';
|
||||
import type { useAppProps } from './context';
|
||||
|
||||
export type AppProps = {
|
||||
className?: string;
|
||||
prefixCls?: string;
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
const useApp: () => useAppProps = () => React.useContext(AppContext);
|
||||
|
||||
const App: React.ForwardRefRenderFunction<HTMLDivElement, AppProps> & {
|
||||
useApp: () => useAppProps;
|
||||
} = (props) => {
|
||||
const { prefixCls: customizePrefixCls, children, className } = props;
|
||||
const { getPrefixCls } = useContext<ConfigConsumerProps>(ConfigContext);
|
||||
const prefixCls = getPrefixCls('app', customizePrefixCls);
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||
const customClassName = classNames(hashId, prefixCls, className);
|
||||
|
||||
const [messageApi, messageContextHolder] = useMessage();
|
||||
const [notificationApi, notificationContextHolder] = useNotification();
|
||||
const [ModalApi, ModalContextHolder] = useModal();
|
||||
|
||||
const memoizedContextValue = React.useMemo(
|
||||
() => ({
|
||||
message: messageApi,
|
||||
notification: notificationApi,
|
||||
modal: ModalApi,
|
||||
}),
|
||||
[messageApi, notificationApi, ModalApi],
|
||||
);
|
||||
|
||||
return wrapSSR(
|
||||
<AppContext.Provider value={memoizedContextValue}>
|
||||
<div className={customClassName}>
|
||||
{ModalContextHolder}
|
||||
{messageContextHolder}
|
||||
{notificationContextHolder}
|
||||
{children}
|
||||
</div>
|
||||
</AppContext.Provider>,
|
||||
);
|
||||
};
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
App.displayName = 'App';
|
||||
}
|
||||
|
||||
App.useApp = useApp;
|
||||
export default App;
|
45
components/app/index.zh-CN.md
Normal file
45
components/app/index.zh-CN.md
Normal file
@ -0,0 +1,45 @@
|
||||
---
|
||||
category: Components
|
||||
subtitle: 包裹组件
|
||||
group: 其他
|
||||
title: App
|
||||
cover: https://gw.alipayobjects.com/zos/bmw-prod/cc3fcbfa-bf5b-4c8c-8a3d-c3f8388c75e8.svg
|
||||
demo:
|
||||
cols: 2
|
||||
---
|
||||
|
||||
新的包裹组件,提供重置样式和提供消费上下文的默认环境。
|
||||
|
||||
## 何时使用
|
||||
|
||||
- 提供可消费 React context 的 `message.xxx`、`Modal.xxx`、`notification.xxx` 的静态方法,可以简化 useMessage 等方法需要手动植入 `contextHolder` 的问题。
|
||||
- 提供基于 `.ant-app` 的默认重置样式,解决原生元素没有 antd 规范样式的问题。
|
||||
|
||||
## 代码演示
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<code src="./demo/message.tsx">message</code>
|
||||
<code src="./demo/notification.tsx">notification</code>
|
||||
<code src="./demo/modal.tsx">modal</code>
|
||||
|
||||
## How to use
|
||||
|
||||
```javascript
|
||||
import React from 'react';
|
||||
import { App } from 'antd';
|
||||
const MyPage = () => {
|
||||
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 = () => (
|
||||
<App>
|
||||
<MyPage />
|
||||
</App>
|
||||
);
|
||||
```
|
22
components/app/style/index.tsx
Normal file
22
components/app/style/index.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import type { FullToken, GenerateStyle } from '../../theme/internal';
|
||||
import { genComponentStyleHook } from '../../theme/internal';
|
||||
|
||||
export type ComponentToken = {};
|
||||
|
||||
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,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
// ============================== Export ==============================
|
||||
export default genComponentStyleHook('App', (token) => [genBaseStyle(token)]);
|
@ -135,6 +135,7 @@ export { default as Tooltip } from './tooltip';
|
||||
export type { TooltipProps } from './tooltip';
|
||||
export { default as Tour } from './tour';
|
||||
export type { TourProps, TourStepProps } from './tour/interface';
|
||||
export { default as App } from './app';
|
||||
export { default as Transfer } from './transfer';
|
||||
export type { TransferProps } from './transfer';
|
||||
export { default as Tree } from './tree';
|
||||
|
@ -103,15 +103,11 @@ export const genLinkStyle = (token: DerivativeToken): CSSObject => ({
|
||||
},
|
||||
});
|
||||
|
||||
export const genCommonStyle = (token: DerivativeToken, componentPrefixCls: string): CSSObject => {
|
||||
const { fontFamily, fontSize } = token;
|
||||
|
||||
export const genCommonStyle = (componentPrefixCls: string): CSSObject => {
|
||||
const rootPrefixSelector = `[class^="${componentPrefixCls}"], [class*=" ${componentPrefixCls}"]`;
|
||||
|
||||
return {
|
||||
[rootPrefixSelector]: {
|
||||
fontFamily,
|
||||
fontSize,
|
||||
boxSizing: 'border-box',
|
||||
|
||||
'&::before, &::after': {
|
||||
|
@ -47,6 +47,7 @@ import type { ComponentToken as TypographyComponentToken } from '../../typograph
|
||||
import type { ComponentToken as UploadComponentToken } from '../../upload/style';
|
||||
import type { ComponentToken as TourComponentToken } from '../../tour/style';
|
||||
import type { ComponentToken as QRCodeComponentToken } from '../../qrcode/style';
|
||||
import type { ComponentToken as AppComponentToken } from '../../app/style';
|
||||
|
||||
export interface ComponentTokenMap {
|
||||
Affix?: {};
|
||||
@ -110,4 +111,5 @@ export interface ComponentTokenMap {
|
||||
Progress?: ProgressComponentToken;
|
||||
Tour?: TourComponentToken;
|
||||
QRCode?: QRCodeComponentToken;
|
||||
App?: AppComponentToken;
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ export default function genComponentStyleHook<ComponentName extends OverrideComp
|
||||
overrideComponentToken: token[component],
|
||||
});
|
||||
flush(component, mergedComponentToken);
|
||||
return [genCommonStyle(token, prefixCls), styleInterpolation];
|
||||
return [genCommonStyle(prefixCls), styleInterpolation];
|
||||
},
|
||||
),
|
||||
hashId,
|
||||
|
@ -5,6 +5,7 @@ exports[`antd dist files exports modules correctly 1`] = `
|
||||
"Affix",
|
||||
"Alert",
|
||||
"Anchor",
|
||||
"App",
|
||||
"AutoComplete",
|
||||
"Avatar",
|
||||
"BackTop",
|
||||
|
@ -7,6 +7,7 @@ import glob from 'glob';
|
||||
import { configureToMatchImageSnapshot } from 'jest-image-snapshot';
|
||||
import MockDate from 'mockdate';
|
||||
import ReactDOMServer from 'react-dom/server';
|
||||
import { App } from '../../components';
|
||||
|
||||
const toMatchImageSnapshot = configureToMatchImageSnapshot({
|
||||
customSnapshotsDir: `${process.cwd()}/imageSnapshots`,
|
||||
@ -35,7 +36,9 @@ export default function imageTest(component: React.ReactElement) {
|
||||
|
||||
const cache = createCache();
|
||||
const html = ReactDOMServer.renderToString(
|
||||
<StyleProvider cache={cache}>{component}</StyleProvider>,
|
||||
<App>
|
||||
<StyleProvider cache={cache}>{component}</StyleProvider>,
|
||||
</App>,
|
||||
);
|
||||
const styleStr = extractStyle(cache);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user