diff --git a/build/config.js b/build/config.js index 63e919618..66fb78626 100644 --- a/build/config.js +++ b/build/config.js @@ -1,5 +1,5 @@ module.exports = { dev: { - componentName: 'calendar', // dev components + componentName: 'card', // dev components }, }; diff --git a/components/card/Card.jsx b/components/card/Card.jsx index f48f596b0..9b1a8885a 100644 --- a/components/card/Card.jsx +++ b/components/card/Card.jsx @@ -3,14 +3,12 @@ import Tabs from '../tabs'; import Row from '../row'; import Col from '../col'; import PropTypes from '../_util/vue-types'; -import addEventListener from '../vc-util/Dom/addEventListener'; import { getComponentFromProp, getSlotOptions, filterEmpty, getListeners, } from '../_util/props-util'; -import throttleByAnimationFrame from '../_util/throttleByAnimationFrame'; import BaseMixin from '../_util/BaseMixin'; import { ConfigConsumerProps } from '../config-provider'; @@ -31,6 +29,7 @@ export default { size: PropTypes.oneOf(['default', 'small']), actions: PropTypes.any, tabList: PropTypes.array, + tabBarExtraContent: PropTypes.any, activeTabKey: PropTypes.string, defaultActiveTabKey: PropTypes.string, }, @@ -38,44 +37,20 @@ export default { configProvider: { default: () => ConfigConsumerProps }, }, data() { - this.updateWiderPaddingCalled = false; return { widerPadding: false, }; }, - beforeMount() { - this.updateWiderPadding = throttleByAnimationFrame(this.updateWiderPadding); - }, - mounted() { - this.updateWiderPadding(); - this.resizeEvent = addEventListener(window, 'resize', this.updateWiderPadding); - }, - beforeDestroy() { - if (this.resizeEvent) { - this.resizeEvent.remove(); - } - this.updateWiderPadding.cancel && this.updateWiderPadding.cancel(); - }, methods: { - updateWiderPadding() { - const cardContainerRef = this.$refs.cardContainerRef; - if (!cardContainerRef) { - return; - } - // 936 is a magic card width pixel number indicated by designer - const WIDTH_BOUNDARY_PX = 936; - if (cardContainerRef.offsetWidth >= WIDTH_BOUNDARY_PX && !this.widerPadding) { - this.setState({ widerPadding: true }, () => { - this.updateWiderPaddingCalled = true; // first render without css transition - }); - } - if (cardContainerRef.offsetWidth < WIDTH_BOUNDARY_PX && this.widerPadding) { - this.setState({ widerPadding: false }, () => { - this.updateWiderPaddingCalled = true; // first render without css transition - }); - } + getAction(actions) { + const actionList = actions.map((action, index) => ( +
  • + {action} +
  • + )); + return actionList; }, - onHandleTabChange(key) { + onTabChange(key) { this.$emit('tabChange', key); }, isContainGrid(obj = []) { @@ -87,17 +62,6 @@ export default { }); return containGrid; }, - getAction(actions) { - if (!actions || !actions.length) { - return null; - } - const actionList = actions.map((action, index) => ( -
  • - {action} -
  • - )); - return actionList; - }, }, render() { const { @@ -118,14 +82,12 @@ export default { const prefixCls = getPrefixCls('card', customizePrefixCls); const { $slots, $scopedSlots } = this; - + const tabBarExtraContent = getComponentFromProp(this, 'tabBarExtraContent'); const classString = { [`${prefixCls}`]: true, [`${prefixCls}-loading`]: loading, [`${prefixCls}-bordered`]: bordered, [`${prefixCls}-hoverable`]: !!hoverable, - [`${prefixCls}-wider-padding`]: this.widerPadding, - [`${prefixCls}-padding-transition`]: this.updateWiderPaddingCalled, [`${prefixCls}-contain-grid`]: this.isContainGrid($slots.default), [`${prefixCls}-contain-tabs`]: tabList && tabList.length, [`${prefixCls}-${size}`]: size !== 'default', @@ -187,9 +149,10 @@ export default { [hasActiveTabKey ? 'activeKey' : 'defaultActiveKey']: hasActiveTabKey ? activeTabKey : defaultActiveTabKey, + tabBarExtraContent, }, on: { - change: this.onHandleTabChange, + change: this.onTabChange, }, class: `${prefixCls}-head-tabs`, }; diff --git a/components/card/Grid.jsx b/components/card/Grid.jsx index 26b276aa3..24df54786 100644 --- a/components/card/Grid.jsx +++ b/components/card/Grid.jsx @@ -7,18 +7,20 @@ export default { __ANT_CARD_GRID: true, props: { prefixCls: PropTypes.string, + hoverable: PropTypes.bool, }, inject: { configProvider: { default: () => ConfigConsumerProps }, }, render() { - const { prefixCls: customizePrefixCls } = this.$props; + const { prefixCls: customizePrefixCls, hoverable = true } = this.$props; const getPrefixCls = this.configProvider.getPrefixCls; const prefixCls = getPrefixCls('card', customizePrefixCls); const classString = { [`${prefixCls}-grid`]: true, + [`${prefixCls}-grid-hoverable`]: hoverable, }; return (
    diff --git a/components/card/__tests__/__snapshots__/demo.test.js.snap b/components/card/__tests__/__snapshots__/demo.test.js.snap index c0c076105..daa90241d 100644 --- a/components/card/__tests__/__snapshots__/demo.test.js.snap +++ b/components/card/__tests__/__snapshots__/demo.test.js.snap @@ -70,13 +70,14 @@ exports[`renders ./components/card/demo/grid-card.md correctly 1`] = `
    -
    Content
    -
    Content
    -
    Content
    -
    Content
    -
    Content
    -
    Content
    -
    Content
    +
    Content
    +
    Content
    +
    Content
    +
    Content
    +
    Content
    +
    Content
    +
    Content
    +
    Content
    `; @@ -291,6 +292,7 @@ exports[`renders ./components/card/demo/tabs.md correctly 1`] = `
    +
    diff --git a/components/card/__tests__/index.test.js b/components/card/__tests__/index.test.js index 3a0d38e38..0a6ebb871 100644 --- a/components/card/__tests__/index.test.js +++ b/components/card/__tests__/index.test.js @@ -1,10 +1,12 @@ import { mount } from '@vue/test-utils'; import Card from '../index'; import Button from '../../button/index'; +import mountTest from '../../../tests/shared/mountTest'; const testMethod = typeof window !== 'undefined' ? it : xit; describe('Card', () => { + mountTest(Card); beforeAll(() => { jest.useFakeTimers(); }); @@ -12,35 +14,6 @@ describe('Card', () => { afterAll(() => { jest.useRealTimers(); }); - - function fakeResizeWindowTo(wrapper, width) { - Object.defineProperties(wrapper.vm.$refs.cardContainerRef, { - offsetWidth: { - get() { - return width; - }, - configurable: true, - }, - }); - window.resizeTo(width); - } - - testMethod('resize card will trigger different padding', () => { - const wrapper = mount(Card, { - propsData: 'xxx', - slots: { - default: 'xxx', - }, - }); - fakeResizeWindowTo(wrapper, 1000); - jest.runAllTimers(); - wrapper.vm.$forceUpdate(); - expect(wrapper.findAll('.ant-card-wider-padding').length).toBe(1); - fakeResizeWindowTo(wrapper, 800); - jest.runAllTimers(); - wrapper.vm.$forceUpdate(); - expect(wrapper.findAll('.ant-card-wider-padding').length).toBe(0); - }); it('should still have padding when card which set padding to 0 is loading', () => { const wrapper = mount({ render() { @@ -66,4 +39,50 @@ describe('Card', () => { }); expect(wrapper.html()).toMatchSnapshot(); }); + + it('onTabChange should work', () => { + const tabList = [ + { + key: 'tab1', + tab: 'tab1', + }, + { + key: 'tab2', + tab: 'tab2', + }, + ]; + const onTabChange = jest.fn(); + const wrapper = mount( + { + render() { + return ( + + xxx + + ); + }, + }, + { + sync: false, + }, + ); + wrapper + .findAll('.ant-tabs-tab') + .at(1) + .trigger('click'); + expect(onTabChange).toHaveBeenCalledWith('tab2'); + }); + + it('should not render when actions is number', () => { + const wrapper = mount({ + render() { + return ( + +

    Card content

    +
    + ); + }, + }); + expect(wrapper.findAll('.ant-card-actions').length).toBe(0); + }); }); diff --git a/components/card/demo/grid-card.md b/components/card/demo/grid-card.md index a7500f526..24b6b10c3 100644 --- a/components/card/demo/grid-card.md +++ b/components/card/demo/grid-card.md @@ -11,13 +11,14 @@ Grid style card content. ```tpl ``` diff --git a/components/card/demo/meta.md b/components/card/demo/meta.md index 9608b24c8..b3b9d2826 100644 --- a/components/card/demo/meta.md +++ b/components/card/demo/meta.md @@ -17,9 +17,9 @@ slot="cover" /> article content

    app content

    project content

    + More
    diff --git a/components/card/index.en-US.md b/components/card/index.en-US.md index ac862d505..bac67a524 100644 --- a/components/card/index.en-US.md +++ b/components/card/index.en-US.md @@ -2,35 +2,36 @@ ### Card -| Property | Description | Type | Default | -| --- | --- | --- | --- | -| actions | The action list, shows at the bottom of the Card. | slots | - | -| activeTabKey | Current TabPane's key | string | - | -| headStyle | Inline style to apply to the card head | object | - | -| bodyStyle | Inline style to apply to the card content | object | - | -| bordered | Toggles rendering of the border around the card | boolean | `true` | -| cover | Card cover | slot | - | -| defaultActiveTabKey | Initial active TabPane's key, if `activeTabKey` is not set. | string | - | -| extra | Content to render in the top-right corner of the card | string\|slot | - | -| hoverable | Lift up when hovering card | boolean | false | -| loading | Shows a loading indicator while the contents of the card are being fetched | boolean | false | -| tabList | List of TabPane's head, Custom tabs can be created with the scopedSlots property | Array<{key: string, tab: any, scopedSlots: {tab: 'XXX'}}> | - | -| size | Size of card | `default` \| `small` | `default` | -| title | Card title | string\|slot | - | -| type | Card style type, can be set to `inner` or not set | string | - | +| Property | Description | Type | Default | Version | +| --- | --- | --- | --- | --- | +| actions | The action list, shows at the bottom of the Card. | slots | - | | +| activeTabKey | Current TabPane's key | string | - | | +| headStyle | Inline style to apply to the card head | object | - | | +| bodyStyle | Inline style to apply to the card content | object | - | | +| bordered | Toggles rendering of the border around the card | boolean | `true` | | +| cover | Card cover | slot | - | | +| defaultActiveTabKey | Initial active TabPane's key, if `activeTabKey` is not set. | string | - | | +| extra | Content to render in the top-right corner of the card | string\|slot | - | | +| hoverable | Lift up when hovering card | boolean | false | | +| loading | Shows a loading indicator while the contents of the card are being fetched | boolean | false | | +| tabList | List of TabPane's head, Custom tabs can be created with the scopedSlots property | Array<{key: string, tab: any, scopedSlots: {tab: 'XXX'}}> | - | | +| tabBarExtraContent | Extra content in tab bar | slot | - | 1.5.0 | +| size | Size of card | `default` \| `small` | `default` | | +| title | Card title | string\|slot | - | | +| type | Card style type, can be set to `inner` or not set | string | - | | ### events -| Events Name | Description | Arguments | -| ----------- | ----------------------------- | ------------- | -| tabChange | Callback when tab is switched | (key) => void | - | +| Events Name | Description | Arguments | Version | +| ----------- | ----------------------------- | ------------- | ------- | +| tabChange | Callback when tab is switched | (key) => void | - | | ### Card.Grid ### Card.Meta -| Property | Description | Type | Default | -| ----------- | ------------------- | ------------ | ------- | -| avatar | avatar or icon | slot | - | -| description | description content | string\|slot | - | -| title | title content | string\|slot | - | +| Property | Description | Type | Default | Version | +| ----------- | ------------------- | ------------ | ------- | ------- | +| avatar | avatar or icon | slot | - | | +| description | description content | string\|slot | - | | +| title | title content | string\|slot | - | | diff --git a/components/card/index.zh-CN.md b/components/card/index.zh-CN.md index f1d5fae93..94e7b5e38 100644 --- a/components/card/index.zh-CN.md +++ b/components/card/index.zh-CN.md @@ -2,35 +2,36 @@ ### Card -| 参数 | 说明 | 类型 | 默认值 | -| --- | --- | --- | --- | -| actions | 卡片操作组,位置在卡片底部 | slots | - | -| activeTabKey | 当前激活页签的 key | string | - | -| headStyle | 自定义标题区域样式 | object | - | -| bodyStyle | 内容区域自定义样式 | object | - | -| bordered | 是否有边框 | boolean | true | -| cover | 卡片封面 | slot | - | -| defaultActiveTabKey | 初始化选中页签的 key,如果没有设置 activeTabKey | string | 第一个页签 | -| extra | 卡片右上角的操作区域 | string\|slot | - | -| hoverable | 鼠标移过时可浮起 | boolean | false | -| loading | 当卡片内容还在加载中时,可以用 loading 展示一个占位 | boolean | false | -| tabList | 页签标题列表, 可以通过 scopedSlots 属性自定义 tab | Array<{key: string, tab: any, scopedSlots: {tab: 'XXX'}}> | - | -| size | card 的尺寸 | `default` \| `small` | `default` | -| title | 卡片标题 | string\|slot | - | -| type | 卡片类型,可设置为 `inner` 或 不设置 | string | - | +| 参数 | 说明 | 类型 | 默认值 | 版本 | +| --- | --- | --- | --- | --- | +| actions | 卡片操作组,位置在卡片底部 | slots | - | | +| activeTabKey | 当前激活页签的 key | string | - | | +| headStyle | 自定义标题区域样式 | object | - | | +| bodyStyle | 内容区域自定义样式 | object | - | | +| bordered | 是否有边框 | boolean | true | | +| cover | 卡片封面 | slot | - | | +| defaultActiveTabKey | 初始化选中页签的 key,如果没有设置 activeTabKey | string | 第一个页签 | | +| extra | 卡片右上角的操作区域 | string\|slot | - | | +| hoverable | 鼠标移过时可浮起 | boolean | false | | +| loading | 当卡片内容还在加载中时,可以用 loading 展示一个占位 | boolean | false | | +| tabList | 页签标题列表, 可以通过 scopedSlots 属性自定义 tab | Array<{key: string, tab: any, scopedSlots: {tab: 'XXX'}}> | - | | +| tabBarExtraContent | tab bar 上额外的元素 | slot | 无 | 1.5.0 | +| size | card 的尺寸 | `default` \| `small` | `default` | | +| title | 卡片标题 | string\|slot | - | | +| type | 卡片类型,可设置为 `inner` 或 不设置 | string | - | | ### 事件 -| 事件名称 | 说明 | 回调参数 | -| --------- | -------------- | ------------- | -| tabChange | 页签切换的回调 | (key) => void | - | +| 事件名称 | 说明 | 回调参数 | 版本 | +| --------- | -------------- | ------------- | ---- | +| tabChange | 页签切换的回调 | (key) => void | - | | ### Card.Grid ### Card.Meta -| Property | Description | Type | Default | -| ----------- | ----------- | ------------ | ------- | -| avatar | 头像/图标 | slot | - | -| description | 描述内容 | string\|slot | - | -| title | 标题内容 | string\|slot | - | +| 参数 | 说明 | 类型 | 默认值 | 版本 | +| ----------- | --------- | ------------ | ------ | ---- | +| avatar | 头像/图标 | slot | - | | +| description | 描述内容 | string\|slot | - | | +| title | 标题内容 | string\|slot | - | | diff --git a/components/tabs/tabs.jsx b/components/tabs/tabs.jsx index 321508c6b..2d04e94cd 100644 --- a/components/tabs/tabs.jsx +++ b/components/tabs/tabs.jsx @@ -26,7 +26,12 @@ export default { defaultActiveKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), hideAdd: PropTypes.bool.def(false), tabBarStyle: PropTypes.object, - tabBarExtraContent: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.func]), + tabBarExtraContent: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number, + PropTypes.func, + PropTypes.array, + ]), destroyInactiveTabPane: PropTypes.bool.def(false), type: PropTypes.oneOf(['line', 'card', 'editable-card']), tabPosition: PropTypes.oneOf(['top', 'right', 'bottom', 'left']).def('top'), diff --git a/types/card.d.ts b/types/card.d.ts index 8ba93d078..e8d355702 100644 --- a/types/card.d.ts +++ b/types/card.d.ts @@ -11,6 +11,7 @@ export declare class Card extends AntdComponent { static Grid: any; static Meta: typeof Meta; + tabBarExtraContent: any; /** * The action list, shows at the bottom of the Card. * @type any (slots)