Skip to content

Commit

Permalink
feat : new components app (#39046)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
3 people committed Dec 6, 2022
1 parent cd8840b commit db4ee53
Show file tree
Hide file tree
Showing 25 changed files with 453 additions and 7 deletions.
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` 静态方法.

## 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` 静态方法.

## 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>
<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}>
{ModalContextHolder}
{messageContextHolder}
{notificationContextHolder}
{children}
</div>
</AppContext.Provider>,
);
};

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

App.useApp = useApp;
export default App;

0 comments on commit db4ee53

Please sign in to comment.