Merge remote-tracking branch 'origin/master' into 2.0

pull/2144/head
tanjinzhou 2020-04-23 15:45:01 +08:00
commit c4a72238c0
24 changed files with 150 additions and 76 deletions

View File

@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@master uses: actions/checkout@v2
- name: cache package-lock.json - name: cache package-lock.json
uses: actions/cache@v1 uses: actions/cache@v1
@ -40,7 +40,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@master uses: actions/checkout@v2
- name: restore cache from package-lock.json - name: restore cache from package-lock.json
uses: actions/cache@v1 uses: actions/cache@v1
@ -74,7 +74,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@master uses: actions/checkout@v2
- name: restore cache from package-lock.json - name: restore cache from package-lock.json
uses: actions/cache@v1 uses: actions/cache@v1
@ -96,12 +96,12 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@master uses: actions/checkout@v2
with: with:
token: ${{ secrets.ACCESS_TOKEN }} token: ${{ secrets.ACCESS_TOKEN }}
- name: Checkout submodules - name: Checkout submodules
uses: actions/checkout@master uses: actions/checkout@v2
with: with:
repository: tangjinzhou/antdv-demo repository: tangjinzhou/antdv-demo
token: ${{ secrets.ACCESS_TOKEN }} token: ${{ secrets.ACCESS_TOKEN }}
@ -122,4 +122,3 @@ jobs:
- name: test - name: test
run: npm test run: npm test
needs: setup needs: setup

View File

