perf: useModal #6517
parent
69640c0af8
commit
5578c14a8e
4
.jest.js
4
.jest.js
|
@ -31,8 +31,8 @@ module.exports = {
|
||||||
testRegex: getTestRegex(libDir),
|
testRegex: getTestRegex(libDir),
|
||||||
moduleNameMapper: {
|
moduleNameMapper: {
|
||||||
'^@/(.*)$/': '<rootDir>/$1',
|
'^@/(.*)$/': '<rootDir>/$1',
|
||||||
'ant-design-vue$/': '<rootDir>/components/index.ts',
|
'^ant-design-vue$': '<rootDir>/components/index',
|
||||||
'ant-design-vue/es/': '<rootDir>/components',
|
'^ant-design-vue/es/(.*)$': '<rootDir>/components/$1',
|
||||||
},
|
},
|
||||||
snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
|
snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
|
||||||
collectCoverage: process.env.COVERAGE === 'true',
|
collectCoverage: process.env.COVERAGE === 'true',
|
||||||
|
|
|
@ -1,17 +1,5 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`renders ./components/modal/demo/HookModal.vue correctly 1`] = `
|
|
||||||
<div><button class="ant-btn ant-btn-default" type="button">
|
|
||||||
<!----><span>Confirm</span>
|
|
||||||
</button><button class="ant-btn ant-btn-default" type="button">
|
|
||||||
<!----><span>With promise</span>
|
|
||||||
</button><button class="ant-btn ant-btn-dashed" type="button">
|
|
||||||
<!----><span>Delete</span>
|
|
||||||
</button><button class="ant-btn ant-btn-dashed" type="button">
|
|
||||||
<!----><span>With extra props</span>
|
|
||||||
</button></div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`renders ./components/modal/demo/async.vue correctly 1`] = `
|
exports[`renders ./components/modal/demo/async.vue correctly 1`] = `
|
||||||
<div><button class="ant-btn ant-btn-primary" type="button">
|
<div><button class="ant-btn ant-btn-primary" type="button">
|
||||||
<!----><span>Open Modal with async logic</span>
|
<!----><span>Open Modal with async logic</span>
|
||||||
|
@ -85,6 +73,29 @@ exports[`renders ./components/modal/demo/fullscreen.vue correctly 1`] = `
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`renders ./components/modal/demo/hook-modal.vue correctly 1`] = `
|
||||||
|
<div class="ant-space ant-space-horizontal ant-space-align-center" style="flex-wrap: wrap; margin-bottom: -8px;">
|
||||||
|
<div class="ant-space-item" style="margin-right: 8px; padding-bottom: 8px;"><button class="ant-btn ant-btn-default" type="button">
|
||||||
|
<!----><span>Confirm</span>
|
||||||
|
</button></div>
|
||||||
|
<!---->
|
||||||
|
<div class="ant-space-item" style="margin-right: 8px; padding-bottom: 8px;"><button class="ant-btn ant-btn-default" type="button">
|
||||||
|
<!----><span>With promise</span>
|
||||||
|
</button></div>
|
||||||
|
<!---->
|
||||||
|
<div class="ant-space-item" style="margin-right: 8px; padding-bottom: 8px;"><button class="ant-btn ant-btn-dashed" type="button">
|
||||||
|
<!----><span>Delete</span>
|
||||||
|
</button></div>
|
||||||
|
<!---->
|
||||||
|
<div class="ant-space-item" style="margin-right: 8px; padding-bottom: 8px;"><button class="ant-btn ant-btn-dashed" type="button">
|
||||||
|
<!----><span>With extra props</span>
|
||||||
|
</button></div>
|
||||||
|
<!---->
|
||||||
|
<div class="ant-space-item" style="padding-bottom: 8px;"></div>
|
||||||
|
<!---->
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`renders ./components/modal/demo/info.vue correctly 1`] = `
|
exports[`renders ./components/modal/demo/info.vue correctly 1`] = `
|
||||||
<div class="ant-space ant-space-horizontal ant-space-align-center" style="flex-wrap: wrap; margin-bottom: -8px;">
|
<div class="ant-space ant-space-horizontal ant-space-align-center" style="flex-wrap: wrap; margin-bottom: -8px;">
|
||||||
<div class="ant-space-item" style="margin-right: 8px; padding-bottom: 8px;"><button class="ant-btn ant-btn-default" type="button">
|
<div class="ant-space-item" style="margin-right: 8px; padding-bottom: 8px;"><button class="ant-btn ant-btn-default" type="button">
|
||||||
|
|
|
@ -98,25 +98,17 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('trigger onCancel once when click on cancel button', async () => {
|
it('trigger onCancel once when click on cancel button', async () => {
|
||||||
const arr = ['info'];
|
const onCancel = jest.fn();
|
||||||
for (let type of arr) {
|
const onOk = jest.fn();
|
||||||
Modal[type]({
|
await open({
|
||||||
title: 'title',
|
title: 'title',
|
||||||
content: 'content',
|
content: 'content',
|
||||||
});
|
onCancel,
|
||||||
await sleep();
|
onOk,
|
||||||
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(1);
|
});
|
||||||
// $$('.ant-btn')[0].click();
|
await sleep();
|
||||||
// await sleep(2000);
|
$$('.ant-btn')[0].click();
|
||||||
// expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(0);
|
expect(onCancel.mock.calls.length).toBe(1);
|
||||||
}
|
expect(onOk.mock.calls.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
// it('should render title', async () => {
|
|
||||||
// open({
|
|
||||||
// title: () => <span>title</span>,
|
|
||||||
// });
|
|
||||||
// await sleep();
|
|
||||||
// expect($$('.ant-modal-confirm-title')[0].innerHTML).toBe('<span>title</span>');
|
|
||||||
// });
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,25 +17,25 @@ Use `Modal.useModal` to get `contextHolder` with context accessible issue.
|
||||||
</docs>
|
</docs>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<a-space wrap>
|
||||||
<a-button @click="showConfirm">Confirm</a-button>
|
<a-button @click="showConfirm">Confirm</a-button>
|
||||||
<a-button @click="showPromiseConfirm">With promise</a-button>
|
<a-button @click="showPromiseConfirm">With promise</a-button>
|
||||||
<a-button type="dashed" @click="showDeleteConfirm">Delete</a-button>
|
<a-button type="dashed" @click="showDeleteConfirm">Delete</a-button>
|
||||||
<a-button type="dashed" @click="showPropsConfirm">With extra props</a-button>
|
<a-button type="dashed" @click="showPropsConfirm">With extra props</a-button>
|
||||||
<contextHolder />
|
<contextHolder />
|
||||||
</div>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Modal } from 'ant-design-vue';
|
import { Modal } from 'ant-design-vue';
|
||||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||||
import { createVNode } from 'vue';
|
import { h } from 'vue';
|
||||||
const [modal, contextHolder] = Modal.useModal();
|
const [modal, contextHolder] = Modal.useModal();
|
||||||
const showConfirm = () => {
|
const showConfirm = () => {
|
||||||
modal.confirm({
|
modal.confirm({
|
||||||
title: 'Do you Want to delete these items?',
|
title: 'Do you Want to delete these items?',
|
||||||
icon: createVNode(ExclamationCircleOutlined),
|
icon: h(ExclamationCircleOutlined),
|
||||||
content: createVNode('div', { style: 'color:red;' }, 'Some descriptions'),
|
content: h('div', { style: 'color:red;' }, 'Some descriptions'),
|
||||||
onOk() {
|
onOk() {
|
||||||
console.log('OK');
|
console.log('OK');
|
||||||
},
|
},
|
||||||
|
@ -48,7 +48,7 @@ const showConfirm = () => {
|
||||||
const showDeleteConfirm = () => {
|
const showDeleteConfirm = () => {
|
||||||
modal.confirm({
|
modal.confirm({
|
||||||
title: 'Are you sure delete this task?',
|
title: 'Are you sure delete this task?',
|
||||||
icon: createVNode(ExclamationCircleOutlined),
|
icon: h(ExclamationCircleOutlined),
|
||||||
content: 'Some descriptions',
|
content: 'Some descriptions',
|
||||||
okText: 'Yes',
|
okText: 'Yes',
|
||||||
okType: 'danger',
|
okType: 'danger',
|
||||||
|
@ -64,7 +64,7 @@ const showDeleteConfirm = () => {
|
||||||
const showPropsConfirm = () => {
|
const showPropsConfirm = () => {
|
||||||
modal.confirm({
|
modal.confirm({
|
||||||
title: 'Are you sure delete this task?',
|
title: 'Are you sure delete this task?',
|
||||||
icon: createVNode(ExclamationCircleOutlined),
|
icon: h(ExclamationCircleOutlined),
|
||||||
content: 'Some descriptions',
|
content: 'Some descriptions',
|
||||||
okText: 'Yes',
|
okText: 'Yes',
|
||||||
okType: 'danger',
|
okType: 'danger',
|
||||||
|
@ -84,7 +84,7 @@ const showPropsConfirm = () => {
|
||||||
function showPromiseConfirm() {
|
function showPromiseConfirm() {
|
||||||
modal.confirm({
|
modal.confirm({
|
||||||
title: 'Do you want to delete these items?',
|
title: 'Do you want to delete these items?',
|
||||||
icon: createVNode(ExclamationCircleOutlined),
|
icon: h(ExclamationCircleOutlined),
|
||||||
content: 'When clicked the OK button, this dialog will be closed after 1 second',
|
content: 'When clicked the OK button, this dialog will be closed after 1 second',
|
||||||
async onOk() {
|
async onOk() {
|
||||||
try {
|
try {
|
|
@ -32,7 +32,7 @@ import Width from './width.vue';
|
||||||
import Fullscreen from './fullscreen.vue';
|
import Fullscreen from './fullscreen.vue';
|
||||||
import ButtonProps from './button-props.vue';
|
import ButtonProps from './button-props.vue';
|
||||||
import modalRenderVue from './modal-render.vue';
|
import modalRenderVue from './modal-render.vue';
|
||||||
import HookModal from './HookModal.vue';
|
import HookModal from './hook-modal.vue';
|
||||||
import CN from '../index.zh-CN.md';
|
import CN from '../index.zh-CN.md';
|
||||||
import US from '../index.en-US.md';
|
import US from '../index.en-US.md';
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import type { ComputedRef, Ref } from 'vue';
|
import type { Ref } from 'vue';
|
||||||
import { isRef, unref, computed, defineComponent, ref, watch } from 'vue';
|
import { isRef, unref, computed, defineComponent, shallowRef, watch } from 'vue';
|
||||||
import type { VueNode } from '../../_util/type';
|
import type { MaybeRef, VueNode } from '../../_util/type';
|
||||||
import type { ModalFuncProps } from '../Modal';
|
import type { ModalFuncProps } from '../Modal';
|
||||||
import type { HookModalRef } from './HookModal';
|
import type { HookModalRef } from './HookModal';
|
||||||
import type { ModalStaticFunctions } from '../confirm';
|
import type { ModalStaticFunctions } from '../confirm';
|
||||||
|
@ -12,16 +12,17 @@ import destroyFns from '../destroyFns';
|
||||||
let uuid = 0;
|
let uuid = 0;
|
||||||
|
|
||||||
interface ElementsHolderRef {
|
interface ElementsHolderRef {
|
||||||
addModal: (modal: ComputedRef<JSX.Element>) => () => void;
|
addModal: (modal: () => JSX.Element) => () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ElementsHolder = defineComponent({
|
const ElementsHolder = defineComponent({
|
||||||
name: 'ElementsHolder',
|
name: 'ElementsHolder',
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
setup(_, { expose }) {
|
setup(_, { expose }) {
|
||||||
const modals = ref<ComputedRef<JSX.Element>[]>([]);
|
const modals = shallowRef<(() => JSX.Element)[]>([]);
|
||||||
const addModal = (modal: ComputedRef<JSX.Element>) => {
|
const addModal = (modal: () => JSX.Element) => {
|
||||||
modals.value.push(modal);
|
modals.value.push(modal);
|
||||||
|
modals.value = modals.value.slice();
|
||||||
return () => {
|
return () => {
|
||||||
modals.value = modals.value.filter(currentModal => currentModal !== modal);
|
modals.value = modals.value.filter(currentModal => currentModal !== modal);
|
||||||
};
|
};
|
||||||
|
@ -29,11 +30,11 @@ const ElementsHolder = defineComponent({
|
||||||
|
|
||||||
expose({ addModal });
|
expose({ addModal });
|
||||||
return () => {
|
return () => {
|
||||||
return <>{modals.value.map(modal => modal.value)}</>;
|
return modals.value.map(modal => modal());
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
export type ModalFuncWithRef = (props: Ref<ModalFuncProps> | ModalFuncProps) => {
|
export type ModalFuncWithRef = (props: MaybeRef<ModalFuncProps>) => {
|
||||||
destroy: () => void;
|
destroy: () => void;
|
||||||
update: (configUpdate: ModalFuncProps) => void;
|
update: (configUpdate: ModalFuncProps) => void;
|
||||||
};
|
};
|
||||||
|
@ -42,9 +43,9 @@ function useModal(): readonly [
|
||||||
Omit<ModalStaticFunctions<ModalFuncWithRef>, 'warn'>,
|
Omit<ModalStaticFunctions<ModalFuncWithRef>, 'warn'>,
|
||||||
() => VueNode,
|
() => VueNode,
|
||||||
] {
|
] {
|
||||||
const holderRef = ref<ElementsHolderRef>(null);
|
const holderRef = shallowRef<ElementsHolderRef>(null);
|
||||||
// ========================== Effect ==========================
|
// ========================== Effect ==========================
|
||||||
const actionQueue = ref([]);
|
const actionQueue = shallowRef([]);
|
||||||
watch(
|
watch(
|
||||||
actionQueue,
|
actionQueue,
|
||||||
() => {
|
() => {
|
||||||
|
@ -65,10 +66,10 @@ function useModal(): readonly [
|
||||||
const getConfirmFunc = (withFunc: (config: ModalFuncProps) => ModalFuncProps) =>
|
const getConfirmFunc = (withFunc: (config: ModalFuncProps) => ModalFuncProps) =>
|
||||||
function hookConfirm(config: Ref<ModalFuncProps> | ModalFuncProps) {
|
function hookConfirm(config: Ref<ModalFuncProps> | ModalFuncProps) {
|
||||||
uuid += 1;
|
uuid += 1;
|
||||||
const open = ref(true);
|
const open = shallowRef(true);
|
||||||
const modalRef = ref<HookModalRef>(null);
|
const modalRef = shallowRef<HookModalRef>(null);
|
||||||
const configRef = ref(unref(config));
|
const configRef = shallowRef(unref(config));
|
||||||
const updateConfig = ref({});
|
const updateConfig = shallowRef({});
|
||||||
watch(
|
watch(
|
||||||
() => config,
|
() => config,
|
||||||
val => {
|
val => {
|
||||||
|
@ -78,9 +79,17 @@ function useModal(): readonly [
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const destroyAction = (...args: any[]) => {
|
||||||
|
open.value = false;
|
||||||
|
const triggerCancel = args.some(param => param && param.triggerCancel);
|
||||||
|
if (configRef.value.onCancel && triggerCancel) {
|
||||||
|
configRef.value.onCancel(() => {}, ...args.slice(1));
|
||||||
|
}
|
||||||
|
};
|
||||||
// eslint-disable-next-line prefer-const
|
// eslint-disable-next-line prefer-const
|
||||||
let closeFunc: Function | undefined;
|
let closeFunc: Function | undefined;
|
||||||
const modal = computed(() => (
|
const modal = () => (
|
||||||
<HookModal
|
<HookModal
|
||||||
key={`modal-${uuid}`}
|
key={`modal-${uuid}`}
|
||||||
config={withFunc(configRef.value)}
|
config={withFunc(configRef.value)}
|
||||||
|
@ -91,7 +100,7 @@ function useModal(): readonly [
|
||||||
closeFunc?.();
|
closeFunc?.();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
));
|
);
|
||||||
|
|
||||||
closeFunc = holderRef.value?.addModal(modal);
|
closeFunc = holderRef.value?.addModal(modal);
|
||||||
|
|
||||||
|
@ -99,14 +108,6 @@ function useModal(): readonly [
|
||||||
destroyFns.push(closeFunc);
|
destroyFns.push(closeFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
const destroyAction = (...args: any[]) => {
|
|
||||||
open.value = false;
|
|
||||||
const triggerCancel = args.some(param => param && param.triggerCancel);
|
|
||||||
if (configRef.value.onCancel && triggerCancel) {
|
|
||||||
configRef.value.onCancel(() => {}, ...args.slice(1));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateAction = (newConfig: ModalFuncProps) => {
|
const updateAction = (newConfig: ModalFuncProps) => {
|
||||||
configRef.value = {
|
configRef.value = {
|
||||||
...configRef.value,
|
...configRef.value,
|
||||||
|
|
Loading…
Reference in New Issue