mirror of
https://github.com/ant-design/ant-design.git
synced 2025-06-07 09:26:06 +08:00
Merge branch 'master' into feature-3.7.0
This commit is contained in:
commit
8eb8c686ce
26
.travis.yml
26
.travis.yml
@ -5,20 +5,24 @@ language: node_js
|
|||||||
node_js:
|
node_js:
|
||||||
- 8
|
- 8
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- node_modules
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
include:
|
include:
|
||||||
- env: TEST_TYPE=lint
|
- env: TEST_TYPE=lint
|
||||||
- env: REACT=16 TEST_TYPE=test:dist
|
- env: REACT=16 TEST_TYPE=test:dist
|
||||||
- env: REACT=16 TEST_TYPE=test:lib
|
- env: REACT=16 TEST_TYPE=test:lib
|
||||||
- env: REACT=16 TEST_TYPE=test:es
|
- env: REACT=16 TEST_TYPE=test:es
|
||||||
- env: REACT=16 TEST_TYPE=test:dom
|
- env: REACT=16 TEST_TYPE=test:dom
|
||||||
- env: REACT=16 TEST_TYPE=test:node
|
- env: REACT=16 TEST_TYPE=test:node
|
||||||
- env: REACT=15 TEST_TYPE=test:dist
|
- env: REACT=15 TEST_TYPE=test:dist
|
||||||
- env: REACT=15 TEST_TYPE=test:lib
|
- env: REACT=15 TEST_TYPE=test:lib
|
||||||
- env: REACT=15 TEST_TYPE=test:es
|
- env: REACT=15 TEST_TYPE=test:es
|
||||||
- env: REACT=15 TEST_TYPE=test:dom
|
- env: REACT=15 TEST_TYPE=test:dom
|
||||||
- env: REACT=15 TEST_TYPE=test:node
|
- env: REACT=15 TEST_TYPE=test:node
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- scripts/install-react.sh
|
- scripts/install-react.sh
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
@alert-message-color: @heading-color;
|
@alert-message-color: @heading-color;
|
||||||
@alert-text-color: @text-color;
|
@alert-text-color: @text-color;
|
||||||
|
@alert-close-color: @text-color-secondary;
|
||||||
|
|
||||||
.@{alert-prefix-cls} {
|
.@{alert-prefix-cls} {
|
||||||
.reset-component;
|
.reset-component;
|
||||||
@ -70,7 +71,7 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
.@{iconfont-css-prefix}-cross {
|
.@{iconfont-css-prefix}-cross {
|
||||||
color: @text-color-secondary;
|
color: @alert-close-color;
|
||||||
transition: color .3s;
|
transition: color .3s;
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #404040;
|
color: #404040;
|
||||||
@ -87,7 +88,7 @@
|
|||||||
padding: 15px 15px 15px 64px;
|
padding: 15px 15px 15px 64px;
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: @border-radius-base;
|
border-radius: @border-radius-base;
|
||||||
color: @text-color;
|
color: @alert-text-color;
|
||||||
line-height: @line-height-base;
|
line-height: @line-height-base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import RangeCalendar from 'rc-calendar/lib/RangeCalendar';
|
|||||||
import RcDatePicker from 'rc-calendar/lib/Picker';
|
import RcDatePicker from 'rc-calendar/lib/Picker';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import Icon from '../icon';
|
import Icon from '../icon';
|
||||||
|
import Tag from '../tag';
|
||||||
import warning from '../_util/warning';
|
import warning from '../_util/warning';
|
||||||
import interopDefault from '../_util/interopDefault';
|
import interopDefault from '../_util/interopDefault';
|
||||||
import { RangePickerValue, RangePickerPresetRange } from './interface';
|
import { RangePickerValue, RangePickerPresetRange } from './interface';
|
||||||
@ -211,14 +212,15 @@ export default class RangePicker extends React.Component<any, RangePickerState>
|
|||||||
const operations = Object.keys(ranges || {}).map((range) => {
|
const operations = Object.keys(ranges || {}).map((range) => {
|
||||||
const value = ranges[range];
|
const value = ranges[range];
|
||||||
return (
|
return (
|
||||||
<a
|
<Tag
|
||||||
key={range}
|
key={range}
|
||||||
|
color="blue"
|
||||||
onClick={() => this.handleRangeClick(value)}
|
onClick={() => this.handleRangeClick(value)}
|
||||||
onMouseEnter={() => this.setState({ hoverValue: value })}
|
onMouseEnter={() => this.setState({ hoverValue: value })}
|
||||||
onMouseLeave={this.handleRangeMouseLeave}
|
onMouseLeave={this.handleRangeMouseLeave}
|
||||||
>
|
>
|
||||||
{range}
|
{range}
|
||||||
</a>
|
</Tag>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
const rangeNode = (
|
const rangeNode = (
|
||||||
|
@ -50,7 +50,7 @@ describe('RangePicker', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const rangeCalendarWrapper = mount(wrapper.find('Trigger').instance().getComponent());
|
const rangeCalendarWrapper = mount(wrapper.find('Trigger').instance().getComponent());
|
||||||
rangeCalendarWrapper.find('.ant-calendar-range-quick-selector a')
|
rangeCalendarWrapper.find('.ant-calendar-range-quick-selector Tag')
|
||||||
.simulate('click');
|
.simulate('click');
|
||||||
expect(render(wrapper.find('Trigger').instance().getComponent()))
|
expect(render(wrapper.find('Trigger').instance().getComponent()))
|
||||||
.toMatchSnapshot();
|
.toMatchSnapshot();
|
||||||
@ -69,7 +69,7 @@ describe('RangePicker', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let rangeCalendarWrapper = mount(wrapper.find('Trigger').instance().getComponent());
|
let rangeCalendarWrapper = mount(wrapper.find('Trigger').instance().getComponent());
|
||||||
rangeCalendarWrapper.find('.ant-calendar-range-quick-selector a')
|
rangeCalendarWrapper.find('.ant-calendar-range-quick-selector Tag')
|
||||||
.simulate('mouseEnter');
|
.simulate('mouseEnter');
|
||||||
rangeCalendarWrapper = mount(wrapper.find('Trigger').instance().getComponent());
|
rangeCalendarWrapper = mount(wrapper.find('Trigger').instance().getComponent());
|
||||||
expect(rangeCalendarWrapper.find('.ant-calendar-selected-day').length).toBe(2);
|
expect(rangeCalendarWrapper.find('.ant-calendar-selected-day').length).toBe(2);
|
||||||
@ -152,7 +152,7 @@ describe('RangePicker', () => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
wrapper.find('.ant-calendar-picker-input').simulate('click');
|
wrapper.find('.ant-calendar-picker-input').simulate('click');
|
||||||
wrapper.find('.ant-calendar-range-quick-selector a').simulate('click');
|
wrapper.find('.ant-calendar-range-quick-selector Tag').simulate('click');
|
||||||
expect(
|
expect(
|
||||||
wrapper.find('.ant-calendar-range-picker-input').first().getDOMNode().value
|
wrapper.find('.ant-calendar-range-picker-input').first().getDOMNode().value
|
||||||
).toBe(range[0].format(format));
|
).toBe(range[0].format(format));
|
||||||
@ -171,7 +171,7 @@ describe('RangePicker', () => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
wrapper.find('.ant-calendar-picker-input').simulate('click');
|
wrapper.find('.ant-calendar-picker-input').simulate('click');
|
||||||
wrapper.find('.ant-calendar-range-quick-selector a').simulate('click');
|
wrapper.find('.ant-calendar-range-quick-selector Tag').simulate('click');
|
||||||
expect(
|
expect(
|
||||||
wrapper.find('.ant-calendar-range-picker-input').first().getDOMNode().value
|
wrapper.find('.ant-calendar-range-picker-input').first().getDOMNode().value
|
||||||
).toBe(range[0].format(format));
|
).toBe(range[0].format(format));
|
||||||
@ -200,7 +200,7 @@ describe('RangePicker', () => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
wrapper.find('.ant-calendar-picker-input').simulate('click');
|
wrapper.find('.ant-calendar-picker-input').simulate('click');
|
||||||
wrapper.find('.ant-calendar-range-quick-selector a').simulate('click');
|
wrapper.find('.ant-calendar-range-quick-selector Tag').simulate('click');
|
||||||
expect(handleOk).toBeCalledWith(range);
|
expect(handleOk).toBeCalledWith(range);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2990,9 +2990,12 @@ exports[`RangePicker switch to corresponding month panel when click presetted ra
|
|||||||
<div
|
<div
|
||||||
class="ant-calendar-footer-extra ant-calendar-range-quick-selector"
|
class="ant-calendar-footer-extra ant-calendar-range-quick-selector"
|
||||||
>
|
>
|
||||||
<a>
|
<div
|
||||||
|
class="ant-tag ant-tag-blue"
|
||||||
|
data-show="true"
|
||||||
|
>
|
||||||
My Birthday
|
My Birthday
|
||||||
</a>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="ant-calendar-footer-btn"
|
class="ant-calendar-footer-btn"
|
||||||
|
@ -36,8 +36,7 @@ import locale from 'antd/lib/date-picker/locale/zh_CN';
|
|||||||
```jsx
|
```jsx
|
||||||
// The default locale is en-US, if you want to use other locale, just set locale in entry file globaly.
|
// The default locale is en-US, if you want to use other locale, just set locale in entry file globaly.
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import 'moment/src/locale/zh-cn';
|
import 'moment/locale/zh-cn';
|
||||||
// import 'moment/locale/zh-cn'; if you are using webpack 1
|
|
||||||
|
|
||||||
<DatePicker defaultValue={moment('2015-01-01', 'YYYY-MM-DD')} />
|
<DatePicker defaultValue={moment('2015-01-01', 'YYYY-MM-DD')} />
|
||||||
```
|
```
|
||||||
|
@ -37,8 +37,7 @@ import locale from 'antd/lib/date-picker/locale/zh_CN';
|
|||||||
```jsx
|
```jsx
|
||||||
// 默认语言为 en-US,如果你需要设置其他语言,推荐在入口文件全局设置 locale
|
// 默认语言为 en-US,如果你需要设置其他语言,推荐在入口文件全局设置 locale
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import 'moment/src/locale/zh-cn';
|
mport 'moment/locale/zh-cn';
|
||||||
// import 'moment/locale/zh-cn'; if you are using webpack 1
|
|
||||||
|
|
||||||
<DatePicker defaultValue={moment('2015-01-01', 'YYYY-MM-DD')} />
|
<DatePicker defaultValue={moment('2015-01-01', 'YYYY-MM-DD')} />
|
||||||
```
|
```
|
||||||
|
@ -4,3 +4,4 @@ import './index.less';
|
|||||||
// style dependencies
|
// style dependencies
|
||||||
import '../../input/style';
|
import '../../input/style';
|
||||||
import '../../time-picker/style';
|
import '../../time-picker/style';
|
||||||
|
import '../../tag/style';
|
||||||
|
@ -66,6 +66,10 @@ const generateId = (() => {
|
|||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
const isNumeric = (n: any) => {
|
||||||
|
return !isNaN(parseFloat(n)) && isFinite(n);
|
||||||
|
};
|
||||||
|
|
||||||
export default class Sider extends React.Component<SiderProps, SiderState> {
|
export default class Sider extends React.Component<SiderProps, SiderState> {
|
||||||
static __ANT_LAYOUT_SIDER: any = true;
|
static __ANT_LAYOUT_SIDER: any = true;
|
||||||
|
|
||||||
@ -191,7 +195,7 @@ export default class Sider extends React.Component<SiderProps, SiderState> {
|
|||||||
'defaultCollapsed', 'onCollapse', 'breakpoint', 'onBreakpoint']);
|
'defaultCollapsed', 'onCollapse', 'breakpoint', 'onBreakpoint']);
|
||||||
const rawWidth = this.state.collapsed ? collapsedWidth : width;
|
const rawWidth = this.state.collapsed ? collapsedWidth : width;
|
||||||
// use "px" as fallback unit for width
|
// use "px" as fallback unit for width
|
||||||
const siderWidth = typeof rawWidth === 'number' ? `${rawWidth}px` : String(rawWidth || 0);
|
const siderWidth = isNumeric(rawWidth) ? `${rawWidth}px` : String(rawWidth);
|
||||||
// special trigger when collapsedWidth == 0
|
// special trigger when collapsedWidth == 0
|
||||||
const zeroWidthTrigger = parseFloat(String(collapsedWidth || 0)) === 0 ? (
|
const zeroWidthTrigger = parseFloat(String(collapsedWidth || 0)) === 0 ? (
|
||||||
<span onClick={this.toggle} className={`${prefixCls}-zero-width-trigger`}>
|
<span onClick={this.toggle} className={`${prefixCls}-zero-width-trigger`}>
|
||||||
|
14
components/layout/__tests__/__snapshots__/index.test.js.snap
Normal file
14
components/layout/__tests__/__snapshots__/index.test.js.snap
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Layout renders string width correctly 1`] = `
|
||||||
|
<div
|
||||||
|
class="ant-layout-sider ant-layout-sider-dark"
|
||||||
|
style="flex:0 0 200px;max-width:200px;min-width:200px;width:200px"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-layout-sider-children"
|
||||||
|
>
|
||||||
|
Sider
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { mount } from 'enzyme';
|
import { mount, render } from 'enzyme';
|
||||||
import Layout from '..';
|
import Layout from '..';
|
||||||
|
|
||||||
const { Sider, Content } = Layout;
|
const { Sider, Content } = Layout;
|
||||||
@ -69,6 +69,13 @@ describe('Layout', () => {
|
|||||||
);
|
);
|
||||||
expect(wrapper.find('.ant-layout-sider').hasClass('ant-layout-sider-light'));
|
expect(wrapper.find('.ant-layout-sider').hasClass('ant-layout-sider-light'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('renders string width correctly', () => {
|
||||||
|
const wrapper = render(
|
||||||
|
<Sider width="200">Sider</Sider>
|
||||||
|
);
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Sider onBreakpoint', () => {
|
describe('Sider onBreakpoint', () => {
|
||||||
|
@ -3,7 +3,11 @@
|
|||||||
&-light {
|
&-light {
|
||||||
background: @layout-sider-background-light;
|
background: @layout-sider-background-light;
|
||||||
}
|
}
|
||||||
&-light > &-trigger {
|
&-light &-trigger {
|
||||||
|
color: @layout-trigger-color-light;
|
||||||
|
background: @layout-trigger-background-light;
|
||||||
|
}
|
||||||
|
&-light &-zero-width-trigger {
|
||||||
color: @layout-trigger-color-light;
|
color: @layout-trigger-color-light;
|
||||||
background: @layout-trigger-background-light;
|
background: @layout-trigger-background-light;
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,7 @@ title: LocaleProvider
|
|||||||
```jsx
|
```jsx
|
||||||
import { LocaleProvider } from 'antd';
|
import { LocaleProvider } from 'antd';
|
||||||
import fr_FR from 'antd/lib/locale-provider/fr_FR';
|
import fr_FR from 'antd/lib/locale-provider/fr_FR';
|
||||||
import 'moment/src/locale/fr';
|
import 'moment/locale/fr';
|
||||||
// import 'moment/locale/fr'; if you are using webpack 1
|
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@ -15,9 +15,7 @@ LocaleProvider 使用 React 的 [context](https://facebook.github.io/react/docs/
|
|||||||
```jsx
|
```jsx
|
||||||
import { LocaleProvider } from 'antd';
|
import { LocaleProvider } from 'antd';
|
||||||
import zh_CN from 'antd/lib/locale-provider/zh_CN';
|
import zh_CN from 'antd/lib/locale-provider/zh_CN';
|
||||||
import 'moment/src/locale/zh-cn';
|
import 'moment/locale/zh-cn';
|
||||||
// import 'moment/locale/zh-cn'; if you are using webpack 1
|
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
return <LocaleProvider locale={zh_CN}><App /></LocaleProvider>;
|
return <LocaleProvider locale={zh_CN}><App /></LocaleProvider>;
|
||||||
|
@ -14,6 +14,8 @@ export default {
|
|||||||
filterConfirm: '확인',
|
filterConfirm: '확인',
|
||||||
filterReset: '초기화',
|
filterReset: '초기화',
|
||||||
emptyText: '데이터 없음',
|
emptyText: '데이터 없음',
|
||||||
|
selectAll: '모두 선택',
|
||||||
|
selectInvert: '선택 반전',
|
||||||
},
|
},
|
||||||
Modal: {
|
Modal: {
|
||||||
okText: '확인',
|
okText: '확인',
|
||||||
|
@ -2746,26 +2746,20 @@ exports[`renders ./components/table/demo/edit-cell.md correctly 1`] = `
|
|||||||
class="ant-table-tbody"
|
class="ant-table-tbody"
|
||||||
>
|
>
|
||||||
<tr
|
<tr
|
||||||
class="ant-table-row ant-table-row-level-0"
|
class="ant-table-row editable-row ant-table-row-level-0"
|
||||||
>
|
>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
>
|
>
|
||||||
<span
|
|
||||||
class="ant-table-row-indent indent-level-0"
|
|
||||||
style="padding-left:0px"
|
|
||||||
/>
|
|
||||||
<div
|
<div
|
||||||
class="editable-cell"
|
class="editable-cell-value-wrap"
|
||||||
|
style="padding-right:24px"
|
||||||
>
|
>
|
||||||
<div
|
<span
|
||||||
style="padding-right:24px"
|
class="ant-table-row-indent indent-level-0"
|
||||||
>
|
style="padding-left:0px"
|
||||||
Edward King 0
|
/>
|
||||||
<i
|
Edward King 0
|
||||||
class="anticon anticon-edit editable-cell-icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
@ -2789,26 +2783,20 @@ exports[`renders ./components/table/demo/edit-cell.md correctly 1`] = `
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr
|
<tr
|
||||||
class="ant-table-row ant-table-row-level-0"
|
class="ant-table-row editable-row ant-table-row-level-0"
|
||||||
>
|
>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
>
|
>
|
||||||
<span
|
|
||||||
class="ant-table-row-indent indent-level-0"
|
|
||||||
style="padding-left:0px"
|
|
||||||
/>
|
|
||||||
<div
|
<div
|
||||||
class="editable-cell"
|
class="editable-cell-value-wrap"
|
||||||
|
style="padding-right:24px"
|
||||||
>
|
>
|
||||||
<div
|
<span
|
||||||
style="padding-right:24px"
|
class="ant-table-row-indent indent-level-0"
|
||||||
>
|
style="padding-left:0px"
|
||||||
Edward King 1
|
/>
|
||||||
<i
|
Edward King 1
|
||||||
class="anticon anticon-edit editable-cell-icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
|
@ -14,60 +14,110 @@ title:
|
|||||||
Table with editable cells.
|
Table with editable cells.
|
||||||
|
|
||||||
````jsx
|
````jsx
|
||||||
import { Table, Input, Icon, Button, Popconfirm } from 'antd';
|
import { Table, Input, Button, Popconfirm, Form } from 'antd';
|
||||||
|
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
const EditableContext = React.createContext();
|
||||||
|
|
||||||
|
const EditableRow = ({ form, index, ...props }) => (
|
||||||
|
<EditableContext.Provider value={form}>
|
||||||
|
<tr {...props} />
|
||||||
|
</EditableContext.Provider>
|
||||||
|
);
|
||||||
|
|
||||||
|
const EditableFormRow = Form.create()(EditableRow);
|
||||||
|
|
||||||
class EditableCell extends React.Component {
|
class EditableCell extends React.Component {
|
||||||
state = {
|
state = {
|
||||||
value: this.props.value,
|
editing: false,
|
||||||
editable: false,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange = (e) => {
|
componentDidMount() {
|
||||||
const value = e.target.value;
|
if (this.props.editable) {
|
||||||
this.setState({ value });
|
document.addEventListener('click', this.handleClickOutside, true);
|
||||||
}
|
|
||||||
|
|
||||||
check = () => {
|
|
||||||
this.setState({ editable: false });
|
|
||||||
if (this.props.onChange) {
|
|
||||||
this.props.onChange(this.state.value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
edit = () => {
|
componentWillUnmount() {
|
||||||
this.setState({ editable: true });
|
if (this.props.editable) {
|
||||||
|
document.removeEventListener('click', this.handleClickOutside, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleEdit = () => {
|
||||||
|
const editing = !this.state.editing;
|
||||||
|
this.setState({ editing }, () => {
|
||||||
|
if (editing) {
|
||||||
|
this.input.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClickOutside = (e) => {
|
||||||
|
const { editing } = this.state;
|
||||||
|
if (editing && this.cell !== e.target && !this.cell.contains(e.target)) {
|
||||||
|
this.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
save = () => {
|
||||||
|
const { record, handleSave } = this.props;
|
||||||
|
this.form.validateFields((error, values) => {
|
||||||
|
if (error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.toggleEdit();
|
||||||
|
handleSave({ ...record, ...values });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { value, editable } = this.state;
|
const { editing } = this.state;
|
||||||
|
const {
|
||||||
|
editable,
|
||||||
|
dataIndex,
|
||||||
|
title,
|
||||||
|
record,
|
||||||
|
index,
|
||||||
|
handleSave,
|
||||||
|
...restProps
|
||||||
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
<div className="editable-cell">
|
<td ref={node => (this.cell = node)} {...restProps}>
|
||||||
{
|
{editable ? (
|
||||||
editable ? (
|
<EditableContext.Consumer>
|
||||||
<Input
|
{(form) => {
|
||||||
value={value}
|
this.form = form;
|
||||||
onChange={this.handleChange}
|
return (
|
||||||
onPressEnter={this.check}
|
editing ? (
|
||||||
suffix={(
|
<FormItem style={{ margin: 0 }}>
|
||||||
<Icon
|
{form.getFieldDecorator(dataIndex, {
|
||||||
type="check"
|
rules: [{
|
||||||
className="editable-cell-icon-check"
|
required: true,
|
||||||
onClick={this.check}
|
message: `${title} is required.`,
|
||||||
/>
|
}],
|
||||||
)}
|
initialValue: record[dataIndex],
|
||||||
/>
|
})(
|
||||||
) : (
|
<Input
|
||||||
<div style={{ paddingRight: 24 }}>
|
ref={node => (this.input = node)}
|
||||||
{value || ' '}
|
onPressEnter={this.save}
|
||||||
<Icon
|
/>
|
||||||
type="edit"
|
)}
|
||||||
className="editable-cell-icon"
|
</FormItem>
|
||||||
onClick={this.edit}
|
) : (
|
||||||
/>
|
<div
|
||||||
</div>
|
className="editable-cell-value-wrap"
|
||||||
)
|
style={{ paddingRight: 24 }}
|
||||||
}
|
onClick={this.toggleEdit}
|
||||||
</div>
|
>
|
||||||
|
{restProps.children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</EditableContext.Consumer>
|
||||||
|
) : restProps.children}
|
||||||
|
</td>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,12 +129,7 @@ class EditableTable extends React.Component {
|
|||||||
title: 'name',
|
title: 'name',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
width: '30%',
|
width: '30%',
|
||||||
render: (text, record) => (
|
editable: true,
|
||||||
<EditableCell
|
|
||||||
value={text}
|
|
||||||
onChange={this.onCellChange(record.key, 'name')}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
}, {
|
}, {
|
||||||
title: 'age',
|
title: 'age',
|
||||||
dataIndex: 'age',
|
dataIndex: 'age',
|
||||||
@ -98,7 +143,7 @@ class EditableTable extends React.Component {
|
|||||||
return (
|
return (
|
||||||
this.state.dataSource.length > 1
|
this.state.dataSource.length > 1
|
||||||
? (
|
? (
|
||||||
<Popconfirm title="Sure to delete?" onConfirm={() => this.onDelete(record.key)}>
|
<Popconfirm title="Sure to delete?" onConfirm={() => this.handleDelete(record.key)}>
|
||||||
<a href="javascript:;">Delete</a>
|
<a href="javascript:;">Delete</a>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
) : null
|
) : null
|
||||||
@ -122,18 +167,7 @@ class EditableTable extends React.Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
onCellChange = (key, dataIndex) => {
|
handleDelete = (key) => {
|
||||||
return (value) => {
|
|
||||||
const dataSource = [...this.state.dataSource];
|
|
||||||
const target = dataSource.find(item => item.key === key);
|
|
||||||
if (target) {
|
|
||||||
target[dataIndex] = value;
|
|
||||||
this.setState({ dataSource });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
onDelete = (key) => {
|
|
||||||
const dataSource = [...this.state.dataSource];
|
const dataSource = [...this.state.dataSource];
|
||||||
this.setState({ dataSource: dataSource.filter(item => item.key !== key) });
|
this.setState({ dataSource: dataSource.filter(item => item.key !== key) });
|
||||||
}
|
}
|
||||||
@ -152,15 +186,52 @@ class EditableTable extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleSave = (row) => {
|
||||||
|
const newData = [...this.state.dataSource];
|
||||||
|
const index = newData.findIndex(item => row.key === item.key);
|
||||||
|
const item = newData[index];
|
||||||
|
newData.splice(index, 1, {
|
||||||
|
...item,
|
||||||
|
...row,
|
||||||
|
});
|
||||||
|
this.setState({ dataSource: newData });
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { dataSource } = this.state;
|
const { dataSource } = this.state;
|
||||||
const columns = this.columns;
|
const components = {
|
||||||
|
body: {
|
||||||
|
row: EditableFormRow,
|
||||||
|
cell: EditableCell,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const columns = this.columns.map((col) => {
|
||||||
|
if (!col.editable) {
|
||||||
|
return col;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...col,
|
||||||
|
onCell: record => ({
|
||||||
|
record,
|
||||||
|
editable: col.editable,
|
||||||
|
dataIndex: col.dataIndex,
|
||||||
|
title: col.title,
|
||||||
|
handleSave: this.handleSave,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Button onClick={this.handleAdd} type="primary" style={{ marginBottom: 16 }}>
|
<Button onClick={this.handleAdd} type="primary" style={{ marginBottom: 16 }}>
|
||||||
Add a row
|
Add a row
|
||||||
</Button>
|
</Button>
|
||||||
<Table bordered dataSource={dataSource} columns={columns} />
|
<Table
|
||||||
|
components={components}
|
||||||
|
rowClassName={() => 'editable-row'}
|
||||||
|
bordered
|
||||||
|
dataSource={dataSource}
|
||||||
|
columns={columns}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -174,26 +245,14 @@ ReactDOM.render(<EditableTable />, mountNode);
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editable-cell-icon,
|
.editable-cell-value-wrap {
|
||||||
.editable-cell-icon-check {
|
padding: 5px 12px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editable-cell-icon {
|
.editable-row:hover .editable-cell-value-wrap {
|
||||||
line-height: 14px;
|
border: 1px solid #d9d9d9;
|
||||||
position: absolute;
|
border-radius: 4px;
|
||||||
right: 0;
|
padding: 4px 11px;
|
||||||
top: 50%;
|
|
||||||
margin-top: -7px;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
td:hover .editable-cell-icon {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editable-cell-icon:hover,
|
|
||||||
.editable-cell-icon-check:hover {
|
|
||||||
color: #108ee9;
|
|
||||||
}
|
}
|
||||||
````
|
````
|
||||||
|
@ -1,164 +1,74 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`Tabs tabPosition remove card 1`] = `
|
exports[`Tabs tabPosition remove card 1`] = `
|
||||||
<Tabs
|
<div
|
||||||
hideAdd={false}
|
class="ant-tabs ant-tabs-left ant-tabs-vertical ant-tabs-line"
|
||||||
prefixCls="ant-tabs"
|
|
||||||
tabBarExtraContent="xxx"
|
|
||||||
tabPosition="left"
|
|
||||||
>
|
>
|
||||||
<Tabs
|
<div
|
||||||
className="ant-tabs-vertical ant-tabs-line"
|
class="ant-tabs-bar"
|
||||||
destroyInactiveTabPane={false}
|
role="tablist"
|
||||||
hideAdd={false}
|
tabindex="0"
|
||||||
onChange={[Function]}
|
|
||||||
prefixCls="ant-tabs"
|
|
||||||
renderTabBar={[Function]}
|
|
||||||
renderTabContent={[Function]}
|
|
||||||
style={Object {}}
|
|
||||||
tabBarExtraContent="xxx"
|
|
||||||
tabBarPosition="left"
|
|
||||||
tabPosition="left"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ant-tabs ant-tabs-left ant-tabs-vertical ant-tabs-line"
|
class="ant-tabs-nav-container"
|
||||||
style={Object {}}
|
|
||||||
>
|
>
|
||||||
<ScrollableInkTabBar
|
<span
|
||||||
activeKey="1"
|
class="ant-tabs-tab-prev ant-tabs-tab-btn-disabled"
|
||||||
extraContent={
|
unselectable="unselectable"
|
||||||
<div
|
>
|
||||||
className="ant-tabs-extra-content"
|
<span
|
||||||
>
|
class="ant-tabs-tab-prev-icon"
|
||||||
xxx
|
/>
|
||||||
</div>
|
</span>
|
||||||
}
|
<span
|
||||||
inkBarAnimated={true}
|
class="ant-tabs-tab-next ant-tabs-tab-btn-disabled"
|
||||||
key="tabBar"
|
unselectable="unselectable"
|
||||||
onKeyDown={[Function]}
|
>
|
||||||
onNextClick={[Function]}
|
<span
|
||||||
onPrevClick={[Function]}
|
class="ant-tabs-tab-next-icon"
|
||||||
onTabClick={[Function]}
|
/>
|
||||||
panels={
|
</span>
|
||||||
<TabPane
|
<div
|
||||||
placeholder={null}
|
class="ant-tabs-nav-wrap"
|
||||||
tab="foo"
|
|
||||||
>
|
|
||||||
foo
|
|
||||||
</TabPane>
|
|
||||||
}
|
|
||||||
prefixCls="ant-tabs"
|
|
||||||
scrollAnimated={true}
|
|
||||||
styles={Object {}}
|
|
||||||
tabBarPosition="left"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ant-tabs-bar"
|
class="ant-tabs-nav-scroll"
|
||||||
onKeyDown={[Function]}
|
|
||||||
role="tablist"
|
|
||||||
tabIndex="0"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ant-tabs-nav-container"
|
class="ant-tabs-nav ant-tabs-nav-animated"
|
||||||
key="content"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className="ant-tabs-tab-prev ant-tabs-tab-btn-disabled"
|
|
||||||
onClick={null}
|
|
||||||
onTransitionEnd={[Function]}
|
|
||||||
unselectable="unselectable"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className="ant-tabs-tab-prev-icon"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
className="ant-tabs-tab-next ant-tabs-tab-btn-disabled"
|
|
||||||
onClick={null}
|
|
||||||
unselectable="unselectable"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className="ant-tabs-tab-next-icon"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
className="ant-tabs-nav-wrap"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="ant-tabs-nav-scroll"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="ant-tabs-nav ant-tabs-nav-animated"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="ant-tabs-ink-bar ant-tabs-ink-bar-animated"
|
|
||||||
key="inkBar"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
aria-disabled="false"
|
|
||||||
aria-selected="true"
|
|
||||||
className="ant-tabs-tab-active ant-tabs-tab"
|
|
||||||
key="1"
|
|
||||||
onClick={[Function]}
|
|
||||||
role="tab"
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"marginRight": undefined,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
foo
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="ant-tabs-extra-content"
|
|
||||||
key="extra"
|
|
||||||
style={Object {}}
|
|
||||||
>
|
|
||||||
xxx
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ScrollableInkTabBar>
|
|
||||||
<TabContent
|
|
||||||
activeKey="1"
|
|
||||||
animated={true}
|
|
||||||
animatedWithMargin={true}
|
|
||||||
destroyInactiveTabPane={false}
|
|
||||||
key="tabContent"
|
|
||||||
onChange={[Function]}
|
|
||||||
prefixCls="ant-tabs"
|
|
||||||
tabBarPosition="left"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="ant-tabs-content ant-tabs-content-animated"
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"marginTop": "0%",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<TabPane
|
|
||||||
active={true}
|
|
||||||
destroyInactiveTabPane={false}
|
|
||||||
key="1"
|
|
||||||
placeholder={null}
|
|
||||||
rootPrefixCls="ant-tabs"
|
|
||||||
tab="foo"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
aria-hidden="false"
|
class="ant-tabs-ink-bar ant-tabs-ink-bar-animated"
|
||||||
className="ant-tabs-tabpane ant-tabs-tabpane-active"
|
/>
|
||||||
role="tabpanel"
|
<div
|
||||||
|
aria-disabled="false"
|
||||||
|
aria-selected="true"
|
||||||
|
class="ant-tabs-tab-active ant-tabs-tab"
|
||||||
|
role="tab"
|
||||||
>
|
>
|
||||||
foo
|
foo
|
||||||
</div>
|
</div>
|
||||||
</TabPane>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</TabContent>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Tabs>
|
<div
|
||||||
</Tabs>
|
class="ant-tabs-extra-content"
|
||||||
|
>
|
||||||
|
xxx
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="ant-tabs-content ant-tabs-content-animated"
|
||||||
|
style="margin-top:0%"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-hidden="false"
|
||||||
|
class="ant-tabs-tabpane ant-tabs-tabpane-active"
|
||||||
|
role="tabpanel"
|
||||||
|
>
|
||||||
|
foo
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { mount } from 'enzyme';
|
import { mount, render } from 'enzyme';
|
||||||
import Tabs from '..';
|
import Tabs from '..';
|
||||||
|
|
||||||
const { TabPane } = Tabs;
|
const { TabPane } = Tabs;
|
||||||
@ -31,7 +31,7 @@ describe('Tabs', () => {
|
|||||||
|
|
||||||
describe('tabPosition', () => {
|
describe('tabPosition', () => {
|
||||||
it('remove card', () => {
|
it('remove card', () => {
|
||||||
const wrapper = mount(
|
const wrapper = render(
|
||||||
<Tabs tabPosition="left" tabBarExtraContent="xxx">
|
<Tabs tabPosition="left" tabBarExtraContent="xxx">
|
||||||
<TabPane tab="foo" key="1">foo</TabPane>
|
<TabPane tab="foo" key="1">foo</TabPane>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
@ -77,6 +77,8 @@ export interface TreeProps {
|
|||||||
checkStrictly?: boolean;
|
checkStrictly?: boolean;
|
||||||
/** 是否支持选中 */
|
/** 是否支持选中 */
|
||||||
checkable?: boolean;
|
checkable?: boolean;
|
||||||
|
/** 是否禁用树 */
|
||||||
|
disabled?: boolean;
|
||||||
/** 默认展开所有树节点 */
|
/** 默认展开所有树节点 */
|
||||||
defaultExpandAll?: boolean;
|
defaultExpandAll?: boolean;
|
||||||
/** 默认展开对应树节点 */
|
/** 默认展开对应树节点 */
|
||||||
|
@ -268,9 +268,9 @@ We have completed a simple application, but you may still have lots of questions
|
|||||||
|
|
||||||
You can:
|
You can:
|
||||||
|
|
||||||
- Visit [dva official website](https://github.com/dvajs/dva).
|
- Visit [dva official website](https://dvajs.com/).
|
||||||
- Be familiar with the [8 Concepts](https://github.com/dvajs/dva/blob/master/docs/Concepts.md), and understand how they are connected together
|
- Be familiar with the [8 Concepts](https://dvajs.com/guide/concepts.html), and understand how they are connected together
|
||||||
- Know all [dva APIs](https://github.com/dvajs/dva/blob/master/docs/API.md)
|
- Know all [dva APIs](https://dvajs.com/api/)
|
||||||
- Checkout [dva knowledgemap](https://github.com/dvajs/dva-knowledgemap), including all the basic knowledge with ES6, React, dva
|
- Checkout [dva knowledgemap](https://dvajs.com/knowledgemap/), including all the basic knowledge with ES6, React, dva
|
||||||
- Checkout [more FAQ](https://github.com/dvajs/dva/issues?q=is%3Aissue+is%3Aclosed+label%3Afaq)
|
- Checkout [more FAQ](https://github.com/dvajs/dva/issues?q=is%3Aissue+is%3Aclosed+label%3Afaq)
|
||||||
- If your project is created with [dva-cli](https://github.com/dvajs/dva-cli) , checkout how to [Configure it](https://github.com/sorrycc/roadhog#configuration)
|
- If your project is created with [dva-cli](https://github.com/dvajs/dva-cli) , checkout how to [Configure it](https://github.com/sorrycc/roadhog#configuration)
|
||||||
|
@ -270,9 +270,9 @@ File sizes after gzip:
|
|||||||
|
|
||||||
你可以:
|
你可以:
|
||||||
|
|
||||||
- 访问 [dva 官网](https://github.com/dvajs/dva)
|
- 访问 [dva 官网](https://dvajs.com/)
|
||||||
- 理解 dva 的 [8 个概念](https://github.com/dvajs/dva/blob/master/docs/Concepts_zh-CN.md) ,以及他们是如何串起来的
|
- 理解 dva 的 [8 个概念](https://dvajs.com/guide/concepts.html) ,以及他们是如何串起来的
|
||||||
- 掌握 dva 的[所有 API](https://github.com/dvajs/dva/blob/master/docs/API_zh-CN.md)
|
- 掌握 dva 的[所有 API](https://dvajs.com/api/)
|
||||||
- 查看 [dva 知识地图](https://github.com/dvajs/dva-knowledgemap) ,包含 ES6, React, dva 等所有基础知识
|
- 查看 [dva 知识地图](https://dvajs.com/knowledgemap/) ,包含 ES6, React, dva 等所有基础知识
|
||||||
- 查看 [更多 FAQ](https://github.com/dvajs/dva/issues?q=is%3Aissue+is%3Aclosed+label%3Afaq),看看别人通常会遇到什么问题
|
- 查看 [更多 FAQ](https://github.com/dvajs/dva/issues?q=is%3Aissue+is%3Aclosed+label%3Afaq),看看别人通常会遇到什么问题
|
||||||
- 如果你基于 dva-cli 创建项目,最好了解他的 [配置方式](https://github.com/sorrycc/roadhog/blob/master/README_zh-cn.md#配置)
|
- 如果你基于 dva-cli 创建项目,最好了解他的 [配置方式](https://github.com/sorrycc/roadhog/blob/master/README_zh-cn.md#配置)
|
||||||
|
@ -90,11 +90,11 @@ $ yarn add react-app-rewired --dev
|
|||||||
```diff
|
```diff
|
||||||
/* package.json */
|
/* package.json */
|
||||||
"scripts": {
|
"scripts": {
|
||||||
- "start": "react-scripts start",
|
- "start": "react-scripts-ts start",
|
||||||
+ "start": "react-app-rewired start --scripts-version react-scripts-ts",
|
+ "start": "react-app-rewired start --scripts-version react-scripts-ts",
|
||||||
- "build": "react-scripts build",
|
- "build": "react-scripts-ts build",
|
||||||
+ "build": "react-app-rewired build --scripts-version react-scripts-ts",
|
+ "build": "react-app-rewired build --scripts-version react-scripts-ts",
|
||||||
- "test": "react-scripts test --env=jsdom",
|
- "test": "react-scripts-ts test --env=jsdom",
|
||||||
+ "test": "react-app-rewired test --env=jsdom --scripts-version react-scripts-ts",
|
+ "test": "react-app-rewired test --env=jsdom --scripts-version react-scripts-ts",
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -89,11 +89,11 @@ $ yarn add react-app-rewired --dev
|
|||||||
```diff
|
```diff
|
||||||
/* package.json */
|
/* package.json */
|
||||||
"scripts": {
|
"scripts": {
|
||||||
- "start": "react-scripts start",
|
- "start": "react-scripts-ts start",
|
||||||
+ "start": "react-app-rewired start --scripts-version react-scripts-ts",
|
+ "start": "react-app-rewired start --scripts-version react-scripts-ts",
|
||||||
- "build": "react-scripts build",
|
- "build": "react-scripts-ts build",
|
||||||
+ "build": "react-app-rewired build --scripts-version react-scripts-ts",
|
+ "build": "react-app-rewired build --scripts-version react-scripts-ts",
|
||||||
- "test": "react-scripts test --env=jsdom",
|
- "test": "react-scripts-ts test --env=jsdom",
|
||||||
+ "test": "react-app-rewired test --env=jsdom --scripts-version react-scripts-ts",
|
+ "test": "react-app-rewired test --env=jsdom --scripts-version react-scripts-ts",
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
11
tests/__mocks__/react.js
vendored
Normal file
11
tests/__mocks__/react.js
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
const React = require.requireActual('react');
|
||||||
|
|
||||||
|
if (!React.createContext) {
|
||||||
|
React.createContext = () => {
|
||||||
|
const Provider = ({ children }) => children;
|
||||||
|
const Consumer = ({ children }) => children();
|
||||||
|
return { Provider, Consumer };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = React;
|
Loading…
Reference in New Issue
Block a user