diff --git a/.travis.yml b/.travis.yml index 86d52a3da6..43173ed0ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,7 @@ before_script: script: - npm run eslint - npm run stylelint + - npm run test:a11y - npm test - codecov diff --git a/CHANGELOG.md b/CHANGELOG.md index 3575d60898..86fe582edf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,70 @@ # Change Log + +## [1.13.8](https://github.com/alibaba-fusion/next/compare/1.13.7...1.13.8) (2019-03-14) + + +### Bug Fixes + +* **ConfigProvider:** config on components should take higher priority ([87917c8](https://github.com/alibaba-fusion/next/commit/87917c8)) + + + + + +## [1.13.7](https://github.com/alibaba-fusion/next/compare/1.13.6...1.13.7) (2019-03-13) + + +### Bug Fixes + +* **Select:** css override fail when using theme ([5f80ead](https://github.com/alibaba-fusion/next/commit/5f80ead)) + + + + + +## [1.13.6](https://github.com/alibaba-fusion/next/compare/1.13.5...1.13.6) (2019-03-13) + + +### Bug Fixes + +* **DatePicker:** disabledDate will auto disable month ([94276d2](https://github.com/alibaba-fusion/next/commit/94276d2)) +* **Step:** labelPlacement value change dbl times ([3c7ae73](https://github.com/alibaba-fusion/next/commit/3c7ae73)) +* **Tag:** close button not showing in select when text too long ([fd341f8](https://github.com/alibaba-fusion/next/commit/fd341f8)) +* **Upload:** className not pass in ([cb33e75](https://github.com/alibaba-fusion/next/commit/cb33e75)) +* **Upload:** drag upload no trigger onDrop ([5f58f29](https://github.com/alibaba-fusion/next/commit/5f58f29)) +* **Upload:** fix setValue fail in onSuccess ([384c379](https://github.com/alibaba-fusion/next/commit/384c379)) + + +### Features + +* **ConfigProvider:** add ErrorBoundary ([ada2b5b](https://github.com/alibaba-fusion/next/commit/ada2b5b)) +* **Range:** RTL feature of Range Component ([2127ee6](https://github.com/alibaba-fusion/next/commit/2127ee6)) +* **Select:** can unselect all ([77077a2](https://github.com/alibaba-fusion/next/commit/77077a2)) +* **Tab:** RTL feature of Tab ([142af04](https://github.com/alibaba-fusion/next/commit/142af04)) + + + +## [1.13.5](https://github.com/alibaba-fusion/next/compare/1.13.3...1.13.5) (2019-03-07) + + +### Bug Fixes + +* **Collapse:** fix nested collapse icon ([7a6842a](https://github.com/alibaba-fusion/next/commit/7a6842a)) +* **DatePicker:** date range picker select time error ([59eaa9c](https://github.com/alibaba-fusion/next/commit/59eaa9c)) +* **Step:** step-item param of labelPlacement change update bug ([f0939af](https://github.com/alibaba-fusion/next/commit/f0939af)) +* **Tab:** can't scroll when active item is inside view ([a8f4d18](https://github.com/alibaba-fusion/next/commit/a8f4d18)) +* **Table:** extra lock columns when enough space && dataSource=[], close[#364](https://github.com/alibaba-fusion/next/issues/364) ([b9c2328](https://github.com/alibaba-fusion/next/commit/b9c2328)) + + +### Features + +* **NumberPicker:** consider [。] as [.] ([8369b4a](https://github.com/alibaba-fusion/next/commit/8369b4a)) +* **Upload:** add rtl support ([5505d6d](https://github.com/alibaba-fusion/next/commit/5505d6d)) + + + + ## [1.13.4](https://github.com/alibaba-fusion/next/compare/1.13.3...1.13.4) (2019-02-28) diff --git a/LATESTLOG.md b/LATESTLOG.md index 2c424852b6..c6b9e7cb7c 100644 --- a/LATESTLOG.md +++ b/LATESTLOG.md @@ -1,10 +1,10 @@ # Latest Log -## [1.13.4](https://github.com/alibaba-fusion/next/compare/1.13.3...1.13.4) (2019-02-28) +## [1.13.8](https://github.com/alibaba-fusion/next/compare/1.13.7...1.13.8) (2019-03-14) -### Features +### Bug Fixes -* **NumberPicker:** consider [。] as [.] ([8369b4a](https://github.com/alibaba-fusion/next/commit/8369b4a)) +* **ConfigProvider:** config on components should take higher priority ([87917c8](https://github.com/alibaba-fusion/next/commit/87917c8)) diff --git a/docs/balloon/index.md b/docs/balloon/index.md index 2ee4c20ec5..d68fca622d 100644 --- a/docs/balloon/index.md +++ b/docs/balloon/index.md @@ -37,13 +37,13 @@ | align | 弹出层位置

**可选值**:
't'(上)
'r'(右)
'b'(下)
'l'(左)
'tl'(上左)
'tr'(上右)
'bl'(下左)
'br'(下右)
'lt'(左上)
'lb'(左下)
'rt'(右上)
'rb'(右下 及其 两两组合) | Enum | 'b' | | offset | 弹层相对于trigger的定位的微调 | Array | [0, 0] | | trigger | 触发元素 | any | <span /> | -| triggerType | 触发行为
鼠标悬浮, 鼠标点击('hover','click')或者它们组成的数组,如 ['hover', 'click'], 若弹窗内容有复杂交互请使用click | String/Array | 'hover' | +| triggerType | 触发行为
鼠标悬浮, 获取到焦点, 鼠标点击('hover','focus','click')或者它们组成的数组,如 ['hover', 'focus'] | String/Array | 'hover' | | onClose | 任何visible为false时会触发的事件

**签名**:
Function() => void | Function | func.noop | | needAdjust | 是否进行自动位置调整 | Boolean | false | | delay | 弹层在触发以后的延时显示, 单位毫秒 ms | Number | - | | afterClose | 浮层关闭后触发的事件, 如果有动画,则在动画结束后触发

**签名**:
Function() => void | Function | func.noop | | shouldUpdatePosition | 强制更新定位信息 | Boolean | - | -| autoFocus | 弹层出现后是否自动focus到内部第一个元素 | Boolean | true | +| autoFocus | 弹层出现后是否自动focus到内部第一个元素 | Boolean | false | | safeNode | 安全节点:对于triggetType为click的浮层,会在点击除了浮层外的其它区域时关闭浮层.safeNode用于添加不触发关闭的节点, 值可以是dom节点的id或者是节点的dom对象 | String | undefined | | safeId | 用来指定safeNode节点的id,和safeNode配合使用 | String | null | | animation | 配置动画的播放方式 | Object/Boolean | { in: 'zoomIn', out: 'zoomOut', } | @@ -61,7 +61,7 @@ | children | tooltip的内容 | any | - | | align | 弹出层位置

**可选值**:
't'(上)
'r'(右)
'b'(下)
'l'(左)
'tl'(上左)
'tr'(上右)
'bl'(下左)
'br'(下右)
'lt'(左上)
'lb'(左下)
'rt'(右上)
'rb'(右下 及其 两两组合) | Enum | 'b' | | trigger | 触发元素 | any | <span /> | -| triggerType | 触发行为
鼠标悬浮, 鼠标点击('hover', 'click')或者它们组成的数组,如 ['hover', 'click'], 若有复杂交互,推荐使用`` | String/Array | 'hover' | +| triggerType | 触发行为
鼠标悬浮, 获取到焦点, 鼠标点击('hover','focus','click')或者它们组成的数组,如 ['hover', 'focus'] | String/Array | 'hover' | | popupStyle | 弹层组件style,透传给Popup | Object | - | | popupClassName | 弹层组件className,透传给Popup | String | - | | popupProps | 弹层组件属性,透传给Popup | Object | - | diff --git a/docs/calendar/index.en-us.md b/docs/calendar/index.en-us.md index eef5ea08d6..5589d0224c 100644 --- a/docs/calendar/index.en-us.md +++ b/docs/calendar/index.en-us.md @@ -40,5 +40,5 @@ moment.locale('zh-cn'); | onSelect | Callback when select a date

**signature**:
Function(value: Object) => void
**parameter**:
_value_: {Object} date object | Function | func.noop | | dateCellRender | Render function for date cell

**signature**:
Function(value: Object) => ReactNode
**parameter**:
_value_: {Object} date object
**return**:
{ReactNode} null
| Function | (value) => value.date() | | monthCellRender | Render function for month cell

**signature**:
Function(calendarDate: Object) => ReactNode
**parameter**:
_calendarDate_: {Object} current date object
**return**:
{ReactNode} null
| Function | - | -| disabledDate | Function to disable dates

**signature**:
Function(calendarDate: Object) => Boolean
**parameter**:
_calendarDate_: {Object} current date object
**return**:
{Boolean} null
| Function | - | +| disabledDate | Function to disable dates

**signature**:
Function(calendarDate: Object) => Boolean
**parameter**:
_calendarDate_: {Object} current date object
_view_: {Enum} current view type: 'year', 'month', 'date'
**return**:
{Boolean} null
| Function | - | diff --git a/docs/calendar/index.md b/docs/calendar/index.md index 20a3b3b113..c78639778e 100644 --- a/docs/calendar/index.md +++ b/docs/calendar/index.md @@ -29,15 +29,15 @@ moment.locale('zh-cn'); ### Calendar -| 参数 | 说明 | 类型 | 默认值 | -| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ----------------------- | -| defaultValue | 默认选中的日期(moment 对象) | custom | - | -| shape | 展现形态

**可选值**:
'card', 'fullscreen', 'panel' | Enum | 'fullscreen' | -| value | 选中的日期值 (moment 对象) | custom | - | -| showOtherMonth | 是否展示非本月的日期 | Boolean | true | -| defaultVisibleMonth | 默认展示的月份

**签名**:
Function() => void | Function | - | -| onSelect | 选择日期单元格时的回调

**签名**:
Function(value: Object) => void
**参数**:
_value_: {Object} 对应的日期值 (moment 对象) | Function | func.noop | -| onVisibleMonthChange | 展现的月份变化时的回调

**签名**:
Function(value: Object, reason: String) => void
**参数**:
_value_: {Object} 显示的月份 (moment 对象)
_reason_: {String} 触发月份改变原因 | Function | func.noop | -| dateCellRender | 自定义日期渲染函数

**签名**:
Function(value: Object) => ReactNode
**参数**:
_value_: {Object} 日期值(moment对象)
**返回值**:
{ReactNode} null
| Function | (value) => value.date() | -| monthCellRender | 自定义月份渲染函数

**签名**:
Function(calendarDate: Object) => ReactNode
**参数**:
_calendarDate_: {Object} 对应 Calendar 返回的自定义日期对象
**返回值**:
{ReactNode} null
| Function | - | -| disabledDate | 不可选择的日期

**签名**:
Function(calendarDate: Object) => Boolean
**参数**:
_calendarDate_: {Object} 对应 Calendar 返回的自定义日期对象
**返回值**:
{Boolean} null
| Function | - | +| 参数 | 说明 | 类型 | 默认值 | +| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------------------- | +| defaultValue | 默认选中的日期(moment 对象) | custom | - | +| shape | 展现形态

**可选值**:
'card', 'fullscreen', 'panel' | Enum | 'fullscreen' | +| value | 选中的日期值 (moment 对象) | custom | - | +| showOtherMonth | 是否展示非本月的日期 | Boolean | true | +| defaultVisibleMonth | 默认展示的月份

**签名**:
Function() => void | Function | - | +| onSelect | 选择日期单元格时的回调

**签名**:
Function(value: Object) => void
**参数**:
_value_: {Object} 对应的日期值 (moment 对象) | Function | func.noop | +| onVisibleMonthChange | 展现的月份变化时的回调

**签名**:
Function(value: Object, reason: String) => void
**参数**:
_value_: {Object} 显示的月份 (moment 对象)
_reason_: {String} 触发月份改变原因 | Function | func.noop | +| dateCellRender | 自定义日期渲染函数

**签名**:
Function(value: Object) => ReactNode
**参数**:
_value_: {Object} 日期值(moment对象)
**返回值**:
{ReactNode} null
| Function | value => value.date() | +| monthCellRender | 自定义月份渲染函数

**签名**:
Function(calendarDate: Object) => ReactNode
**参数**:
_calendarDate_: {Object} 对应 Calendar 返回的自定义日期对象
**返回值**:
{ReactNode} null
| Function | - | +| disabledDate | 不可选择的日期

**签名**:
Function(calendarDate: Object, view: String) => Boolean
**参数**:
_calendarDate_: {Object} 对应 Calendar 返回的自定义日期对象
_view_: {String} 当前视图类型,year: 年, month: 月, date: 日
**返回值**:
{Boolean} null
| Function | - | diff --git a/docs/cascader-select/demo/accessibility.md b/docs/cascader-select/demo/accessibility.md index 2f2497ce4e..086088ee16 100644 --- a/docs/cascader-select/demo/accessibility.md +++ b/docs/cascader-select/demo/accessibility.md @@ -1,6 +1,6 @@ # 无障碍 -- order: 8 +- order: 9 请参考`ARIA and KeyBoard`。 diff --git a/docs/cascader-select/demo/custom.md b/docs/cascader-select/demo/custom.md new file mode 100644 index 0000000000..217fddc506 --- /dev/null +++ b/docs/cascader-select/demo/custom.md @@ -0,0 +1,50 @@ +# 渲染 DataSource 中不存在的 value + +- order: 8 + +--- + +```jsx +import { CascaderSelect } from '@alifd/next'; +import 'whatwg-fetch'; + +class Demo extends React.Component { + constructor(props) { + super(props); + + this.state = { + data: [] + }; + + this.handleChange = this.handleChange.bind(this); + } + + componentDidMount() { + fetch('https://os.alipayobjects.com/rmsportal/ODDwqcDFTLAguOvWEolX.json') + .then(response => response.json()) + .then(data => { + data[1].disabled = true; + this.setState({ data }); + }) + .catch(e => console.log(e)); + } + + handleChange(value, data, extra) { + console.log(value, data, extra); + } + + valueRender = (item) => { + if (item.label) { + return item.label; // 正常的item + } + + // value在 dataSouce里不存在时渲染。 + return item.value === '432988' ? '不存在的' : item.value; + }; + + render() { + return ; + } +} +ReactDOM.render(, mountNode) +``` diff --git a/docs/checkbox/index.md b/docs/checkbox/index.md index 75a1ea0ed0..e513b15b17 100644 --- a/docs/checkbox/index.md +++ b/docs/checkbox/index.md @@ -34,15 +34,15 @@ ### Checkbox.Group -| 参数 | 说明 | 类型 | 默认值 | -| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | --------- | -| disabled | 整体禁用 | Boolean | - | -| dataSource | 可选项列表, 数据项可为 String 或者 Object, 如 `['apple', 'pear', 'orange']` 或者 `[{value: 'apple', label: '苹果',}, {value: 'pear', label: '梨'}, {value: 'orange', label: '橙子'}]` | Array<any> | \[] | -| value | 被选中的值列表 | Array/String/Number | - | -| defaultValue | 默认被选中的值列表 | Array/String/Number | - | -| children | 通过子元素方式设置内部 checkbox | Array<ReactElement> | - | -| onChange | 选中值改变时的事件

**签名**:
Function(value: Array, e: Event) => void
**参数**:
_value_: {Array} 选中项列表
_e_: {Event} Dom 事件对象 | Function | () => { } | -| itemDirection | 子项目的排列方式
- hoz: 水平排列 (default)
- ver: 垂直排列

**可选值**:
'hoz', 'ver' | Enum | 'hoz' | +| 参数 | 说明 | 类型 | 默认值 | +| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | -------- | +| disabled | 整体禁用 | Boolean | - | +| dataSource | 可选项列表, 数据项可为 String 或者 Object, 如 `['apple', 'pear', 'orange']` 或者 `[{value: 'apple', label: '苹果',}, {value: 'pear', label: '梨'}, {value: 'orange', label: '橙子'}]` | Array<any> | \[] | +| value | 被选中的值列表 | Array/String/Number | - | +| defaultValue | 默认被选中的值列表 | Array/String/Number | - | +| children | 通过子元素方式设置内部 checkbox | Array<ReactElement> | - | +| onChange | 选中值改变时的事件

**签名**:
Function(value: Array, e: Event) => void
**参数**:
_value_: {Array} 选中项列表
_e_: {Event} Dom 事件对象 | Function | () => {} | +| itemDirection | 子项目的排列方式
- hoz: 水平排列 (default)
- ver: 垂直排列

