perf: useModal #6517
parent
69640c0af8
commit
5578c14a8e
4
.jest.js
4
.jest.js
|
@ -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',
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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>');
|
||||
// });
|
||||
});
|
||||
|
|
|
@ -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 {
|
|
@ -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';
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue