From c9c293a3b815cf6b6a828073a72f33c270c68288 Mon Sep 17 00:00:00 2001 From: Lukas Maurer Date: Mon, 23 Oct 2023 15:15:40 +0200 Subject: [PATCH 1/5] fix(core/modal): ensure that key down listener gets called --- packages/core/src/components/modal/modal.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/core/src/components/modal/modal.tsx b/packages/core/src/components/modal/modal.tsx index 6fb3d99347c..615ad788f0f 100644 --- a/packages/core/src/components/modal/modal.tsx +++ b/packages/core/src/components/modal/modal.tsx @@ -19,6 +19,7 @@ import { import anime from 'animejs'; import { A11yAttributes, a11yBoolean, a11yHostAttributes } from '../utils/a11y'; import Animation from '../utils/animation'; +import { OnListener } from '../utils/listener'; export type IxModalFixedSize = '360' | '480' | '600' | '720' | '840'; export type IxModalDynamicSize = 'full-width' | 'full-screen'; @@ -86,6 +87,13 @@ export class Modal { */ @Event() dialogDismiss: EventEmitter; + @OnListener('keydown', (self) => !self.keyboard) + onKey(e: KeyboardEvent) { + if (e.key === 'Escape' && this.keyboard === false) { + e.preventDefault(); + } + } + get dialog() { return this.hostElement.shadowRoot.querySelector('dialog'); } @@ -221,11 +229,6 @@ export class Modal { modal: true, [`modal-size-${this.size}`]: true, }} - onKeyDown={(e) => { - if (e.key === 'Escape' && this.keyboard === false) { - e.preventDefault(); - } - }} onClick={(event) => this.onModalClick(event)} onCancel={(e) => { e.preventDefault(); From 3a0a2aea5c42eb287e36a34a6df67c3db7fcb1d5 Mon Sep 17 00:00:00 2001 From: Lukas Maurer Date: Wed, 25 Oct 2023 09:17:29 +0200 Subject: [PATCH 2/5] refactor(core/modal): remove redundant conditinal --- packages/core/src/components/modal/modal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/components/modal/modal.tsx b/packages/core/src/components/modal/modal.tsx index 615ad788f0f..12362e7efc4 100644 --- a/packages/core/src/components/modal/modal.tsx +++ b/packages/core/src/components/modal/modal.tsx @@ -89,7 +89,7 @@ export class Modal { @OnListener('keydown', (self) => !self.keyboard) onKey(e: KeyboardEvent) { - if (e.key === 'Escape' && this.keyboard === false) { + if (e.key === 'Escape') { e.preventDefault(); } } From d40c849c5933ae07dafcaab001cc1bfb2323a324 Mon Sep 17 00:00:00 2001 From: Lukas Maurer Date: Wed, 25 Oct 2023 11:58:37 +0200 Subject: [PATCH 3/5] refactor(core/modal): deprecate property keyboard in favor of new closeonescape --- packages/core/src/components/modal/modal.tsx | 8 +++++- .../src/components/modal/test/modal.ct.ts | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/components/modal/test/modal.ct.ts diff --git a/packages/core/src/components/modal/modal.tsx b/packages/core/src/components/modal/modal.tsx index 12362e7efc4..1c6bf1478da 100644 --- a/packages/core/src/components/modal/modal.tsx +++ b/packages/core/src/components/modal/modal.tsx @@ -74,9 +74,15 @@ export class Modal { /** * Use ESC to dismiss the modal + * @deprecated - Use closeOnEscape instead */ @Prop() keyboard = true; + /** + * If set to true the modal can be closed by pressing the Escape key + */ + @Prop() closeOnEscape = true; + /** * Dialog close */ @@ -87,7 +93,7 @@ export class Modal { */ @Event() dialogDismiss: EventEmitter; - @OnListener('keydown', (self) => !self.keyboard) + @OnListener('keydown', (self) => !self.closeOnEscape || !self.keyboard) onKey(e: KeyboardEvent) { if (e.key === 'Escape') { e.preventDefault(); diff --git a/packages/core/src/components/modal/test/modal.ct.ts b/packages/core/src/components/modal/test/modal.ct.ts new file mode 100644 index 00000000000..87efb8f3880 --- /dev/null +++ b/packages/core/src/components/modal/test/modal.ct.ts @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2023 Siemens AG + * + * SPDX-License-Identifier: MIT + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +import { expect } from '@playwright/test'; +import { test } from '@utils/test'; + +test('closes on Escape key down', async ({ mount, page }) => { + await mount(` + + Title + Content + + `); + const modal = page.locator('ix-modal'); + await modal.evaluate((m: HTMLIxModalElement) => m.showModal()); + const dialog = page.locator('dialog'); + await expect(dialog).toBeVisible(); + await page.locator('ix-modal-content').click(); + await page.keyboard.down('Escape'); + expect(dialog).not.toBeVisible(); +}); From 32f6a7853023426f346ab4a8d9714f543975a86d Mon Sep 17 00:00:00 2001 From: Lukas Maurer Date: Mon, 6 Nov 2023 12:01:40 +0100 Subject: [PATCH 4/5] build(core/modal): update meta files --- packages/angular/src/components.ts | 4 ++-- packages/core/component-doc.json | 25 ++++++++++++++++++++++++- packages/core/src/components.d.ts | 10 ++++++++++ packages/vue/src/components.ts | 1 + 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/packages/angular/src/components.ts b/packages/angular/src/components.ts index 2e12804ae63..4590936285a 100644 --- a/packages/angular/src/components.ts +++ b/packages/angular/src/components.ts @@ -1521,7 +1521,7 @@ export declare interface IxMessageBar extends Components.IxMessageBar { @ProxyCmp({ - inputs: ['animation', 'backdrop', 'beforeDismiss', 'centered', 'closeOnBackdropClick', 'keyboard', 'size'], + inputs: ['animation', 'backdrop', 'beforeDismiss', 'centered', 'closeOnBackdropClick', 'closeOnEscape', 'keyboard', 'size'], methods: ['showModal', 'dismissModal', 'closeModal'] }) @Component({ @@ -1529,7 +1529,7 @@ export declare interface IxMessageBar extends Components.IxMessageBar { changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: ['animation', 'backdrop', 'beforeDismiss', 'centered', 'closeOnBackdropClick', 'keyboard', 'size'], + inputs: ['animation', 'backdrop', 'beforeDismiss', 'centered', 'closeOnBackdropClick', 'closeOnEscape', 'keyboard', 'size'], }) export class IxModal { protected el: HTMLElement; diff --git a/packages/core/component-doc.json b/packages/core/component-doc.json index 5a16acaa9ca..18b5e336762 100644 --- a/packages/core/component-doc.json +++ b/packages/core/component-doc.json @@ -7825,6 +7825,23 @@ "optional": false, "required": false }, + { + "name": "closeOnEscape", + "type": "boolean", + "mutable": false, + "attr": "close-on-escape", + "reflectToAttr": false, + "docs": "If set to true the modal can be closed by pressing the Escape key", + "docsTags": [], + "default": "true", + "values": [ + { + "type": "boolean" + } + ], + "optional": false, + "required": false + }, { "name": "keyboard", "type": "boolean", @@ -7832,8 +7849,14 @@ "attr": "keyboard", "reflectToAttr": false, "docs": "Use ESC to dismiss the modal", - "docsTags": [], + "docsTags": [ + { + "name": "deprecated", + "text": "- Use closeOnEscape instead" + } + ], "default": "true", + "deprecation": "- Use closeOnEscape instead", "values": [ { "type": "boolean" diff --git a/packages/core/src/components.d.ts b/packages/core/src/components.d.ts index f1146020978..baff575c96d 100644 --- a/packages/core/src/components.d.ts +++ b/packages/core/src/components.d.ts @@ -1407,12 +1407,17 @@ export namespace Components { * @since 2.0.0 */ "closeOnBackdropClick": boolean; + /** + * If set to true the modal can be closed by pressing the Escape key + */ + "closeOnEscape": boolean; /** * Dismiss the dialog */ "dismissModal": (reason?: T) => Promise; /** * Use ESC to dismiss the modal + * @deprecated - Use closeOnEscape instead */ "keyboard": boolean; /** @@ -4575,8 +4580,13 @@ declare namespace LocalJSX { * @since 2.0.0 */ "closeOnBackdropClick"?: boolean; + /** + * If set to true the modal can be closed by pressing the Escape key + */ + "closeOnEscape"?: boolean; /** * Use ESC to dismiss the modal + * @deprecated - Use closeOnEscape instead */ "keyboard"?: boolean; /** diff --git a/packages/vue/src/components.ts b/packages/vue/src/components.ts index bacd532a3df..6091f4cb7db 100644 --- a/packages/vue/src/components.ts +++ b/packages/vue/src/components.ts @@ -633,6 +633,7 @@ export const IxModal = /*@__PURE__*/ defineContainer('ix-modal', de 'beforeDismiss', 'centered', 'keyboard', + 'closeOnEscape', 'dialogClose', 'dialogDismiss' ]); From cd3980bc39f65a655832794bfe85799241cb580b Mon Sep 17 00:00:00 2001 From: Lukas Maurer Date: Wed, 8 Nov 2023 09:49:41 +0100 Subject: [PATCH 5/5] test(core/modal): update component test --- .../src/components/modal/test/modal.ct.ts | 42 +++++++++++++++---- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/packages/core/src/components/modal/test/modal.ct.ts b/packages/core/src/components/modal/test/modal.ct.ts index 87efb8f3880..fd943fdc20c 100644 --- a/packages/core/src/components/modal/test/modal.ct.ts +++ b/packages/core/src/components/modal/test/modal.ct.ts @@ -9,18 +9,44 @@ import { expect } from '@playwright/test'; import { test } from '@utils/test'; +declare global { + interface Window { + showModal: any; + } +} + test('closes on Escape key down', async ({ mount, page }) => { - await mount(` - + await mount(``); + + await page.evaluate(() => { + return new Promise((resolve) => { + const script = document.createElement('script'); + script.type = 'module'; + script.innerHTML = ` + import * as ix from 'http://127.0.0.1:8080/www/build/index.esm.js'; + window.showModal = ix.showModal; + `; + document.body.appendChild(script); + resolve(); + }); + }); + + await page.waitForTimeout(1000); + + await page.evaluate(() => { + const elm = document.createElement('ix-modal'); + elm.innerHTML = ` Title Content - - `); - const modal = page.locator('ix-modal'); - await modal.evaluate((m: HTMLIxModalElement) => m.showModal()); - const dialog = page.locator('dialog'); + `; + window.showModal({ + content: elm, + }); + }); + const dialog = page.locator('ix-modal dialog'); await expect(dialog).toBeVisible(); await page.locator('ix-modal-content').click(); await page.keyboard.down('Escape'); - expect(dialog).not.toBeVisible(); + + await expect(dialog).not.toBeVisible(); });