cr pxxx to sxxx and tree

pull/1225/head
tanjinzhou 2019-09-18 20:03:13 +08:00
parent 4ce99b91e1
commit c015c8d34c
32 changed files with 380 additions and 82 deletions

View File

@ -133,6 +133,7 @@ const getComponentFromProp = (instance, prop, options = instance, execute = true
const componentOptions = instance.componentOptions || {}; const componentOptions = instance.componentOptions || {};
(componentOptions.children || []).forEach(child => { (componentOptions.children || []).forEach(child => {
if (child.data && child.data.slot === prop) { if (child.data && child.data.slot === prop) {
delete child.data.slot;
if (child.tag === 'template') { if (child.tag === 'template') {
slotsProp.push(child.children); slotsProp.push(child.children);
} else { } else {

View File

@ -1,6 +1,5 @@
import { Circle as VCCircle } from '../vc-progress'; import { Circle as VCCircle } from '../vc-progress';
import { validProgress } from './utils'; import { validProgress } from './utils';
import { ProgressProps } from './progress';
const statusColorMap = { const statusColorMap = {
normal: '#108ee9', normal: '#108ee9',

View File

@ -1,5 +1,4 @@
import { validProgress } from './utils'; import { validProgress } from './utils';
import { ProgressProps } from './progress';
const Line = { const Line = {
functional: true, functional: true,

View File

@ -11,9 +11,7 @@ export default {
prop: 'value', prop: 'value',
}, },
props: { props: {
prefixCls: { prefixCls: PropTypes.string,
type: String,
},
defaultValue: PropTypes.any, defaultValue: PropTypes.any,
value: PropTypes.any, value: PropTypes.any,
size: { size: {
@ -103,7 +101,6 @@ export default {
prefixCls={prefixCls} prefixCls={prefixCls}
disabled={props.disabled} disabled={props.disabled}
value={option} value={option}
onChange={this.onRadioChange}
checked={this.stateValue === option} checked={this.stateValue === option}
> >
{option} {option}
@ -116,7 +113,6 @@ export default {
prefixCls={prefixCls} prefixCls={prefixCls}
disabled={option.disabled || props.disabled} disabled={option.disabled || props.disabled}
value={option.value} value={option.value}
onChange={this.onRadioChange}
checked={this.stateValue === option.value} checked={this.stateValue === option.value}
> >
{option.label} {option.label}

View File

@ -12,9 +12,7 @@ export default {
prop: 'checked', prop: 'checked',
}, },
props: { props: {
prefixCls: { prefixCls: PropTypes.string,
type: String,
},
defaultChecked: Boolean, defaultChecked: Boolean,
checked: { type: Boolean, default: undefined }, checked: { type: Boolean, default: undefined },
disabled: Boolean, disabled: Boolean,
@ -41,6 +39,12 @@ export default {
blur() { blur() {
this.$refs.vcCheckbox.blur(); this.$refs.vcCheckbox.blur();
}, },
onChange(e) {
this.$emit('change', e);
if (this.radioGroupContext && this.radioGroupContext.onRadioChange) {
this.radioGroupContext.onRadioChange(e);
}
},
}, },
render() { render() {
@ -60,7 +64,7 @@ export default {
if (radioGroup) { if (radioGroup) {
radioProps.props.name = radioGroup.name; radioProps.props.name = radioGroup.name;
radioProps.on.change = radioGroup.onRadioChange; radioProps.on.change = this.onChange;
radioProps.props.checked = props.value === radioGroup.stateValue; radioProps.props.checked = props.value === radioGroup.stateValue;
radioProps.props.disabled = props.disabled || radioGroup.disabled; radioProps.props.disabled = props.disabled || radioGroup.disabled;
} else { } else {

View File

@ -1,4 +1,5 @@
import Radio from './Radio'; import Radio from './Radio';
import PropTypes from '../_util/vue-types';
import { getOptionProps } from '../_util/props-util'; import { getOptionProps } from '../_util/props-util';
import { ConfigConsumerProps } from '../config-provider'; import { ConfigConsumerProps } from '../config-provider';
@ -6,9 +7,6 @@ export default {
name: 'ARadioButton', name: 'ARadioButton',
props: { props: {
...Radio.props, ...Radio.props,
prefixCls: {
type: String,
},
}, },
inject: { inject: {
radioGroupContext: { default: undefined }, radioGroupContext: { default: undefined },

View File

@ -2,6 +2,7 @@ import { mount } from '@vue/test-utils';
import { asyncExpect } from '@/tests/utils'; import { asyncExpect } from '@/tests/utils';
import Radio from '../Radio'; import Radio from '../Radio';
import RadioGroup from '../Group'; import RadioGroup from '../Group';
import RadioButton from '../radioButton';
describe('Radio', () => { describe('Radio', () => {
function createRadioGroup(props, listeners = {}) { function createRadioGroup(props, listeners = {}) {
@ -50,10 +51,16 @@ describe('Radio', () => {
}, },
}); });
wrapper.trigger('mouseenter'); wrapper
.findAll('div')
.at(0)
.trigger('mouseenter');
expect(onMouseEnter).toHaveBeenCalled(); expect(onMouseEnter).toHaveBeenCalled();
wrapper.trigger('mouseleave'); wrapper
.findAll('div')
.at(0)
.trigger('mouseleave');
expect(onMouseLeave).toHaveBeenCalled(); expect(onMouseLeave).toHaveBeenCalled();
}); });
@ -89,6 +96,80 @@ describe('Radio', () => {
}); });
}); });
it('both of radio and radioGroup will trigger onchange event when they exists', async () => {
const onChange = jest.fn();
const onChangeRadioGroup = jest.fn();
const wrapper = mount(
{
props: ['value'],
render() {
const groupProps = {};
if (this.value !== undefined) {
groupProps.value = this.value;
}
return (
<RadioGroup ref="radioGroup" {...groupProps} onChange={onChangeRadioGroup}>
<Radio value="A" onChange={onChange}>
A
</Radio>
<Radio value="B" onChange={onChange}>
B
</Radio>
<Radio value="C" onChange={onChange}>
C
</Radio>
</RadioGroup>
);
},
},
{ sync: false },
);
const radios = wrapper.findAll('input');
// uncontrolled component
wrapper.vm.$refs.radioGroup.stateValue = 'B';
radios.at(0).trigger('change');
expect(onChange.mock.calls.length).toBe(1);
expect(onChangeRadioGroup.mock.calls.length).toBe(1);
// controlled component
wrapper.setProps({ value: 'A' });
radios.at(1).trigger('change');
expect(onChange.mock.calls.length).toBe(2);
});
it('Trigger onChange when both of radioButton and radioGroup exists', () => {
const onChange = jest.fn();
const props = {};
const wrapper = mount(
createRadioGroup(props, {
change: onChange,
}),
{ sync: false },
);
const radios = wrapper.findAll('input');
// uncontrolled component
wrapper.vm.$refs.radioGroup.stateValue = 'B';
radios.at(0).trigger('change');
expect(onChange.mock.calls.length).toBe(1);
// controlled component
wrapper.setProps({ value: 'A' });
radios.at(1).trigger('change');
expect(onChange.mock.calls.length).toBe(2);
});
// it('should only trigger once when in group with options', () => {
// const onChange = jest.fn();
// const options = [{ label: 'Bamboo', value: 'Bamboo' }];
// const wrapper = mount(<RadioGroup options={options} onChange={onChange} />);
// wrapper.find('input').trigger('change');
// expect(onChange).toHaveBeenCalledTimes(1);
// });
// it('won\'t fire change events when value not changes', async () => { // it('won\'t fire change events when value not changes', async () => {
// const onChange = jest.fn() // const onChange = jest.fn()

View File

@ -28,12 +28,12 @@ describe('Radio', () => {
{ sync: false }, { sync: false },
); );
await asyncExpect(() => { await asyncExpect(() => {
wrapper.trigger('mouseenter'); wrapper.find('label').trigger('mouseenter');
}); });
await asyncExpect(() => { await asyncExpect(() => {
expect(onMouseEnter).toHaveBeenCalled(); expect(onMouseEnter).toHaveBeenCalled();
}); });
wrapper.trigger('mouseleave'); wrapper.find('label').trigger('mouseleave');
await asyncExpect(() => { await asyncExpect(() => {
expect(onMouseLeave).toHaveBeenCalled(); expect(onMouseLeave).toHaveBeenCalled();
}); });

View File

@ -40,12 +40,7 @@ const Rate = {
characterRender(node, { index }) { characterRender(node, { index }) {
const { tooltips } = this.$props; const { tooltips } = this.$props;
if (!tooltips) return node; if (!tooltips) return node;
const tooltipsProps = { return <Tooltip title={tooltips[index]}>{node}</Tooltip>;
props: {
title: tooltips[index],
},
};
return <Tooltip {...tooltipsProps}>{node}</Tooltip>;
}, },
}, },
render() { render() {

View File

@ -1,4 +1,4 @@
import warning from 'warning'; import warning from '../_util/warning';
import omit from 'omit.js'; import omit from 'omit.js';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import { Select as VcSelect, Option, OptGroup } from '../vc-select'; import { Select as VcSelect, Option, OptGroup } from '../vc-select';
@ -96,7 +96,6 @@ const Select = {
name: 'ASelect', name: 'ASelect',
props: { props: {
...SelectProps, ...SelectProps,
prefixCls: PropTypes.string,
showSearch: PropTypes.bool.def(false), showSearch: PropTypes.bool.def(false),
transitionName: PropTypes.string.def('slide-up'), transitionName: PropTypes.string.def('slide-up'),
choiceTransitionName: PropTypes.string.def('zoom'), choiceTransitionName: PropTypes.string.def('zoom'),

View File

@ -1,5 +1,4 @@
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import { initDefaultProps } from '../_util/props-util';
const skeletonTitleProps = { const skeletonTitleProps = {
prefixCls: PropTypes.string, prefixCls: PropTypes.string,

View File

@ -16,8 +16,6 @@ const md = {
- 网络较慢需要长时间等待加载处理的情况下 - 网络较慢需要长时间等待加载处理的情况下
- 图文信息内容较多的列表/卡片中 - 图文信息内容较多的列表/卡片中
- 只适合用在第一次加载数据的场景
- 可以被 Spin 完全代替但是在可用的场景下可以比 Spin 提供更好的视觉效果和用户体验
## 代码演示`, ## 代码演示`,
us: `# Skeleton us: `# Skeleton
@ -27,8 +25,6 @@ const md = {
- When resource needs long time to load, like low network speed. - When resource needs long time to load, like low network speed.
- The component contains much information. Such as List or Card. - The component contains much information. Such as List or Card.
- Only works when loading data at first time.
- Could be replaced by Spin in all situation, but provide better user experience then spin if it works.
## Examples ## Examples
`, `,
}; };
@ -54,7 +50,7 @@ export default {
<List /> <List />
<br /> <br />
<api> <api>
<template slot='cn'> <template slot="cn">
<CN /> <CN />
</template> </template>
<US /> <US />

View File

@ -3,11 +3,11 @@
| Property | Description | Type | Default | | Property | Description | Type | Default |
| -------- | ----------- | ---- | ------- | | -------- | ----------- | ---- | ------- |
| autoFocus | get focus when component mounted | boolean | false | | autoFocus | get focus when component mounted | boolean | false |
| defaultValue | The default value of slider. When `range` is `false`, use `number`, otherwise, use `[number, number]` | number\|number\[] | 0 or [0, 0] | | defaultValue | The default value of slider. When `range` is `false`, use `number`, otherwise, use `[number, number]` | number\|number\[] | 0 or \[0, 0] |
| disabled | If true, the slider will not be interactable. | boolean | false | | disabled | If true, the slider will not be interactable. | boolean | false |
| dots | Whether the thumb can drag over tick only. | boolean | false | | dots | Whether the thumb can drag over tick only. | boolean | false |
| included | Make effect when `marks` not null`true` means containment and `false` means coordinative | boolean | true | | included | Make effect when `marks` not null`true` means containment and `false` means coordinative | boolean | true |
| marks | Tick mark of Slider, type of key must be `number`, and must in closed interval [min, max] each mark can declare its own style. | object | { number: string\|VNode } or { number: { style: object, label: string\|VNode } } or { number: () => VNode } | | marks | Tick mark of Slider, type of key must be `number`, and must in closed interval \[min, max], each mark can declare its own style. | object | { number: string\|VNode } or { number: { style: object, label: string\|VNode } } or { number: () => VNode } |
| max | The maximum value the slider can slide to | number | 100 | | max | The maximum value the slider can slide to | number | 100 |
| min | The minimum value the slider can slide to. | number | 0 | | min | The minimum value the slider can slide to. | number | 0 |
| range | dual thumb mode | boolean | false | | range | dual thumb mode | boolean | false |

View File

@ -6,6 +6,7 @@ import VcRange from '../vc-slider/src/Range';
import VcHandle from '../vc-slider/src/Handle'; import VcHandle from '../vc-slider/src/Handle';
import Tooltip from '../tooltip'; import Tooltip from '../tooltip';
import Base from '../base'; import Base from '../base';
import { ConfigConsumerProps } from '../config-provider';
// export interface SliderMarks { // export interface SliderMarks {
// [key]: React.ReactNode | { // [key]: React.ReactNode | {
@ -43,10 +44,11 @@ const Slider = {
event: 'change', event: 'change',
}, },
mixins: [BaseMixin], mixins: [BaseMixin],
inject: {
configProvider: { default: () => ConfigConsumerProps },
},
props: { props: {
...SliderProps(), ...SliderProps(),
prefixCls: PropTypes.string.def('ant-slider'),
tooltipPrefixCls: PropTypes.string.def('ant-tooltip'),
tipFormatter: PropTypes.oneOfType([PropTypes.func, PropTypes.object]).def(value => tipFormatter: PropTypes.oneOfType([PropTypes.func, PropTypes.object]).def(value =>
value.toString(), value.toString(),
), ),
@ -65,8 +67,8 @@ const Slider = {
}, },
})); }));
}, },
handleWithTooltip({ value, dragging, index, directives, on, ...restProps }) { handleWithTooltip(tooltipPrefixCls, { value, dragging, index, directives, on, ...restProps }) {
const { tooltipPrefixCls, tipFormatter, tooltipVisible } = this.$props; const { tipFormatter, tooltipVisible } = this.$props;
const { visibles } = this; const { visibles } = this;
const isTipFormatter = tipFormatter ? visibles[index] || dragging : false; const isTipFormatter = tipFormatter ? visibles[index] || dragging : false;
const visible = tooltipVisible || (tooltipVisible === undefined && isTipFormatter); const visible = tooltipVisible || (tooltipVisible === undefined && isTipFormatter);
@ -106,12 +108,22 @@ const Slider = {
}, },
}, },
render() { render() {
const { range, ...restProps } = getOptionProps(this); const {
range,
prefixCls: customizePrefixCls,
tooltipPrefixCls: customizeTooltipPrefixCls,
...restProps
} = getOptionProps(this);
const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('skeleton', customizePrefixCls);
const tooltipPrefixCls = getPrefixCls('skeleton', customizeTooltipPrefixCls);
if (range) { if (range) {
const vcRangeProps = { const vcRangeProps = {
props: { props: {
...restProps, ...restProps,
handle: this.handleWithTooltip, prefixCls,
tooltipPrefixCls,
handle: info => this.handleWithTooltip(tooltipPrefixCls, info),
}, },
ref: 'sliderRef', ref: 'sliderRef',
on: this.$listeners, on: this.$listeners,
@ -121,7 +133,9 @@ const Slider = {
const vcSliderProps = { const vcSliderProps = {
props: { props: {
...restProps, ...restProps,
handle: this.handleWithTooltip, prefixCls,
tooltipPrefixCls,
handle: info => this.handleWithTooltip(tooltipPrefixCls, info),
}, },
ref: 'sliderRef', ref: 'sliderRef',
on: this.$listeners, on: this.$listeners,

View File

@ -3,11 +3,11 @@
| 参数 | 说明 | 类型 | 默认值 | | 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| autoFocus | 自动获取焦点 | boolean | false | | autoFocus | 自动获取焦点 | boolean | false |
| defaultValue | 设置初始取值。当 `range``false` 时,使用 `number`,否则用 `[number, number]` | number\|number\[] | 0 or [0, 0] | | defaultValue | 设置初始取值。当 `range``false` 时,使用 `number`,否则用 `[number, number]` | number\|number\[] | 0 or \[0, 0] |
| disabled | 值为 `true` 时,滑块为禁用状态 | boolean | false | | disabled | 值为 `true` 时,滑块为禁用状态 | boolean | false |
| dots | 是否只能拖拽到刻度上 | boolean | false | | dots | 是否只能拖拽到刻度上 | boolean | false |
| included | `marks` 不为空对象时有效,值为 true 时表示值为包含关系false 表示并列 | boolean | true | | included | `marks` 不为空对象时有效,值为 true 时表示值为包含关系false 表示并列 | boolean | true |
| marks | 刻度标记key 的类型必须为 `number` 且取值在闭区间 [min, max] 内,每个标签可以单独设置样式 | object | { number: string\|VNode } or { number: { style: object, label: string\|VNode } } or { number: () => VNode } | | marks | 刻度标记key 的类型必须为 `number` 且取值在闭区间 \[min, max] 内,每个标签可以单独设置样式 | object | { number: string\|VNode } or { number: { style: object, label: string\|VNode } } or { number: () => VNode } |
| max | 最大值 | number | 100 | | max | 最大值 | number | 100 |
| min | 最小值 | number | 0 | | min | 最小值 | number | 0 |
| range | 双滑块模式 | boolean | false | | range | 双滑块模式 | boolean | false |

View File

@ -51,10 +51,11 @@ export default {
}, },
data() { data() {
const { spinning, delay } = this; const { spinning, delay } = this;
const shouldBeDelayed = shouldDelay(spinning, delay);
this.originalUpdateSpinning = this.updateSpinning; this.originalUpdateSpinning = this.updateSpinning;
this.debouncifyUpdateSpinning(this.$props); this.debouncifyUpdateSpinning(this.$props);
return { return {
sSpinning: spinning && !shouldDelay(spinning, delay), sSpinning: spinning && !shouldBeDelayed,
}; };
}, },
mounted() { mounted() {

View File

@ -0,0 +1,69 @@
import { mount } from '@vue/test-utils';
import { asyncExpect } from '@/tests/utils';
import Spin from '..';
describe('delay spinning', () => {
it("should render with delay when it's mounted with spinning=true and delay", async () => {
const props = {
propsData: {
delay: 500,
spinning: true,
},
sync: false,
};
const wrapper = mount(Spin, props);
await asyncExpect(() => {
expect(
wrapper
.find('.ant-spin')
.classes()
.includes('ant-spin-spinning'),
).toEqual(false);
});
});
it('should render when delay is init set', async () => {
const props = {
propsData: {
delay: 100,
spinning: true,
},
sync: false,
};
const wrapper = mount(Spin, props);
expect(
wrapper
.findAll('.ant-spin')
.at(0)
.classes()
.includes('ant-spin-spinning'),
).toEqual(false);
// use await not jest.runAllTimers()
// because of https://github.com/facebook/jest/issues/3465
await new Promise(resolve => setTimeout(resolve, 500));
expect(
wrapper
.findAll('.ant-spin')
.at(0)
.classes()
.includes('ant-spin-spinning'),
).toEqual(true);
});
it('should cancel debounce function when unmount', async () => {
const props = {
propsData: {
delay: 100,
spinning: true,
},
sync: false,
};
const wrapper = mount(Spin, props);
const spy = jest.spyOn(wrapper.vm.updateSpinning, 'cancel');
expect(wrapper.vm.updateSpinning.cancel).toEqual(expect.any(Function));
expect(spy).not.toHaveBeenCalled();
});
});

View File

@ -17,24 +17,16 @@ export default {
format: 'HH:mm:ss', format: 'HH:mm:ss',
}), }),
data() { created() {
return { this.countdownId = undefined;
uniKey: 0,
};
}, },
countdownId: undefined,
mounted() { mounted() {
this.$nextTick(() => {
this.syncTimer(); this.syncTimer();
});
}, },
updated() { updated() {
this.$nextTick(() => {
this.syncTimer(); this.syncTimer();
});
}, },
beforeDestroy() { beforeDestroy() {
@ -53,11 +45,9 @@ export default {
}, },
startTimer() { startTimer() {
if (this.countdownId) { if (this.countdownId) return;
return;
}
this.countdownId = window.setInterval(() => { this.countdownId = window.setInterval(() => {
this.uniKey++; this.$refs.statistic.$forceUpdate();
}, REFRESH_INTERVAL); }, REFRESH_INTERVAL);
}, },
@ -74,7 +64,7 @@ export default {
} }
}, },
formatCountdown(value, config) { formatCountdown({ value, config }) {
const { format } = this.$props; const { format } = this.$props;
return formatCountdown(value, { ...config, format }); return formatCountdown(value, { ...config, format });
}, },
@ -91,13 +81,14 @@ export default {
render() { render() {
return ( return (
<Statistic <Statistic
key={this.uniKey} ref="statistic"
{...{ {...{
props: { props: {
...this.$props, ...this.$props,
valueRender: this.valueRenderHtml, valueRender: this.valueRenderHtml,
formatter: this.formatCountdown, formatter: this.formatCountdown,
}, },
on: this.$listeners,
}} }}
/> />
); );

View File

@ -12,12 +12,11 @@ export default {
groupSeparator = '', groupSeparator = '',
prefixCls, prefixCls,
} = context.props; } = context.props;
let valueNode; let valueNode;
if (typeof formatter === 'function') { if (typeof formatter === 'function') {
// Customize formatter // Customize formatter
valueNode = formatter(value); valueNode = formatter({ value, h });
} else { } else {
// Internal formatter // Internal formatter
const val = String(value); const val = String(value);

View File

@ -8,7 +8,7 @@ export const StatisticProps = {
decimalSeparator: PropTypes.string, decimalSeparator: PropTypes.string,
groupSeparator: PropTypes.string, groupSeparator: PropTypes.string,
format: PropTypes.string, format: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object]),
valueStyle: PropTypes.any, valueStyle: PropTypes.any,
valueRender: PropTypes.any, valueRender: PropTypes.any,
formatter: PropTypes.any, formatter: PropTypes.any,
@ -21,7 +21,6 @@ export const StatisticProps = {
export default { export default {
name: 'AStatistic', name: 'AStatistic',
props: initDefaultProps(StatisticProps, { props: initDefaultProps(StatisticProps, {
prefixCls: 'ant-statistic',
decimalSeparator: '.', decimalSeparator: '.',
groupSeparator: ',', groupSeparator: ',',
}), }),
@ -37,9 +36,9 @@ export default {
const title = getComponentFromProp(this, 'title'); const title = getComponentFromProp(this, 'title');
let prefix = getComponentFromProp(this, 'prefix'); let prefix = getComponentFromProp(this, 'prefix');
let suffix = getComponentFromProp(this, 'suffix'); let suffix = getComponentFromProp(this, 'suffix');
const formatter = getComponentFromProp(this, 'formatter'); const formatter = getComponentFromProp(this, 'formatter', {}, false);
let valueNode = ( let valueNode = (
<StatisticNumber {...{ props: this.$props }} value={value} formatter={formatter} /> <StatisticNumber {...{ props: { ...this.$props, prefixCls, value, formatter } }} />
); );
if (valueRender) { if (valueRender) {
valueNode = valueRender(valueNode); valueNode = valueRender(valueNode);

View File

@ -0,0 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Statistic support negetive number 1`] = `
<div class="ant-statistic">
<div class="ant-statistic-title">Account Balance (CNY)</div>
<div class="ant-statistic-content"><span class="ant-statistic-content-value"><span class="ant-statistic-content-value-int">-112,893</span><span class="ant-statistic-content-value-decimal">.12</span></span></div>
</div>
`;

View File

@ -0,0 +1,107 @@
import { mount } from '@vue/test-utils';
import { asyncExpect } from '@/tests/utils';
import MockDate from 'mockdate';
import moment from 'moment';
import Statistic from '..';
describe('Statistic', () => {
beforeAll(() => {
MockDate.set(moment('2018-11-28 00:00:00'));
});
afterAll(() => {
MockDate.reset();
});
it('customize formatter', () => {
const formatter = jest.fn(() => 93);
const props = {
propsData: {
value: 1128,
formatter,
},
};
const wrapper = mount(Statistic, props);
expect(formatter).toBeCalledWith(expect.objectContaining({ value: 1128 }));
expect(wrapper.find('.ant-statistic-content-value').text()).toEqual('93');
});
it('groupSeparator', () => {
const props = {
propsData: {
value: 1128,
groupSeparator: '__TEST__',
},
};
const wrapper = mount(Statistic, props);
expect(wrapper.find('.ant-statistic-content-value').text()).toEqual('1__TEST__128');
});
it('not a number', () => {
const props = {
propsData: {
value: 'bamboo',
},
};
const wrapper = mount(Statistic, props);
expect(wrapper.find('.ant-statistic-content-value').text()).toEqual('bamboo');
});
it('support negetive number', () => {
const props = {
propsData: {
title: 'Account Balance (CNY)',
value: -112893.12345,
precision: 2,
},
};
const wrapper = mount(Statistic, props);
expect(wrapper.html()).toMatchSnapshot();
});
describe('Countdown', () => {
it('render correctly', () => {
const now = moment()
.add(2, 'd')
.add(11, 'h')
.add(28, 'm')
.add(9, 's')
.add(3, 'ms');
[
['H:m:s', '59:28:9'],
['HH:mm:ss', '59:28:09'],
['HH:mm:ss:SSS', '59:28:09:003'],
['DD-HH:mm:ss', '02-11:28:09'],
].forEach(([format, value]) => {
const props = {
propsData: {
format,
value: now,
},
};
const wrapper = mount(Statistic.Countdown, props);
expect(wrapper.find('.ant-statistic-content-value').text()).toEqual(value);
});
});
it('time going', async () => {
const now = Date.now() + 1000;
const props = {
propsData: {
value: now,
},
};
const wrapper = mount(Statistic.Countdown, props);
// setInterval should work
const instance = wrapper.vm;
expect(instance.countdownId).not.toBe(undefined);
// await delay(50);
// wrapper.unmount();
// expect(instance.countdownId).toBe(undefined);
});
});
});

View File

@ -5,13 +5,14 @@
| Property | Description | Type | Default | | Property | Description | Type | Default |
| -------- | ----------- | ---- | ------- | | -------- | ----------- | ---- | ------- |
| decimalSeparator | decimal separator | string | . | | decimalSeparator | decimal separator | string | . |
| formatter | customize value display logic | v-slot \|(h) => VNode | - | | formatter | customize value display logic | v-slot \|({h, value}) => VNode | - |
| groupSeparator | group separator | string | , | | groupSeparator | group separator | string | , |
| precision | precision of input value | number | - | | precision | precision of input value | number | - |
| prefix | prefix node of value | string \| v-slot | - | | prefix | prefix node of value | string \| v-slot | - |
| suffix | suffix node of value | string \| v-slot | - | | suffix | suffix node of value | string \| v-slot | - |
| title | Display title | string \| v-slot | - | | title | Display title | string \| v-slot | - |
| value | Display value | string \| number | - | | value | Display value | string \| number | - |
| valueStyle | Set value css style | style | - |
### Statistic.Countdown ### Statistic.Countdown
@ -23,6 +24,7 @@
| suffix | suffix node of value | string \| v-slot | - | | suffix | suffix node of value | string \| v-slot | - |
| title | Display title | string \| v-slot | - | | title | Display title | string \| v-slot | - |
| value | Set target countdown time | number \| moment | - | | value | Set target countdown time | number \| moment | - |
| valueStyle | Set value css style | style | - |
#### Statistic.Countdown Events #### Statistic.Countdown Events
| Events Name | Description | Arguments | | Events Name | Description | Arguments |

View File

@ -1,9 +1,11 @@
import Statistic from './Statistic'; import Statistic from './Statistic';
import Countdown from './Countdown'; import Countdown from './Countdown';
import Base from '../base';
Statistic.Countdown = Countdown; Statistic.Countdown = Countdown;
/* istanbul ignore next */ /* istanbul ignore next */
Statistic.install = function(Vue) { Statistic.install = function(Vue) {
Vue.use(Base);
Vue.component(Statistic.name, Statistic); Vue.component(Statistic.name, Statistic);
Vue.component(Statistic.Countdown.name, Statistic.Countdown); Vue.component(Statistic.Countdown.name, Statistic.Countdown);
}; };

View File

@ -5,13 +5,14 @@
| 参数 | 说明 | 类型 | 默认值 | | 参数 | 说明 | 类型 | 默认值 |
| -------- | ----------- | ---- | ------- | | -------- | ----------- | ---- | ------- |
| decimalSeparator | 设置小数点 | string | . | | decimalSeparator | 设置小数点 | string | . |
| formatter | 自定义数值展示 | v-slot \| (h) => VNode | - | | formatter | 自定义数值展示 | v-slot \| ({h, value}) => VNode | - |
| groupSeparator | 设置千分位标识符 | string | , | | groupSeparator | 设置千分位标识符 | string | , |
| precision | 数值精度 | number | - | | precision | 数值精度 | number | - |
| prefix | 设置数值的前缀 | string \| v-slot | - | | prefix | 设置数值的前缀 | string \| v-slot | - |
| suffix | 设置数值的后缀 | string \| v-slot | - | | suffix | 设置数值的后缀 | string \| v-slot | - |
| title | 数值的标题 | string \| v-slot | - | | title | 数值的标题 | string \| v-slot | - |
| value | 数值内容 | string \| number | - | | value | 数值内容 | string \| number | - |
| valueStyle | 设置数值的样式 | style | - |
### Statistic.Countdown ### Statistic.Countdown
@ -22,6 +23,7 @@
| suffix | 设置数值的后缀 | string \| v-slot | - | | suffix | 设置数值的后缀 | string \| v-slot | - |
| title | 数值的标题 | string \| v-slot | - | | title | 数值的标题 | string \| v-slot | - |
| value | 数值内容 | number \| moment | - | | value | 数值内容 | number \| moment | - |
| valueStyle | 设置数值的样式 | style | - |
#### Statistic.Countdown事件 #### Statistic.Countdown事件
| 事件名称 | 说明 | 回调参数 | | 事件名称 | 说明 | 回调参数 |

View File

@ -141,7 +141,6 @@ export default {
const switcherOriginCls = getClass(switcherIcon[0]); const switcherOriginCls = getClass(switcherIcon[0]);
return cloneElement(switcherIcon, { return cloneElement(switcherIcon, {
class: { class: {
...switcherOriginCls,
[switcherCls]: true, [switcherCls]: true,
}, },
}); });

View File

@ -16,7 +16,7 @@ You can customize icons for different nodes.
defaultExpandAll defaultExpandAll
:defaultSelectedKeys="['0-0-0']" :defaultSelectedKeys="['0-0-0']"
> >
<a-icon type="down" slot="switcherIcon" /> <a-icon type="down" slot="switcherIcon" class="test" />
<a-icon slot="smile" type="smile-o" /> <a-icon slot="smile" type="smile-o" />
<a-icon slot="meh" type="smile-o" /> <a-icon slot="meh" type="smile-o" />
<template slot="custom" slot-scope="{selected}"> <template slot="custom" slot-scope="{selected}">

View File

@ -24,7 +24,7 @@
| multiple | Allows selecting multiple treeNodes | boolean | false | | multiple | Allows selecting multiple treeNodes | boolean | false |
| selectedKeys(.sync) | (Controlled) Specifies the keys of the selected treeNodes | string\[] \| number\[] | - | | selectedKeys(.sync) | (Controlled) Specifies the keys of the selected treeNodes | string\[] \| number\[] | - |
| showIcon | Shows the icon before a TreeNode's title. There is no default style; you must set a custom style for it if set to `true` | boolean | false | | showIcon | Shows the icon before a TreeNode's title. There is no default style; you must set a custom style for it if set to `true` | boolean | false |
| switcherIcon | customize collapse/expand icon of tree node | slot(vnode) | - | | switcherIcon | customize collapse/expand icon of tree node | slot | - |
| showLine | Shows a connecting line | boolean | false | | showLine | Shows a connecting line | boolean | false |
### Events ### Events

View File

@ -24,7 +24,7 @@
| multiple | 支持点选多个节点(节点本身) | boolean | false | | multiple | 支持点选多个节点(节点本身) | boolean | false |
| selectedKeys(.sync) | (受控)设置选中的树节点 | string\[] \| number\[] | - | | selectedKeys(.sync) | (受控)设置选中的树节点 | string\[] \| number\[] | - |
| showIcon | 是否展示 TreeNode title 前的图标,没有默认样式,如设置为 true需要自行定义图标相关样式 | boolean | false | | showIcon | 是否展示 TreeNode title 前的图标,没有默认样式,如设置为 true需要自行定义图标相关样式 | boolean | false |
| switcherIcon | 自定义树节点的展开/折叠图标 | slot(vnode) | - | | switcherIcon | 自定义树节点的展开/折叠图标 | slot | - |
| showLine | 是否展示连接线 | boolean | false | | showLine | 是否展示连接线 | boolean | false |

2
types/rate.d.ts vendored
View File

@ -60,6 +60,8 @@ export declare class Rate extends AntdComponent {
*/ */
value: number; value: number;
tooltips: Array<string>;
/** /**
* remove focus * remove focus
*/ */

View File

@ -0,0 +1,34 @@
// Project: https://github.com/vueComponent/ant-design-vue
// Definitions by: akki-jat <https://github.com/akki-jat>
// Definitions: https://github.com/vueComponent/ant-design-vue/types
import { AntdComponent } from '../component';
import { VNode } from 'vue';
export declare class AStatisticCountdown extends AntdComponent {
format: string;
/**
* prefix node of value
* @type string | VNode
*/
prefix: string | VNode;
/**
* suffix node of value
* @type string | VNode
*/
suffix: string | VNode;
/**
* Display title
* @type string | VNode
*/
title: string | VNode;
/**
* Display value
* @type string or number
*/
value: string | number;
valueStyle: object;
}

View File

@ -2,10 +2,12 @@
// Definitions by: akki-jat <https://github.com/akki-jat> // Definitions by: akki-jat <https://github.com/akki-jat>
// Definitions: https://github.com/vueComponent/ant-design-vue/types // Definitions: https://github.com/vueComponent/ant-design-vue/types
import { AntdComponent } from './component'; import { AntdComponent } from '../component';
import { VNode } from 'vue'; import { VNode } from 'vue';
import AStatisticCountdown from './statistic-countdown';
export declare class Statistic extends AntdComponent { export declare class Statistic extends AntdComponent {
static AStatisticCountdown: typeof AStatisticCountdown;
/** /**
* decimal separator * decimal separator
* @default '.' * @default '.'