Merge pull request #46937 from ant-design/master

chore: feature merge master
This commit is contained in:
二货爱吃白萝卜 2024-01-12 13:50:02 +08:00 committed by GitHub
commit ec25f2230c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 217 additions and 8 deletions

View File

@ -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

View File

@ -1,5 +1,7 @@
import { imageDemoTest } from '../../../tests/shared/imageTest';
describe('DatePicker image', () => {
imageDemoTest('date-picker');
imageDemoTest('date-picker', {
openTriggerClassName: 'ant-picker-dropdown',
});
});

View File

@ -146,6 +146,7 @@ const genSegmentedStyle: GenerateStyle<SegmentedToken> = (token: SegmentedToken)
'&::after': {
content: '""',
position: 'absolute',
zIndex: -1,
width: '100%',
height: '100%',
top: 0,

View File

@ -1,5 +1,7 @@
import { imageDemoTest } from '../../../tests/shared/imageTest';
describe('TimePicker image', () => {
imageDemoTest('time-picker');
imageDemoTest('time-picker', {
openTriggerClassName: 'ant-picker-dropdown',
});
});

View 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.

View 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。

View File

@ -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,
});
});
});