@ -42,7 +42,7 @@ export default {
renderComponent(props = {}, ready) { renderComponent(props = {}, ready) {
const { visible, forceRender, getContainer, parent } = this; const { visible, forceRender, getContainer, parent } = this;
const self = this; const self = this;
if (visible || parent.$refs._component || forceRender) { if (visible || parent._component || parent.$refs._component || forceRender) {
let el = this.componentEl; let el = this.componentEl;
if (!this.container) { if (!this.container) {
this.container = getContainer(); this.container = getContainer();
@ -50,12 +50,14 @@ export default {
this.componentEl = el; this.componentEl = el;
this.container.appendChild(el); this.container.appendChild(el);
} }
// self.getComponent render render
const com = { component: self.getComponent(props) };
if (!this._component) { if (!this._component) {
this._component = new this.$root.constructor({ this._component = new this.$root.constructor({
el, el,
parent: self, parent: self,
data: { data: {
comProps: props, _com: com,
}, },
mounted() { mounted() {
this.$nextTick(() => { this.$nextTick(() => {
@ -72,17 +74,16 @@ export default {
}); });
}, },
methods: { methods: {
forceRender(p) { setComponent(_com) {
this.comProps = p; this.$data._com = _com;
this.$forceUpdate();
}, },
}, },
render() { render() {
return self.getComponent(this.comProps); return this.$data._com.component;
}, },
}); });
} else { } else {
this._component.forceRender(props); this._component.setComponent(com);
} }
} }
}, },

View File

@ -81,8 +81,10 @@ export default {
}, },
getBadgeClassName(prefixCls) { getBadgeClassName(prefixCls) {
const children = filterEmpty(this.$slots.default); const children = filterEmpty(this.$slots.default);
const hasStatus = this.hasStatus();
return classNames(prefixCls, { return classNames(prefixCls, {
[`${prefixCls}-status`]: this.hasStatus(), [`${prefixCls}-status`]: hasStatus,
[`${prefixCls}-dot-status`]: hasStatus && this.dot && !this.isZero(),
[`${prefixCls}-not-a-wrapper`]: !children.length, [`${prefixCls}-not-a-wrapper`]: !children.length,
}); });
}, },

View File

@ -114,6 +114,10 @@
} }
} }
&-dot-status {
line-height: 1;
}
&-zoom-appear, &-zoom-appear,
&-zoom-enter { &-zoom-enter {
animation: antZoomBadgeIn 0.3s @ease-out-back; animation: antZoomBadgeIn 0.3s @ease-out-back;

View File

@ -26,6 +26,7 @@ const ConfigProvider = {
autoInsertSpaceInButton: PropTypes.bool, autoInsertSpaceInButton: PropTypes.bool,
locale: PropTypes.object, locale: PropTypes.object,
pageHeader: PropTypes.object, pageHeader: PropTypes.object,
transformCellText: PropTypes.func,
}, },
provide() { provide() {
const _self = this; const _self = this;
@ -43,7 +44,14 @@ const ConfigProvider = {
}; };
}, },
watch: { watch: {
...getWatch(['prefixCls', 'csp', 'autoInsertSpaceInButton', 'locale', 'pageHeader']), ...getWatch([
'prefixCls',
'csp',
'autoInsertSpaceInButton',
'locale',
'pageHeader',
'transformCellText',
]),
}, },
methods: { methods: {
renderEmptyComponent(h, name) { renderEmptyComponent(h, name) {

View File

@ -35,6 +35,7 @@ export const PickerProps = () => ({
autoFocus: PropTypes.bool, autoFocus: PropTypes.bool,
tagPrefixCls: PropTypes.string, tagPrefixCls: PropTypes.string,
tabIndex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), tabIndex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
align: PropTypes.object.def(() => ({})),
}); });
export const SinglePickerProps = () => ({ export const SinglePickerProps = () => ({

View File

@ -17,6 +17,7 @@ export const FormProps = {
hideRequiredMark: PropTypes.bool, hideRequiredMark: PropTypes.bool,
model: PropTypes.object, model: PropTypes.object,
rules: PropTypes.object, rules: PropTypes.object,
validateMessages: PropTypes.any,
validateOnRuleChange: PropTypes.bool, validateOnRuleChange: PropTypes.bool,
}; };

View File

@ -142,6 +142,9 @@ export default {
} }
descriptor[this.prop] = rules; descriptor[this.prop] = rules;
const validator = new AsyncValidator(descriptor); const validator = new AsyncValidator(descriptor);
if (this.FormContext && this.FormContext.validateMessages) {
validator.messages(this.FormContext.validateMessages);
}
const model = {}; const model = {};
model[this.prop] = this.fieldValue; model[this.prop] = this.fieldValue;
validator.validate(model, { firstFields: true }, (errors, invalidFields) => { validator.validate(model, { firstFields: true }, (errors, invalidFields) => {

View File

@ -160,7 +160,7 @@
} }
.@{slider-prefix-cls}-handle { .@{slider-prefix-cls}-handle {
margin-bottom: -7px; margin-top: -6px;
margin-left: -5px; margin-left: -5px;
} }

View File

@ -1161,6 +1161,7 @@ export default {
dropdownPrefixCls, dropdownPrefixCls,
contextLocale, contextLocale,
getPopupContainer: contextGetPopupContainer, getPopupContainer: contextGetPopupContainer,
transformCellText,
}) { }) {
const { showHeader, locale, getPopupContainer, ...restProps } = getOptionProps(this); const { showHeader, locale, getPopupContainer, ...restProps } = getOptionProps(this);
const data = this.getCurrentPageData(); const data = this.getCurrentPageData();
@ -1217,6 +1218,7 @@ export default {
expandIconColumnIndex, expandIconColumnIndex,
expandIconAsCell, expandIconAsCell,
emptyText: mergedLocale.emptyText, emptyText: mergedLocale.emptyText,
transformCellText,
}, },
on: getListeners(this), on: getListeners(this),
class: classString, class: classString,
@ -1227,10 +1229,18 @@ export default {
}, },
render() { render() {
const { prefixCls: customizePrefixCls, dropdownPrefixCls: customizeDropdownPrefixCls } = this; const {
prefixCls: customizePrefixCls,
dropdownPrefixCls: customizeDropdownPrefixCls,
transformCellText: customizeTransformCellText,
} = this;
const data = this.getCurrentPageData(); const data = this.getCurrentPageData();
const { getPopupContainer: getContextPopupContainer } = this.configProvider; const {
getPopupContainer: getContextPopupContainer,
transformCellText: tct,
} = this.configProvider;
const getPopupContainer = this.getPopupContainer || getContextPopupContainer; const getPopupContainer = this.getPopupContainer || getContextPopupContainer;
const transformCellText = customizeTransformCellText || tct;
let loading = this.loading; let loading = this.loading;
if (typeof loading === 'boolean') { if (typeof loading === 'boolean') {
loading = { loading = {
@ -1260,6 +1270,7 @@ export default {
dropdownPrefixCls, dropdownPrefixCls,
contextLocale: locale, contextLocale: locale,
getPopupContainer, getPopupContainer,
transformCellText,
}) })
} }
/> />

View File

@ -11,7 +11,9 @@ describe('Table.filter', () => {
beforeEach(() => { beforeEach(() => {
document.body.innerHTML = ''; document.body.innerHTML = '';
}); });
const filterFn = (value, record) => record.name.indexOf(value) !== -1; const filterFn = (value, record) => {
return record.name.indexOf(value) !== -1;
};
const column = { const column = {
title: 'Name', title: 'Name',
dataIndex: 'name', dataIndex: 'name',
@ -49,6 +51,7 @@ describe('Table.filter', () => {
...listeners, ...listeners,
}, },
sync: false, sync: false,
attachToDocument: true,
}; };
} }
@ -309,24 +312,15 @@ describe('Table.filter', () => {
], ],
}), }),
); );
// jest.useFakeTimers()
const dropdownWrapper = mount(
{
render() {
return wrapper.find({ name: 'Trigger' }).vm.getComponent();
},
},
{ sync: false, attachToDocument: true },
);
await asyncExpect(() => { await asyncExpect(() => {
dropdownWrapper $$('.ant-dropdown-trigger')[0].click();
.findAll('.ant-dropdown-menu-submenu-title')
.at(0)
.trigger('mouseenter');
}); });
await asyncExpect(() => {
$$('.ant-dropdown-menu-submenu-title')[0].dispatchEvent(new MouseEvent('mouseenter'));
}, 0);
await asyncExpect(() => { await asyncExpect(() => {
$$('.ant-dropdown-menu-submenu-title')[1].dispatchEvent(new MouseEvent('mouseenter')); $$('.ant-dropdown-menu-submenu-title')[1].dispatchEvent(new MouseEvent('mouseenter'));
}, 1000); }, 500);
await asyncExpect(() => { await asyncExpect(() => {
const menuItem = $$('.ant-dropdown-menu-item'); const menuItem = $$('.ant-dropdown-menu-item');
menuItem[menuItem.length - 1].click(); menuItem[menuItem.length - 1].click();
@ -439,22 +433,35 @@ describe('Table.filter', () => {
}); });
}); });
// it('confirm filter when dropdown hidden', (done) => { fit('confirm filter when dropdown hidden', async () => {
// const handleChange = jest.fn() const handleChange = jest.fn();
// const wrapper = mount(Table, { ...getTableOptions({ const wrapper = mount(Table, {
// columns: [{ ...getTableOptions(
// ...column, {
// filters: [ columns: [
// { text: 'Jack', value: 'Jack' }, {
// { text: 'Lucy', value: 'Lucy' }, ...column,
// ], filters: [
// }], { text: 'Jack', value: 'Jack' },
// }, { change: handleChange }), attachToDocument: true }) { text: 'Lucy', value: 'Lucy' },
],
},
],
},
{ change: handleChange },
),
attachToDocument: true,
});
await asyncExpect(() => {
wrapper.find('.ant-dropdown-trigger').trigger('click');
}, 0);
await asyncExpect(() => {
$$('.ant-dropdown-menu-item')[0].click();
}, 500);
await asyncExpect(() => {
wrapper.find('.ant-dropdown-trigger').trigger('click');
}, 500);
// wrapper.find('.ant-dropdown-trigger').first().simulate('click') expect(handleChange).toBeCalled();
// wrapper.find('.ant-dropdown-menu-item').first().simulate('click') });
// wrapper.find('.ant-dropdown-trigger').first().simulate('click')
// expect(handleChange).toBeCalled()
// })
}); });