**可选值**:
'hoz', 'ver' | Enum | 'hoz' | ## ARIA and KeyBoard diff --git a/docs/config-provider/demo/error-boundary.md b/docs/config-provider/demo/error-boundary.md new file mode 100644 index 0000000000..72026f8f52 --- /dev/null +++ b/docs/config-provider/demo/error-boundary.md @@ -0,0 +1,82 @@ +# ErrorBoundary 捕获错误 + +- order: 5 + +使用 `` 可以避免由于局部区域的错误,所引起的页面白屏。 + +:::lang=en-us +# Basic + +- order: 5 + +`` can be used to avoid blank screen caused by local errors +::: + +--- + +````jsx +import { ConfigProvider, Button } from '@alifd/next'; + +const { ErrorBoundary, config } = ConfigProvider; + +class Demo extends React.Component { + render() { + if (this.props.throwError) { + throw Error('There is something going wrong!'); + } else { + return ( + normal + ); + } + } +} + +const NewDemo = config(Demo); + +const fallbackUI = (props) => { + const { error, errorInfo } = props; + return {error.toString()}; +}; + +class App extends React.Component { + state = { + throwError: false + }; + + onClick = () => { + this.setState({ + throwError: true + }); + }; + + render() { + return (
+ Click to throw an error +
+
+ Default fallback UI: +
+ + + +
+
+ Customize fallback UI of configed Component(Basic Components / Biz Components): +
+ { + const { error, errorInfo } = props; + return Error: {error.toString()}; + }, + afterCatch: () => { + console.log('catching'); + } + }}> + + +
); + } +} + +ReactDOM.render(, mountNode); +```` diff --git a/docs/config-provider/index.en-us.md b/docs/config-provider/index.en-us.md index 30d32cbceb..f346185d96 100644 --- a/docs/config-provider/index.en-us.md +++ b/docs/config-provider/index.en-us.md @@ -149,6 +149,7 @@ export default config(Component); | Param | Description | Type | Default Value | | -------- | ----------------------------------- | ------------ | --- | +| errorBoundary | turn errorBoundary on or not
If you pass object, properties:

fallbackUI `Function(error?: {}, errorInfo?: {}) => Element`
afterCatch `Function(error?: {}, errorInfo?: {})` after being catched, e.g. send data to server for data statistics | Boolean/Object | false | | pure | whether enable the Pure Render mode, it will improve performance, but it will also have side effects | Boolean | - | | warning | whether to display the warning prompt for component properties being deprecated in development mode | Boolean | true | | children | component tree | ReactElement | - | diff --git a/docs/config-provider/index.md b/docs/config-provider/index.md index 22d36377ce..f87b75fee2 100644 --- a/docs/config-provider/index.md +++ b/docs/config-provider/index.md @@ -214,12 +214,13 @@ export default config(Component); ### ConfigProvider -| 参数 | 说明 | 类型 | 默认值 | -| -------- | ----------------------------------- | ------------ | ---- | -| pure | 是否开启 Pure Render 模式,会提高性能,但是也会带来副作用 | Boolean | - | -| warning | 是否在开发模式下显示组件属性被废弃的 warning 提示 | Boolean | true | -| rtl | 是否开启 rtl 模式 | Boolean | - | -| children | 组件树 | ReactElement | - | +| 参数 | 说明 | 类型 | 默认值 | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------- | ----- | +| errorBoundary | 是否开启错误捕捉 errorBoundary
如需自定义参数,请传入对象 对象接受参数列表如下:

fallbackUI `Function(error?: {}, errorInfo?: {}) => Element` 捕获错误后的展示
afterCatch `Function(error?: {}, errorInfo?: {})` 捕获错误后的行为, 比如埋点上传 | Boolean/Object | false | +| pure | 是否开启 Pure Render 模式,会提高性能,但是也会带来副作用 | Boolean | - | +| warning | 是否在开发模式下显示组件属性被废弃的 warning 提示 | Boolean | true | +| rtl | 是否开启 rtl 模式 | Boolean | - | +| children | 组件树 | ReactElement | - | diff --git a/docs/date-picker/demo/disabled-date.md b/docs/date-picker/demo/disabled-date.md index a2cd449372..1f084f2b55 100644 --- a/docs/date-picker/demo/disabled-date.md +++ b/docs/date-picker/demo/disabled-date.md @@ -23,8 +23,16 @@ const { RangePicker, MonthPicker, YearPicker } = DatePicker; const currentDate = moment(); // Disable all dates before today -const disabledDate = function (date) { - return date.valueOf() <= currentDate.valueOf(); +const disabledDate = function (date, view) { + switch (view) { + case 'date': + return date.valueOf() <= currentDate.valueOf(); + case 'year': + return date.year() < currentDate.year(); + case 'month': + return date.year() * 100 + date.month() < currentDate.year() * 100 + currentDate.month(); + default: return false; + } }; ReactDOM.render(
diff --git a/docs/date-picker/index.en-us.md b/docs/date-picker/index.en-us.md index af11ebb5e4..b735a43974 100644 --- a/docs/date-picker/index.en-us.md +++ b/docs/date-picker/index.en-us.md @@ -27,7 +27,7 @@ DatePicker are used to select a single date for an input. | format | Format of date value (it will also effect user input) | String | 'YYYY-MM-DD' | | showTime | Enable time-picker, pass object like `{ defaultValue, format, ... }` | Object/Boolean | false | | resetTime | If reset time for every re-select | Boolean | false | -| disabledDate | Function to disable date

**signature**:
Function(dateValue: MomentObject) => Boolean
**parameter**:
_dateValue_: {MomentObject} null
**return**:
{Boolean} if disable current date
| Function | () => false | +| disabledDate | Function to disable date

**signature**:
Function(dateValue: MomentObject) => Boolean
**parameter**:
_dateValue_: {MomentObject} null
_view_: {Enum} current view type: 'year', 'month', 'date'
**return**:
{Boolean} if disable current date
| Function | () => false | | footerRender | Template render for custom footer

**signature**:
Function() => Node
**return**:
{Node} Custom footer
| Function | () => null | | onChange | Callback when date changes

**signature**:
Function() => MomentObject
**return**:
{MomentObject} dateValue
| Function | func.noop | | onOk | Callback when click the ok button

**signature**:
Function() => MomentObject
**return**:
{MomentObject} dateValue
| Function | func.noop | @@ -54,7 +54,7 @@ DatePicker are used to select a single date for an input. | format | Date format | String | 'YYYY-MM-DD' | | showTime | Enable time picker | Object/Boolean | false | | resetTime | If reset time for every select | Boolean | false | -| disabledDate | Function to disable dates

**signature**:
Function(dateValue: MomentObject) => Boolean
**parameter**:
_dateValue_: {MomentObject} null
**return**:
{Boolean} if disabled
| Function | () => false | +| disabledDate | Function to disable dates

**signature**:
Function(dateValue: MomentObject) => Boolean
**parameter**:
_dateValue_: {MomentObject} null
_view_: {Enum} current view type: 'year', 'month', 'date'
**return**:
{Boolean} if disabled
| Function | () => false | | footerRender | Template render for footer

**signature**:
Function() => Node
**return**:
{Node} custom footer
| Function | () => null | | onChange | Callback when date changes

**signature**:
Function() => MomentObject
**return**:
{MomentObject} range values
| Function | func.noop | | onOk | Callback when click ok button

**signature**:
Function() => MomentObject
**return**:
{MomentObject} range values
| Function | func.noop | @@ -82,4 +82,3 @@ DatePicker are used to select a single date for an input. | number key | Need to manual input the date, the specified date format | | Enter | open the calendar or select date | | Esc | close the calendar | - \ No newline at end of file diff --git a/docs/date-picker/index.md b/docs/date-picker/index.md index 5cd564b838..07c6e904a9 100644 --- a/docs/date-picker/index.md +++ b/docs/date-picker/index.md @@ -89,123 +89,123 @@ DatePicker 默认情况下接收和返回的数据类型都是 Moment 对象。 ### DatePicker -| 参数 | 说明 | 类型 | 默认值 | -| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | ------------ | -| label | 输入框内置标签 | ReactNode | - | -| size | 输入框尺寸

**可选值**:
'small', 'medium', 'large' | Enum | 'medium' | -| state | 输入框状态

**可选值**:
'success', 'loading', 'error' | Enum | - | -| placeholder | 输入提示 | String | - | -| defaultVisibleMonth | 默认展现的月

**签名**:
Function() => MomentObject
**返回值**:
{MomentObject} 返回包含指定月份的 moment 对象实例
| Function | - | -| value | 日期值(受控)moment 对象 | custom | - | -| defaultValue | 初始日期值,moment 对象 | custom | - | -| format | 日期值的格式(用于限定用户输入和展示) | String | 'YYYY-MM-DD' | -| showTime | 是否使用时间控件,传入 TimePicker 的属性 { defaultValue, format, ... } | Object/Boolean | false | -| resetTime | 每次选择日期时是否重置时间(仅在 showTime 开启时有效) | Boolean | false | -| disabledDate | 禁用日期函数

**签名**:
Function(日期值: MomentObject) => Boolean
**参数**:
_日期值_: {MomentObject} null
**返回值**:
{Boolean} 是否禁用
| Function | () => false | -| footerRender | 自定义面板页脚

**签名**:
Function() => Node
**返回值**:
{Node} 自定义的面板页脚组件
| Function | () => null | -| onChange | 日期值改变时的回调

**签名**:
Function(value: MomentObject/String) => void
**参数**:
_value_: {MomentObject/String} 日期值 | Function | func.noop | -| onOk | 点击确认按钮时的回调

**签名**:
Function() => MomentObject/String
**返回值**:
{MomentObject/String} 日期值
| Function | func.noop | -| disabled | 是否禁用 | Boolean | - | -| hasClear | 是否显示清空按钮 | Boolean | true | -| visible | 弹层显示状态 | Boolean | - | -| defaultVisible | 弹层默认是否显示 | Boolean | - | -| onVisibleChange | 弹层展示状态变化时的回调

**签名**:
Function(visible: Boolean, reason: String) => void
**参数**:
_visible_: {Boolean} 弹层是否显示
_reason_: {String} 触发弹层显示和隐藏的来源 | Function | func.noop | -| popupTriggerType | 弹层触发方式

**可选值**:
'click', 'hover' | Enum | 'click' | -| popupAlign | 弹层对齐方式,具体含义见 OverLay文档 | String | 'tl tl' | -| popupContainer | 弹层容器

**签名**:
Function(target: Element) => Element
**参数**:
_target_: {Element} 目标元素
**返回值**:
{Element} 弹层的容器元素
| Function | - | -| popupStyle | 弹层自定义样式 | Object | - | -| popupClassName | 弹层自定义样式类 | String | - | -| popupProps | 弹层其他属性 | Object | - | -| inputProps | 输入框其他属性 | Object | - | -| dateCellRender | 自定义日期渲染函数

**签名**:
Function(value: Object) => ReactNode
**参数**:
_value_: {Object} 日期值(moment对象)
**返回值**:
{ReactNode} null
| Function | - | -| monthCellRender | 自定义月份渲染函数

**签名**:
Function(calendarDate: Object) => ReactNode
**参数**:
_calendarDate_: {Object} 对应 Calendar 返回的自定义日期对象
**返回值**:
{ReactNode} null
| Function | - | +| 参数 | 说明 | 类型 | 默认值 | +| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | ------------ | +| label | 输入框内置标签 | ReactNode | - | +| size | 输入框尺寸

**可选值**:
'small', 'medium', 'large' | Enum | 'medium' | +| state | 输入框状态

**可选值**:
'success', 'loading', 'error' | Enum | - | +| placeholder | 输入提示 | String | - | +| defaultVisibleMonth | 默认展现的月

**签名**:
Function() => MomentObject
**返回值**:
{MomentObject} 返回包含指定月份的 moment 对象实例
| Function | - | +| value | 日期值(受控)moment 对象 | custom | - | +| defaultValue | 初始日期值,moment 对象 | custom | - | +| format | 日期值的格式(用于限定用户输入和展示) | String | 'YYYY-MM-DD' | +| showTime | 是否使用时间控件,传入 TimePicker 的属性 { defaultValue, format, ... } | Object/Boolean | false | +| resetTime | 每次选择日期时是否重置时间(仅在 showTime 开启时有效) | Boolean | false | +| disabledDate | 禁用日期函数

**签名**:
Function(日期值: MomentObject, view: String) => Boolean
**参数**:
_日期值_: {MomentObject} null
_view_: {String} 当前视图类型,year: 年, month: 月, date: 日
**返回值**:
{Boolean} 是否禁用
| Function | () => false | +| footerRender | 自定义面板页脚

**签名**:
Function() => Node
**返回值**:
{Node} 自定义的面板页脚组件
| Function | () => null | +| onChange | 日期值改变时的回调

**签名**:
Function(value: MomentObject/String) => void
**参数**:
_value_: {MomentObject/String} 日期值 | Function | func.noop | +| onOk | 点击确认按钮时的回调

**签名**:
Function() => MomentObject/String
**返回值**:
{MomentObject/String} 日期值
| Function | func.noop | +| disabled | 是否禁用 | Boolean | - | +| hasClear | 是否显示清空按钮 | Boolean | true | +| visible | 弹层显示状态 | Boolean | - | +| defaultVisible | 弹层默认是否显示 | Boolean | - | +| onVisibleChange | 弹层展示状态变化时的回调

**签名**:
Function(visible: Boolean, reason: String) => void
**参数**:
_visible_: {Boolean} 弹层是否显示
_reason_: {String} 触发弹层显示和隐藏的来源 | Function | func.noop | +| popupTriggerType | 弹层触发方式

**可选值**:
'click', 'hover' | Enum | 'click' | +| popupAlign | 弹层对齐方式,具体含义见 OverLay文档 | String | 'tl tl' | +| popupContainer | 弹层容器

**签名**:
Function(target: Element) => Element
**参数**:
_target_: {Element} 目标元素
**返回值**:
{Element} 弹层的容器元素
| Function | - | +| popupStyle | 弹层自定义样式 | Object | - | +| popupClassName | 弹层自定义样式类 | String | - | +| popupProps | 弹层其他属性 | Object | - | +| inputProps | 输入框其他属性 | Object | - | +| dateCellRender | 自定义日期渲染函数

**签名**:
Function(value: Object) => ReactNode
**参数**:
_value_: {Object} 日期值(moment对象)
**返回值**:
{ReactNode} null
| Function | - | +| monthCellRender | 自定义月份渲染函数

**签名**:
Function(calendarDate: Object) => ReactNode
**参数**:
_calendarDate_: {Object} 对应 Calendar 返回的自定义日期对象
**返回值**:
{ReactNode} null
| Function | - | ### DatePicker.MonthPicker -| 参数 | 说明 | 类型 | 默认值 | -| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ----------- | -| label | 输入框内置标签 | ReactNode | - | -| size | 输入框尺寸

**可选值**:
'small', 'medium', 'large' | Enum | 'medium' | -| state | 输入框状态

**可选值**:
'success', 'loading', 'error' | Enum | - | -| placeholder | 输入提示 | String | - | -| defaultVisibleYear | 默认展现的年

**签名**:
Function() => MomentObject
**返回值**:
{MomentObject} 返回包含指定年份的 moment 对象实例
| Function | - | -| value | 日期值(受控)moment 对象 | custom | - | -| defaultValue | 初始日期值,moment 对象 | custom | - | -| format | 日期值的格式(用于限定用户输入和展示) | String | 'YYYY-MM' | -| disabledDate | 禁用日期函数

**签名**:
Function(日期值: MomentObject) => Boolean
**参数**:
_日期值_: {MomentObject} null
**返回值**:
{Boolean} 是否禁用
| Function | () => false | -| footerRender | 自定义面板页脚

**签名**:
Function() => Node
**返回值**:
{Node} 自定义的面板页脚组件
| Function | () => null | -| onChange | 日期值改变时的回调

**签名**:
Function(value: MomentObject/String) => void
**参数**:
_value_: {MomentObject/String} 日期值 | Function | func.noop | -| disabled | 是否禁用 | Boolean | - | -| hasClear | 是否显示清空按钮 | Boolean | true | -| visible | 弹层显示状态 | Boolean | - | -| defaultVisible | 弹层默认是否显示 | Boolean | - | -| onVisibleChange | 弹层展示状态变化时的回调

**签名**:
Function(visible: Boolean, reason: String) => void
**参数**:
_visible_: {Boolean} 弹层是否显示
_reason_: {String} 触发弹层显示和隐藏的来源 | Function | func.noop | -| popupTriggerType | 弹层触发方式

**可选值**:
'click', 'hover' | Enum | 'click' | -| popupAlign | 弹层对齐方式, 具体含义见 OverLay文档 | String | 'tl tl' | -| popupContainer | 弹层容器

