From 07296a45541919c4cc30830cebf6fd4458d91ac8 Mon Sep 17 00:00:00 2001 From: Gabor Babicz Date: Mon, 19 Feb 2024 10:14:08 +0100 Subject: [PATCH] Throw on invalid instance count --- src/lib/ModalContainer.svelte | 4 ++- src/lib/service.ts | 7 ++++++ tests-ct/TestApp.svelte | 20 ++++++++++++--- tests-ct/modal-container.spec.js | 42 ++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 tests-ct/modal-container.spec.js diff --git a/src/lib/ModalContainer.svelte b/src/lib/ModalContainer.svelte index 38f1b78..0df00b6 100644 --- a/src/lib/ModalContainer.svelte +++ b/src/lib/ModalContainer.svelte @@ -2,13 +2,15 @@ import { onDestroy, onMount } from 'svelte'; import Modal from './Modal.svelte'; - import { animating, destroyModals, stack, updateOptions } from './service'; + import { animating, destroyModals, stack, updateOptions, modalContainerCount } from './service'; export let options: Record = {}; $: updateOptions(options); onMount(() => { + modalContainerCount.update(($count) => $count + 1); + let unsubscribeStack = stack.subscribe(($stack) => { document.body.classList.toggle('spm-scrolling-disabled', $stack.length > 0); }); diff --git a/src/lib/service.ts b/src/lib/service.ts index 5ab97de..49feeaf 100644 --- a/src/lib/service.ts +++ b/src/lib/service.ts @@ -5,6 +5,7 @@ import { derived, get, writable } from 'svelte/store'; import { Modal } from './modal'; import type { ModalOptions, PropsWithoutCloseModal } from './types'; +export const modalContainerCount = writable(0); export const animating = writable(false); export const stack = writable[]>([]); export const count = derived(stack, ($stack) => $stack.filter((modal) => !modal.isClosing).length); @@ -30,6 +31,12 @@ export const openModal = ( props?: PropsWithoutCloseModal | null, // `null` is a convenience for when you don't want to pass any props but do want to pass options options?: ModalOptions ): Modal => { + if (get(modalContainerCount) === 0) { + throw new Error(' is missing'); + } else if (get(modalContainerCount) > 1) { + throw new Error('Multiple instances exist in the application. Please make sure there is only one rendered at a time.'); + } + let modal: Modal = new Modal(component, props ?? undefined, options); stack.update((modals) => [...modals, modal]); diff --git a/tests-ct/TestApp.svelte b/tests-ct/TestApp.svelte index d705273..7911e0c 100644 --- a/tests-ct/TestApp.svelte +++ b/tests-ct/TestApp.svelte @@ -17,14 +17,22 @@ export let modalContainerOptions = {}; export let modalProps: any; export let openModalOptions = {}; + export let modalContainerCount = 1; export let resultCallback = (_: unknown) => { /**/ }; + let errorMessage: string | undefined; + async function openFooModal() { - let result = await openModal(TestModal, modalProps, openModalOptions); - resultCallback(result); + try { + await openModal(TestModal); + let result = await openModal(TestModal, modalProps, openModalOptions); + resultCallback(result); + } catch (error) { + errorMessage = (error as Error).toString(); + } } @@ -44,4 +52,10 @@ {/if} - +{#each Array.from({ length: modalContainerCount }) as _} + +{/each} + +{#if errorMessage} +

{errorMessage}

+{/if} diff --git a/tests-ct/modal-container.spec.js b/tests-ct/modal-container.spec.js new file mode 100644 index 0000000..df463de --- /dev/null +++ b/tests-ct/modal-container.spec.js @@ -0,0 +1,42 @@ +import { expect, test } from '@playwright/experimental-ct-svelte'; + +import TestApp from './TestApp.svelte'; + +test.describe('ModalContainer', () => { + test.beforeEach(async ({ page }) => { + // Reduced motion will speed up animations which comes handy for testing + await page.emulateMedia({ reducedMotion: 'reduce' }); + }); + + test('missing', async ({ mount, page }) => { + await mount(TestApp, { + props: { + modalContainerCount: 0, + }, + }); + + await expect(page.getByTestId('error-message')).toBeHidden(); + + await page.getByTestId('open-modal-button').click(); + + await expect(page.getByTestId('error-message')).toHaveText( + 'Error: is missing' + ); + }); + + test('too many', async ({ mount, page, context }) => { + await mount(TestApp, { + props: { + modalContainerCount: 2, + }, + }); + + await expect(page.getByTestId('error-message')).toBeHidden(); + + await page.getByTestId('open-modal-button').click(); + + await expect(page.getByTestId('error-message')).toHaveText( + 'Error: Multiple instances exist in the application. Please make sure there is only one rendered at a time.' + ); + }); +});