perf: useModal #6517

pull/6527/head
tangjinzhou 2023-05-03 15:09:01 +08:00
parent 69640c0af8
commit 5578c14a8e
6 changed files with 71 additions and 67 deletions

View File

@ -31,8 +31,8 @@ module.exports = {
testRegex: getTestRegex(libDir),
moduleNameMapper: {
'^@/(.*)$/': '<rootDir>/$1',
'ant-design-vue$/': '<rootDir>/components/index.ts',
'ant-design-vue/es/': '<rootDir>/components',
'^ant-design-vue$': '<rootDir>/components/index',
'^ant-design-vue/es/(.*)$': '<rootDir>/components/$1',
},
snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
collectCoverage: process.env.COVERAGE === 'true',

View File

@ -1,17 +1,5 @@
// 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`] = `
<div><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Open Modal with async logic</span>
@ -85,6 +73,29 @@ exports[`renders ./components/modal/demo/fullscreen.vue correctly 1`] = `
</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`] = `
<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">

View File

@ -98,25 +98,17 @@ describe('Modal.confirm triggers callbacks correctly', () => {
});
it('trigger onCancel once when click on cancel button', async () => {
const arr = ['info'];
for (let type of arr) {
Modal[type]({
title: 'title',
content: 'content',
});
await sleep();
expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(1);
// $$('.ant-btn')[0].click();
// await sleep(2000);
// expect($$(`.ant-modal-confirm-${type}`)).toHaveLength(0);
}
const onCancel = jest.fn();
const onOk = jest.fn();
await open({
title: 'title',
content: 'content',
onCancel,
onOk,
});
await sleep();
$$('.ant-btn')[0].click();
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>');
// });
});

View File

@ -17,25 +17,25 @@ Use `Modal.useModal` to get `contextHolder` with context accessible issue.
</docs>
<template>
<div>
<a-space wrap>
<a-button @click="showConfirm">Confirm</a-button>
<a-button @click="showPromiseConfirm">With promise</a-button>
<a-button type="dashed" @click="showDeleteConfirm">Delete</a-button>
<a-button type="dashed" @click="showPropsConfirm">With extra props</a-button>
<contextHolder />
</div>
</a-space>
</template>
<script lang="ts" setup>
import { Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import { createVNode } from 'vue';
import { h } from 'vue';
const [modal, contextHolder] = Modal.useModal();
const showConfirm = () => {
modal.confirm({
title: 'Do you Want to delete these items?',
icon: createVNode(ExclamationCircleOutlined),
content: createVNode('div', { style: 'color:red;' }, 'Some descriptions'),
icon: h(ExclamationCircleOutlined),
content: h('div', { style: 'color:red;' }, 'Some descriptions'),
onOk() {
console.log('OK');
},
@ -48,7 +48,7 @@ const showConfirm = () => {
const showDeleteConfirm = () => {
modal.confirm({
title: 'Are you sure delete this task?',
icon: createVNode(ExclamationCircleOutlined),
icon: h(ExclamationCircleOutlined),
content: 'Some descriptions',
okText: 'Yes',
okType: 'danger',
@ -64,7 +64,7 @@ const showDeleteConfirm = () => {
const showPropsConfirm = () => {
modal.confirm({
title: 'Are you sure delete this task?',
icon: createVNode(ExclamationCircleOutlined),
icon: h(ExclamationCircleOutlined),
content: 'Some descriptions',
okText: 'Yes',
okType: 'danger',
@ -84,7 +84,7 @@ const showPropsConfirm = () => {
function showPromiseConfirm() {
modal.confirm({
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',
async onOk() {
try {

View File

@ -32,7 +32,7 @@ import Width from './width.vue';
import Fullscreen from './fullscreen.vue';
import ButtonProps from './button-props.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 US from '../index.en-US.md';
import { defineComponent } from 'vue';

View File

@ -1,6 +1,6 @@
import type { ComputedRef, Ref } from 'vue';
import { isRef, unref, computed, defineComponent, ref, watch } from 'vue';
import type { VueNode } from '../../_util/type';
import type { Ref } from 'vue';
import { isRef, unref, computed, defineComponent, shallowRef, watch } from 'vue';
import type { MaybeRef, VueNode } from '../../_util/type';
import type { ModalFuncProps } from '../Modal';
import type { HookModalRef } from './HookModal';
import type { ModalStaticFunctions } from '../confirm';
@ -12,16 +12,17 @@ import destroyFns from '../destroyFns';
let uuid = 0;
interface ElementsHolderRef {
addModal: (modal: ComputedRef<JSX.Element>) => () => void;
addModal: (modal: () => JSX.Element) => () => void;
}
const ElementsHolder = defineComponent({
name: 'ElementsHolder',
inheritAttrs: false,
setup(_, { expose }) {
const modals = ref<ComputedRef<JSX.Element>[]>([]);
const addModal = (modal: ComputedRef<JSX.Element>) => {
const modals = shallowRef<(() => JSX.Element)[]>([]);
const addModal = (modal: () => JSX.Element) => {
modals.value.push(modal);
modals.value = modals.value.slice();
return () => {
modals.value = modals.value.filter(currentModal => currentModal !== modal);
};
@ -29,11 +30,11 @@ const ElementsHolder = defineComponent({
expose({ addModal });
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;
update: (configUpdate: ModalFuncProps) => void;
};
@ -42,9 +43,9 @@ function useModal(): readonly [
Omit<ModalStaticFunctions<ModalFuncWithRef>, 'warn'>,
() => VueNode,
] {
const holderRef = ref<ElementsHolderRef>(null);
const holderRef = shallowRef<ElementsHolderRef>(null);
// ========================== Effect ==========================
const actionQueue = ref([]);
const actionQueue = shallowRef([]);
watch(
actionQueue,
() => {
@ -65,10 +66,10 @@ function useModal(): readonly [
const getConfirmFunc = (withFunc: (config: ModalFuncProps) => ModalFuncProps) =>
function hookConfirm(config: Ref<ModalFuncProps> | ModalFuncProps) {
uuid += 1;
const open = ref(true);
const modalRef = ref<HookModalRef>(null);
const configRef = ref(unref(config));
const updateConfig = ref({});
const open = shallowRef(true);
const modalRef = shallowRef<HookModalRef>(null);
const configRef = shallowRef(unref(config));
const updateConfig = shallowRef({});
watch(
() => config,
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
let closeFunc: Function | undefined;
const modal = computed(() => (
const modal = () => (
<HookModal
key={`modal-${uuid}`}
config={withFunc(configRef.value)}
@ -91,7 +100,7 @@ function useModal(): readonly [
closeFunc?.();
}}
/>
));
);
closeFunc = holderRef.value?.addModal(modal);
@ -99,14 +108,6 @@ function useModal(): readonly [
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) => {
configRef.value = {
...configRef.value,