**签名**:
Function(target: Element) => Element
**参数**:
_target_: {Element} 目标元素
**返回值**:
{Element} 弹层的容器元素
| Function | - | -| popupStyle | 弹层自定义样式 | Object | - | -| popupClassName | 弹层自定义样式类 | String | - | -| popupProps | 弹层其他属性 | Object | - | -| inputProps | 输入框其他属性 | Object | - | -| monthCellRender | 自定义月份渲染函数

**签名**:
Function(calendarDate: Object) => ReactNode
**参数**:
_calendarDate_: {Object} 对应 Calendar 返回的自定义日期对象
**返回值**:
{ReactNode} null
| Function | - | +| 参数 | 说明 | 类型 | 默认值 | +| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ----------- | +| label | 输入框内置标签 | ReactNode | - | +| size | 输入框尺寸

**可选值**:
'small', 'medium', 'large' | Enum | 'medium' | +| state | 输入框状态

**可选值**:
'success', 'loading', 'error' | Enum | - | +| placeholder | 输入提示 | String | - | +| defaultVisibleYear | 默认展现的年

**签名**:
Function() => MomentObject
**返回值**:
{MomentObject} 返回包含指定年份的 moment 对象实例
| Function | - | +| value | 日期值(受控)moment 对象 | custom | - | +| defaultValue | 初始日期值,moment 对象 | custom | - | +| format | 日期值的格式(用于限定用户输入和展示) | String | 'YYYY-MM' | +| disabledDate | 禁用日期函数

**签名**:
Function(日期值: MomentObject, view: String) => Boolean
**参数**:
_日期值_: {MomentObject} null
_view_: {String} 当前视图类型,year: 年, month: 月, date: 日
**返回值**:
{Boolean} 是否禁用
| Function | () => false | +| footerRender | 自定义面板页脚

**签名**:
Function() => Node
**返回值**:
{Node} 自定义的面板页脚组件
| Function | () => null | +| onChange | 日期值改变时的回调

**签名**:
Function(value: MomentObject/String) => void
**参数**:
_value_: {MomentObject/String} 日期值 | Function | func.noop | +| disabled | 是否禁用 | Boolean | - | +| hasClear | 是否显示清空按钮 | Boolean | true | +| visible | 弹层显示状态 | Boolean | - | +| defaultVisible | 弹层默认是否显示 | Boolean | - | +| onVisibleChange | 弹层展示状态变化时的回调

**签名**:
Function(visible: Boolean, reason: String) => void
**参数**:
_visible_: {Boolean} 弹层是否显示
_reason_: {String} 触发弹层显示和隐藏的来源 | Function | func.noop | +| popupTriggerType | 弹层触发方式

**可选值**:
'click', 'hover' | Enum | 'click' | +| popupAlign | 弹层对齐方式, 具体含义见 OverLay文档 | String | 'tl tl' | +| popupContainer | 弹层容器

**签名**:
Function(target: Element) => Element
**参数**:
_target_: {Element} 目标元素
**返回值**:
{Element} 弹层的容器元素
| Function | - | +| popupStyle | 弹层自定义样式 | Object | - | +| popupClassName | 弹层自定义样式类 | String | - | +| popupProps | 弹层其他属性 | Object | - | +| inputProps | 输入框其他属性 | Object | - | +| monthCellRender | 自定义月份渲染函数

**签名**:
Function(calendarDate: Object) => ReactNode
**参数**:
_calendarDate_: {Object} 对应 Calendar 返回的自定义日期对象
**返回值**:
{ReactNode} null
| Function | - | ### DatePicker.RangePicker -| 参数 | 说明 | 类型 | 默认值 | | | -| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | -------------------------------------------------------------------------------------------- | -------- | --------- | -| defaultVisibleMonth | 默认展示的起始月份

**签名**:
Function() => MomentObject
**返回值**:
{MomentObject} 返回包含指定月份的 moment 对象实例
| Function | - | | | -| size | 输入框尺寸

**可选值**:
'small', 'medium', 'large' | Enum | 'medium' | | | -| value | 日期范围值数组 [moment, moment] | Array | - | | | -| defaultValue | 初始的日期范围值数组 [moment, moment] | Array | - | | | -| format | 日期格式 | String | 'YYYY-MM-DD' | | | -| showTime | 是否使用时间控件,支持传入 TimePicker 的属性 | Object/Boolean | false | | | -| resetTime | 每次选择是否重置时间(仅在 showTime 开启时有效) | Boolean | false | | | -| disabledDate | 禁用日期函数

**签名**:
Function(日期值: MomentObject) => Boolean
**参数**:
_日期值_: {MomentObject} null
**返回值**:
{Boolean} 是否禁用
| Function | () => false | | | -| footerRender | 自定义面板页脚

**签名**:
Function() => Node
**返回值**:
{Node} 自定义的面板页脚组件
| Function | () => null | | | -| onChange | 日期范围值改变时的回调 \[ MomentObject | String, MomentObject | String ]

**签名**:
Function(value: Array) => void
**参数**:
_value_: {Array} 日期值 | Function | func.noop | -| onOk | 点击确认按钮时的回调 返回开始时间和结束时间`[ MomentObject|String, MomentObject|String ]`

**签名**:
Function() => Array
**返回值**:
{Array} 日期范围
| Function | func.noop | | | -| label | 输入框内置标签 | ReactNode | - | | | -| state | 输入框状态

**可选值**:
'error', 'loading', 'success' | Enum | - | | | -| disabled | 是否禁用 | Boolean | - | | | -| hasClear | 是否显示清空按钮 | Boolean | true | | | -| visible | 弹层显示状态 | Boolean | - | | | -| defaultVisible | 弹层默认是否显示 | Boolean | - | | | -| onVisibleChange | 弹层展示状态变化时的回调

**签名**:
Function(visible: Boolean, reason: String) => void
**参数**:
_visible_: {Boolean} 弹层是否显示
_reason_: {String} 触发弹层显示和隐藏的来源 | Function | func.noop | | | -| popupTriggerType | 弹层触发方式

**可选值**:
'click', 'hover' | Enum | 'click' | | | -| popupAlign | 弹层对齐方式, 具体含义见 OverLay文档 | String | 'tl tl' | | | -| popupContainer | 弹层容器

**签名**:
Function(target: Element) => Element
**参数**:
_target_: {Element} 目标元素
**返回值**:
{Element} 弹层的容器元素
| Function | - | | | -| popupStyle | 弹层自定义样式 | Object | - | | | -| popupClassName | 弹层自定义样式类 | String | - | | | -| popupProps | 弹层其他属性 | Object | - | | | -| inputProps | 输入框其他属性 | Object | - | | | -| dateCellRender | 自定义日期单元格渲染

**签名**:
Function() => void | Function | - | | | +| 参数 | 说明 | 类型 | 默认值 | | | +| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | -------------------------------------------------------------------------------------------- | -------- | --------- | +| defaultVisibleMonth | 默认展示的起始月份

**签名**:
Function() => MomentObject
**返回值**:
{MomentObject} 返回包含指定月份的 moment 对象实例
| Function | - | | | +| size | 输入框尺寸

**可选值**:
'small', 'medium', 'large' | Enum | 'medium' | | | +| value | 日期范围值数组 [moment, moment] | Array | - | | | +| defaultValue | 初始的日期范围值数组 [moment, moment] | Array | - | | | +| format | 日期格式 | String | 'YYYY-MM-DD' | | | +| showTime | 是否使用时间控件,支持传入 TimePicker 的属性 | Object/Boolean | false | | | +| resetTime | 每次选择是否重置时间(仅在 showTime 开启时有效) | Boolean | false | | | +| disabledDate | 禁用日期函数

**签名**:
Function(日期值: MomentObject, view: String) => Boolean
**参数**:
_日期值_: {MomentObject} null
_view_: {String} 当前视图类型,year: 年, month: 月, date: 日
**返回值**:
{Boolean} 是否禁用
| Function | () => false | | | +| footerRender | 自定义面板页脚

**签名**:
Function() => Node
**返回值**:
{Node} 自定义的面板页脚组件
| Function | () => null | | | +| onChange | 日期范围值改变时的回调 \[ MomentObject | String, MomentObject | String ]

**签名**:
Function(value: Array) => void
**参数**:
_value_: {Array} 日期值 | Function | func.noop | +| onOk | 点击确认按钮时的回调 返回开始时间和结束时间`[ MomentObject|String, MomentObject|String ]`

**签名**:
Function() => Array
**返回值**:
{Array} 日期范围
| Function | func.noop | | | +| label | 输入框内置标签 | ReactNode | - | | | +| state | 输入框状态

**可选值**:
'error', 'loading', 'success' | Enum | - | | | +| disabled | 是否禁用 | Boolean | - | | | +| hasClear | 是否显示清空按钮 | Boolean | true | | | +| visible | 弹层显示状态 | Boolean | - | | | +| defaultVisible | 弹层默认是否显示 | Boolean | - | | | +| onVisibleChange | 弹层展示状态变化时的回调

**签名**:
Function(visible: Boolean, reason: String) => void
**参数**:
_visible_: {Boolean} 弹层是否显示
_reason_: {String} 触发弹层显示和隐藏的来源 | Function | func.noop | | | +| popupTriggerType | 弹层触发方式

**可选值**:
'click', 'hover' | Enum | 'click' | | | +| popupAlign | 弹层对齐方式, 具体含义见 OverLay文档 | String | 'tl tl' | | | +| popupContainer | 弹层容器

**签名**:
Function(target: Element) => Element
**参数**:
_target_: {Element} 目标元素
**返回值**:
{Element} 弹层的容器元素
| Function | - | | | +| popupStyle | 弹层自定义样式 | Object | - | | | +| popupClassName | 弹层自定义样式类 | String | - | | | +| popupProps | 弹层其他属性 | Object | - | | | +| inputProps | 输入框其他属性 | Object | - | | | +| dateCellRender | 自定义日期单元格渲染

**签名**:
Function() => void | Function | - | | | ### DatePicker.YearPicker -| 参数 | 说明 | 类型 | 默认值 | -| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ----------- | -| label | 输入框内置标签 | ReactNode | - | -| size | 输入框尺寸

**可选值**:
'small', 'medium', 'large' | Enum | 'medium' | -| state | 输入框状态

**可选值**:
'success', 'loading', 'error' | Enum | - | -| placeholder | 输入提示 | String | - | -| value | 日期值(受控)moment 对象 | custom | - | -| defaultValue | 初始日期值,moment 对象 | custom | - | -| format | 日期值的格式(用于限定用户输入和展示) | String | 'YYYY' | -| disabledDate | 禁用日期函数

**签名**:
Function(日期值: MomentObject) => Boolean
**参数**:
_日期值_: {MomentObject} null
**返回值**:
{Boolean} 是否禁用
| Function | () => false | -| footerRender | 自定义面板页脚

**签名**:
Function() => Node
**返回值**:
{Node} 自定义的面板页脚组件
| Function | () => null | -| onChange | 日期值改变时的回调

**签名**:
Function(value: MomentObject/String) => void
**参数**:
_value_: {MomentObject/String} 日期值 | Function | func.noop | -| disabled | 是否禁用 | Boolean | - | -| hasClear | 是否显示清空按钮 | Boolean | true | -| visible | 弹层显示状态 | Boolean | - | -| defaultVisible | 弹层默认是否显示 | Boolean | - | -| onVisibleChange | 弹层展示状态变化时的回调

**签名**:
Function(visible: Boolean, reason: String) => void
**参数**:
_visible_: {Boolean} 弹层是否显示
_reason_: {String} 触发弹层显示和隐藏的来源 | Function | func.noop | -| popupTriggerType | 弹层触发方式

**可选值**:
'click', 'hover' | Enum | 'click' | -| popupAlign | 弹层对齐方式, 具体含义见 OverLay文档 | String | 'tl tl' | -| popupContainer | 弹层容器

**签名**:
Function(target: Element) => Element
**参数**:
_target_: {Element} 目标元素
**返回值**:
{Element} 弹层的容器元素
| Function | - | -| popupStyle | 弹层自定义样式 | Object | - | -| popupClassName | 弹层自定义样式类 | String | - | -| popupProps | 弹层其他属性 | Object | - | -| inputProps | 输入框其他属性 | Object | - | +| 参数 | 说明 | 类型 | 默认值 | +| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ----------- | +| label | 输入框内置标签 | ReactNode | - | +| size | 输入框尺寸

**可选值**:
'small', 'medium', 'large' | Enum | 'medium' | +| state | 输入框状态

**可选值**:
'success', 'loading', 'error' | Enum | - | +| placeholder | 输入提示 | String | - | +| value | 日期值(受控)moment 对象 | custom | - | +| defaultValue | 初始日期值,moment 对象 | custom | - | +| format | 日期值的格式(用于限定用户输入和展示) | String | 'YYYY' | +| disabledDate | 禁用日期函数

**签名**:
Function(日期值: MomentObject, view: String) => Boolean
**参数**:
_日期值_: {MomentObject} null
_view_: {String} 当前视图类型,year: 年, month: 月, date: 日
**返回值**:
{Boolean} 是否禁用
| Function | () => false | +| footerRender | 自定义面板页脚

**签名**:
Function() => Node
**返回值**:
{Node} 自定义的面板页脚组件
| Function | () => null | +| onChange | 日期值改变时的回调

**签名**:
Function(value: MomentObject/String) => void
**参数**:
_value_: {MomentObject/String} 日期值 | Function | func.noop | +| disabled | 是否禁用 | Boolean | - | +| hasClear | 是否显示清空按钮 | Boolean | true | +| visible | 弹层显示状态 | Boolean | - | +| defaultVisible | 弹层默认是否显示 | Boolean | - | +| onVisibleChange | 弹层展示状态变化时的回调

**签名**:
Function(visible: Boolean, reason: String) => void
**参数**:
_visible_: {Boolean} 弹层是否显示
_reason_: {String} 触发弹层显示和隐藏的来源 | Function | func.noop | +| popupTriggerType | 弹层触发方式

**可选值**:
'click', 'hover' | Enum | 'click' | +| popupAlign | 弹层对齐方式, 具体含义见 OverLay文档 | String | 'tl tl' | +| popupContainer | 弹层容器

**签名**:
Function(target: Element) => Element
**参数**:
_target_: {Element} 目标元素
**返回值**:
{Element} 弹层的容器元素
| Function | - | +| popupStyle | 弹层自定义样式 | Object | - | +| popupClassName | 弹层自定义样式类 | String | - | +| popupProps | 弹层其他属性 | Object | - | +| inputProps | 输入框其他属性 | Object | - | ## ARIA and KeyBoard diff --git a/docs/dialog/demo/accessibility.md b/docs/dialog/demo/accessibility.md index 3507d4e7a6..a479266b11 100644 --- a/docs/dialog/demo/accessibility.md +++ b/docs/dialog/demo/accessibility.md @@ -28,9 +28,7 @@ class Demo extends React.Component { visible: true }); }; - onClose = reason => { - console.log(reason); - + onClose = () => { this.setState({ visible: false }); @@ -44,6 +42,7 @@ class Demo extends React.Component { ` 和 `` 会加上 `role="row"` 和 `role="gridcell"`, 但是为了完美的无障碍实现, 开发者还应该在外部容器加上 `role="grid"`。示例代码如下: -```` -
- 1234 - 12 -
-```` +
+ 1234 + 12 +
## API diff --git a/docs/overlay/demo/accessibility.md b/docs/overlay/demo/accessibility.md index 67dfb4e02f..744082f0cb 100644 --- a/docs/overlay/demo/accessibility.md +++ b/docs/overlay/demo/accessibility.md @@ -21,24 +21,20 @@ import { Overlay } from '@alifd/next'; class Demo extends React.Component { constructor(props) { super(props); - this.state = { visible: false }; - } - + }; onClick = () => { this.setState({ visible: !this.state.visible }); - } - + }; onClose = () => { this.setState({ visible: false }); - } - + }; render() { return (
@@ -48,6 +44,7 @@ class Demo extends React.Component { overlay accessibility this.btn} safeNode={() => this.btn} onRequestClose={this.onClose}> diff --git a/docs/radio/index.md b/docs/radio/index.md index bf9c61d767..ae9b6707b2 100644 --- a/docs/radio/index.md +++ b/docs/radio/index.md @@ -34,19 +34,19 @@ ### Radio.Group -| 参数 | 说明 | 类型 | 默认值 | -| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | --------- | -| name | name | String | - | -| size | 与 `shape` 属性配套使用,shape设为button时有效

**可选值**:
'large'(大)
'medium'(中)
'small'(小) | Enum | 'medium' | -| shape | 可以设置成 button 展示形状

