Implement custom lightweight modal #230

Introduce a brand new lightweight and efficient modal component. It is
designed to be visually similar to the previous one to not introduce a
change in feel of the application in a patch release, but behind the
scenes it features:

- Enhanced application speed and reduced bundle size.
- New flexbox-driven layout, eliminating JS calculations.
- Composition API ready for Vue 3.0 #230.

Other changes:

- Adopt idiomatic Vue via `v-modal` binding.
- Add unit tests for both the modal and dialog.
- Remove `vue-js-modal` dependency in favor of the new implementation.
- Adjust modal shadow color to better match theme.
- Add `@vue/test-utils` for unit testing.
This commit is contained in:
undergroundwires
2023-08-11 19:35:26 +02:00
parent 986ba078a6
commit 9e5491fdbf
28 changed files with 2126 additions and 171 deletions

View File

@@ -0,0 +1,113 @@
import 'mocha';
import { shallowMount } from '@vue/test-utils';
import { expect } from 'chai';
import { ref, nextTick, defineComponent } from 'vue';
import { useLockBodyBackgroundScroll } from '@/presentation/components/Shared/Modal/Hooks/UseLockBodyBackgroundScroll';
describe('useLockBodyBackgroundScroll', () => {
afterEach(() => {
document.body.style.overflow = '';
document.body.style.width = '';
});
describe('initialization', () => {
it('blocks scroll if initially active', async () => {
// arrange
createComponent(true);
// act
await nextTick();
// assert
expect(document.body.style.overflow).to.equal('hidden');
expect(document.body.style.width).to.equal('100vw');
});
it('preserves initial styles if inactive', async () => {
// arrange
const originalOverflow = 'scroll';
const originalWidth = '90vw';
document.body.style.overflow = originalOverflow;
document.body.style.width = originalWidth;
// act
createComponent(false);
await nextTick();
// assert
expect(document.body.style.overflow).to.equal(originalOverflow);
expect(document.body.style.width).to.equal(originalWidth);
});
});
describe('toggling', () => {
it('blocks scroll when activated', async () => {
// arrange
const { isActive } = createComponent(false);
// act
isActive.value = true;
await nextTick();
// assert
expect(document.body.style.overflow).to.equal('hidden');
expect(document.body.style.width).to.equal('100vw');
});
it('unblocks scroll when deactivated', async () => {
// arrange
const { isActive } = createComponent(true);
// act
isActive.value = false;
await nextTick();
// assert
expect(document.body.style.overflow).not.to.equal('hidden');
expect(document.body.style.width).not.to.equal('100vw');
});
});
describe('unmounting', () => {
it('restores original styles on unmount', async () => {
// arrange
const originalOverflow = 'scroll';
const originalWidth = '90vw';
document.body.style.overflow = originalOverflow;
document.body.style.width = originalWidth;
// act
const { component } = createComponent(true);
component.destroy();
await nextTick();
// assert
expect(document.body.style.overflow).to.equal(originalOverflow);
expect(document.body.style.width).to.equal(originalWidth);
});
it('resets styles on unmount', async () => {
// arrange
const { component } = createComponent(true);
// act
component.destroy();
await nextTick();
// assert
expect(document.body.style.overflow).to.equal('');
expect(document.body.style.width).to.equal('');
});
});
});
function createComponent(initialIsActiveValue: boolean) {
const isActive = ref(initialIsActiveValue);
const component = shallowMount(defineComponent({
setup() {
useLockBodyBackgroundScroll(isActive);
},
template: '<div></div>',
}));
return { component, isActive };
}