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:
@@ -0,0 +1,164 @@
|
||||
import 'mocha';
|
||||
import { ref, nextTick } from 'vue';
|
||||
import { expect } from 'chai';
|
||||
import { useCurrentFocusToggle } from '@/presentation/components/Shared/Modal/Hooks/UseCurrentFocusToggle';
|
||||
|
||||
describe('useCurrentFocusToggle', () => {
|
||||
describe('initialization', () => {
|
||||
it('blurs active element when initialized with disabled focus', async () => {
|
||||
// arrange
|
||||
const shouldDisableFocus = ref(true);
|
||||
const testElement = createElementInBody('input');
|
||||
testElement.focus();
|
||||
|
||||
// act
|
||||
useCurrentFocusToggle(shouldDisableFocus);
|
||||
await nextTick();
|
||||
|
||||
// assert
|
||||
expect(!isFocused(testElement));
|
||||
});
|
||||
it('doesn\'t blur active element when initialized with enabled focus', async () => {
|
||||
// arrange
|
||||
const isCurrentFocusDisabled = ref(false);
|
||||
const testElement = createElementInBody('input');
|
||||
|
||||
// act
|
||||
testElement.focus();
|
||||
useCurrentFocusToggle(isCurrentFocusDisabled);
|
||||
await nextTick();
|
||||
|
||||
// assert
|
||||
expect(isFocused(testElement));
|
||||
});
|
||||
});
|
||||
|
||||
describe('focus toggling', () => {
|
||||
it('blurs when focus disabled programmatically', async () => {
|
||||
// arrange
|
||||
const shouldDisableFocus = ref(false);
|
||||
const testElement = createElementInBody('input');
|
||||
testElement.focus();
|
||||
|
||||
// act
|
||||
useCurrentFocusToggle(shouldDisableFocus);
|
||||
shouldDisableFocus.value = true;
|
||||
await nextTick();
|
||||
|
||||
// assert
|
||||
expect(!isFocused(testElement));
|
||||
});
|
||||
|
||||
it('restores focus when re-enabled', async () => {
|
||||
// arrange
|
||||
const isCurrentFocusDisabled = ref(true);
|
||||
const testElement = createElementInBody('input');
|
||||
|
||||
// act
|
||||
useCurrentFocusToggle(isCurrentFocusDisabled);
|
||||
testElement.focus();
|
||||
isCurrentFocusDisabled.value = true;
|
||||
await nextTick();
|
||||
isCurrentFocusDisabled.value = false;
|
||||
await nextTick();
|
||||
|
||||
// assert
|
||||
expect(isFocused(testElement));
|
||||
});
|
||||
|
||||
it('maintains focus if not disabled', async () => {
|
||||
// arrange
|
||||
const isCurrentFocusDisabled = ref(false);
|
||||
const testElement = createElementInBody('input');
|
||||
|
||||
// act
|
||||
testElement.focus();
|
||||
useCurrentFocusToggle(isCurrentFocusDisabled);
|
||||
isCurrentFocusDisabled.value = false;
|
||||
await nextTick();
|
||||
|
||||
// assert
|
||||
expect(isFocused(testElement));
|
||||
});
|
||||
|
||||
it('handles multiple toggles correctly', async () => {
|
||||
// arrange
|
||||
const shouldDisableFocus = ref(false);
|
||||
const testElement = createElementInBody('input');
|
||||
testElement.focus();
|
||||
|
||||
// act
|
||||
useCurrentFocusToggle(shouldDisableFocus);
|
||||
shouldDisableFocus.value = true;
|
||||
await nextTick();
|
||||
shouldDisableFocus.value = false;
|
||||
await nextTick();
|
||||
shouldDisableFocus.value = true;
|
||||
await nextTick();
|
||||
|
||||
// assert
|
||||
expect(!isFocused(testElement));
|
||||
});
|
||||
});
|
||||
|
||||
describe('document.body handling', () => {
|
||||
it('blurs body when focus is disabled while body is active', async () => {
|
||||
// arrange
|
||||
document.body.focus();
|
||||
|
||||
const shouldDisableFocus = ref(false);
|
||||
|
||||
// act
|
||||
useCurrentFocusToggle(shouldDisableFocus);
|
||||
shouldDisableFocus.value = true;
|
||||
await nextTick();
|
||||
|
||||
// assert
|
||||
expect(!isFocused(document.body));
|
||||
});
|
||||
|
||||
it('doesn\'t restore focus to document body once focus is re-enabled', async () => {
|
||||
// arrange
|
||||
document.body.focus();
|
||||
|
||||
const shouldDisableFocus = ref(false);
|
||||
|
||||
// act
|
||||
useCurrentFocusToggle(shouldDisableFocus);
|
||||
shouldDisableFocus.value = true;
|
||||
await nextTick();
|
||||
shouldDisableFocus.value = false;
|
||||
await nextTick();
|
||||
|
||||
// assert
|
||||
expect(!isFocused(document.body));
|
||||
});
|
||||
});
|
||||
|
||||
it('handles removal of a previously focused element gracefully', async () => {
|
||||
// arrange
|
||||
const shouldDisableFocus = ref(true);
|
||||
const testElement = createElementInBody('input');
|
||||
testElement.focus();
|
||||
|
||||
useCurrentFocusToggle(shouldDisableFocus);
|
||||
shouldDisableFocus.value = true;
|
||||
await nextTick();
|
||||
testElement.remove();
|
||||
shouldDisableFocus.value = false;
|
||||
await nextTick();
|
||||
|
||||
// assert
|
||||
expect(!isFocused(testElement));
|
||||
});
|
||||
|
||||
function createElementInBody(tagName: keyof HTMLElementTagNameMap): HTMLElement {
|
||||
const element = document.createElement(tagName);
|
||||
document.body.appendChild(element);
|
||||
return element;
|
||||
}
|
||||
});
|
||||
|
||||
function isFocused(element: HTMLElement): boolean {
|
||||
return document.activeElement === element;
|
||||
}
|
||||
Reference in New Issue
Block a user