**可选值**:
'button'(按钮状) | Enum | - | -| value | radio group的选中项的值 | String/Number/Boolean | - | -| defaultValue | radio group的默认值 | String/Number/Boolean | - | -| component | 设置标签类型 | String/Function | 'div' | -| onChange | 选中值改变时的事件

**签名**:
Function(value: String/Number, e: Event) => void
**参数**:
_value_: {String/Number} 选中项的值
_e_: {Event} Dom 事件对象 | Function | () => { } | -| disabled | 表示radio被禁用 | Boolean | - | -| dataSource | 可选项列表, 数据项可为 String 或者 Object, 如 `['apple', 'pear', 'orange']` | Array<any> | \[] | -| children | 通过子元素方式设置内部radio | Array<ReactElement>/ReactElement | - | -| itemDirection | 子项目的排列方式
- hoz: 水平排列 (default)
- ver: 垂直排列

**可选值**:
'hoz', 'ver' | Enum | 'hoz' | +| 参数 | 说明 | 类型 | 默认值 | +| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | -------- | +| name | name | String | - | +| size | 与 `shape` 属性配套使用,shape设为button时有效

**可选值**:
'large'(大)
'medium'(中)
'small'(小) | Enum | 'medium' | +| shape | 可以设置成 button 展示形状

**可选值**:
'button'(按钮状) | Enum | - | +| value | radio group的选中项的值 | String/Number/Boolean | - | +| defaultValue | radio group的默认值 | String/Number/Boolean | - | +| component | 设置标签类型 | String/Function | 'div' | +| onChange | 选中值改变时的事件

**签名**:
Function(value: String/Number, e: Event) => void
**参数**:
_value_: {String/Number} 选中项的值
_e_: {Event} Dom 事件对象 | Function | () => {} | +| disabled | 表示radio被禁用 | Boolean | - | +| dataSource | 可选项列表, 数据项可为 String 或者 Object, 如 `['apple', 'pear', 'orange']` | Array<any> | \[] | +| children | 通过子元素方式设置内部radio | Array<ReactElement>/ReactElement | - | +| itemDirection | 子项目的排列方式
- hoz: 水平排列 (default)
- ver: 垂直排列

