diff --git a/build/config.js b/build/config.js index 7695f23eb..daa63f67e 100644 --- a/build/config.js +++ b/build/config.js @@ -1,5 +1,5 @@ module.exports = { dev: { - componentName: 'auto-complete', // dev components + componentName: 'avatar', // dev components }, }; diff --git a/components/avatar/Avatar.jsx b/components/avatar/Avatar.jsx index c13d1d1b6..71d76979d 100644 --- a/components/avatar/Avatar.jsx +++ b/components/avatar/Avatar.jsx @@ -1,6 +1,7 @@ import { ConfigConsumerProps } from '../config-provider'; import Icon from '../icon'; -import { getListeners } from '../_util/props-util'; +import { getListeners, getComponentFromProp } from '../_util/props-util'; +import PropTypes from '../_util/vue-types'; export default { name: 'AAvatar', @@ -22,7 +23,7 @@ export default { src: String, /** Srcset of image avatar */ srcSet: String, - icon: String, + icon: PropTypes.any, alt: String, loadError: Function, }, @@ -32,6 +33,7 @@ export default { data() { return { isImgExist: true, + isMounted: false, scale: 1, }; }, @@ -46,38 +48,35 @@ export default { }, }, mounted() { - this.prevChildren = this.$slots.default; - this.prevState = { ...this.$data }; + this.$nextTick(() => { + this.setScale(); + this.isMounted = true; + }); + }, + updated() { this.$nextTick(() => { this.setScale(); }); }, - updated() { - if ( - this.preChildren !== this.$slots.default || - (this.prevState.scale !== this.$data.scale && this.$data.scale === 1) || - this.prevState.isImgExist !== this.$data.isImgExist - ) { - this.$nextTick(() => { - this.setScale(); - }); - } - this.preChildren = this.$slots.default; - this.prevState = { ...this.$data }; - }, methods: { setScale() { - const childrenNode = this.$refs.avatarChildren; - if (childrenNode) { - const childrenWidth = childrenNode.offsetWidth; - const avatarWidth = this.$el.getBoundingClientRect().width; - if (avatarWidth - 8 < childrenWidth) { - this.scale = (avatarWidth - 8) / childrenWidth; - } else { - this.scale = 1; - } - this.$forceUpdate(); + if (!this.$refs.avatarChildren || !this.$refs.avatarNode) { + return; } + const childrenWidth = this.$refs.avatarChildren.offsetWidth; // offsetWidth avoid affecting be transform scale + const nodeWidth = this.$refs.avatarNode.offsetWidth; + // denominator is 0 is no meaning + if ( + childrenWidth === 0 || + nodeWidth === 0 || + (this.lastChildrenWidth === childrenWidth && this.lastNodeWidth === nodeWidth) + ) { + return; + } + this.lastChildrenWidth = childrenWidth; + this.lastNodeWidth = nodeWidth; + // add 4px gap for each side to get better performance + this.scale = nodeWidth - 8 < childrenWidth ? (nodeWidth - 8) / childrenWidth : 1; }, handleImgLoadError() { const { loadError } = this.$props; @@ -88,12 +87,12 @@ export default { }, }, render() { - const { prefixCls: customizePrefixCls, shape, size, src, icon, alt, srcSet } = this.$props; - + const { prefixCls: customizePrefixCls, shape, size, src, alt, srcSet } = this.$props; + const icon = getComponentFromProp(this, 'icon'); const getPrefixCls = this.configProvider.getPrefixCls; const prefixCls = getPrefixCls('avatar', customizePrefixCls); - const { isImgExist, scale } = this.$data; + const { isImgExist, scale, isMounted } = this.$data; const sizeCls = { [`${prefixCls}-lg`]: size === 'large', @@ -122,10 +121,14 @@ export default { if (src && isImgExist) { children = {alt}; } else if (icon) { - children = ; + if (typeof icon === 'string') { + children = ; + } else { + children = icon; + } } else { const childrenNode = this.$refs.avatarChildren; - if (childrenNode || (scale !== 1 && childrenNode)) { + if (childrenNode || scale !== 1) { const transformString = `scale(${scale}) translateX(-50%)`; const childrenStyle = { msTransform: transformString, @@ -148,15 +151,21 @@ export default { ); } else { + const childrenStyle = {}; + if (!isMounted) { + childrenStyle.opacity = 0; + } children = ( - + {children} ); } } return ( - {children} + + {children} + ); }, }; diff --git a/components/avatar/__tests__/Avatar.test.js b/components/avatar/__tests__/Avatar.test.js index c8991327f..5b2dedfec 100644 --- a/components/avatar/__tests__/Avatar.test.js +++ b/components/avatar/__tests__/Avatar.test.js @@ -3,6 +3,26 @@ import { asyncExpect } from '@/tests/utils'; import Avatar from '..'; describe('Avatar Render', () => { + let originOffsetWidth; + beforeAll(() => { + // Mock offsetHeight + originOffsetWidth = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetWidth').get; + Object.defineProperty(HTMLElement.prototype, 'offsetWidth', { + get() { + if (this.className === 'ant-avatar-string') { + return 100; + } + return 80; + }, + }); + }); + + afterAll(() => { + // Restore Mock offsetHeight + Object.defineProperty(HTMLElement.prototype, 'offsetWidth', { + get: originOffsetWidth, + }); + }); it('Render long string correctly', () => { const wrapper = mount(Avatar, { slots: { @@ -25,6 +45,9 @@ describe('Avatar Render', () => { attachToDocument: true, }); wrapper.vm.setScale = jest.fn(() => { + if (wrapper.vm.scale === 0.5) { + return; + } wrapper.setData({ scale: 0.5 }); wrapper.vm.$forceUpdate(); }); @@ -35,14 +58,14 @@ describe('Avatar Render', () => { const children = wrapper.findAll('.ant-avatar-string'); expect(children.length).toBe(1); expect(children.at(0).text()).toBe('Fallback'); - expect(wrapper.vm.setScale).toBeCalled(); + expect(wrapper.vm.setScale).toHaveBeenCalled(); }); await asyncExpect(() => { expect(global.document.body.querySelector('.ant-avatar-string').style.transform).toContain( 'scale(0.5)', ); global.document.body.innerHTML = ''; - }, 0); + }, 1000); }); it('should handle onError correctly', async () => { global.document.body.innerHTML = ''; diff --git a/components/avatar/__tests__/__snapshots__/demo.test.js.snap b/components/avatar/__tests__/__snapshots__/demo.test.js.snap index 4303d33c5..d9a743629 100644 --- a/components/avatar/__tests__/__snapshots__/demo.test.js.snap +++ b/components/avatar/__tests__/__snapshots__/demo.test.js.snap @@ -9,6 +9,6 @@ exports[`renders ./components/avatar/demo/basic.md correctly 1`] = ` `; -exports[`renders ./components/avatar/demo/dynamic.md correctly 1`] = `
U
`; +exports[`renders ./components/avatar/demo/dynamic.md correctly 1`] = `
U
`; -exports[`renders ./components/avatar/demo/type.md correctly 1`] = `
U USER U
`; +exports[`renders ./components/avatar/demo/type.md correctly 1`] = `
U USER U
`; diff --git a/components/avatar/demo/badge.md b/components/avatar/demo/badge.md index 9ab7a7c73..033d7485e 100644 --- a/components/avatar/demo/badge.md +++ b/components/avatar/demo/badge.md @@ -5,7 +5,7 @@ #### With Badge -Usually used for messages remind. +Usually used for reminders and notifications. ```tpl diff --git a/components/avatar/demo/type.md b/components/avatar/demo/type.md index ffcae27ac..4618095c6 100644 --- a/components/avatar/demo/type.md +++ b/components/avatar/demo/type.md @@ -12,6 +12,9 @@ Image, Icon and letter are supported, and the latter two kinds avatar can have c