Skip to content

Commit

Permalink
feat(base-modal): migrate base-modal component and side effects (#1479)
Browse files Browse the repository at this point in the history
  • Loading branch information
joseacabaneros authored May 28, 2024
1 parent a160a0a commit 610ec16
Show file tree
Hide file tree
Showing 8 changed files with 287 additions and 293 deletions.
2 changes: 1 addition & 1 deletion packages/_vue3-migration-test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>vue3-migration-test</title>
</head>
<body>
<body style="margin: 0;padding: 10px;">
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
Expand Down
2 changes: 1 addition & 1 deletion packages/_vue3-migration-test/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div id="app">
<h1>vue3-migration-test</h1>
<h1 class="header">vue3-migration-test</h1>
<nav>
<RouterLink v-for="route in $router.options.routes" :key="route.path" :to="route.path">
{{ route.name }}
Expand Down
1 change: 1 addition & 0 deletions packages/_vue3-migration-test/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './animations';
export { default as TestBaseColumnPickerDropdown } from './column-picker/test-base-column-picker-dropdown.vue';
export { default as TestBaseColumnPickerList } from './column-picker/test-base-column-picker-list.vue';
export { default as TestBaseModal } from './modals/test-base-modal.vue';
export { default as TestBaseDropdown } from './test-base-dropdown.vue';
export { default as TestBaseEventButton } from './test-base-event-button.vue';
export { default as TestBaseVariableColumnGrid } from './test-base-variable-column-grid.vue';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<template>
<div class="base-modal">
<button @click="open = true">Open modal</button>
<BaseModal
@click:overlay="open = false"
@focusin:body="open = false"
:open="open"
:focusOnOpen="true"
referenceSelector=".header"
:animation="Fade"
:overlayAnimation="Fade"
contentClass="content"
overlayClass="overlay"
>
<h1>Hello</h1>
<p>The modal is working</p>
<button @click="open = false">Close modal</button>
</BaseModal>
</div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import BaseModal from '../../../../x-components/src/components/modals/base-modal.vue';
import Fade from '../../../../x-components/src/components/animations/fade.vue';
const open = ref(false);
</script>

<style>
.base-modal {
.content {
background: white;
margin: auto;
width: 50%;
border: 3px solid green;
padding: 10px;
}
.overlay {
background-color: red;
}
}
</style>
6 changes: 6 additions & 0 deletions packages/_vue3-migration-test/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
TestSortList,
TestSortPickerList,
TestBaseScroll,
TestBaseModal,
TestSearchBox,
TestBaseVariableColumnGrid,
TestEmpathize,
Expand Down Expand Up @@ -48,6 +49,11 @@ const routes = [
name: 'Fade',
component: TestFade
},
{
path: '/base-modal',
name: 'BaseModal',
component: TestBaseModal
},
{
path: '/base-dropdown',
name: 'BaseDropdown',
Expand Down
2 changes: 1 addition & 1 deletion packages/x-components/src/components/animations/fade.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
&--leave-to,
&--enter {
opacity: 0;
opacity: 0 !important;
}
}
</style>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { createLocalVue, mount, Wrapper } from '@vue/test-utils';
import Vue from 'vue';
import { mount } from '@vue/test-utils';
import { getDataTestSelector } from '../../../__tests__/utils';
import BaseModal from '../base-modal.vue';

Expand All @@ -14,58 +13,52 @@ function mountBaseModal({
defaultSlot = '<span data-test="default-slot">Modal</span>',
open = false,
focusOnOpen = true,
contentClass,
overlayClass
}: MountBaseModalOptions = {}): MountBaseModalAPI {
const localVue = createLocalVue();
const wrapper = mount(BaseModal, {
localVue,
propsData: {
open,
focusOnOpen,
contentClass,
overlayClass
contentClass = '',
overlayClass = ''
} = {}) {
const wrapper = mount(
{
template: `
<BaseModal
:open="open"
:focusOnOpen="focusOnOpen"
:contentClass="contentClass"
:overlayClass="overlayClass"
>
<slot/>
</BaseModal>`,
components: { BaseModal },
props: ['open', 'focusOnOpen', 'contentClass', 'overlayClass']
},
slots: {
default: defaultSlot
{
propsData: { open, focusOnOpen, contentClass, overlayClass },
slots: { default: defaultSlot }
}
});
const appendToBody = (): void => {
document.body.appendChild(wrapper.element);
};
);

const baseModalWrapper = wrapper.findComponent(BaseModal);
const appendToBody = () => document.body.appendChild(wrapper.element);

return {
wrapper,
getModalContent() {
return wrapper.find(getDataTestSelector('modal-content'));
},
async setOpen(open) {
await wrapper.setProps({ open });
},
async closeModal() {
await wrapper.find(getDataTestSelector('modal-overlay'))?.trigger('click');
},
wrapper: baseModalWrapper,
getModalContent: () => baseModalWrapper.find(getDataTestSelector('modal-content')),
setOpen: async (open: boolean) => await wrapper.setProps({ open }),
closeModal: async () =>
await baseModalWrapper.find(getDataTestSelector('modal-overlay'))?.trigger('click'),
appendToBody,
async fakeFocusIn() {
const buttonWrapper = mount({
template: `<button>Button</button>`
});
fakeFocusIn: async () => {
const buttonWrapper = mount({ template: `<button>Button</button>` });
appendToBody();
document.body.appendChild(buttonWrapper.element);
jest.runAllTimers();
await buttonWrapper.trigger('focusin');
}
};
} as const;
}

describe('testing Base Modal component', () => {
beforeAll(() => {
jest.useFakeTimers();
});

afterAll(() => {
jest.useRealTimers();
});
beforeAll(() => jest.useFakeTimers());
afterAll(() => jest.useRealTimers());

it('renders only when the open prop is set to true', async () => {
const { getModalContent, setOpen } = mountBaseModal();
Expand Down Expand Up @@ -104,13 +97,15 @@ describe('testing Base Modal component', () => {
open: true
});

expect(wrapper.find(getDataTestSelector('default-slot-overridden')).exists()).toBe(true);
expect(wrapper.find(getDataTestSelector('default-slot-overridden')).exists()).toBeTruthy();
});

it('changes the focus to the correct element when the modal opens', async () => {
const dataTestSelector = 'expected-focus';
let { wrapper, setOpen, appendToBody } = mountBaseModal({
defaultSlot: `<div>
<button data-test="expected-focus">First button</button>
defaultSlot: `
<div>
<button data-test="${dataTestSelector}">First button</button>
<button>Second button</button>
</div>`,
open: false
Expand All @@ -119,29 +114,31 @@ describe('testing Base Modal component', () => {
appendToBody();
await setOpen(true);

expect(wrapper.find(getDataTestSelector('expected-focus')).element).toBe(
expect(wrapper.find(getDataTestSelector(dataTestSelector)).element).toEqual(
document.activeElement
);

({ wrapper, setOpen, appendToBody } = mountBaseModal({
defaultSlot: `<div>
defaultSlot: `
<div>
<button>First button</button>
<button tabindex="1" data-test="expected-focus">Second button</button>
<button tabindex="1" data-test="${dataTestSelector}">Second button</button>
</div>`,
open: false
}));

appendToBody();
await setOpen(true);

expect(wrapper.find(getDataTestSelector('expected-focus')).element).toBe(
expect(wrapper.find(getDataTestSelector(dataTestSelector)).element).toEqual(
document.activeElement
);
});

it("doesn't change the focus if the focusOnOpen prop is false", async () => {
const { setOpen, appendToBody } = mountBaseModal({
defaultSlot: `<div>
defaultSlot: `
<div>
<button tabindex="1">First button</button>
<button>Second button</button>
</div>`,
Expand All @@ -153,7 +150,7 @@ describe('testing Base Modal component', () => {
const focusedElementBeforeOpen = document.activeElement;
await setOpen(true);

expect(focusedElementBeforeOpen).toBe(document.activeElement);
expect(focusedElementBeforeOpen).toEqual(document.activeElement);
});

it('allows adding classes to the modal content', () => {
Expand All @@ -172,34 +169,6 @@ describe('testing Base Modal component', () => {
});

const overlay = wrapper.find(getDataTestSelector('modal-overlay'));
expect(overlay.classes('custom-class')).toBe(true);
expect(overlay.classes('custom-class')).toBeTruthy();
});
});

interface MountBaseModalOptions {
/** The default slot to render. */
defaultSlot?: string;
/** Events that when emitted should open the modal. */
open?: boolean;
/** Indicates if the focus changes to an element inside the modal when it opens. */
focusOnOpen?: boolean;
/** Class to add to the content element of the modal. */
contentClass?: string;
/** Class to add to the overlay element of the modal. */
overlayClass?: string;
}

interface MountBaseModalAPI {
/** The wrapper for the modal component. */
wrapper: Wrapper<Vue>;
/** Fakes a click on the close button. */
setOpen: (open: boolean) => Promise<void>;
/** Retrieves the modal container wrapper. */
getModalContent: () => Wrapper<Vue>;
/** Fakes a click on the modal close. */
closeModal: () => Promise<void>;
/** Appends the component's element the body. */
appendToBody: () => void;
/** Fakes a focusin event in another HTMLElement of the body. */
fakeFocusIn: () => Promise<void>;
}
Loading

0 comments on commit 610ec16

Please sign in to comment.