View File

@ -135,6 +135,7 @@ export const TableProps = {
tableLayout: PropTypes.string, tableLayout: PropTypes.string,
getPopupContainer: PropTypes.func, getPopupContainer: PropTypes.func,
expandIcon: PropTypes.func, expandIcon: PropTypes.func,
transformCellText: PropTypes.func,
// className?: PropTypes.string, // className?: PropTypes.string,
// style?: React.CSSProperties; // style?: React.CSSProperties;
// children?: React.ReactNode; // children?: React.ReactNode;

View File

@ -169,7 +169,7 @@ export default {
$slots[slots.switcherIcon] || $slots[slots.switcherIcon] ||
restProps.switcherIcon, restProps.switcherIcon,
title: title:
($scopedSlots[scopedSlots.title] && $scopedSlots[scopedSlots.title](item)) || $scopedSlots[scopedSlots.title] ||
$slots[slots.title] || $slots[slots.title] ||
restProps[replaceFields.title], restProps[replaceFields.title],
dataRef: item, dataRef: item,

View File

@ -32,6 +32,10 @@ export default {
visible(val) { visible(val) {
if (!val) { if (!val) {
this.lastVisible = val; this.lastVisible = val;
} else {
this.$nextTick(() => {
this.scrollActiveItemToView();
});
} }
}, },
}, },
@ -50,11 +54,11 @@ export default {
}, },
updated() { updated() {
const props = this.$props; const props = this.$props;
if (!this.prevVisible && props.visible) { // if (!this.prevVisible && props.visible) {
this.$nextTick(() => { // this.$nextTick(() => {
this.scrollActiveItemToView(); // this.scrollActiveItemToView();
}); // });
} // }
this.lastVisible = props.visible; this.lastVisible = props.visible;
this.lastInputValue = props.inputValue; this.lastInputValue = props.inputValue;
this.prevVisible = this.visible; this.prevVisible = this.visible;

View File

@ -67,6 +67,7 @@ export default {
expandRowByClick: PropTypes.bool, expandRowByClick: PropTypes.bool,
expandIcon: PropTypes.func, expandIcon: PropTypes.func,
tableLayout: PropTypes.string, tableLayout: PropTypes.string,
transformCellText: PropTypes.func,
}, },
{ {
data: [], data: [],

View File

@ -21,6 +21,9 @@ export default {
expandIcon: PropTypes.any, expandIcon: PropTypes.any,
component: PropTypes.any, component: PropTypes.any,
}, },
inject: {
table: { default: () => ({}) },
},
methods: { methods: {
handleClick(e) { handleClick(e) {
const { const {
@ -45,6 +48,7 @@ export default {
component: BodyCell, component: BodyCell,
} = this; } = this;
const { dataIndex, customRender, className = '' } = column; const { dataIndex, customRender, className = '' } = column;
const { transformCellText } = this.table;
// We should return undefined if no dataIndex is specified, but in order to // We should return undefined if no dataIndex is specified, but in order to
// be compatible with object-path's behavior, we return the record object instead. // be compatible with object-path's behavior, we return the record object instead.
let text; let text;
@ -87,6 +91,10 @@ export default {
text = null; text = null;
} }
if (transformCellText) {
text = transformCellText({ text, column, record, index });
}
const indentText = expandIcon ? ( const indentText = expandIcon ? (
<span <span
style={{ paddingLeft: `${indentSize * indent}px` }} style={{ paddingLeft: `${indentSize * indent}px` }}

View File

@ -224,14 +224,14 @@ const Select = {
this.forcePopupAlign(); this.forcePopupAlign();
}); });
}, },
'$data._open'() { '$data._open'(open) {
this.$nextTick(() => { this.$nextTick(() => {
const { prefixCls } = this.$props; const { prefixCls } = this.$props;
const { _selectorValueList: selectorValueList, _valueEntities: valueEntities } = this.$data; const { _selectorValueList: selectorValueList, _valueEntities: valueEntities } = this.$data;
const isMultiple = this.isMultiple(); const isMultiple = this.isMultiple();
// Scroll to value position, only need sync on single mode // Scroll to value position, only need sync on single mode
if (!isMultiple && selectorValueList.length && this.popup) { if (!isMultiple && selectorValueList.length && open && this.popup) {
const { value } = selectorValueList[0]; const { value } = selectorValueList[0];
const { domTreeNodes } = this.popup.getTree(); const { domTreeNodes } = this.popup.getTree();
const { key } = valueEntities[value] || {}; const { key } = valueEntities[value] || {};
@ -823,7 +823,7 @@ const Select = {
onDropdownVisibleChange(open) { onDropdownVisibleChange(open) {
const { multiple, treeCheckable } = this.$props; const { multiple, treeCheckable } = this.$props;
const { _searchValue } = this; const { _searchValue } = this.$data;
// When set open success and single mode, // When set open success and single mode,
// we will reset the input content. // we will reset the input content.

View File

@ -345,7 +345,7 @@ const TreeNode = {
class={classNames(`${prefixCls}-switcher`, `${prefixCls}-switcher-noop`)} class={classNames(`${prefixCls}-switcher`, `${prefixCls}-switcher-noop`)}
> >
{typeof switcherIcon === 'function' {typeof switcherIcon === 'function'
? switcherIcon({ ...this.$props, isLeaf: true }) ? switcherIcon({ ...this.$props, ...this.$props.dataRef, isLeaf: true })
: switcherIcon} : switcherIcon}
</span> </span>
); );
@ -358,7 +358,7 @@ const TreeNode = {
return ( return (
<span key="switcher" onClick={this.onExpand} class={switcherCls}> <span key="switcher" onClick={this.onExpand} class={switcherCls}>
{typeof switcherIcon === 'function' {typeof switcherIcon === 'function'
? switcherIcon({ ...this.$props, isLeaf: false }) ? switcherIcon({ ...this.$props, ...this.$props.dataRef, isLeaf: false })
: switcherIcon} : switcherIcon}
</span> </span>
); );
@ -420,7 +420,7 @@ const TreeNode = {
vcTree: { prefixCls, showIcon, icon: treeIcon, draggable, loadData }, vcTree: { prefixCls, showIcon, icon: treeIcon, draggable, loadData },
} = this; } = this;
const disabled = this.isDisabled(); const disabled = this.isDisabled();
const title = getComponentFromProp(this, 'title') || defaultTitle; const title = getComponentFromProp(this, 'title', {}, false);
const wrapClass = `${prefixCls}-node-content-wrapper`; const wrapClass = `${prefixCls}-node-content-wrapper`;
// Icon - Still show loading icon when loading without showIcon // Icon - Still show loading icon when loading without showIcon
@ -430,7 +430,9 @@ const TreeNode = {
const currentIcon = icon || treeIcon; const currentIcon = icon || treeIcon;
$icon = currentIcon ? ( $icon = currentIcon ? (
<span class={classNames(`${prefixCls}-iconEle`, `${prefixCls}-icon__customize`)}> <span class={classNames(`${prefixCls}-iconEle`, `${prefixCls}-icon__customize`)}>
{typeof currentIcon === 'function' ? currentIcon({ ...this.$props }, h) : currentIcon} {typeof currentIcon === 'function'
? currentIcon({ ...this.$props, ...this.$props.dataRef }, h)
: currentIcon}
</span> </span>
) : ( ) : (
this.renderIcon() this.renderIcon()
@ -439,8 +441,16 @@ const TreeNode = {
$icon = this.renderIcon(); $icon = this.renderIcon();
} }
// Title const currentTitle = title;
const $title = <span class={`${prefixCls}-title`}>{title}</span>; let $title = currentTitle ? (
<span class={`${prefixCls}-title`}>
{typeof currentTitle === 'function'
? currentTitle({ ...this.$props, ...this.$props.dataRef }, h)
: currentTitle}
</span>
) : (
<span class={`${prefixCls}-title`}>{defaultTitle}</span>
);
return ( return (
<span <span

View File

@ -46,12 +46,14 @@ export default {
this.setStretchSize(); this.setStretchSize();
}); });
}, },
beforeUpdate() { // https://github.com/vueComponent/ant-design-vue/issues/1327
if (this.domEl && this.domEl.rcEndListener) { // ()
this.domEl.rcEndListener(); // beforeUpdate() {
this.domEl = null; // if (this.domEl && this.domEl.rcEndListener) {
} // this.domEl.rcEndListener();
}, // this.domEl = null;
// }
// },
updated() { updated() {
this.$nextTick(() => { this.$nextTick(() => {
this.setStretchSize(); this.setStretchSize();
@ -207,10 +209,10 @@ export default {
style: { ...sizeStyle, ...popupStyle, ...this.getZIndexStyle() }, style: { ...sizeStyle, ...popupStyle, ...this.getZIndexStyle() },
}; };
let transitionProps = { let transitionProps = {
props: Object.assign({ props: {
appear: true, appear: true,
css: false, css: false,
}), },
}; };
const transitionName = getTransitionName(); const transitionName = getTransitionName();
let useTransition = !!transitionName; let useTransition = !!transitionName;
@ -227,6 +229,8 @@ export default {
this.domEl = el; this.domEl = el;
animate(el, `${transitionName}-enter`, done); animate(el, `${transitionName}-enter`, done);
}); });
} else {
done();
} }
}); });
}, },

View File

@ -128,9 +128,10 @@ export default {
if (this.sPopupVisible !== this.prevPopupVisible) { if (this.sPopupVisible !== this.prevPopupVisible) {
this.afterPopupVisibleChange(this.sPopupVisible); this.afterPopupVisibleChange(this.sPopupVisible);
} }
this.prevPopupVisible = this.sPopupVisible;
}; };
this.renderComponent(null, triggerAfterPopupVisibleChange);
this.$nextTick(() => { this.$nextTick(() => {
this.renderComponent(null, triggerAfterPopupVisibleChange);
this.updatedCal(); this.updatedCal();
}); });
}, },

View File

@ -13,4 +13,5 @@ export declare class ConfigProvider extends AntdComponent {
renderEmpty: Function; renderEmpty: Function;
csp?: CSPConfig; csp?: CSPConfig;
autoInsertSpaceInButton?: boolean; autoInsertSpaceInButton?: boolean;
transformCellText?: Function;
} }

View File

@ -132,8 +132,14 @@ export declare class FormModel extends AntdComponent {
* validation rules of form * validation rules of form
* @type object * @type object
*/ */
rules: object; rules: object;
/**
* Default validate message. And its format is similar with newMessages's returned value
* @type any
*/
validateMessages?: any;
/** /**
* whether to trigger validation when the rules prop is changed * whether to trigger validation when the rules prop is changed
* @type Boolean * @type Boolean

View File

@ -389,7 +389,7 @@ export declare class Form extends AntdComponent {
*/ */
options: object; options: object;
createForm(context: Vue, options?: IformCreateOption): any; createForm(context: Vue, options?: IformCreateOption): WrappedFormUtils;
/** /**
* Convert props to field value * Convert props to field value

View File

@ -300,4 +300,5 @@ export declare class Table extends AntdComponent {
style: object; style: object;
nativeOn: object; nativeOn: object;
}; };
transformCellText: Function;
} }