**可选值**:
'hoz', 'ver' | Enum | 'hoz' | ## ARIA and KeyBoard diff --git a/docs/range/demo/accessibility.md b/docs/range/demo/accessibility.md index a3a5ce2cdd..40bfc1ba27 100644 --- a/docs/range/demo/accessibility.md +++ b/docs/range/demo/accessibility.md @@ -22,14 +22,10 @@ const style = { marginBottom: '15px' }; -ReactDOM.render(( -
- +ReactDOM.render(

single slider - from left to right

single slider - from right to left

- -
-), mountNode); +
, mountNode); ```` \ No newline at end of file diff --git a/docs/range/demo/basic.md b/docs/range/demo/basic.md index 435b983743..0f90ce3615 100644 --- a/docs/range/demo/basic.md +++ b/docs/range/demo/basic.md @@ -24,9 +24,7 @@ const style = { marginBottom: '15px' }; -ReactDOM.render(( -
- +ReactDOM.render(

single slider - from left to right

single slider - from right to left

@@ -47,6 +45,5 @@ ReactDOM.render((

tooltipVisible

-
-), mountNode); +
, mountNode); ```` diff --git a/docs/range/demo/marks.md b/docs/range/demo/marks.md index 44645d3160..21d6ee52a9 100644 --- a/docs/range/demo/marks.md +++ b/docs/range/demo/marks.md @@ -23,8 +23,7 @@ const style = { marginTop: '20px' }; -ReactDOM.render(( -
+ReactDOM.render(

With minimal and maximal value

@@ -41,6 +40,5 @@ ReactDOM.render(( -
-), mountNode); +
, mountNode); ```` diff --git a/docs/range/demo/range.md b/docs/range/demo/range.md index d26675c944..33cb41798b 100644 --- a/docs/range/demo/range.md +++ b/docs/range/demo/range.md @@ -22,7 +22,7 @@ const style = { marginBottom: '40px', marginTop: '40px' }; -ReactDOM.render(( +ReactDOM.render(

Range 0 ~ 1024

@@ -30,5 +30,5 @@ ReactDOM.render((
-), mountNode); +, mountNode); ```` diff --git a/docs/range/demo/reverse.md b/docs/range/demo/reverse.md index 01988372b8..0627d7fd7c 100644 --- a/docs/range/demo/reverse.md +++ b/docs/range/demo/reverse.md @@ -22,7 +22,7 @@ const style = { marginBottom: '15px' }; -ReactDOM.render(( +ReactDOM.render(
@@ -32,6 +32,5 @@ ReactDOM.render(( -
-), mountNode); +
, mountNode); ```` diff --git a/docs/range/index.md b/docs/range/index.md index e17916cacd..495d75d06a 100644 --- a/docs/range/index.md +++ b/docs/range/index.md @@ -43,6 +43,7 @@ | pure | 是否pure render | Boolean | false | | fixedWidth | 是否为拖动线段类型,默认slider为double, defaultValue必传且指定区间 | Boolean | false | | tooltipVisible | tooltip是否默认展示 | Boolean | false | +| rtl | 是否已rtl模式展示 | Boolean | false | ## ARIA and KeyBoard diff --git a/docs/search/demo/accessibility.md b/docs/search/demo/accessibility.md index 98ec43e64e..0f0d264679 100644 --- a/docs/search/demo/accessibility.md +++ b/docs/search/demo/accessibility.md @@ -34,7 +34,7 @@ class Demo extends React.Component { onsearch() { console.log(this.state.name); } - ender() { + render() { return (
search} style={{width: '400px'}}/>
); diff --git a/docs/select/demo/multiple.md b/docs/select/demo/multiple.md index 3f2751b87d..02d92ed911 100644 --- a/docs/select/demo/multiple.md +++ b/docs/select/demo/multiple.md @@ -32,5 +32,33 @@ function handleChange(value) { console.log(value); } -ReactDOM.render() + } +} + + +ReactDOM.render( +
+ +      + +
+, mountNode); ```` diff --git a/docs/step/index.md b/docs/step/index.md index e844e7c496..3cc809dcdb 100644 --- a/docs/step/index.md +++ b/docs/step/index.md @@ -23,13 +23,13 @@ ### Step.Item -| 参数 | 说明 | 类型 | 默认值 | -| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | --------- | -| status | 步骤的状态,如不传,会根据外层的 Step 的 current 属性生成,可选值为 `wait`, `process`, `finish`

**可选值**:
'wait', 'process', 'finish' | Enum | - | -| title | 标题 | ReactNode | - | -| icon | 图标 | String | - | -| content | 内容,用于垂直状态下的内容填充 | ReactNode | - | -| itemRender | StepItem 的自定义渲染, 会覆盖父节点设置的itemRender

**签名**:
Function(index: Number, status: String) => Node
**参数**:
_index_: {Number} 节点索引
_status_: {String} 节点状态
**返回值**:
{Node} 节点的渲染结果
| Function | - | -| percent | 百分比 | Number | - | -| disabled | 是否禁用 | Boolean | - | -| onClick | 点击步骤时的回调

**签名**:
Function(index: Number) => void
**参数**:
_index_: {Number} 节点索引 | Function | () => { } | +| 参数 | 说明 | 类型 | 默认值 | +| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -------- | +| status | 步骤的状态,如不传,会根据外层的 Step 的 current 属性生成,可选值为 `wait`, `process`, `finish`

**可选值**:
'wait', 'process', 'finish' | Enum | - | +| title | 标题 | ReactNode | - | +| icon | 图标 | String | - | +| content | 内容,用于垂直状态下的内容填充 | ReactNode | - | +| itemRender | StepItem 的自定义渲染, 会覆盖父节点设置的itemRender

**签名**:
Function(index: Number, status: String) => Node
**参数**:
_index_: {Number} 节点索引
_status_: {String} 节点状态
**返回值**:
{Node} 节点的渲染结果
| Function | - | +| percent | 百分比 | Number | - | +| disabled | 是否禁用 | Boolean | - | +| onClick | 点击步骤时的回调

**签名**:
Function(index: Number) => void
**参数**:
_index_: {Number} 节点索引 | Function | () => {} | diff --git a/docs/switch/index.md b/docs/switch/index.md index 1991a98aa7..2acea77b23 100644 --- a/docs/switch/index.md +++ b/docs/switch/index.md @@ -23,17 +23,17 @@ ### Switch -| 参数 | 说明 | 类型 | 默认值 | -| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------- | -| checkedChildren | 打开时的内容 | any | - | -| size | switch的尺寸

**可选值**:
'medium'(正常大小)
'small'(缩小版大小) | Enum | 'medium' | -| unCheckedChildren | 关闭时的内容 | any | - | -| onChange | 开关状态改变是触发此事件

**签名**:
Function(checked: Boolean, e: Event) => void
**参数**:
_checked_: {Boolean} 是否为打开状态
_e_: {Event} DOM事件对象 | Function | () => { } | -| checked | 开关当前的值(针对受控组件) | Boolean | - | -| defaultChecked | 开关默认值 (针对非受控组件) | Boolean | - | -| disabled | 表示开关被禁用 | Boolean | false | -| onClick | 鼠标点击事件

**签名**:
Function(e: Event) => void
**参数**:
_e_: {Event} DOM事件对象 | Function | - | -| onKeyDown | 键盘按键事件

**签名**:
Function(e: Event) => void
**参数**:
_e_: {Event} DOM事件对象 | Function | - | +| 参数 | 说明 | 类型 | 默认值 | +| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -------- | +| checkedChildren | 打开时的内容 | any | - | +| size | switch的尺寸

**可选值**:
'medium'(正常大小)
'small'(缩小版大小) | Enum | 'medium' | +| unCheckedChildren | 关闭时的内容 | any | - | +| onChange | 开关状态改变是触发此事件

**签名**:
Function(checked: Boolean, e: Event) => void
**参数**:
_checked_: {Boolean} 是否为打开状态
_e_: {Event} DOM事件对象 | Function | () => {} | +| checked | 开关当前的值(针对受控组件) | Boolean | - | +| defaultChecked | 开关默认值 (针对非受控组件) | Boolean | - | +| disabled | 表示开关被禁用 | Boolean | false | +| onClick | 鼠标点击事件

**签名**:
Function(e: Event) => void
**参数**:
_e_: {Event} DOM事件对象 | Function | - | +| onKeyDown | 键盘按键事件

**签名**:
Function(e: Event) => void
**参数**:
_e_: {Event} DOM事件对象 | Function | - | ## 键盘支持 diff --git a/docs/table/demo/fixed-header.md b/docs/table/demo/fixed-header.md index 067ae1ed31..edcd81896e 100644 --- a/docs/table/demo/fixed-header.md +++ b/docs/table/demo/fixed-header.md @@ -31,20 +31,39 @@ const dataSource = (length) => { class App extends React.Component { state = { - sticky: false + sticky: false, + lock: false, + dataSource: dataSource(50), } - onSwitch() { - this.setState({ - sticky: true - }); + onSwitch(tag) { + const props = {}; + switch (tag) { + case 'sticky': + props.sticky = true; + break; + case 'lock': + props.lock = true; + break; + case 'dataSource': + props.dataSource = this.state.dataSource.length > 0 ? [] : dataSource(50); + break; + default: + break; + } + + this.setState(props); } render() { return (
-

- - - - +

+   +   + +

+
+ + +
); diff --git a/docs/table/index.md b/docs/table/index.md index 31af14dc00..9250cfdfc1 100644 --- a/docs/table/index.md +++ b/docs/table/index.md @@ -106,7 +106,7 @@ ReactDOM.render( | hasHeader | 表格是否具有头部 | Boolean | true | | isZebra | 表格是否是斑马线 | Boolean | false | | loading | 表格是否在加载中 | Boolean | false | -| loadingComponent | 自定义 Loading 组件

**签名**:
Function() => void | Function | - | +| loadingComponent | 自定义 Loading 组件
请务必传递 props, 使用方式: loadingComponent={props => <Loading {...props}/>}

**签名**:
Function(props: Object) => void
**参数**:
_props_: {Object} 当前点击行的key | Function | - | | filterParams | 当前过滤的的keys,使用此属性可以控制表格的头部的过滤选项中哪个菜单被选中,格式为 {dataIndex: {selectedKeys:\[]}}
示例:
假设要控制dataIndex为id的列的过滤菜单中key为one的菜单项选中
`` | Object | - | | sort | 当前排序的字段,使用此属性可以控制表格的字段的排序,格式为{dataIndex: 'asc'} | Object | - | | emptyContent | 设置数据为空的时候的表格内容展现 | ReactNode | - | @@ -133,19 +133,19 @@ ReactDOM.render( ### Table.Column -| 参数 | 说明 | 类型 | 默认值 | -| ----------- | --------------------------------------------------------------------------------------------------- | ------------------------------- | ---------------- | -| dataIndex | 指定列对应的字段,支持`a.b`形式的快速取值 | String | - | -| cell | 行渲染的逻辑
value, rowIndex, record, context四个属性只可读不可被更改
Function(value, index, record) => Element | ReactElement/ReactNode/Function | (value) => value | -| title | 表头显示的内容
value, rowIndex, record, context四个属性只可读不可被更改 | ReactElement/ReactNode/Function | - | -| sortable | 是否支持排序 | Boolean | - | -| width | 列宽,注意在锁列的情况下一定需要配置宽度 | Number/String | - | -| align | 单元格的对齐方式

**可选值**:
'left', 'center', 'right' | Enum | - | -| alignHeader | 单元格标题的对齐方式, 不配置默认读取align值

**可选值**:
'left', 'center', 'right' | Enum | - | -| filters | 生成标题过滤的菜单, 格式为`[{label:'xxx', value:'xxx'}]` | Array<Object> | - | -| filterMode | 过滤的模式是单选还是多选

**可选值**:
'single', 'multiple' | Enum | 'multiple' | -| lock | 是否支持锁列,可选值为`left`,`right`, `true` | Boolean/String | - | -| resizable | 是否支持列宽调整, 当该值设为true,table的布局方式会修改为fixed. | Boolean | false | +| 参数 | 说明 | 类型 | 默认值 | +| ----------- | --------------------------------------------------------------------------------------------------- | ------------------------------- | -------------- | +| dataIndex | 指定列对应的字段,支持`a.b`形式的快速取值 | String | - | +| cell | 行渲染的逻辑
value, rowIndex, record, context四个属性只可读不可被更改
Function(value, index, record) => Element | ReactElement/ReactNode/Function | value => value | +| title | 表头显示的内容
value, rowIndex, record, context四个属性只可读不可被更改 | ReactElement/ReactNode/Function | - | +| sortable | 是否支持排序 | Boolean | - | +| width | 列宽,注意在锁列的情况下一定需要配置宽度 | Number/String | - | +| align | 单元格的对齐方式

**可选值**:
'left', 'center', 'right' | Enum | - | +| alignHeader | 单元格标题的对齐方式, 不配置默认读取align值

**可选值**:
'left', 'center', 'right' | Enum | - | +| filters | 生成标题过滤的菜单, 格式为`[{label:'xxx', value:'xxx'}]` | Array<Object> | - | +| filterMode | 过滤的模式是单选还是多选

**可选值**:
'single', 'multiple' | Enum | 'multiple' | +| lock | 是否支持锁列,可选值为`left`,`right`, `true` | Boolean/String | - | +| resizable | 是否支持列宽调整, 当该值设为true,table的布局方式会修改为fixed. | Boolean | false | ### Table.ColumnGroup diff --git a/index.js b/index.js index dc33f19c6a..181976134c 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,5 @@ var next = require('./lib/index.js'); -next.version = '1.13.4'; +next.version = '1.13.8'; module.exports = next; diff --git a/package.json b/package.json index b96004ebc5..ec94703e89 100644 --- a/package.json +++ b/package.json @@ -1,179 +1,180 @@ { - "name": "@alifd/next", - "version": "1.13.4", - "description": "A configurable component library for web built on React.", - "keywords": [ - "fusion", - "fusion design", - "next", - "component", - "ui tookit" + "name": "@alifd/next", + "version": "1.13.8", + "description": "A configurable component library for web built on React.", + "keywords": [ + "fusion", + "fusion design", + "next", + "component", + "ui tookit" + ], + "homepage": "https://github.com/alibaba-fusion/next", + "bugs": "https://github.com/alibaba-fusion/next/issues", + "license": "MIT", + "files": [ + "dist", + "es", + "lib", + "index-noreset.scss", + "index-with-locales.js", + "index.js", + "index.scss", + "reset.scss", + "variables.scss", + ".fusion" + ], + "main": "index.js", + "module": "es/index.js", + "typings": "lib/index.d.ts", + "repository": { + "type": "git", + "url": "https://github.com/alibaba-fusion/next.git" + }, + "scripts": { + "report-coverage": "codecov", + "dev": "node ./scripts/server/index.js", + "build": "node ./scripts/build/index.js", + "check": "node ./scripts/check/index.js", + "check-sass": "node ./scripts/check/sass.js", + "docs": "node ./scripts/docs/index.js", + "api": "node ./scripts/api.js", + "pack": "node ./scripts/pack.js", + "release": "node ./scripts/release/index.js", + "test": "node --max_old_space_size=8192 ./scripts/test/index.js", + "test:a11y": "node --max_old_space_size=8192 ./scripts/test/a11y-index.js", + "order-var": "node ./scripts/order-var.js", + "eslint": "eslint '@(src|scripts)/**/*.@(js|jsx)' && eslint --fix 'docs/**/@(demo|theme)/*.@(md)'", + "prettierjs": "prettier --write '@(src|test|scripts)/**/*.@(js|jsx)'", + "stylelint": "stylelint --fix 'src/**/*.@(css|scss)'", + "commitmsg": "commitlint -E GIT_PARAMS", + "precommit": "lint-staged", + "changelog": "node ./scripts/changelog.js", + "prepub": "npm run eslint && npm run stylelint && npm run changelog && npm run build && npm run check && npm run docs && npm run pack && npm run pack -- minimize" + }, + "lint-staged": { + "@(src|scripts|docs/*/demo)/**/*.@(js|jsx)": [ + "prettier --write" ], - "homepage": "https://github.com/alibaba-fusion/next", - "bugs": "https://github.com/alibaba-fusion/next/issues", - "license": "MIT", - "files": [ - "dist", - "es", - "lib", - "index-noreset.scss", - "index-with-locales.js", - "index.js", - "index.scss", - "reset.scss", - "variables.scss", - ".fusion" + "@(src|scripts|docs/*/demo)/**/*.@(js|jsx|md)": [ + "eslint", + "git add" ], - "main": "index.js", - "module": "es/index.js", - "typings": "lib/index.d.ts", - "repository": { - "type": "git", - "url": "https://github.com/alibaba-fusion/next.git" - }, - "scripts": { - "report-coverage": "codecov", - "dev": "node ./scripts/server/index.js", - "build": "node ./scripts/build/index.js", - "check": "node ./scripts/check/index.js", - "check-sass": "node ./scripts/check/sass.js", - "docs": "node ./scripts/docs/index.js", - "api": "node ./scripts/api.js", - "pack": "node ./scripts/pack.js", - "release": "node ./scripts/release/index.js", - "test": "node --max_old_space_size=8192 ./scripts/test/index.js", - "order-var": "node ./scripts/order-var.js", - "eslint": "eslint '@(src|scripts)/**/*.@(js|jsx)' && eslint --fix 'docs/**/@(demo|theme)/*.@(md)'", - "prettierjs": "prettier --write '@(src|test|scripts)/**/*.@(js|jsx)'", - "stylelint": "stylelint --fix 'src/**/*.@(css|scss)'", - "commitmsg": "commitlint -E GIT_PARAMS", - "precommit": "lint-staged", - "changelog": "node ./scripts/changelog.js", - "prepub": "npm run eslint && npm run stylelint && npm run changelog && npm run build && npm run check && npm run docs && npm run pack && npm run pack -- minimize" - }, - "lint-staged": { - "@(src|scripts|docs/*/demo)/**/*.@(js|jsx)": [ - "prettier --write" - ], - "@(src|scripts|docs/*/demo)/**/*.@(js|jsx|md)": [ - "eslint", - "git add" - ], - "src/**/*.@(css|scss)": [ - "stylelint", - "git add" - ] - }, - "dependencies": { - "babel-runtime": "^6.26.0", - "classnames": "^2.2.3", - "hoist-non-react-statics": "^2.1.0", - "prop-types": "^15.6.0", - "react-transition-group": "^2.2.1", - "shallow-element-equals": "^1.0.1" - }, - "devDependencies": { - "@alifd/api-extractor": "^3.4.0", - "@alifd/babel-preset-next": "^2.0.0", - "@alifd/doc-parser": "^1.0.0", - "@alifd/dts-generator": "^1.0.3", - "@alifd/eslint-config-next": "^2.0.0", - "@alifd/stylelint-config-next": "^1.0.0", - "@commitlint/cli": "^7.0.0", - "@no-repeat/sass-mapper": "^0.0.6", - "@no-repeat/sass-tracker": "^0.0.10", - "@no-repeat/sassdoc-parser": "^0.0.11", - "@octokit/rest": "^15.15.1", - "autoprefixer": "^7.1.4", - "axe-core": "3.1.2", - "babel-core": "^6.26.0", - "babel-eslint": "^8.2.3", - "babel-loader": "^7.0.0", - "babel-plugin-espower": "^2.3.2", - "babel-plugin-istanbul": "^4.1.5", - "babel-polyfill": "^6.26.0", - "case-sensitive-paths-webpack-plugin": "^2.1.1", - "chalk": "^2.3.2", - "cheerio": "^1.0.0-rc.2", - "chokidar": "^2.0.4", - "chrome-launcher": "^0.10.2", - "co": "^4.6.0", - "console-polyfill": "^0.3.0", - "conventional-changelog": "^2.0.1", - "css-loader": "^0.28.7", - "css-split-webpack-plugin": "^0.2.5", - "ejs": "^2.5.9", - "enzyme": "^3.3.0", - "enzyme-adapter-react-16": "^1.1.1", - "es5-shim": "^4.5.9", - "es6-promise-polyfill": "^1.2.0", - "eslint": "^4.19.1", - "eslint-config-prettier": "^4.0.0", - "eslint-plugin-html": "^5.0.0", - "eslint-plugin-import": "^2.12.0", - "eslint-plugin-markdown": "^1.0.0-beta.6", - "eslint-plugin-react": "7.11.1", - "expect.js": "^0.3.1", - "extract-text-webpack-plugin": "^3.0.0", - "fast-sass-loader": "^1.2.5", - "fs-extra": "^5.0.0", - "glob": "^7.1.2", - "highlight.js": "^9.12.0", - "html5shiv": "^3.7.3", - "husky": "^0.14.3", - "inquirer": "^6.1.0", - "jsonp": "^0.2.1", - "karma": "^3.0.0", - "karma-chrome-launcher": "^2.2.0", - "karma-coverage": "^1.1.1", - "karma-mocha": "^1.3.0", - "karma-sourcemap-loader": "^0.3.7", - "karma-spec-reporter": "^0.0.26", - "karma-webdriver-launcher": "^1.0.5", - "karma-webpack": "^3.0.5", - "lint-staged": "^7.2.2", - "loader-utils": "^1.1.0", - "lodash": "^4.17.5", - "markdown-it": "^8.4.2", - "md5": "^2.2.1", - "minimist": "^1.2.0", - "mocha": "^3.5.3", - "moment": "^2.20.1", - "node-sass": "4.6.0", - "node-sass-package-importer": "^5.2.0", - "postcss-loader": "^2.0.6", - "postcss-scss": "1.0.2", - "power-assert": "^1.5.0", - "prettier": "1.16.4", - "promise-polyfill": "^8.1.0", - "react": "^16.0.0", - "react-axe": "^3.0.2", - "react-copy-to-clipboard": "^5.0.1", - "react-cropper": "^1.0.0", - "react-dev-utils": "^4.2.1", - "react-dom": "^16.0.0", - "react-redux": "^5.0.7", - "react-router": "^4.3.1", - "redux": "^4.0.0", - "remark": "^9.0.0", - "sass": "^1.15.2", - "sassaby-next": "^3.0.0", - "simulate-event": "^1.4.0", - "sinon": "^5.0.10", - "style-loader": "^0.18.2", - "stylelint": "^9.2.1", - "stylelint-scss": "^3.1.1", - "typescript": "^3.0.1", - "webpack": "^3.0.0", - "webpack-dev-server": "^2.x", - "whatwg-fetch": "^2.0.3" - }, - "peerDependencies": { - "moment": "^2.22.1", - "react": "^16.0.0", - "react-dom": "^16.0.0" - }, - "publishConfig": { - "access": "public" - } + "src/**/*.@(css|scss)": [ + "stylelint", + "git add" + ] + }, + "dependencies": { + "babel-runtime": "^6.26.0", + "classnames": "^2.2.3", + "hoist-non-react-statics": "^2.1.0", + "prop-types": "^15.6.0", + "react-transition-group": "^2.2.1", + "shallow-element-equals": "^1.0.1" + }, + "devDependencies": { + "@alifd/api-extractor": "^3.4.0", + "@alifd/babel-preset-next": "^2.0.0", + "@alifd/doc-parser": "^1.0.0", + "@alifd/dts-generator": "^1.0.3", + "@alifd/eslint-config-next": "^2.0.0", + "@alifd/stylelint-config-next": "^1.0.0", + "@commitlint/cli": "^7.0.0", + "@no-repeat/sass-mapper": "^0.0.6", + "@no-repeat/sass-tracker": "^0.0.10", + "@no-repeat/sassdoc-parser": "^0.0.11", + "@octokit/rest": "^15.15.1", + "autoprefixer": "^7.1.4", + "axe-core": "^3.2.0", + "babel-core": "^6.26.0", + "babel-eslint": "^8.2.3", + "babel-loader": "^7.0.0", + "babel-plugin-espower": "^2.3.2", + "babel-plugin-istanbul": "^4.1.5", + "babel-polyfill": "^6.26.0", + "case-sensitive-paths-webpack-plugin": "^2.1.1", + "chalk": "^2.3.2", + "cheerio": "^1.0.0-rc.2", + "chokidar": "^2.0.4", + "chrome-launcher": "^0.10.2", + "co": "^4.6.0", + "console-polyfill": "^0.3.0", + "conventional-changelog": "^2.0.1", + "css-loader": "^0.28.7", + "css-split-webpack-plugin": "^0.2.5", + "ejs": "^2.5.9", + "enzyme": "^3.3.0", + "enzyme-adapter-react-16": "~1.10.0", + "es5-shim": "^4.5.9", + "es6-promise-polyfill": "^1.2.0", + "eslint": "^4.19.1", + "eslint-config-prettier": "^4.0.0", + "eslint-plugin-html": "^5.0.0", + "eslint-plugin-import": "^2.12.0", + "eslint-plugin-markdown": "^1.0.0-beta.6", + "eslint-plugin-react": "7.11.1", + "expect.js": "^0.3.1", + "extract-text-webpack-plugin": "^3.0.0", + "fast-sass-loader": "^1.2.5", + "fs-extra": "^5.0.0", + "glob": "^7.1.2", + "highlight.js": "^9.12.0", + "html5shiv": "^3.7.3", + "husky": "^0.14.3", + "inquirer": "^6.1.0", + "jsonp": "^0.2.1", + "karma": "^3.0.0", + "karma-chrome-launcher": "^2.2.0", + "karma-coverage": "^1.1.1", + "karma-mocha": "^1.3.0", + "karma-sourcemap-loader": "^0.3.7", + "karma-spec-reporter": "^0.0.26", + "karma-webdriver-launcher": "^1.0.5", + "karma-webpack": "^3.0.5", + "lint-staged": "^7.2.2", + "loader-utils": "^1.1.0", + "lodash": "^4.17.5", + "markdown-it": "^8.4.2", + "md5": "^2.2.1", + "minimist": "^1.2.0", + "mocha": "^3.5.3", + "moment": "^2.20.1", + "node-sass": "4.6.0", + "node-sass-package-importer": "^5.2.0", + "postcss-loader": "^2.0.6", + "postcss-scss": "1.0.2", + "power-assert": "^1.5.0", + "prettier": "1.16.4", + "promise-polyfill": "^8.1.0", + "react": "^16.0.0", + "react-axe": "^3.0.2", + "react-copy-to-clipboard": "^5.0.1", + "react-cropper": "^1.0.0", + "react-dev-utils": "^4.2.1", + "react-dom": "^16.0.0", + "react-redux": "^5.0.7", + "react-router": "^4.3.1", + "redux": "^4.0.0", + "remark": "^9.0.0", + "sass": "^1.15.2", + "sassaby-next": "^3.0.0", + "simulate-event": "^1.4.0", + "sinon": "^5.0.10", + "style-loader": "^0.18.2", + "stylelint": "^9.2.1", + "stylelint-scss": "^3.1.1", + "typescript": "^3.0.1", + "webpack": "^3.0.0", + "webpack-dev-server": "^2.x", + "whatwg-fetch": "^2.0.3" + }, + "peerDependencies": { + "moment": "^2.22.1", + "react": "^16.0.0", + "react-dom": "^16.0.0" + }, + "publishConfig": { + "access": "public" + } } diff --git a/scripts/test/a11y-allinone.js b/scripts/test/a11y-allinone.js new file mode 100644 index 0000000000..f6630e232a --- /dev/null +++ b/scripts/test/a11y-allinone.js @@ -0,0 +1,2 @@ +const testsContext = require.context('../../test/', true, /a11y-spec.js$/); +testsContext.keys().forEach(testsContext); diff --git a/scripts/test/a11y-index.js b/scripts/test/a11y-index.js new file mode 100644 index 0000000000..53a2e40b3c --- /dev/null +++ b/scripts/test/a11y-index.js @@ -0,0 +1,47 @@ +// const fs = require('fs-extra'); +const { join } = require('path'); +const { Server } = require('karma'); +const co = require('co'); +const inquirer = require('inquirer'); +const { logger } = require('../utils'); + +let server = null; + +const config = { + configFile: join(__dirname, 'karma.js'), + component: 'all', + runAll: true, + a11y: true, +}; + +const runAllTestA11y = () => { + config.runAll = true; + config.a11y = true; + server = new Server(config); + server.start(); +}; + +co(function*() { + if (process.env.TRAVIS) { + runAllTestA11y(); + } else { + const allTest = yield inquirer.prompt([ + { + name: 'runAllTest', + type: 'list', + choices: ['yes', 'no'], + default: 1, + message: logger.success( + 'This will run ALL components test cases, are you sure to run all?' + ), + }, + ]); + + if (allTest.runAllTest === 'no') { + logger.success('quit'); + return; + } else { + runAllTestA11y(); + } + } +}); diff --git a/scripts/test/allinone.js b/scripts/test/allinone.js index d80b40742c..de21a8a722 100644 --- a/scripts/test/allinone.js +++ b/scripts/test/allinone.js @@ -1,2 +1,6 @@ -const testsContext = require.context('../../test/', true, /-spec.js$/); +const testsContext = require.context( + '../../test/', + true, + /^.*(? { @@ -63,11 +64,12 @@ const runRest = components => { } }; -const runAllTest = () => { +const runAllTest = (a11y = false) => { // const components = fs.readdirSync(join(process.cwd(), 'test')); // runRest(components); config.runAll = true; + config.a11y = a11y; server = new Server(config); server.start(); }; diff --git a/scripts/test/karma.js b/scripts/test/karma.js index 538573f81e..e905f9d540 100644 --- a/scripts/test/karma.js +++ b/scripts/test/karma.js @@ -3,7 +3,7 @@ const _ = require('lodash'); const getWebpackConfig = require('./webpack'); module.exports = function(config) { - const { runAll } = config; + const { runAll, a11y } = config; const componentName = config.component ? _.kebabCase(config.component) : config.component; @@ -13,9 +13,15 @@ module.exports = function(config) { // 'table' or 'table|tree|tree-select' // const componentList = componentArray ? componentArray.join('|') : componentName; - const specPath = runAll - ? resolveCwd('scripts/test/allinone.js') - : resolveCwd('test', `@(${componentName})/*-spec.js`); + let specPath; + + if (runAll && a11y) { + specPath = resolveCwd('scripts/test/a11y-allinone.js'); + } else if (runAll) { + specPath = resolveCwd('scripts/test/allinone.js'); + } else { + specPath = resolveCwd('test', `@(${componentName})/*-spec.js`); + } const options = { frameworks: ['mocha'], diff --git a/src/calendar/calendar.jsx b/src/calendar/calendar.jsx index acdce2dc75..2d2ef56a0e 100644 --- a/src/calendar/calendar.jsx +++ b/src/calendar/calendar.jsx @@ -83,6 +83,7 @@ class Calendar extends Component { /** * 不可选择的日期 * @param {Object} calendarDate 对应 Calendar 返回的自定义日期对象 + * @param {String} view 当前视图类型,year: 年, month: 月, date: 日 * @returns {Boolean} */ disabledDate: PropTypes.func, diff --git a/src/calendar/range-calendar.jsx b/src/calendar/range-calendar.jsx index e2ef1d7e07..0e7bd8f9a2 100644 --- a/src/calendar/range-calendar.jsx +++ b/src/calendar/range-calendar.jsx @@ -66,6 +66,7 @@ class RangeCalendar extends React.Component { /** * 不可选择的日期 * @param {Object} calendarDate 对应 Calendar 返回的自定义日期对象 + * @param {String} view 当前视图类型,year: 年, month: 月, date: 日 * @returns {Boolean} */ disabledDate: PropTypes.func, diff --git a/src/calendar/table/date-table.jsx b/src/calendar/table/date-table.jsx index f1b993a3d8..e10290fec2 100644 --- a/src/calendar/table/date-table.jsx +++ b/src/calendar/table/date-table.jsx @@ -88,7 +88,11 @@ class DateTable extends PureComponent { const isNextMonth = isNextMonthDate(currentDate, visibleMonth); const isCurrentMonth = !isLastMonth && !isNextMonth; - const isDisabled = isDisabledDate(currentDate, disabledDate); + const isDisabled = isDisabledDate( + currentDate, + disabledDate, + 'date' + ); const isToday = !isDisabled && isSameDay(currentDate, today) && diff --git a/src/calendar/table/month-table.jsx b/src/calendar/table/month-table.jsx index 7cf3cd9d9b..2f7d1c208b 100644 --- a/src/calendar/table/month-table.jsx +++ b/src/calendar/table/month-table.jsx @@ -38,7 +38,11 @@ class MonthTable extends PureComponent { const rowList = []; for (let j = 0; j < MONTH_TABLE_COL_COUNT; j++) { const monthDate = visibleMonth.clone().month(counter); - const isDisabled = isDisabledDate(monthDate, disabledDate); + const isDisabled = isDisabledDate( + monthDate, + disabledDate, + 'month' + ); const isSelected = isSameMonth(monthDate, value); const isThisMonth = isSameMonth(monthDate, today); const elementCls = classnames({ diff --git a/src/calendar/table/year-table.jsx b/src/calendar/table/year-table.jsx index 39bd911f11..77070343f3 100644 --- a/src/calendar/table/year-table.jsx +++ b/src/calendar/table/year-table.jsx @@ -56,7 +56,7 @@ class YearTable extends React.PureComponent { content = year; title = year; const yearDate = visibleMonth.clone().year(year); - isDisabled = isDisabledDate(yearDate, disabledDate); + isDisabled = isDisabledDate(yearDate, disabledDate, 'year'); !isDisabled && (onClick = this.onYearCellClick.bind(this, yearDate)); diff --git a/src/calendar/utils/index.js b/src/calendar/utils/index.js index 16a0018c83..3b834b162d 100644 --- a/src/calendar/utils/index.js +++ b/src/calendar/utils/index.js @@ -26,8 +26,8 @@ export const CALENDAR_MODES = [ CALENDAR_MODE_YEAR, ]; -export function isDisabledDate(date, fn) { - if (typeof fn === 'function' && fn(date)) { +export function isDisabledDate(date, fn, view) { + if (typeof fn === 'function' && fn(date, view)) { return true; } return false; diff --git a/src/cascader-select/cascader-select.jsx b/src/cascader-select/cascader-select.jsx index b336bca338..2003ec5a35 100644 --- a/src/cascader-select/cascader-select.jsx +++ b/src/cascader-select/cascader-select.jsx @@ -392,9 +392,13 @@ export default class CascaderSelect extends Component { return null; } + if (Array.isArray(value)) value = value[0]; + const data = this._v2n[value]; if (!data) { - return null; + return { + value, + }; } const labelPath = this.getLabelPath(data); @@ -408,12 +412,21 @@ export default class CascaderSelect extends Component { } getMultipleData(value) { + if (!value.length) { + return null; + } + const { checkStrictly, canOnlyCheckLeaf, displayRender } = this.props; - let data = this.getData( - checkStrictly || canOnlyCheckLeaf ? value : this.flatValue(value) - ); + let data = (checkStrictly || canOnlyCheckLeaf + ? value + : this.flatValue(value) + ).map(v => this._v2n[v] || { value: v }); + if (displayRender) { data = data.map(item => { + if (!item.pos) { + return item; + } const labelPath = this.getLabelPath(item); return { diff --git a/src/collapse/main.scss b/src/collapse/main.scss index 8708c88e57..8d9bbe5ccf 100644 --- a/src/collapse/main.scss +++ b/src/collapse/main.scss @@ -11,6 +11,11 @@ border: $collapse-border-width solid $collapse-border-color; border-radius: $collapse-border-corner; overflow: hidden; + &:focus, + & *:focus { + outline: 0; + } + &-panel { &:not(:first-child) { border-top: $collapse-title-border-width solid $collapse-panel-border-color; diff --git a/src/config-provider/config.jsx b/src/config-provider/config.jsx index 8075b92ddf..efdedd8446 100644 --- a/src/config-provider/config.jsx +++ b/src/config-provider/config.jsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import hoistNonReactStatic from 'hoist-non-react-statics'; import { obj, log } from '../util'; import getContextProps from './get-context-props'; +import ErrorBoundary from './error-boundary'; const { shallowEqual } = obj; @@ -89,6 +90,10 @@ export function config(Component, options = {}) { locale: PropTypes.object, pure: PropTypes.bool, rtl: PropTypes.bool, + errorBoundary: PropTypes.oneOfType([ + PropTypes.bool, + PropTypes.object, + ]), }; static contextTypes = { ...(Component.contextTypes || {}), @@ -97,6 +102,10 @@ export function config(Component, options = {}) { nextPure: PropTypes.bool, nextRtl: PropTypes.bool, nextWarning: PropTypes.bool, + nextErrorBoundary: PropTypes.oneOfType([ + PropTypes.bool, + PropTypes.object, + ]), }; constructor(props, context) { @@ -132,18 +141,26 @@ export function config(Component, options = {}) { } render() { - const { prefix, locale, pure, rtl, ...others } = this.props; + const { + prefix, + locale, + pure, + rtl, + errorBoundary, + ...others + } = this.props; const { nextPrefix, nextLocale = {}, nextPure, nextRtl, + nextErrorBoundary, } = this.context; const displayName = options.componentName || getDisplayName(Component); const contextProps = getContextProps( - { prefix, locale, pure, rtl }, + { prefix, locale, pure, rtl, errorBoundary }, { nextPrefix, nextLocale: { ...currentGlobalLocale, ...nextLocale }, @@ -154,10 +171,12 @@ export function config(Component, options = {}) { : currentGlobalRtl === true ? true : undefined, + nextErrorBoundary, }, displayName ); + // errorBoundary is only for const newContextProps = ['prefix', 'locale', 'pure', 'rtl'].reduce( (ret, name) => { if (typeof contextProps[name] !== 'undefined') { @@ -172,13 +191,21 @@ export function config(Component, options = {}) { ? options.transform(others, this._deprecated) : others; - return ( + const content = ( ); + + const { open, ...othersBoundary } = contextProps.errorBoundary; + + return open ? ( + {content} + ) : ( + content + ); } } diff --git a/src/config-provider/error-boundary.jsx b/src/config-provider/error-boundary.jsx new file mode 100644 index 0000000000..27041de87f --- /dev/null +++ b/src/config-provider/error-boundary.jsx @@ -0,0 +1,63 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +DefaultUI.propTypes = { + error: PropTypes.object, + errorInfo: PropTypes.object, +}; + +function DefaultUI() { + return ''; +} + +export default class ErrorBoundary extends React.Component { + static propTypes = { + children: PropTypes.element, + /** + * 捕获错误后的自定义处理, 比如埋点上传 + * @param {Object} error 错误 + * @param {Object} errorInfo 错误详细信息 + */ + afterCatch: PropTypes.func, + /** + * 捕获错误后的展现 自定义组件 + * @param {Object} error 错误 + * @param {Object} errorInfo 错误详细信息 + * @returns {Element} 捕获错误后的处理 + */ + fallbackUI: PropTypes.func, + }; + + constructor(props) { + super(props); + this.state = { error: null, errorInfo: null }; + } + + componentDidCatch(error, errorInfo) { + this.setState({ + error: error, + errorInfo: errorInfo, + }); + + const { afterCatch } = this.props; + + if ('afterCatch' in this.props && typeof afterCatch === 'function') { + this.props.afterCatch(error, errorInfo); + } + } + + render() { + const { fallbackUI: FallbackUI = DefaultUI } = this.props; + + if (this.state.errorInfo) { + return ( + + ); + } + // Normally, just render children + return this.props.children; + } +} diff --git a/src/config-provider/get-context-props.jsx b/src/config-provider/get-context-props.jsx index 8438162d26..e2382ef99d 100644 --- a/src/config-provider/get-context-props.jsx +++ b/src/config-provider/get-context-props.jsx @@ -1,6 +1,31 @@ +/** + * + * @param {Object|Boolean} input + * @returns {Object} typeof obj.open === 'boolean' + */ +const parseBoundary = input => { + let obj; + if (input === undefined || input === null) { + return {}; + } else if (typeof input === 'boolean') { + obj = { open: input }; + } else { + obj = { open: true, ...input }; + } + + return obj; +}; + export default function getContextProps(props, context, displayName) { - const { prefix, locale, pure, rtl } = props; - const { nextPrefix, nextLocale, nextPure, nextWarning, nextRtl } = context; + const { prefix, locale, pure, rtl, errorBoundary } = props; + const { + nextPrefix, + nextLocale, + nextPure, + nextWarning, + nextRtl, + nextErrorBoundary, + } = context; const newPrefix = prefix || nextPrefix; @@ -21,11 +46,24 @@ export default function getContextProps(props, context, displayName) { const newPure = typeof pure === 'boolean' ? pure : nextPure; const newRtl = typeof rtl === 'boolean' ? rtl : nextRtl; + // ProtoType of [nextE|e]rrorBoundary can be one of [boolean, object] + // but typeof newErrorBoundary === 'object' + // newErrorBoundary should always have the key 'open', which indicates ErrorBoundary on or off + const newErrorBoundary = { + ...parseBoundary(nextErrorBoundary), + ...parseBoundary(errorBoundary), + }; + + if (!('open' in newErrorBoundary)) { + newErrorBoundary.open = false; + } + return { prefix: newPrefix, locale: newLocale, pure: newPure, rtl: newRtl, warning: nextWarning, + errorBoundary: newErrorBoundary, }; } diff --git a/src/config-provider/index.jsx b/src/config-provider/index.jsx index 45db6ee964..a150d092cd 100644 --- a/src/config-provider/index.jsx +++ b/src/config-provider/index.jsx @@ -12,6 +12,7 @@ import { getDirection, } from './config'; import Consumer from './consumer'; +import ErrorBoundary from './error-boundary'; import Cache from './cache'; const childContextCache = new Cache(); @@ -30,6 +31,14 @@ class ConfigProvider extends Component { * 国际化文案对象,属性为组件的 displayName */ locale: PropTypes.object, + /** + * 是否开启错误捕捉 errorBoundary + * 如需自定义参数,请传入对象 对象接受参数列表如下: + * + * fallbackUI `Function(error?: {}, errorInfo?: {}) => Element` 捕获错误后的展示 + * afterCatch `Function(error?: {}, errorInfo?: {})` 捕获错误后的行为, 比如埋点上传 + */ + errorBoundary: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]), /** * 是否开启 Pure Render 模式,会提高性能,但是也会带来副作用 */ @@ -50,6 +59,7 @@ class ConfigProvider extends Component { static defaultProps = { warning: true, + errorBoundary: false, }; static childContextTypes = { @@ -58,6 +68,10 @@ class ConfigProvider extends Component { nextPure: PropTypes.bool, nextRtl: PropTypes.bool, nextWarning: PropTypes.bool, + nextErrorBoundary: PropTypes.oneOfType([ + PropTypes.bool, + PropTypes.object, + ]), }; /** @@ -92,10 +106,17 @@ class ConfigProvider extends Component { static getLocale = getLocale; static getDirection = getDirection; static Consumer = Consumer; + static ErrorBoundary = ErrorBoundary; static getContext = () => { - const { nextPrefix, nextLocale, nextPure, nextRtl, nextWarning } = - childContextCache.root() || {}; + const { + nextPrefix, + nextLocale, + nextPure, + nextRtl, + nextWarning, + nextErrorBoundary, + } = childContextCache.root() || {}; return { prefix: nextPrefix, @@ -103,6 +124,7 @@ class ConfigProvider extends Component { pure: nextPure, rtl: nextRtl, warning: nextWarning, + errorBoundary: nextErrorBoundary, }; }; @@ -119,7 +141,14 @@ class ConfigProvider extends Component { } getChildContext() { - const { prefix, locale, pure, warning, rtl } = this.props; + const { + prefix, + locale, + pure, + warning, + rtl, + errorBoundary, + } = this.props; return { nextPrefix: prefix, @@ -127,6 +156,7 @@ class ConfigProvider extends Component { nextPure: pure, nextRtl: rtl, nextWarning: warning, + nextErrorBoundary: errorBoundary, }; } diff --git a/src/date-picker/date-picker.jsx b/src/date-picker/date-picker.jsx index c05550016f..9297db900d 100644 --- a/src/date-picker/date-picker.jsx +++ b/src/date-picker/date-picker.jsx @@ -68,6 +68,7 @@ export default class DatePicker extends Component { /** * 禁用日期函数 * @param {MomentObject} 日期值 + * @param {String} view 当前视图类型,year: 年, month: 月, date: 日 * @return {Boolean} 是否禁用 */ disabledDate: PropTypes.func, @@ -300,7 +301,7 @@ export default class DatePicker extends Component { inputing: false, }); - if (parsed.isValid() && !disabledDate(parsed)) { + if (parsed.isValid() && !disabledDate(parsed, 'date')) { this.handleChange(parsed, this.state.value); } } diff --git a/src/date-picker/month-picker.jsx b/src/date-picker/month-picker.jsx index b441b3d235..a6339e7e2f 100644 --- a/src/date-picker/month-picker.jsx +++ b/src/date-picker/month-picker.jsx @@ -50,6 +50,7 @@ class MonthPicker extends Component { /** * 禁用日期函数 * @param {MomentObject} 日期值 + * @param {String} view 当前视图类型,year: 年, month: 月, date: 日 * @return {Boolean} 是否禁用 */ disabledDate: PropTypes.func, @@ -240,7 +241,7 @@ class MonthPicker extends Component { inputing: false, }); - if (parsed.isValid() && !disabledDate(parsed)) { + if (parsed.isValid() && !disabledDate(parsed, 'month')) { this.handleChange(parsed, this.state.value); } } diff --git a/src/date-picker/range-picker.jsx b/src/date-picker/range-picker.jsx index 4f1e48a40c..3513e946dd 100644 --- a/src/date-picker/range-picker.jsx +++ b/src/date-picker/range-picker.jsx @@ -82,6 +82,7 @@ export default class RangePicker extends Component { /** * 禁用日期函数 * @param {MomentObject} 日期值 + * @param {String} view 当前视图类型,year: 年, month: 月, date: 日 * @return {Boolean} 是否禁用 */ disabledDate: PropTypes.func, @@ -397,7 +398,7 @@ export default class RangePicker extends Component { inputing: false, }); - if (parsed.isValid() && !disabledDate(parsed)) { + if (parsed.isValid() && !disabledDate(parsed, 'date')) { const valueName = this.state.activeDateInput; const newValue = parsed; diff --git a/src/date-picker/year-picker.jsx b/src/date-picker/year-picker.jsx index 31c80ca21d..1ec41fc2e3 100644 --- a/src/date-picker/year-picker.jsx +++ b/src/date-picker/year-picker.jsx @@ -45,6 +45,7 @@ class YearPicker extends Component { /** * 禁用日期函数 * @param {MomentObject} 日期值 + * @param {String} view 当前视图类型,year: 年, month: 月, date: 日 * @return {Boolean} 是否禁用 */ disabledDate: PropTypes.func, @@ -230,7 +231,7 @@ class YearPicker extends Component { inputing: false, }); - if (parsed.isValid() && !disabledDate(parsed)) { + if (parsed.isValid() && !disabledDate(parsed, 'year')) { this.handleChange(parsed, this.state.value); } } diff --git a/src/locale/en-us.js b/src/locale/en-us.js index 7f2aa47a69..c0daaa8935 100644 --- a/src/locale/en-us.js +++ b/src/locale/en-us.js @@ -116,4 +116,7 @@ export default { Tag: { delete: 'Delete', }, + Rating: { + description: 'Rating Options', + }, }; diff --git a/src/locale/ja-jp.js b/src/locale/ja-jp.js index f7a4ce84ff..cf945133d3 100644 --- a/src/locale/ja-jp.js +++ b/src/locale/ja-jp.js @@ -114,4 +114,7 @@ export default { Tag: { delete: 'デリート', }, + Rating: { + description: '評価オプション', + }, }; diff --git a/src/locale/zh-cn.js b/src/locale/zh-cn.js index dcdc7bedcb..7b2b2342d1 100644 --- a/src/locale/zh-cn.js +++ b/src/locale/zh-cn.js @@ -114,4 +114,7 @@ export default { Tag: { delete: '删除', }, + Rating: { + description: '评分选项', + }, }; diff --git a/src/locale/zh-tw.js b/src/locale/zh-tw.js index 1f0afd64ed..1cce051afa 100644 --- a/src/locale/zh-tw.js +++ b/src/locale/zh-tw.js @@ -114,4 +114,7 @@ export default { Tag: { delete: '删除', }, + Rating: { + description: '評分選項', + }, }; diff --git a/src/overlay/utils/position.js b/src/overlay/utils/position.js index 8eacb0e6b0..67eead3014 100644 --- a/src/overlay/utils/position.js +++ b/src/overlay/utils/position.js @@ -150,7 +150,7 @@ export default class Position { ); this._setPinElementPostion( pinElement, - { left: inViewportLeft, right: inViewportTop }, + { left: inViewportLeft, top: inViewportTop }, this.offset ); return expectedAlign[0]; diff --git a/src/range/main.scss b/src/range/main.scss index 9960a57daf..37eb97fe3c 100644 --- a/src/range/main.scss +++ b/src/range/main.scss @@ -54,4 +54,18 @@ ); } } + + &range[dir=rtl] { + .#{$css-prefix}range-mark { + position: relative; + cursor: auto; + .#{$css-prefix}range-mark-text { + position: absolute; + right: 0; + transform: translateX(50%); + padding-right: 2px; + text-align: center; + } + } + } } diff --git a/src/range/view/fixedSlider.jsx b/src/range/view/fixedSlider.jsx index bd358e91c2..cab75a3e18 100644 --- a/src/range/view/fixedSlider.jsx +++ b/src/range/view/fixedSlider.jsx @@ -7,7 +7,13 @@ import { getPercent } from '../utils'; const Tooltip = Balloon.Tooltip; const { noop } = func; -function _getStyle(min, max, value) { +function _getStyle(min, max, value, rtl) { + if (rtl) { + return { + left: `${getPercent(min, max, max + min - value[1])}%`, + right: `${getPercent(min, max, value[0])}%`, + }; + } return { left: `${getPercent(min, max, value[0])}%`, right: `${100 - getPercent(min, max, value[1])}%`, @@ -24,6 +30,7 @@ function sliderFrag(props) { onMouseEnter, onMouseLeave, onMouseDown, + rtl, } = props; const activeClass = @@ -32,7 +39,7 @@ function sliderFrag(props) { return (
value, reverse: false, + rtl: false, }; constructor(props) { diff --git a/src/range/view/mark.jsx b/src/range/view/mark.jsx index efb8fb140c..5d8174ddf0 100644 --- a/src/range/view/mark.jsx +++ b/src/range/view/mark.jsx @@ -14,6 +14,7 @@ export default class Mark extends React.Component { prefix: PropTypes.string, marks: PropTypes.object, marksPosition: PropTypes.string, + rtl: PropTypes.bool, }; static defaultProps = { @@ -22,10 +23,11 @@ export default class Mark extends React.Component { max: 100, value: 0, marksPosition: '', + rtl: false, }; _renderItems() { - const { min, max, value, prefix, marks } = this.props; + const { min, max, value, prefix, marks, rtl } = this.props; const items = []; Object.keys(marks).forEach((mark, i) => { @@ -33,11 +35,22 @@ export default class Mark extends React.Component { [`${prefix}range-mark-text`]: true, activated: inRange(mark, value, min), }); - const left = `${getPercent(min, max, mark)}%`; + let style; + if (rtl) { + style = { + right: `${getPercent(min, max, mark)}%`, + left: 'auto', + }; + } else { + style = { + left: `${getPercent(min, max, mark)}%`, + right: 'auto', + }; + } items.push( // "key" is for https://fb.me/react-warning-keys - + {marks[mark]} ); diff --git a/src/range/view/range.jsx b/src/range/view/range.jsx index 5095d2e9ba..bfb5354618 100644 --- a/src/range/view/range.jsx +++ b/src/range/view/range.jsx @@ -243,6 +243,10 @@ export default class Range extends React.Component { * tooltip是否默认展示 */ tooltipVisible: PropTypes.bool, + /** + * 是否已rtl模式展示 + */ + rtl: PropTypes.bool, }; static defaultProps = { @@ -262,6 +266,7 @@ export default class Range extends React.Component { reverse: false, pure: false, marksPosition: 'above', + rtl: false, }; constructor(props) { @@ -553,15 +558,15 @@ export default class Range extends React.Component { // position => current (value type) _positionToCurrent(position) { const { start, end } = this._moving; - const { step, min, max } = this.props; + const { step, min, max, rtl } = this.props; if (position < start) { position = start; } else if (position > end) { position = end; } - const percent = getPercent(start, end, position); - + let percent = getPercent(start, end, position); + percent = rtl ? 100 - percent : percent; // reset by step const newValue = parseFloat( (Math.round(((percent / 100) * (max - min)) / step) * step).toFixed( @@ -574,12 +579,12 @@ export default class Range extends React.Component { _currentToValue(current, preValue, lastPosition, isFixedWidth) { const { dragging } = this._moving; + const { min, max } = this.props; if (!_isMultiple(this.props.slider, isFixedWidth)) { return current; } else { let result; - const { min, max } = this.props; const precision = getPrecision(this.props.step); const diff = current - lastPosition; @@ -659,6 +664,7 @@ export default class Range extends React.Component { fixedWidth, defaultValue, tooltipVisible, + rtl, } = this.props; const classes = classNames({ [`${prefix}range`]: true, @@ -689,6 +695,7 @@ export default class Range extends React.Component { tooltipVisible, hasMovingClass: this.state.hasMovingClass, disabled, + rtl, }; this.isFixedWidth = @@ -707,6 +714,7 @@ export default class Range extends React.Component { style={style} className={classes} id={id} + dir={rtl ? 'rtl' : 'ltr'} onMouseDown={disabled ? noop : this._onMouseDown.bind(this)} > {marks !== false && marksPosition === 'above' ? ( diff --git a/src/range/view/scale.jsx b/src/range/view/scale.jsx index bfd0519443..002ee2b629 100644 --- a/src/range/view/scale.jsx +++ b/src/range/view/scale.jsx @@ -13,6 +13,7 @@ export default class Scale extends React.Component { ]), prefix: PropTypes.string, scales: PropTypes.arrayOf(PropTypes.number), + rtl: PropTypes.bool, }; static defaultProps = { @@ -20,10 +21,11 @@ export default class Scale extends React.Component { min: 0, max: 100, value: 0, + rtl: false, }; _renderItems() { - const { min, max, value, prefix, scales } = this.props; + const { min, max, value, prefix, scales, rtl } = this.props; const items = []; scales.forEach((scale, i) => { @@ -31,11 +33,22 @@ export default class Scale extends React.Component { [`${prefix}range-scale-item`]: true, activated: inRange(scale, value, min), }); - const left = `${getPercent(min, max, scale)}%`; + let style; + if (rtl) { + style = { + right: `${getPercent(min, max, scale)}%`, + left: 'auto', + }; + } else { + style = { + left: `${getPercent(min, max, scale)}%`, + right: 'auto', + }; + } items.push( // "key" is for https://fb.me/react-warning-keys - + ); }); diff --git a/src/range/view/selected.jsx b/src/range/view/selected.jsx index 82f3843ae5..65c1bcb99e 100644 --- a/src/range/view/selected.jsx +++ b/src/range/view/selected.jsx @@ -14,6 +14,7 @@ export default class Selected extends React.Component { ]), prefix: PropTypes.string, reverse: PropTypes.bool, + rtl: PropTypes.bool, }; static defaultProps = { @@ -23,10 +24,11 @@ export default class Selected extends React.Component { max: 100, value: 0, reverse: false, + rtl: false, }; _getStyle() { - const { min, max, reverse } = this.props; + const { min, max, reverse, rtl } = this.props; let { value } = this.props; if (!Array.isArray(value)) { @@ -34,21 +36,39 @@ export default class Selected extends React.Component { } const width = ((value[1] - value[0]) * 100) / (max - min); - let style = { - width: `${width}%`, - left: `${getPercent(min, max, value[0])}%`, - }; - if (reverse) { + let style; + + if (!rtl && !reverse) { + // normal select + style = { + width: `${width}%`, + left: `${getPercent(min, max, value[0])}%`, + }; + } else if (rtl && !reverse) { + // select in rtl mode + style = { + width: `${width}%`, + left: `${getPercent(min, max, max + min - value[1])}%`, + }; + } else if (!rtl && reverse) { + // select in reverse mode style = { width: `${100 - width}%`, left: `${getPercent(min, max, value[0]) + width}%`, }; + } else { + // select in rtl & reverse mode + style = { + width: `${100 - width}%`, + left: `${getPercent(min, max, value[0])}%`, + }; } + return style; } _getStyleLeft() { - const { min, max } = this.props; + const { min, max, rtl } = this.props; let { value } = this.props; if (!Array.isArray(value)) { @@ -59,11 +79,14 @@ export default class Selected extends React.Component { width: `${getPercent(min, max, value[0])}%`, left: 0, }; + if (rtl) { + style.width = `${100 - getPercent(min, max, value[1])}%`; + } return style; } _getStyleRight() { - const { min, max } = this.props; + const { min, max, rtl } = this.props; let { value } = this.props; if (!Array.isArray(value)) { @@ -71,15 +94,22 @@ export default class Selected extends React.Component { } const width = ((value[1] - value[0]) * 100) / (max - min); - const style = { + let style = { width: `${100 - getPercent(min, max, value[0]) - width}%`, left: `${getPercent(min, max, value[0]) + width}%`, }; + + if (rtl) { + style = { + width: `${getPercent(min, max, value[0])}%`, + left: `${100 - getPercent(min, max, value[0])}%`, + }; + } return style; } render() { - const { prefix, slider, reverse } = this.props; + const { prefix, slider, reverse, rtl } = this.props; const classes = classNames({ [`${prefix}range-selected`]: true, }); diff --git a/src/range/view/slider.jsx b/src/range/view/slider.jsx index 174cccc94a..b739b17649 100644 --- a/src/range/view/slider.jsx +++ b/src/range/view/slider.jsx @@ -3,10 +3,12 @@ import classNames from 'classnames'; import PropTypes from 'prop-types'; import { getPercent } from '../utils'; -function _getProps(min, max, value) { +function _getProps(min, max, value, rtl) { return { style: { - left: `${getPercent(min, max, value)}%`, + left: rtl + ? `${100 - getPercent(min, max, value)}%` + : `${getPercent(min, max, value)}%`, zIndex: 100, }, 'aria-valuenow': value, @@ -16,7 +18,7 @@ function _getProps(min, max, value) { }; } -function Slider({ prefix, hasMovingClass, min, max, value, onKeyDown }) { +function Slider({ prefix, hasMovingClass, min, max, value, onKeyDown, rtl }) { const classes = classNames({ [`${prefix}range-slider`]: true, [`${prefix}range-slider-moving`]: hasMovingClass, @@ -27,7 +29,7 @@ function Slider({ prefix, hasMovingClass, min, max, value, onKeyDown }) { onKeyDown={onKeyDown} role="slider" tabIndex={0} - {..._getProps(min, max, value)} + {..._getProps(min, max, value, rtl)} >
@@ -40,6 +42,7 @@ Slider.propTypes = { value: PropTypes.number, prefix: PropTypes.string, hasMovingClass: PropTypes.bool, + rtl: PropTypes.bool, }; Slider.defaultProps = { @@ -48,6 +51,7 @@ Slider.defaultProps = { max: 100, value: 0, hasMovingClass: false, + rtl: false, }; export default Slider; diff --git a/src/rating/rating.jsx b/src/rating/rating.jsx index 1734383780..4476ebebb0 100644 --- a/src/rating/rating.jsx +++ b/src/rating/rating.jsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import classNames from 'classnames'; import Icon from '../icon'; import { func, KEYCODE, obj } from '../util'; +import zhCN from '../locale/zh-cn'; const { noop, bindCtx } = func; const { ENTER, LEFT, UP, RIGHT, DOWN } = KEYCODE; @@ -68,6 +69,10 @@ export default class Rating extends Component { className: PropTypes.string, id: PropTypes.string, rtl: PropTypes.bool, + /** + * 自定义国际化文案对象 + */ + locale: PropTypes.object, }; static defaultProps = { @@ -82,6 +87,7 @@ export default class Rating extends Component { iconType: 'favorites-filling', onChange: noop, onHoverChange: noop, + locale: zhCN.Rating, }; static currentValue(min, max, hoverValue, stateValue) { @@ -306,6 +312,7 @@ export default class Rating extends Component { disabled, readAs, rtl, + locale, } = this.props; const others = obj.pickOthers(Rating.propTypes, this.props); const { hoverValue, clicked } = this.state; @@ -355,7 +362,6 @@ export default class Rating extends Component { onChange={this.handleChecked.bind(this, i + 1)} type="radio" name="rating" - aria-labelledby={id} /> ); } @@ -419,11 +425,14 @@ export default class Rating extends Component { className={ratingCls} onKeyDown={this.onKeyDown} tabIndex="0" + role="group" + aria-label={locale.description} >
(this.underlayNode = n)} + aria-hidden > {underlay}
diff --git a/src/select/main.scss b/src/select/main.scss index 110e2a54f2..cf577e3470 100644 --- a/src/select/main.scss +++ b/src/select/main.scss @@ -307,14 +307,20 @@ &-all { display: block; cursor: pointer; - line-height: $select-menu-item-height; - padding: $s-1 $s-2; + padding: 0 $s-2; margin: 0 $s-3 $s-2; border-bottom: 1px solid $color-line1-2; &:hover { color: $color-link-3; } + + .#{$css-prefix}menu-icon-selected.#{$css-prefix}icon { + // remove display: inline-block !important; in next Y + display: inline-block !important; + top: initial; + color: $select-menu-icon-color; + } } &-highlight { diff --git a/src/select/scss/variable.scss b/src/select/scss/variable.scss index 11826649dc..921ba53dde 100644 --- a/src/select/scss/variable.scss +++ b/src/select/scss/variable.scss @@ -52,3 +52,4 @@ $select-tag-spacing-lr: $s-1; $select-tag-spacing-tb: 3px; $select-tag-padding-lr: $s-1; $select-menu-item-height: $s-5; +$select-menu-icon-color: $color-brand1-6; diff --git a/src/select/select.jsx b/src/select/select.jsx index 053422ae27..00bf9ae911 100644 --- a/src/select/select.jsx +++ b/src/select/select.jsx @@ -232,6 +232,7 @@ class Select extends Base { this.valueDataSource.mapValueDS, this.dataStore.getMapDS() ); + this.updateSelectAllYet(this.valueDataSource.value); } else if ( 'defaultValue' in nextProps && nextProps.defaultValue === this.valueDataSource.value && @@ -375,6 +376,8 @@ class Select extends Base { this.handleChange(itemObj.value, triggerType, itemObj.valueDS); } + this.updateSelectAllYet(itemObj.value); + // 清空搜索 if (!('searchValue' in this.props) && this.state.searchValue) { // 因为 SearchValue 被 clear 后会重新渲染 Menu,所以在 Overlay 检测 safeNode 的时候 e.target 可能会找不到导致弹窗关闭 @@ -384,6 +387,28 @@ class Select extends Base { } } + updateSelectAllYet(value) { + // multiple mode + // is current state select all or not + this.selectAllYet = false; + if (this.props.hasSelectAll && Array.isArray(value)) { + const selectAllValues = this.dataStore + .getEnableDS() + .map(item => item.value); + + if (selectAllValues.length <= value.length) { + this.selectAllYet = true; + + selectAllValues.forEach(val => { + if (value.indexOf(val) === -1) { + this.selectAllYet = false; + return; + } + }); + } + } + } + handleSearchValue(value) { if (this.state.searchValue === value) { return; @@ -595,7 +620,14 @@ class Select extends Base { */ handleSelectAll(e) { e && e.preventDefault(); - const nextValues = this.dataStore.getEnableDS().map(item => item.value); + let nextValues; + + if (this.selectAllYet) { + nextValues = []; + } else { + nextValues = this.dataStore.getEnableDS().map(item => item.value); + } + // 直接传 values,减少 toString 操作 this.handleMultipleSelect(nextValues, 'selectAll'); } @@ -913,13 +945,34 @@ class Select extends Base { const text = typeof hasSelectAll === 'boolean' ? 'Select All' : hasSelectAll; + const selectAllYet = this.selectAllYet; + + const cls = classNames({ + [`${prefix}select-all`]: true, + [`${prefix}selected`]: selectAllYet, + }); + + const clsInner = classNames({ + [`${prefix}select-all-inner`]: true, + }); + + // remove style={{'lineHeight': 'unset'}} in next Y + // remove style={{'display': 'none'}} in next Y return (
- {text} + {selectAllYet ? ( + + ) : null} + {text}
); } diff --git a/src/step/view/step-item.jsx b/src/step/view/step-item.jsx index b01d390c77..3ef186b5d0 100644 --- a/src/step/view/step-item.jsx +++ b/src/step/view/step-item.jsx @@ -108,6 +108,13 @@ class StepItem extends Component { if (shape === 'arrow') { return; } + const resetTailStyle = () => { + dom.setStyle(this.tail, { + width: '', + // eslint-disable-next-line + top: '', + }); + }; if (direction === 'vertical' || direction === 'ver') { this.resize(); @@ -117,19 +124,18 @@ class StepItem extends Component { width: '', [pos]: '', }); - } else if ( - shape === 'circle' && - (labelPlacement === 'horizontal' || labelPlacement === 'hoz') && - index !== total - 1 - ) { - // 调整横向Content - this.adjustTail(); + if ( + shape === 'circle' && + (labelPlacement === 'horizontal' || labelPlacement === 'hoz') && + index !== total - 1 + ) { + // 调整横向Content + this.adjustTail(); + } else { + resetTailStyle(); + } } else if (index !== total - 1) { - dom.setStyle(this.tail, { - width: '', - // eslint-disable-next-line - top: '', - }); + resetTailStyle(); } } diff --git a/src/tab/rtl.scss b/src/tab/rtl.scss index b583674425..8d875b33e4 100644 --- a/src/tab/rtl.scss +++ b/src/tab/rtl.scss @@ -1,6 +1,27 @@ @import "../core/index-noreset.scss"; +@import "scss/variable"; #{$tab-prefix}[dir="rtl"] { + &.#{$css-prefix}medium { + & #{$tab-prefix}-nav-container-scrolling { + padding-left: $tab-nav-scroll-padding-right-m; + padding-right: 0; + } + .#{$css-prefix}tabs-tab-close { + padding-right: $tab-nav-close-icon-padding-l-size-m; + padding-left: 0; + } + } + &.#{$css-prefix}small { + & #{$tab-prefix}-nav-container-scrolling { + padding-left: $tab-nav-scroll-padding-right-s; + padding-right: 0; + } + .#{$css-prefix}tabs-tab-close { + padding-right: $tab-nav-close-icon-padding-l-size-s; + padding-left: 0; + } + } &#{$tab-prefix}-wrapped#{$tab-prefix}-top > #{$tab-prefix}-bar { #{$tab-prefix}-nav-extra { right: auto; @@ -21,4 +42,18 @@ left: 0; } } + + #{$tab-prefix}-btn-next { + left: $tab-nav-arrow-right-positon-right; + right: auto; + } + #{$tab-prefix}-btn-prev { + left: $tab-nav-arrow-left-positon-right; + right: auto; + } + #{$tab-prefix}-btn-down { + left: $tab-nav-arrow-down-positon-right; + right: auto; + } } + diff --git a/src/tab/tab.jsx b/src/tab/tab.jsx index 278238f280..5991d689c1 100644 --- a/src/tab/tab.jsx +++ b/src/tab/tab.jsx @@ -243,7 +243,10 @@ export default class Tab extends Component { const { activeKey } = this.state; const tabs = toArray(children); - + let newPosition = tabPosition; + if (rtl && ['left', 'right'].indexOf(tabPosition) >= 0) { + newPosition = tabPosition === 'left' ? 'right' : 'left'; + } const classNames = classnames( { [`${prefix}tabs`]: true, @@ -251,7 +254,7 @@ export default class Tab extends Component { [`${prefix}tabs-vertical`]: shape === 'wrapped' && ['left', 'right'].indexOf(tabPosition) >= 0, - [`${prefix}tabs-${tabPosition}`]: shape === 'wrapped', + [`${prefix}tabs-${newPosition}`]: shape === 'wrapped', [`${prefix + size}`]: size, }, className diff --git a/src/tab/tabs/nav.jsx b/src/tab/tabs/nav.jsx index 443ff3e0be..d012903e66 100644 --- a/src/tab/tabs/nav.jsx +++ b/src/tab/tabs/nav.jsx @@ -109,15 +109,17 @@ class Nav extends React.Component { const wrapperOffset = getOffsetLT(this.wrapper); if ( + // active tab partially in visible zone wrapperOffset + wrapperWH < activeTabOffset + activeTabWH && activeTabOffset < wrapperOffset + wrapperWH ) { - target -= + target -= // Move more to make active tab totally in visible zone activeTabOffset + activeTabWH - (wrapperOffset + wrapperWH); } } if (this.offset !== target) { + // needs move this.offset = target; let navOffset = {}; @@ -133,7 +135,7 @@ class Nav extends React.Component { name: 'top', value: `${target}px`, }; - } else { + } else if (!this.props.rtl) { navOffset = canTransform ? { value: `translate3d(${target}px, 0, 0)`, @@ -142,6 +144,15 @@ class Nav extends React.Component { name: 'left', value: `${target}px`, }; + } else { + navOffset = canTransform + ? { + value: `translate3d(${-target}px, 0, 0)`, + } + : { + name: 'right', + value: `${target}px`, + }; } if (canTransform) { @@ -346,12 +357,12 @@ class Nav extends React.Component { onPrevClick = () => { const wrapperWH = getOffsetWH(this.wrapper); this.setOffset(this.offset + wrapperWH, true, false); - } + }; onNextClick = () => { const wrapperWH = getOffsetWH(this.wrapper); this.setOffset(this.offset - wrapperWH, true, false); - } + }; onNavItemClick(key, callback, e) { this.props.onTriggerEvent(triggerEvents.CLICK, key); @@ -394,7 +405,7 @@ class Nav extends React.Component { return null; } - const { prefix, activeKey, triggerType, popupProps } = this.props; + const { prefix, activeKey, triggerType, popupProps, rtl } = this.props; const trigger = ( ); @@ -519,7 +532,7 @@ class Nav extends React.Component { onClick={state.next ? this.onNextClick : noop} className={nextBtnCls} > - + ); restButton = null; diff --git a/src/table/lock.jsx b/src/table/lock.jsx index 483c9204fc..eb97c00303 100644 --- a/src/table/lock.jsx +++ b/src/table/lock.jsx @@ -477,10 +477,13 @@ export default function lock(BaseComponent) { ); setTimeout(() => { - this.tableRightInc.affixRef && - this.tableRightInc.affixRef - .getInstance() - .updatePosition(); + const affixRef = this.tableRightInc.affixRef; + // if rendered then update postion of affix + return ( + affixRef && + affixRef.getInstance() && + affixRef.getInstance().updatePosition() + ); }); } @@ -494,10 +497,13 @@ export default function lock(BaseComponent) { ); setTimeout(() => { - this.tableLeftInc.affixRef && - this.tableLeftInc.affixRef - .getInstance() - .updatePosition(); + const affixRef = this.tableLeftInc.affixRef; + // if rendered then update postion of affix + return ( + affixRef && + affixRef.getInstance() && + affixRef.getInstance().updatePosition() + ); }); } }); diff --git a/src/table/main.scss b/src/table/main.scss index 4293f4a318..9c73f81353 100644 --- a/src/table/main.scss +++ b/src/table/main.scss @@ -303,6 +303,9 @@ cursor: pointer; width: 20px; display: inline-block; + &:focus { + outline: 0; + } .#{$css-prefix}icon { @include icon-size($table-filter-icon-size); color: $table-sort-color; diff --git a/src/tag/main.scss b/src/tag/main.scss index e17ca12140..6d56edf360 100644 --- a/src/tag/main.scss +++ b/src/tag/main.scss @@ -121,6 +121,18 @@ &tag-closable { position: relative; + &.#{$css-prefix}tag-large > .#{$css-prefix}tag-body { + max-width: calc(100% - #{$tag-size-l-height}); + } + + &.#{$css-prefix}tag-medium > .#{$css-prefix}tag-body { + max-width: calc(100% - #{$tag-size-m-height}); + } + + &.#{$css-prefix}tag-small > .#{$css-prefix}tag-body { + max-width: calc(100% - #{$tag-size-s-height}); + } + > .#{$css-prefix}tag-close-btn { display: inline-block; vertical-align: middle; diff --git a/src/tag/scss/mixin.scss b/src/tag/scss/mixin.scss index 19271a46af..80af2edc14 100644 --- a/src/tag/scss/mixin.scss +++ b/src/tag/scss/mixin.scss @@ -20,7 +20,7 @@ line-height: $height - $borderWidth * 2; font-size: 0; - $_marginLeft: $height / 4 - 1px; + $_marginLeft: $iconSize; > .#{$css-prefix}tag-body { font-size: $fontSize; diff --git a/src/time-picker/module/time-menu.jsx b/src/time-picker/module/time-menu.jsx index e1f1419cc2..00f9edab0c 100644 --- a/src/time-picker/module/time-menu.jsx +++ b/src/time-picker/module/time-menu.jsx @@ -96,7 +96,6 @@ class TimeMenu extends React.Component { list.push(
  • { @@ -450,10 +450,13 @@ class List extends Component { if (rtl && listType === 'card' && Array.isArray(list)) { list = list.reverse(); } - const listclassNames = classNames({ - [`${prefixCls}-list`]: true, - [`${prefixCls}-list-${this.props.listType}`]: true, - }); + const listclassNames = classNames( + { + [`${prefixCls}-list`]: true, + [`${prefixCls}-list-${this.props.listType}`]: true, + }, + className + ); const others = obj.pickAttrsWith(this.props, 'data-'); return ( diff --git a/src/upload/main.scss b/src/upload/main.scss index 4e67d192c9..91afbef6bd 100644 --- a/src/upload/main.scss +++ b/src/upload/main.scss @@ -208,6 +208,7 @@ text-align: center; overflow: hidden; box-sizing: border-box; + img { width: 100%; height: 100%; @@ -291,6 +292,9 @@ img { width: 100%; height: 100%; + &:focus { + outline: 0; + } } .#{$css-prefix}icon { diff --git a/src/upload/upload.jsx b/src/upload/upload.jsx index ffcf9d1a62..35f55ac459 100644 --- a/src/upload/upload.jsx +++ b/src/upload/upload.jsx @@ -259,6 +259,7 @@ class Upload extends Base { onDrop = files => { this.onSelect(files); + this.props.onDrop(files); }; /** @@ -374,8 +375,8 @@ class Upload extends Base { targetItem.imgURL = response.imgURL || response.url; // 缩略图地址(可选) } - this.props.onSuccess(targetItem, value); this.onChange(value, targetItem); + this.props.onSuccess(targetItem, value); }; onError = (err, response, file) => { @@ -394,8 +395,8 @@ class Upload extends Base { response, }); - this.props.onError(targetItem, value); this.onChange(value, targetItem); + this.props.onError(targetItem, value); }; /** diff --git a/src/util/log.js b/src/util/log.js index 1492ad1ff9..f6bd1548e7 100644 --- a/src/util/log.js +++ b/src/util/log.js @@ -24,11 +24,12 @@ export function deprecated(props, instead, component) { /** * 控制台警告日志 - * @param {String} msg + * @param {String} msg + * @return {Console | void} */ export function warning(msg) { /* istanbul ignore else */ - if (typeof console !== 'undefined' && console.error) { + if (!isProduction() && typeof console !== 'undefined' && console.error) { return console.error(`Warning: ${msg}`); } } diff --git a/test/calendar/index-spec.js b/test/calendar/index-spec.js index 623ce1dd23..f4623ae825 100644 --- a/test/calendar/index-spec.js +++ b/test/calendar/index-spec.js @@ -85,7 +85,10 @@ describe('Calendar', () => { }); it('should render with disabled dates', () => { - const disabledDate = date => date.valueOf() > defaultVal.valueOf(); + const disabledDate = (date, view) => { + assert(view === 'date'); + return date.valueOf() > defaultVal.valueOf(); + }; wrapper = mount( defaultVal} diff --git a/test/cascader-select/index-spec.js b/test/cascader-select/index-spec.js index 083c1afa04..26c8c5317a 100644 --- a/test/cascader-select/index-spec.js +++ b/test/cascader-select/index-spec.js @@ -416,6 +416,33 @@ describe('CascaderSelect', () => { done(); }, 2000); }); + + it('should support signle value not in dataSource', () => { + const VALUE = '222333'; + let called = false; + const valueRender = (item) => { + assert(!item.label); + assert(item.value === VALUE); + called = true; + } + wrapper = mount(); + assert(called); + }); + + it('should support multiple value not in dataSource', () => { + const VALUE = '222333'; + let called = false; + const valueRender = (item) => { + assert(!item.label); + assert(item.value === VALUE); + called = true; + } + wrapper = mount( item.label || ''} dataSource={ChinaArea} valueRender={valueRender} />); + wrapper.setProps({ + value: VALUE, + }); + assert(called); + }); }); function findItem(menuIndex, itemIndex) { diff --git a/test/config-provider/index-spec.js b/test/config-provider/index-spec.js index 50a247d000..db4f3c4f8b 100644 --- a/test/config-provider/index-spec.js +++ b/test/config-provider/index-spec.js @@ -1,11 +1,12 @@ import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; -import Enzyme, { mount } from 'enzyme'; +import Enzyme, { mount, shallow } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; import moment from 'moment'; import assert from 'power-assert'; import Select from '../../src/select'; +import Button from '../../src/button'; import enUS from '../../src/locale/en-us'; import zhCN from '../../src/locale/zh-cn'; import ConfigProvider from '../../src/config-provider'; @@ -15,7 +16,7 @@ import ConfigProvider from '../../src/config-provider'; Enzyme.configure({ adapter: new Adapter() }); -const { config, getContextProps } = ConfigProvider; +const { config, getContextProps, ErrorBoundary } = ConfigProvider; class Output extends Component { static propTypes = { @@ -408,3 +409,131 @@ describe('ConfigProvider.Consumer', () => { assert(output.prop('warning') === contextState.warning); }); }); + +describe('ConfigProvider.ErrorBoundary', () => { + let wrapper; + + afterEach(() => { + if (wrapper) { + wrapper.unmount(); + wrapper = null; + } + }); + + it('should catch errors with componentDidCatch', () => { + const Something = () => null; + + wrapper = mount( + + + + ); + const error = new Error('test'); + + wrapper.find(Something).simulateError(error); + }); + + it('should catch errors with componentDidCatch as to basic component', () => { + const Something = () => null; + wrapper = mount( + + + + ); + const error = new Error('test'); + + wrapper.find(Something).simulateError(error); + }); + + it('should on: errorBoundary should work', () => { + wrapper = shallow( + +
  • + + +
    + ); + } + } + + const div = document.createElement('div'); + document.body.appendChild(div); + ReactDOM.render(, div); + + assert(document.querySelectorAll('div.next-table-empty').length === 1); + ReactDOM.unmountComponentAtNode(div); + document.body.removeChild(div); + }); }); diff --git a/test/util/a11y/validate.js b/test/util/a11y/validate.js index 18722bf7d5..20bd3f7d04 100644 --- a/test/util/a11y/validate.js +++ b/test/util/a11y/validate.js @@ -26,6 +26,13 @@ function formatViolations(violations, verbose = false) { return JSON.stringify(violations, null, 2); } + +function delay(duration) { + return new Promise((res) => { + setTimeout(res, duration) + }); +} + /** * Run Axe-core tests on a dom node * @@ -110,7 +117,9 @@ export const mountReact = async function(node, id = A11Y_ROOT_ID) { */ // TODO: resolve issue where failing tests do not pass wrapper and so cannot be cleaned up correctly export const testReact = async function(node, options = {}) { - const wrapper = mountReact(node, A11Y_ROOT_ID); + const wrapper = await mountReact(node, A11Y_ROOT_ID); + + await delay(options.delay || 200); await test(`#${A11Y_ROOT_ID}`, options); return wrapper; };