mirror of
https://github.com/ant-design/ant-design.git
synced 2025-08-06 16:06:28 +08:00
Merge branch 'master' into antd-3.0
This commit is contained in:
commit
01f4b4e713
@ -222,14 +222,13 @@ export default class FormItem extends React.Component<FormItemProps, any> {
|
||||
|
||||
// Resolve duplicated ids bug between different forms
|
||||
// https://github.com/ant-design/ant-design/issues/7351
|
||||
onLabelClick = (e: React.MouseEvent<HTMLLabelElement>) => {
|
||||
onLabelClick = () => {
|
||||
const id = this.props.id || this.getId();
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
const controls = document.querySelectorAll(`[id="${id}"]`);
|
||||
if (controls.length !== 1) {
|
||||
e.preventDefault();
|
||||
const control = ReactDOM.findDOMNode(this).querySelector(`[id="${id}"]`) as HTMLElement;
|
||||
if (control && control.focus) {
|
||||
control.focus();
|
||||
|
@ -2379,6 +2379,7 @@ exports[`renders ./components/form/demo/validate-other.md correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-radio-group"
|
||||
id="radio-group"
|
||||
>
|
||||
<label
|
||||
class="ant-radio-wrapper"
|
||||
@ -2460,6 +2461,7 @@ exports[`renders ./components/form/demo/validate-other.md correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-radio-group"
|
||||
id="radio-button"
|
||||
>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
|
@ -75,8 +75,8 @@ input[type="checkbox"] {
|
||||
transition: margin .15s steps(1);
|
||||
|
||||
// nested FormItem
|
||||
& > &:last-child,
|
||||
& :not(.@{form-prefix-cls}) > & {
|
||||
&-control > &:last-child,
|
||||
& [class^="@{ant-prefix}-col-"] > &:only-child {
|
||||
margin-bottom: -@form-item-margin-bottom;
|
||||
}
|
||||
|
||||
@ -261,6 +261,20 @@ form {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
// fix input with addon position. https://github.com/ant-design/ant-design/issues/8243
|
||||
.@{ant-prefix}-input-group-wrapper {
|
||||
position: relative;
|
||||
top: (@input-height-lg - @input-height-base) / 2;
|
||||
|
||||
&.@{ant-prefix}-input-group-wrapper-lg {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&.@{ant-prefix}-input-group-wrapper-sm {
|
||||
top: (@input-height-lg - @input-height-sm) / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Input combined with select
|
||||
|
@ -138,12 +138,17 @@ export default class Input extends React.Component<InputProps, any> {
|
||||
[wrapperClassName]: (addonBefore || addonAfter),
|
||||
});
|
||||
|
||||
const groupClassName = classNames(`${props.prefixCls}-group-wrapper`, {
|
||||
[`${props.prefixCls}-group-wrapper-sm`]: props.size === 'small',
|
||||
[`${props.prefixCls}-group-wrapper-lg`]: props.size === 'large',
|
||||
});
|
||||
|
||||
// Need another wrapper for changing display:table to display:inline-block
|
||||
// and put style prop in wrapper
|
||||
if (addonBefore || addonAfter) {
|
||||
return (
|
||||
<span
|
||||
className={`${props.prefixCls}-group-wrapper`}
|
||||
className={groupClassName}
|
||||
style={props.style}
|
||||
>
|
||||
<span className={className}>
|
||||
|
@ -38,4 +38,10 @@ export default {
|
||||
Select: {
|
||||
notFoundContent: 'Nicht gefunden',
|
||||
},
|
||||
Upload: {
|
||||
uploading: 'Hochladen...',
|
||||
removeFile: 'Datei entfernen',
|
||||
uploadError: 'Fehler beim Hochladen',
|
||||
previewFile: 'Dateivorschau',
|
||||
},
|
||||
};
|
||||
|
@ -43,6 +43,9 @@ export interface ModalProps {
|
||||
className?: string;
|
||||
getContainer?: (instance: React.ReactInstance) => HTMLElement;
|
||||
zIndex?: number;
|
||||
bodyStyle?: React.CSSProperties;
|
||||
maskStyle?: React.CSSProperties;
|
||||
mask?: boolean;
|
||||
}
|
||||
|
||||
export interface ModalFuncProps {
|
||||
@ -175,11 +178,11 @@ export default class Modal extends React.Component<ModalProps, {}> {
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
onClose={this.handleCancel}
|
||||
footer={footer === undefined ? defaultFooter : footer}
|
||||
{...this.props}
|
||||
footer={footer === undefined ? defaultFooter : footer}
|
||||
visible={visible}
|
||||
mousePosition={mousePosition}
|
||||
onClose={this.handleCancel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ export default class RadioGroup extends React.Component<RadioGroupProps, RadioGr
|
||||
}
|
||||
render() {
|
||||
const props = this.props;
|
||||
const { prefixCls = 'ant-radio-group', className = '' } = props;
|
||||
const { prefixCls = 'ant-radio-group', className = '', options } = props;
|
||||
const classString = classNames(prefixCls, {
|
||||
[`${prefixCls}-${props.size}`]: props.size,
|
||||
}, className);
|
||||
@ -97,8 +97,8 @@ export default class RadioGroup extends React.Component<RadioGroupProps, RadioGr
|
||||
let children: React.ReactChildren[] | React.ReactElement<any>[] | React.ReactNode = props.children;
|
||||
|
||||
// 如果存在 options, 优先使用
|
||||
if (props.options && props.options.length > 0) {
|
||||
children = props.options.map((option, index) => {
|
||||
if (options && options.length > 0) {
|
||||
children = options.map((option, index) => {
|
||||
if (typeof option === 'string') { // 此处类型自动推导为 string
|
||||
return (
|
||||
<Radio
|
||||
@ -133,6 +133,7 @@ export default class RadioGroup extends React.Component<RadioGroupProps, RadioGr
|
||||
style={props.style}
|
||||
onMouseEnter={props.onMouseEnter}
|
||||
onMouseLeave={props.onMouseLeave}
|
||||
id={props.id}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
@ -11,6 +11,7 @@ export interface RadioGroupProps extends AbstractCheckboxGroupProps {
|
||||
onMouseLeave?: React.MouseEventHandler<HTMLDivElement>;
|
||||
name?: string;
|
||||
children?: React.ReactNode;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export interface RadioGroupState {
|
||||
|
@ -25,7 +25,7 @@ One or more elements can be selected from either column, one click on the proper
|
||||
| listStyle | A custom CSS style used for rendering the transfer columns. | object | |
|
||||
| notFoundContent | Text to display when a column is empty. | string\|ReactNode | 'The list is empty' |
|
||||
| operations | A set of operations that are sorted from bottom to top. | string\[] | ['>', '<'] |
|
||||
| render | The function to generate the item shown on a column. Based on an record (element of the dataSource array), this function should return a React element which is generated from that record. | Function(record) | |
|
||||
| render | The function to generate the item shown on a column. Based on an record (element of the dataSource array), this function should return a React element which is generated from that record. Also, it can return a plain object with `value` and `label`, `label` is a React element and `value` is for title | Function(record) | |
|
||||
| searchPlaceholder | The hint text of the search box. | string | 'Search here' |
|
||||
| selectedKeys | A set of keys of selected items. | string\[] | \[] |
|
||||
| showSearch | If included, a search box is shown on each column. | boolean | false |
|
||||
|
@ -27,7 +27,7 @@ title: Transfer
|
||||
| listStyle | 两个穿梭框的自定义样式 | object | |
|
||||
| notFoundContent | 当列表为空时显示的内容 | string\|ReactNode | '列表为空' |
|
||||
| operations | 操作文案集合,顺序从下至上 | string\[] | ['>', '<'] |
|
||||
| render | 每行数据渲染函数,该函数的入参为 `dataSource` 中的项,返回值为 ReactElement | Function(record) | |
|
||||
| render | 每行数据渲染函数,该函数的入参为 `dataSource` 中的项,返回值为 ReactElement。或者返回一个普通对象,其中 `label` 字段为 ReactElement,`value` 字段为 title | Function(record) | |
|
||||
| searchPlaceholder | 搜索框的默认值 | string | '请输入搜索内容' |
|
||||
| selectedKeys | 设置哪些项应该被选中 | string\[] | \[] |
|
||||
| showSearch | 是否显示搜索框 | boolean | false |
|
||||
|
@ -90,6 +90,11 @@ class Demo extends React.Component {
|
||||
if (dropPosition === -1) {
|
||||
ar.splice(i, 0, dragObj);
|
||||
} else {
|
||||
// drag node and drop node in the same level
|
||||
// and drop to the last node
|
||||
if (dragKey.length === dropKey.length && ar.length - 1 === i) {
|
||||
i += 2;
|
||||
}
|
||||
ar.splice(i - 1, 0, dragObj);
|
||||
}
|
||||
} else {
|
||||
|
@ -153,8 +153,8 @@ export default class Upload extends React.Component<UploadProps, UploadState> {
|
||||
this.handleRemove(file);
|
||||
}
|
||||
|
||||
onChange = (info: UploadChangeParam) => {
|
||||
if (!('fileList' in this.props)) {
|
||||
onChange = (info: UploadChangeParam, updateState = true) => {
|
||||
if (!('fileList' in this.props) && updateState) {
|
||||
this.setState({ fileList: info.fileList });
|
||||
}
|
||||
|
||||
@ -187,7 +187,7 @@ export default class Upload extends React.Component<UploadProps, UploadState> {
|
||||
this.onChange({
|
||||
file,
|
||||
fileList,
|
||||
});
|
||||
}, false);
|
||||
return false;
|
||||
} else if (result && (result as PromiseLike<any>).then) {
|
||||
return result;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Upload from '..';
|
||||
import Form from '../../form';
|
||||
import { errorRequest, successRequest } from './requests';
|
||||
|
||||
const delay = timeout => new Promise(resolve => setTimeout(resolve, timeout));
|
||||
@ -116,4 +117,90 @@ describe('Upload List', () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('does not change filelist when beforeUpload returns false', () => {
|
||||
const handleChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
<Upload
|
||||
listType="picture"
|
||||
defaultFileList={fileList}
|
||||
onChange={handleChange}
|
||||
beforeUpload={() => false}
|
||||
>
|
||||
<button>upload</button>
|
||||
</Upload>
|
||||
);
|
||||
|
||||
wrapper.find('input').simulate('change', {
|
||||
target: {
|
||||
files: [
|
||||
{ filename: 'foo.png' },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.state().fileList).toBe(fileList);
|
||||
expect(handleChange.mock.calls[0][0].fileList).toHaveLength(1);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/7762
|
||||
it('work with form validation', () => {
|
||||
let errors;
|
||||
class TestForm extends React.Component {
|
||||
handleSubmit = () => {
|
||||
const { validateFields } = this.props.form;
|
||||
validateFields((err) => {
|
||||
errors = err;
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
|
||||
return (
|
||||
<Form onSubmit={this.handleSubmit}>
|
||||
<Form.Item>
|
||||
{getFieldDecorator('file', {
|
||||
valuePropname: 'fileList',
|
||||
getValueFromEvent: e => e.fileList,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
validator: (rule, value, callback) => {
|
||||
if (!value || value.length === 0) {
|
||||
callback('file required');
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Upload
|
||||
beforeUpload={() => false}
|
||||
>
|
||||
<button>upload</button>
|
||||
</Upload>
|
||||
)}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const App = Form.create()(TestForm);
|
||||
const wrapper = mount(<App />);
|
||||
wrapper.find(Form).simulate('submit');
|
||||
expect(errors.file.errors).toEqual([{ message: 'file required', field: 'file' }]);
|
||||
|
||||
wrapper.find('input').simulate('change', {
|
||||
target: {
|
||||
files: [
|
||||
{ filename: 'foo.png' },
|
||||
],
|
||||
},
|
||||
});
|
||||
wrapper.find(Form).simulate('submit');
|
||||
expect(errors).toBeNull();
|
||||
});
|
||||
});
|
||||
|
@ -25,7 +25,7 @@ Code highlight | [react-syntax-highlighter](https://github.com/conorhastings/rea
|
||||
Markdown renderer | [react-markdown](http://rexxars.github.io/react-markdown/)
|
||||
Infinite Scroll | [react-virtualized](https://github.com/bvaughn/react-virtualized)
|
||||
Map | [react-google-maps](https://github.com/tomchentw/react-google-maps) [google-map-react](https://github.com/istarkov/google-map-react) [react-amap](https://github.com/ElemeFE/react-amap)
|
||||
Emoji | [emoji-mart)](https://github.com/missive/emoji-mart)
|
||||
Emoji | [emoji-mart](https://github.com/missive/emoji-mart)
|
||||
|
||||
<style>
|
||||
.markdown table td:first-child {
|
||||
|
@ -25,7 +25,7 @@ title: 社区精选组件
|
||||
Markdown 渲染 | [react-markdown](http://rexxars.github.io/react-markdown/)
|
||||
无限滚动 | [react-virtualized](https://github.com/bvaughn/react-virtualized)
|
||||
地图 | [react-google-maps](https://github.com/tomchentw/react-google-maps) [google-map-react](https://github.com/istarkov/google-map-react) [react-amap 高德](https://github.com/ElemeFE/react-amap)
|
||||
Emoji | [emoji-mart)](https://github.com/missive/emoji-mart)
|
||||
Emoji | [emoji-mart](https://github.com/missive/emoji-mart)
|
||||
|
||||
<style>
|
||||
.markdown table td:first-child {
|
||||
|
@ -3,7 +3,7 @@ order: 1
|
||||
title: Features
|
||||
---
|
||||
|
||||
Unlike other design specifications, Ant Design pursues not only user experience, but also experience of desigers and developers, which practice a humanist design idea.
|
||||
Unlike other design specifications, Ant Design pursues not only user experience, but also experience of designers and developers, which practice a humanist design idea.
|
||||
|
||||
<div style="margin-left:-40px;margin-right:-40px;overflow:hidden;margin-top:30px;">
|
||||
<div class="ant-col-8 features">
|
||||
|
@ -47,7 +47,7 @@
|
||||
"dom-closest": "^0.2.0",
|
||||
"enquire.js": "^2.1.1",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"moment": "^2.18.1",
|
||||
"moment": "^2.19.3",
|
||||
"omit.js": "^1.0.0",
|
||||
"prop-types": "^15.5.7",
|
||||
"rc-animate": "^2.4.1",
|
||||
|
@ -80,8 +80,8 @@ export default class Article extends React.Component {
|
||||
type="warning"
|
||||
message={(
|
||||
<span>
|
||||
This article has not been translated, hope that your can PR to translated it.
|
||||
<a href="https://github.com/ant-design/ant-design/issues/1471"> Help us!</a>
|
||||
This article has not been translated yet. Wan't to help us out?
|
||||
<a href="https://github.com/ant-design/ant-design/issues/1471">See this issue on GitHub.</a>
|
||||
</span>
|
||||
)}
|
||||
/>
|
||||
|
Loading…
Reference in New Issue
Block a user