mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-24 11:10:01 +08:00
Merge pull request #32036 from ant-design/master
branch: merge master into feature
This commit is contained in:
commit
3d81a10d61
2
.github/workflows/issue-remove-inactive.yml
vendored
2
.github/workflows/issue-remove-inactive.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
||||
steps:
|
||||
- name: remove inactive
|
||||
if: github.event.issue.state == 'open'
|
||||
uses: actions-cool/issues-helper@v1.2
|
||||
uses: actions-cool/issues-helper@v2.4.1
|
||||
with:
|
||||
actions: 'remove-labels'
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
|
2
.github/workflows/pr-open-check.yml
vendored
2
.github/workflows/pr-open-check.yml
vendored
@ -2,7 +2,7 @@ name: PR Open Check
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, edited, reopened]
|
||||
types: [opened, edited, reopened, synchronize]
|
||||
|
||||
jobs:
|
||||
refuse:
|
||||
|
27
components/date-picker/__tests__/type.test.tsx
Normal file
27
components/date-picker/__tests__/type.test.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import * as React from 'react';
|
||||
import DatePicker from '..';
|
||||
|
||||
describe('DatePicker.typescript', () => {
|
||||
it('DatePicker ref methods', () => {
|
||||
const datePicker = (
|
||||
<DatePicker
|
||||
ref={picker => {
|
||||
picker?.focus();
|
||||
picker?.blur();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
expect(datePicker).toBeTruthy();
|
||||
});
|
||||
it('RangePicker ref methods', () => {
|
||||
const rangePicker = (
|
||||
<DatePicker.RangePicker
|
||||
ref={picker => {
|
||||
picker?.focus();
|
||||
picker?.blur();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
expect(rangePicker).toBeTruthy();
|
||||
});
|
||||
});
|
@ -12,10 +12,11 @@ import SizeContext from '../../config-provider/SizeContext';
|
||||
import LocaleReceiver from '../../locale-provider/LocaleReceiver';
|
||||
import { getRangePlaceholder } from '../util';
|
||||
import { RangePickerProps, PickerLocale, getTimeProps, Components } from '.';
|
||||
import { PickerComponentClass } from './interface';
|
||||
|
||||
export default function generateRangePicker<DateType>(
|
||||
generateConfig: GenerateConfig<DateType>,
|
||||
): React.ComponentClass<RangePickerProps<DateType>> {
|
||||
): PickerComponentClass<RangePickerProps<DateType>> {
|
||||
class RangePicker extends React.Component<RangePickerProps<DateType>> {
|
||||
static contextType = ConfigContext;
|
||||
|
||||
|
@ -20,6 +20,7 @@ import {
|
||||
getTimeProps,
|
||||
Components,
|
||||
} from '.';
|
||||
import { PickerComponentClass } from './interface';
|
||||
|
||||
export default function generatePicker<DateType>(generateConfig: GenerateConfig<DateType>) {
|
||||
type DatePickerProps = PickerProps<DateType>;
|
||||
@ -147,7 +148,7 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
|
||||
Picker.displayName = displayName;
|
||||
}
|
||||
|
||||
return Picker as React.ComponentClass<InnerPickerProps>;
|
||||
return Picker as PickerComponentClass<InnerPickerProps>;
|
||||
}
|
||||
|
||||
const DatePicker = getPicker<DatePickerProps>();
|
||||
|
@ -1,4 +1,3 @@
|
||||
import * as React from 'react';
|
||||
import { GenerateConfig } from 'rc-picker/lib/generate/index';
|
||||
import {
|
||||
PickerBaseProps as RCPickerBaseProps,
|
||||
@ -124,14 +123,8 @@ export type RangePickerProps<DateType> =
|
||||
|
||||
function generatePicker<DateType>(generateConfig: GenerateConfig<DateType>) {
|
||||
// =========================== Picker ===========================
|
||||
const {
|
||||
DatePicker,
|
||||
WeekPicker,
|
||||
MonthPicker,
|
||||
YearPicker,
|
||||
TimePicker,
|
||||
QuarterPicker,
|
||||
} = generateSinglePicker(generateConfig);
|
||||
const { DatePicker, WeekPicker, MonthPicker, YearPicker, TimePicker, QuarterPicker } =
|
||||
generateSinglePicker(generateConfig);
|
||||
|
||||
// ======================== Range Picker ========================
|
||||
const RangePicker = generateRangePicker(generateConfig);
|
||||
@ -141,7 +134,7 @@ function generatePicker<DateType>(generateConfig: GenerateConfig<DateType>) {
|
||||
WeekPicker: typeof WeekPicker;
|
||||
MonthPicker: typeof MonthPicker;
|
||||
YearPicker: typeof YearPicker;
|
||||
RangePicker: React.ComponentClass<RangePickerProps<DateType>>;
|
||||
RangePicker: typeof RangePicker;
|
||||
TimePicker: typeof TimePicker;
|
||||
QuarterPicker: typeof QuarterPicker;
|
||||
};
|
||||
|
11
components/date-picker/generatePicker/interface.tsx
Normal file
11
components/date-picker/generatePicker/interface.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { ComponentClass } from 'react';
|
||||
|
||||
export interface CommonPickerMethods {
|
||||
focus: () => void;
|
||||
blur: () => void;
|
||||
}
|
||||
|
||||
export interface PickerComponentClass<P = {}, S = unknown> extends ComponentClass<P, S> {
|
||||
new (...args: ConstructorParameters<ComponentClass<P, S>>): InstanceType<ComponentClass<P, S>> &
|
||||
CommonPickerMethods;
|
||||
}
|
@ -19,6 +19,8 @@ type EventType =
|
||||
|
||||
type getContainerFunc = () => HTMLElement;
|
||||
|
||||
type ILevelMove = number | [number, number];
|
||||
|
||||
const PlacementTypes = tuple('top', 'right', 'bottom', 'left');
|
||||
type placementType = typeof PlacementTypes[number];
|
||||
|
||||
@ -62,6 +64,9 @@ export interface DrawerProps {
|
||||
footer?: React.ReactNode;
|
||||
footerStyle?: React.CSSProperties;
|
||||
level?: string | string[] | null | undefined;
|
||||
levelMove?:
|
||||
| ILevelMove
|
||||
| ((e: { target: HTMLElement; open: boolean }) => ILevelMove);
|
||||
}
|
||||
|
||||
export interface IDrawerState {
|
||||
|
@ -27,7 +27,7 @@ When there are more than a few options to choose from, you can wrap them in a `D
|
||||
| placement | Placement of popup menu: `bottomLeft`, `bottomCenter`, `bottomRight`, `topLeft`, `topCenter` or `topRight` | string | `bottomLeft` | |
|
||||
| trigger | The trigger mode which executes the dropdown action. Note that hover can't be used on touchscreens | Array<`click`\|`hover`\|`contextMenu`> | \[`hover`] | |
|
||||
| visible | Whether the dropdown menu is currently visible | boolean | - | |
|
||||
| onVisibleChange | Called when the visible state is changed | (visible: boolean) => void | - | |
|
||||
| onVisibleChange | Called when the visible state is changed. Not trigger when hidden by click item | (visible: boolean) => void | - | |
|
||||
|
||||
You should use [Menu](/components/menu/) as `overlay`. The menu items and dividers are also available by using `Menu.Item` and `Menu.Divider`.
|
||||
|
||||
|
@ -31,7 +31,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/eedWN59yJ/Dropdown.svg
|
||||
| placement | 菜单弹出位置:`bottomLeft` `bottomCenter` `bottomRight` `topLeft` `topCenter` `topRight` | string | `bottomLeft` | |
|
||||
| trigger | 触发下拉的行为, 移动端不支持 hover | Array<`click`\|`hover`\|`contextMenu`> | \[`hover`] | |
|
||||
| visible | 菜单是否显示 | boolean | - | |
|
||||
| onVisibleChange | 菜单显示状态改变时调用,参数为 `visible` | (visible: boolean) => void | - | |
|
||||
| onVisibleChange | 菜单显示状态改变时调用,参数为 `visible`。点击菜单按钮导致的消失不会触发 | (visible: boolean) => void | - | |
|
||||
|
||||
`overlay` 菜单使用 [Menu](/components/menu/),还包括菜单项 `Menu.Item`,分割线 `Menu.Divider`。
|
||||
|
||||
|
@ -119,6 +119,9 @@ const localeValues: Locale = {
|
||||
},
|
||||
},
|
||||
},
|
||||
Image: {
|
||||
preview: 'Vorschau',
|
||||
},
|
||||
};
|
||||
|
||||
export default localeValues;
|
||||
|
@ -441,6 +441,76 @@ describe('Table.rowSelection', () => {
|
||||
expect(handleSelectEven).toHaveBeenCalledWith([0, 1, 2, 3]);
|
||||
});
|
||||
|
||||
describe('preset selection options', () => {
|
||||
const presetData = [
|
||||
{ key: 0, name: 'Jack' },
|
||||
{ key: 1, name: 'Lucy', disabled: true },
|
||||
{ key: 2, name: 'Tom' },
|
||||
];
|
||||
|
||||
const getCheckboxProps = record => record;
|
||||
|
||||
it('SELECTION_ALL', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
createTable({
|
||||
dataSource: presetData,
|
||||
rowSelection: {
|
||||
onChange,
|
||||
defaultSelectedRowKeys: [2],
|
||||
getCheckboxProps,
|
||||
selections: [Table.SELECTION_ALL],
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
wrapper.find('Trigger').setState({ popupVisible: true });
|
||||
wrapper.find('li.ant-dropdown-menu-item').first().simulate('click');
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith([0, 2], expect.anything());
|
||||
});
|
||||
|
||||
it('SELECTION_INVERT', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
createTable({
|
||||
dataSource: presetData,
|
||||
rowSelection: {
|
||||
onChange,
|
||||
defaultSelectedRowKeys: [2],
|
||||
getCheckboxProps,
|
||||
selections: [Table.SELECTION_INVERT],
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
wrapper.find('Trigger').setState({ popupVisible: true });
|
||||
wrapper.find('li.ant-dropdown-menu-item').first().simulate('click');
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith([0], expect.anything());
|
||||
});
|
||||
|
||||
it('SELECTION_NONE', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
createTable({
|
||||
dataSource: presetData,
|
||||
rowSelection: {
|
||||
onChange,
|
||||
defaultSelectedRowKeys: [1, 2],
|
||||
getCheckboxProps,
|
||||
selections: [Table.SELECTION_NONE],
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
wrapper.find('Trigger').setState({ popupVisible: true });
|
||||
wrapper.find('li.ant-dropdown-menu-item').first().simulate('click');
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith([1], expect.anything());
|
||||
});
|
||||
});
|
||||
|
||||
it('could hide selectAll checkbox and custom selection', () => {
|
||||
const rowSelection = {
|
||||
hideSelectAll: true,
|
||||
|
@ -79,7 +79,7 @@ class SortableTable extends React.Component {
|
||||
onSortEnd = ({ oldIndex, newIndex }) => {
|
||||
const { dataSource } = this.state;
|
||||
if (oldIndex !== newIndex) {
|
||||
const newData = arrayMove([].concat(dataSource), oldIndex, newIndex).filter(el => !!el);
|
||||
const newData = arrayMoveImmutable([].concat(dataSource), oldIndex, newIndex).filter(el => !!el);
|
||||
console.log('Sorted items: ', newData);
|
||||
this.setState({ dataSource: newData });
|
||||
}
|
||||
|
@ -281,7 +281,14 @@ export default function useSelection<RecordType>(
|
||||
key: 'all',
|
||||
text: tableLocale.selectionAll,
|
||||
onSelect() {
|
||||
setSelectedKeys(data.map((record, index) => getRowKey(record, index)));
|
||||
setSelectedKeys(
|
||||
data
|
||||
.map((record, index) => getRowKey(record, index))
|
||||
.filter(key => {
|
||||
const checkProps = checkboxPropsMap.get(key);
|
||||
return !checkProps?.disabled || derivedSelectedKeySet.has(key);
|
||||
}),
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -293,11 +300,14 @@ export default function useSelection<RecordType>(
|
||||
const keySet = new Set(derivedSelectedKeySet);
|
||||
pageData.forEach((record, index) => {
|
||||
const key = getRowKey(record, index);
|
||||
const checkProps = checkboxPropsMap.get(key);
|
||||
|
||||
if (keySet.has(key)) {
|
||||
keySet.delete(key);
|
||||
} else {
|
||||
keySet.add(key);
|
||||
if (!checkProps?.disabled) {
|
||||
if (keySet.has(key)) {
|
||||
keySet.delete(key);
|
||||
} else {
|
||||
keySet.add(key);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -321,7 +331,12 @@ export default function useSelection<RecordType>(
|
||||
text: tableLocale.selectNone,
|
||||
onSelect() {
|
||||
onSelectNone?.();
|
||||
setSelectedKeys([]);
|
||||
setSelectedKeys(
|
||||
Array.from(derivedSelectedKeySet).filter(key => {
|
||||
const checkProps = checkboxPropsMap.get(key);
|
||||
return checkProps?.disabled;
|
||||
}),
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -86,16 +86,32 @@ title: 按钮
|
||||
经常独立出现,行动号召按钮就像是电脑在对用户大声说“跟我来吧”,有点命令用户点击的意味,通常出现于 landing page 或者 一些引导性场景。最大可以将按钮放宽到与父区域等宽。一个屏幕空间中,建议只有一个行动号召按钮。
|
||||
|
||||
## 位置
|
||||
### 按钮区
|
||||
|
||||
<img class="preview-img no-padding" align="right" src="https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*B8D0RJnirLkAAAAAAAAAAABkARQnAQ">
|
||||
<img class="preview-img no-padding" align="right" src="https://img.alicdn.com/imgextra/i1/O1CN01Wd9Dbh1z6A5MQwEnh_!!6000000006664-2-tps-930-290.png">
|
||||
|
||||
按钮区是用于放置按钮的区域,一个按钮区内可以有多个按钮。
|
||||
|
||||
|
||||
### 跟随内容的按钮区
|
||||
|
||||
<img class="preview-img no-padding" align="right" src="https://img.alicdn.com/imgextra/i4/O1CN01OVOv5G27z8YLYdWED_!!6000000007867-2-tps-928-342.png">
|
||||
|
||||
按钮区跟随受控内容。将按钮区放置于用户浏览路径中,便于被用户发现。
|
||||
|
||||
|
||||
### 工具栏中的按钮区
|
||||
|
||||
<img class="preview-img no-padding" align="right" src="https://img.alicdn.com/imgextra/i2/O1CN01aAZHoi1uZrgx1C3zR_!!6000000006052-2-tps-928-332.png">
|
||||
|
||||
工具栏中的按钮区,靠右放置。控制工具栏控制的内容范围。
|
||||
|
||||
将按钮区放置于用户浏览路径中,便于被用户发现,如 “F 浏览模式” 和 “Z 浏览模式” 。
|
||||
|
||||
### 如何确定按钮区的放置位置?
|
||||
|
||||
#### 页面/卡片/一组信息都能够呈现一个主题,主题的描述可以抽象为三个区域:
|
||||
|
||||
<img class="preview-img no-padding" align="right" src="https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*iVZpRpdN_2AAAAAAAAAAAABkARQnAQ">
|
||||
<img class="preview-img no-padding" align="right" src="https://img.alicdn.com/imgextra/i2/O1CN017b7PRO1TEnquClCYx_!!6000000002351-2-tps-928-622.png">
|
||||
|
||||
- Header:主题的标题和摘要信息内容区的导航等
|
||||
- Body:具体内容
|
||||
@ -109,10 +125,9 @@ title: 按钮
|
||||
|
||||
<img class="preview-img no-padding" align="right" src="https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*KGGWQLCBfm0AAAAAAAAAAABkARQnAQ">
|
||||
|
||||
- Body 区部分内容被折叠或隐藏,例如单屏无法展示完整内容;
|
||||
- Body 区的内容复杂度高,例如有多个分组,分组中又有独立的按钮区,这时候需要将该主题的“完成”操作从 body 区区分出来,避免混淆按钮所能影响的内容范围。
|
||||
|
||||
简而言之,Footer 的存在就是为了要和 Body 区区分开来。
|
||||
为避免页脚工具栏滥用,我们不推荐使用页脚工具栏,仅建议以下两种场景使用:
|
||||
- 1)对象详情页,「推进」对象的进展,例如审批流「通过」「驳回」。
|
||||
- 2)异常复杂的表单页,表单的内容复杂到需要切分为多张卡片。
|
||||
|
||||
## 按钮顺序
|
||||
|
||||
|
@ -179,7 +179,7 @@
|
||||
"antd-pro-merge-less": "^3.0.11",
|
||||
"antd-theme-generator": "^1.2.3",
|
||||
"argos-cli": "^0.3.0",
|
||||
"array-move": "^4.0.0",
|
||||
"array-move": "^3.0.0",
|
||||
"babel-plugin-add-react-displayname": "^0.0.5",
|
||||
"bisheng": "^3.0.0",
|
||||
"bisheng-plugin-description": "^0.1.4",
|
||||
@ -230,7 +230,7 @@
|
||||
"less-vars-to-js": "^1.3.0",
|
||||
"lz-string": "^1.4.4",
|
||||
"mockdate": "^3.0.0",
|
||||
"node-fetch": "^2.6.0",
|
||||
"node-fetch": "^3.0.0",
|
||||
"open": "^8.0.1",
|
||||
"prettier": "^2.3.2",
|
||||
"prettier-plugin-jsdoc": "^0.3.0",
|
||||
|
Loading…
Reference in New Issue
Block a user