Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat : new components app #39046

Merged
merged 23 commits into from Dec 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions components/__tests__/__snapshots__/index.test.ts.snap
Expand Up @@ -5,6 +5,7 @@ exports[`antd exports modules correctly 1`] = `
"Affix",
"Alert",
"Anchor",
"App",
"AutoComplete",
"Avatar",
"BackTop",
Expand Down
46 changes: 46 additions & 0 deletions components/app/__tests__/__snapshots__/demo-extend.test.ts.snap
@@ -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 changes: 46 additions & 0 deletions components/app/__tests__/__snapshots__/demo.test.ts.snap
@@ -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 changes: 17 additions & 0 deletions components/app/__tests__/__snapshots__/index.test.tsx.snap
@@ -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 changes: 3 additions & 0 deletions components/app/__tests__/demo-extend.test.ts
@@ -0,0 +1,3 @@
import { extendTest } from '../../../tests/shared/demoTest';

extendTest('app');
3 changes: 3 additions & 0 deletions components/app/__tests__/demo.test.ts
@@ -0,0 +1,3 @@
import demoTest from '../../../tests/shared/demoTest';

demoTest('app');
5 changes: 5 additions & 0 deletions components/app/__tests__/image.test.ts
@@ -0,0 +1,5 @@
import { imageDemoTest } from '../../../tests/shared/imageTest';

describe('app', () => {
imageDemoTest('app');
});
33 changes: 33 additions & 0 deletions components/app/__tests__/index.test.tsx
@@ -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 changes: 19 additions & 0 deletions components/app/context.ts
@@ -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 changes: 7 additions & 0 deletions components/app/demo/message.md
@@ -0,0 +1,7 @@
## zh-CN

获取 `message` 静态方法.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

找个地方写一下这种方式和 useMessage 的差异是啥?前提是啥,好处是啥?


## en-US

Static method for `message`.
24 changes: 24 additions & 0 deletions components/app/demo/message.tsx
@@ -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 changes: 7 additions & 0 deletions components/app/demo/modal.md
@@ -0,0 +1,7 @@
## zh-CN

获取 `modal` 静态方法.

## en-US

Static method for `modal`.
27 changes: 27 additions & 0 deletions components/app/demo/modal.tsx
@@ -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 changes: 7 additions & 0 deletions components/app/demo/notification.md
@@ -0,0 +1,7 @@
## zh-CN

获取 `notification` 静态方法.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
获取 `notification` 静态方法.
获取 `notification` 静态方法


## en-US

Static method for `notification`.
28 changes: 28 additions & 0 deletions components/app/demo/notification.tsx
@@ -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>
Wxh16144 marked this conversation as resolved.
Show resolved Hide resolved
<MyPage />
</App>
);
43 changes: 43 additions & 0 deletions components/app/index.en-US.md
@@ -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 changes: 60 additions & 0 deletions components/app/index.tsx
@@ -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}>
zombieJ marked this conversation as resolved.
Show resolved Hide resolved
{ModalContextHolder}
{messageContextHolder}
{notificationContextHolder}
{children}
</div>
</AppContext.Provider>,
);
};

if (process.env.NODE_ENV !== 'production') {
App.displayName = 'App';
}

App.useApp = useApp;
export default App;