diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md
index 6f86c913ce..19996b7cf4 100644
--- a/CHANGELOG.en-US.md
+++ b/CHANGELOG.en-US.md
@@ -20,7 +20,9 @@ timeline: true
`2018-10-26`
- 🌟 `Icon`: Add new icons.
+- 🌟 `Table`: Add onColumnFilterChange. Callback executed when ColumnFilter is changed.
- 💄 `Demo`: Fix bisheng demo site can't expand code by click the button。
+- 💄 `Avatar`: Fix avatar Chinese text positioning is not accurate.
## 0.4.0
diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md
index 7e84387152..d7c4d0ede6 100644
--- a/CHANGELOG.zh-CN.md
+++ b/CHANGELOG.zh-CN.md
@@ -20,7 +20,9 @@ timeline: true
`2018-10-26`
- 🌟 `Icon`: 增加新的图标。
+- 🌟 `Table`: 增加onColumnFilterChange,在表格列过滤器变化时触发。
- 💄 `Demo`: 修复使用bisheng生成的文档网站无法展开样例代码的bug。
+- 💄 `Avatar`: 修复头像中文字定位不准确。
## 0.4.0
diff --git a/components/avatar/__tests__/Avatar.test.js b/components/avatar/__tests__/Avatar.test.js
index 685cb14228..746433206e 100644
--- a/components/avatar/__tests__/Avatar.test.js
+++ b/components/avatar/__tests__/Avatar.test.js
@@ -8,4 +8,60 @@ describe('Avatar Render', () => {
const children = wrapper.find('.ant-avatar-string');
expect(children.length).toBe(1);
});
+
+ it('should render fallback string correctly', () => {
+ const div = global.document.createElement('div');
+ global.document.body.appendChild(div);
+
+ const wrapper = mount(Fallback, { attachTo: div });
+ wrapper.instance().setScale = jest.fn(() => wrapper.instance().setState({ scale: 0.5 }));
+
+ wrapper.find('img').simulate('error');
+
+ const children = wrapper.find('.ant-avatar-string');
+ expect(children.length).toBe(1);
+ expect(children.text()).toBe('Fallback');
+ expect(wrapper.instance().setScale).toBeCalled();
+ expect(div.querySelector('.ant-avatar-string').style.transform).toContain('scale(0.5)');
+
+ wrapper.detach();
+ global.document.body.removeChild(div);
+ });
+
+ it('should handle onError correctly', () => {
+ const LOAD_FAILURE_SRC = 'http://error.url';
+ const LOAD_SUCCESS_SRC = 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png';
+
+ const div = global.document.createElement('div');
+ global.document.body.appendChild(div);
+
+ class Foo extends React.Component {
+ state = {
+ src: LOAD_FAILURE_SRC,
+ }
+
+ handleImgError = () => {
+ this.setState({
+ src: LOAD_SUCCESS_SRC,
+ });
+ return false;
+ }
+
+ render() {
+ const { src } = this.state;
+ return ;
+ }
+ }
+
+ const wrapper = mount(, { attachTo: div });
+ // mock img load Error, since jsdom do not load resource by default
+ // https://github.com/jsdom/jsdom/issues/1816
+ wrapper.find('img').simulate('error');
+
+ expect(wrapper.find(Avatar).instance().state.isImgExist).toBe(true);
+ expect(div.querySelector('img').getAttribute('src')).toBe(LOAD_SUCCESS_SRC);
+
+ wrapper.detach();
+ global.document.body.removeChild(div);
+ });
});
diff --git a/components/avatar/__tests__/__snapshots__/demo.test.js.snap b/components/avatar/__tests__/__snapshots__/demo.test.js.snap
index 86780003bc..04de7927b6 100644
--- a/components/avatar/__tests__/__snapshots__/demo.test.js.snap
+++ b/components/avatar/__tests__/__snapshots__/demo.test.js.snap
@@ -201,6 +201,14 @@ exports[`renders ./components/avatar/demo/badge.md correctly 1`] = `
exports[`renders ./components/avatar/demo/basic.md correctly 1`] = `
+
+
+
@@ -224,6 +232,14 @@ exports[`renders ./components/avatar/demo/basic.md correctly 1`] = `
+
+
+
diff --git a/components/avatar/demo/basic.md b/components/avatar/demo/basic.md
index 169571cff3..2592a075ef 100644
--- a/components/avatar/demo/basic.md
+++ b/components/avatar/demo/basic.md
@@ -19,11 +19,13 @@ import { Avatar } from 'choerodon-ui';
ReactDOM.render(
+
diff --git a/components/avatar/index.tsx b/components/avatar/index.tsx
index fc15727d9e..2bf6063f4c 100644
--- a/components/avatar/index.tsx
+++ b/components/avatar/index.tsx
@@ -6,8 +6,11 @@ import classNames from 'classnames';
export interface AvatarProps {
/** Shape of avatar, options:`circle`, `square` */
shape?: 'circle' | 'square';
- /** Size of avatar, options:`large`, `small`, `default` */
- size?: 'large' | 'small' | 'default';
+ /*
+ * Size of avatar, options: `large`, `small`, `default`
+ * or a custom number size
+ * */
+ size?: 'large' | 'small' | 'default' | number;
/** Src of image avatar */
src?: string;
/** Type of the Icon to be used in avatar */
@@ -16,6 +19,10 @@ export interface AvatarProps {
prefixCls?: string;
className?: string;
children?: any;
+ alt?: string;
+ /* callback when img load error */
+ /* return false to prevent Avatar show default fallback behavior, then you can do fallback by your self*/
+ onError?: () => boolean;
}
export interface AvatarState {
@@ -46,7 +53,8 @@ export default class Avatar extends React.Component
{
componentDidUpdate(prevProps: AvatarProps, prevState: AvatarState) {
if (prevProps.children !== this.props.children
- || (prevState.scale !== this.state.scale && this.state.scale === 1)) {
+ || (prevState.scale !== this.state.scale && this.state.scale === 1)
+ || (prevState.isImgExist !== this.state.isImgExist)) {
this.setScale();
}
}
@@ -55,7 +63,8 @@ export default class Avatar extends React.Component {
const childrenNode = this.avatarChildren;
if (childrenNode) {
const childrenWidth = childrenNode.offsetWidth;
- const avatarWidth = (ReactDOM.findDOMNode(this) as HTMLElement).getBoundingClientRect().width;
+ const avatarNode = ReactDOM.findDOMNode(this) as Element;
+ const avatarWidth = avatarNode.getBoundingClientRect().width;
// add 4px gap for each side to get better performance
if (avatarWidth - 8 < childrenWidth) {
this.setState({
@@ -69,13 +78,21 @@ export default class Avatar extends React.Component {
}
}
- handleImgLoadError = () => this.setState({ isImgExist: false });
+ handleImgLoadError = () => {
+ const { onError } = this.props;
+ const errorFlag = onError ? onError() : undefined;
+ if (errorFlag !== false) {
+ this.setState({ isImgExist: false });
+ }
+ }
render() {
const {
- prefixCls, shape, size, src, icon, className, ...others,
+ prefixCls, shape, size, src, icon, className, alt, ...others,
} = this.props;
+ const { isImgExist, scale } = this.state;
+
const sizeCls = classNames({
[`${prefixCls}-lg`]: size === 'large',
[`${prefixCls}-sm`]: size === 'small',
@@ -83,36 +100,46 @@ export default class Avatar extends React.Component {
const classString = classNames(prefixCls, className, sizeCls, {
[`${prefixCls}-${shape}`]: shape,
- [`${prefixCls}-image`]: src && this.state.isImgExist,
+ [`${prefixCls}-image`]: src && isImgExist,
[`${prefixCls}-icon`]: icon,
});
+ const sizeStyle: React.CSSProperties = typeof size === 'number' ? {
+ width: size,
+ height: size,
+ lineHeight: `${size}px`,
+ fontSize: icon ? size / 2 : 18,
+ } : {};
+
let children = this.props.children;
- if (src && this.state.isImgExist) {
+ if (src && isImgExist) {
children = (
);
} else if (icon) {
children = ;
} else {
const childrenNode = this.avatarChildren;
- if (childrenNode || this.state.scale !== 1) {
+ if (childrenNode || scale !== 1) {
+ const transformString = `scale(${scale}) translateX(-50%)`;
const childrenStyle: React.CSSProperties = {
- msTransform: `scale(${this.state.scale})`,
- WebkitTransform: `scale(${this.state.scale})`,
- transform: `scale(${this.state.scale})`,
- position: 'absolute',
- display: 'inline-block',
- left: `calc(50% - ${Math.round(childrenNode.offsetWidth / 2)}px)`,
+ msTransform: transformString,
+ WebkitTransform: transformString,
+ transform: transformString,
};
+ const sizeChildrenStyle: React.CSSProperties =
+ typeof size === 'number' ? {
+ lineHeight: `${size}px`,
+ } : {};
children = (
this.avatarChildren = span}
- style={childrenStyle}
+ style={{ ...sizeChildrenStyle, ...childrenStyle }}
>
{children}
@@ -129,7 +156,11 @@ export default class Avatar extends React.Component {
}
}
return (
-
+
{children}
);
diff --git a/components/avatar/style/index.less b/components/avatar/style/index.less
index bea8f554c1..da52c414dc 100644
--- a/components/avatar/style/index.less
+++ b/components/avatar/style/index.less
@@ -43,12 +43,18 @@
width: @size;
height: @size;
line-height: @size;
- border-radius: @size / 2;
+ border-radius: 50%;
& > * {
line-height: @size;
}
+ &-string {
+ position: absolute;
+ left: 50%;
+ transform-origin: 0 center;
+ }
+
&.@{avatar-prefix-cls}-icon {
font-size: @font-size;
}
diff --git a/components/table/ColumnFilter.tsx b/components/table/ColumnFilter.tsx
index 077e39df2c..1029c5f118 100644
--- a/components/table/ColumnFilter.tsx
+++ b/components/table/ColumnFilter.tsx
@@ -9,7 +9,7 @@ import { getColumnKey } from './util';
export interface ColumnFilterProps {
prefixCls?: string;
columns?: ColumnProps[];
- onColumnFilterChange?: () => void;
+ onColumnFilterChange?: (item?: any) => void;
getPopupContainer?: (triggerNode?: Element) => HTMLElement;
}
@@ -51,12 +51,12 @@ export default class ColumnFilter extends React.Component {
item.item.props.value.hidden = false;
- this.fireChange();
+ this.fireChange(item);
};
onMenuDeselect = (item: any) => {
item.item.props.value.hidden = true;
- this.fireChange();
+ this.fireChange(item);
};
onDropdownVisibleChange = (open: boolean) => {
@@ -67,10 +67,10 @@ export default class ColumnFilter extends React.Component extends React.Component, TableState<
static propTypes = {
dataSource: PropTypes.array,
empty: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
+ onColumnFilterChange: PropTypes.func,
columns: PropTypes.array,
prefixCls: PropTypes.string,
useFixedHeader: PropTypes.bool,
@@ -463,7 +464,11 @@ export default class Table extends React.Component, TableState<
});
};
- handleColumnFilterChange = () => {
+ handleColumnFilterChange = (e?: any) => {
+ const { onColumnFilterChange } = this.props;
+ if (onColumnFilterChange) {
+ onColumnFilterChange(e);
+ }
this.forceUpdate();
};
diff --git a/components/table/demo/basic.md b/components/table/demo/basic.md
index 1bc7ddaa52..a8324bf098 100644
--- a/components/table/demo/basic.md
+++ b/components/table/demo/basic.md
@@ -62,5 +62,5 @@ const data = [{
address: 'Sidney No. 1 Lake Park',
}];
-ReactDOM.render(, mountNode);
+ReactDOM.render( console.log(item)} />, mountNode);
````
diff --git a/components/table/index.en-US.md b/components/table/index.en-US.md
index 51a383d197..f7c42c2b1a 100644
--- a/components/table/index.en-US.md
+++ b/components/table/index.en-US.md
@@ -75,6 +75,7 @@ const columns = [{
| size | Size of table | `default` \| `middle` \| `small` | `default` |
| title | Table title renderer | Function(currentPageData) | |
| onChange | Callback executed when pagination, filters or sorter is changed | Function(pagination, filters, sorter) | |
+| onColumnFilterChange | Callback executed when ColumnFilter is changed | Function(item) | |
| onExpand | Callback executed when the row expand icon is clicked | Function(expanded, record) | |
| onExpandedRowsChange | Callback executed when the expanded rows change | Function(expandedRows) | |
| onHeaderRow | Set props on per header row | Function(column, index) | - |
diff --git a/components/table/index.zh-CN.md b/components/table/index.zh-CN.md
index 669a3ded59..5e22db03e1 100644
--- a/components/table/index.zh-CN.md
+++ b/components/table/index.zh-CN.md
@@ -76,6 +76,7 @@ const columns = [{
| size | 正常或迷你类型,`default` or `small` | string | default |
| title | 表格标题 | Function(currentPageData) | |
| onChange | 分页、排序、筛选变化时触发 | Function(pagination, filters, sorter) | |
+| onColumnFilterChange | 右上角行过滤按钮中选项变化时触发 | Function(item) | |
| onExpand | 点击展开图标时触发 | Function(expanded, record) | |
| onExpandedRowsChange | 展开的行变化时触发 | Function(expandedRows) | |
| onHeaderRow | 设置头部行属性 | Function(column, index) | - |
diff --git a/components/table/interface.tsx b/components/table/interface.tsx
index 7035ec76e6..a2f817db4d 100644
--- a/components/table/interface.tsx
+++ b/components/table/interface.tsx
@@ -18,6 +18,7 @@ export interface ColumnProps {
align?: 'left' | 'right' | 'center';
filters?: ColumnFilterItem[];
onFilter?: (value: any, record: T, filters?: ColumnFilterItem[]) => boolean;
+ onColumnFilterChange?: (item: any) => void;
filterMultiple?: boolean;
filterDropdown?: React.ReactNode;
filterDropdownVisible?: boolean;
@@ -103,6 +104,7 @@ export interface TableProps {
onExpandedRowsChange?: (expandedRowKeys: string[] | number[]) => void;
onExpand?: (expanded: boolean, record: T) => void;
onChange?: (pagination: TablePaginationConfig | boolean, filters: string[], sorter: Object) => any;
+ onColumnFilterChange?: (item: any) => void;
loading?: boolean | SpinProps;
locale?: Object;
indentSize?: number;