mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-27 20:49:53 +08:00
Merge pull request #46937 from ant-design/master
chore: feature merge master
This commit is contained in:
commit
ec25f2230c
8
.github/ISSUE_TEMPLATE/config.yml
vendored
8
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,8 +1,8 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: 🆕 Create new issue
|
||||
url: http://new-issue.ant.design
|
||||
about: The issue which is not created via http://new-issue.ant.design will be closed immediately.
|
||||
url: https://new-issue.ant.design
|
||||
about: The issue which is not created via https://new-issue.ant.design will be closed immediately.
|
||||
- name: 🆕 创建一个新 Issue
|
||||
url: http://new-issue.ant.design
|
||||
about: 不是用 http://new-issue.ant.design 创建的 issue 会被机器人自动关闭。另外『如何使用...』类问题建议使用讨论区 https://github.com/ant-design/ant-design/discussions
|
||||
url: https://new-issue.ant.design
|
||||
about: 不是用 https://new-issue.ant.design 创建的 issue 会被机器人自动关闭。另外『如何使用...』类问题建议使用讨论区 https://github.com/ant-design/ant-design/discussions
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { imageDemoTest } from '../../../tests/shared/imageTest';
|
||||
|
||||
describe('DatePicker image', () => {
|
||||
imageDemoTest('date-picker');
|
||||
imageDemoTest('date-picker', {
|
||||
openTriggerClassName: 'ant-picker-dropdown',
|
||||
});
|
||||
});
|
||||
|
@ -146,6 +146,7 @@ const genSegmentedStyle: GenerateStyle<SegmentedToken> = (token: SegmentedToken)
|
||||
'&::after': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
zIndex: -1,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
top: 0,
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { imageDemoTest } from '../../../tests/shared/imageTest';
|
||||
|
||||
describe('TimePicker image', () => {
|
||||
imageDemoTest('time-picker');
|
||||
imageDemoTest('time-picker', {
|
||||
openTriggerClassName: 'ant-picker-dropdown',
|
||||
});
|
||||
});
|
||||
|
82
docs/blog/type-util.en-US.md
Normal file
82
docs/blog/type-util.en-US.md
Normal file
@ -0,0 +1,82 @@
|
||||
---
|
||||
title: Type Util
|
||||
date: 2024-01-11
|
||||
author: zombieJ
|
||||
---
|
||||
|
||||
The definition of TypeScript is very powerful, it can solve many problems, help developers find type errors in advance to avoid painful debugging at runtime. In antd, we also export the basic definitions of components:
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
import { Table, type TableColumnsType } from 'antd';
|
||||
|
||||
const columns: TableColumnsType = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
},
|
||||
];
|
||||
|
||||
export default () => <Table columns={columns} />;
|
||||
```
|
||||
|
||||
These definitions meet most scenarios, but sometimes developers want to get more refined type definitions, which antd may not export. In the past, we recommended developers to extend them by themselves through TypeScript's type gymnastics to meet their needs:
|
||||
|
||||
```tsx
|
||||
import type { SelectProps } from 'antd';
|
||||
|
||||
type SelectOption<T> = NonNullable<SelectProps<T>['options']>[number];
|
||||
```
|
||||
|
||||
It's not a difficult task for developer who are familiar with TypeScript. But for TypeScript beginners, this may be a difficult problem. Therefore, we have launched a type tool library to help developers simplify the process of extracting types.
|
||||
|
||||
### Type Util
|
||||
|
||||
We now provide 3 additional utility types in antd:
|
||||
|
||||
- `GetProps<ComponentType>`
|
||||
- `GetProp<ComponentTypeOrComponentPropsType, PropName>`
|
||||
- `GetRef<ComponentType>`
|
||||
|
||||
Previous two are used to help developers extract the props type of the component, and the last one is used to extract the ref type of the component. We can understand the usage of these types through the following examples:
|
||||
|
||||
#### Get props definition by GetProps
|
||||
|
||||
Some sub-component definition may not be exported in antd. You can get it directly through `GetProps`:
|
||||
|
||||
```tsx
|
||||
import type { Checkbox, GetProps } from 'antd';
|
||||
|
||||
type CheckboxGroupType = GetProps<typeof Checkbox.Group>;
|
||||
```
|
||||
|
||||
#### Get property type by GetProp
|
||||
|
||||
For the property type of the component, we can get it through `GetProp`. It has been encapsulated with `NonNullable`. So there is no need to consider the null case:
|
||||
|
||||
```tsx
|
||||
import type { GetProp, Select, SelectProps } from 'antd';
|
||||
|
||||
// Both of this can work
|
||||
type SelectOptionType1 = GetProp<SelectProps, 'options'>[number];
|
||||
type SelectOptionType2 = GetProp<typeof Select, 'options'>[number];
|
||||
```
|
||||
|
||||
#### Get ref definition by GetRef
|
||||
|
||||
Through `GetRef`, you don't need to remember what the ref type of the component is, HTMLElement or some special definition. Just use it:
|
||||
|
||||
```tsx
|
||||
import React, { forwardRef } from 'react';
|
||||
import type { GetRef, Select } from 'antd';
|
||||
|
||||
type SelectRefType = GetRef<typeof Select>; // BaseSelectRef
|
||||
|
||||
const Div = forwardRef<HTMLDivElement>((_, ref) => <div ref={ref} />);
|
||||
type DomRefType = GetRef<typeof Div>; // HTMLDivElement
|
||||
```
|
||||
|
||||
### The End
|
||||
|
||||
Here is the type util we provided, hope it can help you. If you have better ideas, please feel free to raise an issue or PR on Github.
|
82
docs/blog/type-util.zh-CN.md
Normal file
82
docs/blog/type-util.zh-CN.md
Normal file
@ -0,0 +1,82 @@
|
||||
---
|
||||
title: antd 里常用的 TypeScript 工具方法
|
||||
date: 2024-01-11
|
||||
author: zombieJ
|
||||
---
|
||||
|
||||
TypeScript 的类型定义是非常强大的帮手,它可以解决很多问题,帮助开发者提前发现类型错误从而避免在运行时痛苦的调试。在 antd 中,我们也将组件的基本定义都进行了导出:
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
import { Table, type TableColumnsType } from 'antd';
|
||||
|
||||
const columns: TableColumnsType = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
},
|
||||
];
|
||||
|
||||
export default () => <Table columns={columns} />;
|
||||
```
|
||||
|
||||
这些类型定义满足了大多数场景,但是有时候开发者希望获得更精细的类型定义,antd 并不一定将其导出。在过去,我们推荐开发通过 TypeScript 的类型体操进行自行拓展来满足需求:
|
||||
|
||||
```tsx
|
||||
import type { SelectProps } from 'antd';
|
||||
|
||||
type SelectOption<T> = NonNullable<SelectProps<T>['options']>[number];
|
||||
```
|
||||
|
||||
这对于 TypeScript 熟悉的朋友而言,应该不是什么难事。但是对于 TypeScript 初学者而言,这可能是一道难题。因此,我们推出了一个工具类型库,帮助开发者简化抽取类型的过程。
|
||||
|
||||
### 工具类型
|
||||
|
||||
现在在 antd 中,我们额外提供了 3 个工具类型:
|
||||
|
||||
- `GetProps<ComponentType>`
|
||||
- `GetProp<ComponentTypeOrComponentPropsType, PropName>`
|
||||
- `GetRef<ComponentType>`
|
||||
|
||||
前两者用户帮助开发者抽取组件的 props 类型,最后一个用于抽取组件的 ref 类型。我们可以通过下面的例子来理解这些类型的用法:
|
||||
|
||||
#### GetProps 获取属性定义
|
||||
|
||||
antd 中,对于一些组件的子组件定义不一定被导出。你可以直接通过 `GetProps` 来获取:
|
||||
|
||||
```tsx
|
||||
import type { Checkbox, GetProps } from 'antd';
|
||||
|
||||
type CheckboxGroupType = GetProps<typeof Checkbox.Group>;
|
||||
```
|
||||
|
||||
#### GetProp 获取属性类型
|
||||
|
||||
对于组件的属性类型,我们可以通过 `GetProp` 来获取。它已经将 `NonNullable` 进行了封装。所以不用在考虑为空的情况:
|
||||
|
||||
```tsx
|
||||
import type { GetProp, Select, SelectProps } from 'antd';
|
||||
|
||||
// Both of this can work
|
||||
type SelectOptionType1 = GetProp<SelectProps, 'options'>[number];
|
||||
type SelectOptionType2 = GetProp<typeof Select, 'options'>[number];
|
||||
```
|
||||
|
||||
#### GetRef 获取 ref 类型
|
||||
|
||||
通过 `GetRef`,你不用再记忆组件的 ref 类型到底是 HTMLElement 或者什么特别的定义。直接用就完了:
|
||||
|
||||
```tsx
|
||||
import React, { forwardRef } from 'react';
|
||||
import type { GetRef, Select } from 'antd';
|
||||
|
||||
type SelectRefType = GetRef<typeof Select>; // BaseSelectRef
|
||||
|
||||
const Div = forwardRef<HTMLDivElement>((_, ref) => <div ref={ref} />);
|
||||
type DomRefType = GetRef<typeof Div>; // HTMLDivElement
|
||||
```
|
||||
|
||||
### 最后
|
||||
|
||||
以上就是我们推出的工具类型,希望能够帮助到大家。如果你有更好的想法,欢迎在 Github 上提出 issue 或者 PR。
|
@ -13,6 +13,7 @@ import ReactDOMServer from 'react-dom/server';
|
||||
import { App, ConfigProvider, theme } from '../../components';
|
||||
import { fillWindowEnv } from '../setup';
|
||||
import { render } from '../utils';
|
||||
import { TriggerMockContext } from './demoTestContext';
|
||||
|
||||
jest.mock('../../components/grid/hooks/useBreakpoint', () => () => ({}));
|
||||
|
||||
@ -33,6 +34,7 @@ interface ImageTestOptions {
|
||||
onlyViewport?: boolean;
|
||||
splitTheme?: boolean;
|
||||
ssr?: boolean;
|
||||
openTriggerClassName?: string;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line jest/no-export
|
||||
@ -121,6 +123,8 @@ export default function imageTest(
|
||||
}
|
||||
};
|
||||
|
||||
const { openTriggerClassName } = options;
|
||||
|
||||
MockDate.set(dayjs('2016-11-22').valueOf());
|
||||
page.on('request', onRequestHandle);
|
||||
await page.goto(`file://${process.cwd()}/tests/index.html`);
|
||||
@ -131,12 +135,21 @@ export default function imageTest(
|
||||
|
||||
const emptyStyleHolder = doc.createElement('div');
|
||||
|
||||
const element = (
|
||||
let element = (
|
||||
<StyleProvider cache={cache} container={emptyStyleHolder}>
|
||||
<App>{themedComponent}</App>
|
||||
</StyleProvider>
|
||||
);
|
||||
|
||||
// Do inject open trigger
|
||||
if (openTriggerClassName) {
|
||||
element = (
|
||||
<TriggerMockContext.Provider value={{ popupVisible: true }}>
|
||||
{element}
|
||||
</TriggerMockContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
let html: string;
|
||||
let styleStr: string;
|
||||
|
||||
@ -154,15 +167,39 @@ export default function imageTest(
|
||||
unmount();
|
||||
}
|
||||
|
||||
if (openTriggerClassName) {
|
||||
styleStr += `<style>
|
||||
.${openTriggerClassName} {
|
||||
position: relative !important;
|
||||
left: 0 !important;
|
||||
top: 0 !important;
|
||||
opacity: 1 !important;
|
||||
display: inline-block !important;
|
||||
}
|
||||
</style>`;
|
||||
}
|
||||
|
||||
await page.evaluate(
|
||||
(innerHTML, ssrStyle) => {
|
||||
(innerHTML, ssrStyle, triggerClassName) => {
|
||||
document.querySelector('#root')!.innerHTML = innerHTML;
|
||||
|
||||
const head = document.querySelector('head')!;
|
||||
head.innerHTML += ssrStyle;
|
||||
|
||||
// Inject open trigger with block style
|
||||
if (triggerClassName) {
|
||||
document.querySelectorAll(`.${triggerClassName}`).forEach((node) => {
|
||||
const blockStart = document.createElement('div');
|
||||
const blockEnd = document.createElement('div');
|
||||
|
||||
node.parentNode!.insertBefore(blockStart, node);
|
||||
node.parentNode!.insertBefore(blockEnd, node.nextSibling);
|
||||
});
|
||||
}
|
||||
},
|
||||
html,
|
||||
styleStr,
|
||||
openTriggerClassName,
|
||||
);
|
||||
|
||||
if (!options.onlyViewport) {
|
||||
@ -233,6 +270,8 @@ type Options = {
|
||||
splitTheme?: boolean | string[];
|
||||
/** Use SSR render instead. Only used when the third part deps component */
|
||||
ssr?: boolean;
|
||||
/** Open Trigger to check the popup render */
|
||||
openTriggerClassName?: string;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line jest/no-export
|
||||
@ -261,6 +300,7 @@ export function imageDemoTest(component: string, options: Options = {}) {
|
||||
options.splitTheme === true ||
|
||||
(Array.isArray(options.splitTheme) && options.splitTheme.some((c) => file.endsWith(c))),
|
||||
ssr: options.ssr,
|
||||
openTriggerClassName: options.openTriggerClassName,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user