Skip to content

Commit

Permalink
Merge pull request #24 from binjie09/master
Browse files Browse the repository at this point in the history
[IMP] upgrade avatar and table
  • Loading branch information
devane001 committed Oct 30, 2018
2 parents 53e05df + 3b6a0ff commit 9954b64
Show file tree
Hide file tree
Showing 13 changed files with 149 additions and 25 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ timeline: true
`2018-10-26`

- 🌟 `Icon`: 增加新的图标。
- 🌟 `Table`: 增加onColumnFilterChange,在表格列过滤器变化时触发。
- 💄 `Demo`: 修复使用bisheng生成的文档网站无法展开样例代码的bug。
- 💄 `Avatar`: 修复头像中文字定位不准确。

## 0.4.0

Expand Down
56 changes: 56 additions & 0 deletions components/avatar/__tests__/Avatar.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(<Avatar src="http://error.url">Fallback</Avatar>, { 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 <Avatar src={src} onError={this.handleImgError} />;
}
}

const wrapper = mount(<Foo />, { 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);
});
});
16 changes: 16 additions & 0 deletions components/avatar/__tests__/__snapshots__/demo.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,14 @@ exports[`renders ./components/avatar/demo/badge.md correctly 1`] = `
exports[`renders ./components/avatar/demo/basic.md correctly 1`] = `
<div>
<div>
<span
class="ant-avatar ant-avatar-circle ant-avatar-icon"
style="width:64px;height:64px;line-height:64px;font-size:32px"
>
<i
class="icon icon-user"
/>
</span>
<span
class="ant-avatar ant-avatar-lg ant-avatar-circle ant-avatar-icon"
>
Expand All @@ -224,6 +232,14 @@ exports[`renders ./components/avatar/demo/basic.md correctly 1`] = `
</span>
</div>
<div>
<span
class="ant-avatar ant-avatar-square ant-avatar-icon"
style="width:64px;height:64px;line-height:64px;font-size:32px"
>
<i
class="icon icon-user"
/>
</span>
<span
class="ant-avatar ant-avatar-lg ant-avatar-square ant-avatar-icon"
>
Expand Down
2 changes: 2 additions & 0 deletions components/avatar/demo/basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ import { Avatar } from 'choerodon-ui';
ReactDOM.render(
<div>
<div>
<Avatar size={64} icon="user" />
<Avatar size="large" icon="user" />
<Avatar icon="user" />
<Avatar size="small" icon="user" />
</div>
<div>
<Avatar shape="square" size={64} icon="user" />
<Avatar shape="square" size="large" icon="user" />
<Avatar shape="square" icon="user" />
<Avatar shape="square" size="small" icon="user" />
Expand Down
65 changes: 48 additions & 17 deletions components/avatar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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 {
Expand Down Expand Up @@ -46,7 +53,8 @@ export default class Avatar extends React.Component<AvatarProps, AvatarState> {

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();
}
}
Expand All @@ -55,7 +63,8 @@ export default class Avatar extends React.Component<AvatarProps, AvatarState> {
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({
Expand All @@ -69,50 +78,68 @@ export default class Avatar extends React.Component<AvatarProps, AvatarState> {
}
}

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',
});

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 = (
<img
src={src}
onError={this.handleImgLoadError}
alt={alt}
/>
);
} else if (icon) {
children = <Icon type={icon} />;
} 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 = (
<span
className={`${prefixCls}-string`}
ref={span => this.avatarChildren = span}
style={childrenStyle}
style={{ ...sizeChildrenStyle, ...childrenStyle }}
>
{children}
</span>
Expand All @@ -129,7 +156,11 @@ export default class Avatar extends React.Component<AvatarProps, AvatarState> {
}
}
return (
<span {...others} className={classString}>
<span
{...others}
style={{ ...sizeStyle, ...others.style }}
className={classString}
>
{children}
</span>
);
Expand Down
8 changes: 7 additions & 1 deletion components/avatar/style/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
10 changes: 5 additions & 5 deletions components/table/ColumnFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { getColumnKey } from './util';
export interface ColumnFilterProps<T> {
prefixCls?: string;
columns?: ColumnProps<T>[];
onColumnFilterChange?: () => void;
onColumnFilterChange?: (item?: any) => void;
getPopupContainer?: (triggerNode?: Element) => HTMLElement;
}

Expand Down Expand Up @@ -51,12 +51,12 @@ export default class ColumnFilter<T> extends React.Component<ColumnFilterProps<T

onMenuSelect = (item: any) => {
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) => {
Expand All @@ -67,10 +67,10 @@ export default class ColumnFilter<T> extends React.Component<ColumnFilterProps<T
}
};

fireChange() {
fireChange(item?: any) {
const { onColumnFilterChange } = this.props;
if (onColumnFilterChange) {
onColumnFilterChange();
onColumnFilterChange(item);
}
}

Expand Down
7 changes: 6 additions & 1 deletion components/table/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export default class Table<T> extends React.Component<TableProps<T>, 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,
Expand Down Expand Up @@ -463,7 +464,11 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
});
};

handleColumnFilterChange = () => {
handleColumnFilterChange = (e?: any) => {
const { onColumnFilterChange } = this.props;
if (onColumnFilterChange) {
onColumnFilterChange(e);
}
this.forceUpdate();
};

Expand Down
2 changes: 1 addition & 1 deletion components/table/demo/basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,5 @@ const data = [{
address: 'Sidney No. 1 Lake Park',
}];

ReactDOM.render(<Table columns={columns} dataSource={data} filterBarPlaceholder="过滤表" />, mountNode);
ReactDOM.render(<Table columns={columns} dataSource={data} filterBarPlaceholder="过滤表" onColumnFilterChange={item => console.log(item)} />, mountNode);
````
1 change: 1 addition & 0 deletions components/table/index.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) | - |
Expand Down
1 change: 1 addition & 0 deletions components/table/index.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) | - |
Expand Down
2 changes: 2 additions & 0 deletions components/table/interface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface ColumnProps<T> {
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;
Expand Down Expand Up @@ -103,6 +104,7 @@ export interface TableProps<T> {
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;
Expand Down

0 comments on commit 9954b64

Please sign in to comment.