From 42792184b5946196570686102e450093e6ddba51 Mon Sep 17 00:00:00 2001 From: Alba Silvente Fuentes <36744484+Dawntraoz@users.noreply.github.com> Date: Wed, 25 Sep 2024 15:10:02 +0200 Subject: [PATCH] feat(lib): add event listener for ESC key handler (#404) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(SHAPE-2151): add event listener for ESC key handler * lint: add eslint fixes * test: include main functionalities testing * fix type error * fix lint error --------- Co-authored-by: Eunjae Lee Co-authored-by: Demetrius Feijóo --- .../createFieldPlugin/createFieldPlugin.ts | 41 +++++++---- .../createKeydownEscListener.test.ts | 72 +++++++++++++++++++ .../createKeydownEscListener.ts | 15 ++++ .../createPluginActions.ts | 7 ++ 4 files changed, 120 insertions(+), 15 deletions(-) create mode 100644 packages/field-plugin/src/createFieldPlugin/createKeydownEscListener.test.ts create mode 100644 packages/field-plugin/src/createFieldPlugin/createKeydownEscListener.ts diff --git a/packages/field-plugin/src/createFieldPlugin/createFieldPlugin.ts b/packages/field-plugin/src/createFieldPlugin/createFieldPlugin.ts index f45f32a1..594f515c 100644 --- a/packages/field-plugin/src/createFieldPlugin/createFieldPlugin.ts +++ b/packages/field-plugin/src/createFieldPlugin/createFieldPlugin.ts @@ -1,5 +1,6 @@ import { createPluginActions, ValidateContent } from './createPluginActions' import { createHeightChangeListener } from './createHeightChangeListener' +import { createKeydownEscListener } from './createKeydownEscListener' import { disableDefaultStoryblokStyles } from './disableDefaultStoryblokStyles' import { pluginUrlParamsFromUrl } from '../messaging' import { FieldPluginResponse } from './FieldPluginResponse' @@ -50,6 +51,8 @@ export const createFieldPlugin: CreateFieldPlugin = ({ } const { uid, host } = params + + // ToDo: In development we need to load localhost:3300 const origin = typeof targetOrigin === 'string' ? targetOrigin @@ -85,24 +88,31 @@ export const createFieldPlugin: CreateFieldPlugin = ({ Exclude >['content'] - const { actions, messageCallbacks, onHeightChange, initialize } = - createPluginActions({ - uid, - postToContainer, - onUpdateState: (data) => { - onUpdateState({ - type: 'loaded', - data, - actions, - }) - }, - validateContent: - validateContent || - ((content) => ({ content: content as InferredContent })), - }) + const { + actions, + messageCallbacks, + onHeightChange, + onKeydownEsc, + initialize, + } = createPluginActions({ + uid, + postToContainer, + onUpdateState: (data) => { + onUpdateState({ + type: 'loaded', + data, + actions, + }) + }, + validateContent: + validateContent || + ((content) => ({ content: content as InferredContent })), + }) const cleanupHeightChangeListener = createHeightChangeListener(onHeightChange) + const cleanupKeydownEscListener = createKeydownEscListener(onKeydownEsc) + const cleanupMessageListenerSideEffects = createPluginMessageListener( params.uid, origin, @@ -114,6 +124,7 @@ export const createFieldPlugin: CreateFieldPlugin = ({ return () => { cleanupMessageListenerSideEffects() cleanupHeightChangeListener() + cleanupKeydownEscListener() cleanupStyleSideEffects() } } diff --git a/packages/field-plugin/src/createFieldPlugin/createKeydownEscListener.test.ts b/packages/field-plugin/src/createFieldPlugin/createKeydownEscListener.test.ts new file mode 100644 index 00000000..f644f410 --- /dev/null +++ b/packages/field-plugin/src/createFieldPlugin/createKeydownEscListener.test.ts @@ -0,0 +1,72 @@ +import { createKeydownEscListener } from './createKeydownEscListener' +import { MockInstance } from 'vitest' + +describe('createKeydownEscListener', () => { + let addEventListenerSpy: MockInstance + let removeEventListenerSpy: MockInstance + const escapeEvent = new KeyboardEvent('keydown', { key: 'Escape' }) + + beforeEach(() => { + addEventListenerSpy = vi.spyOn(document, 'addEventListener') + removeEventListenerSpy = vi.spyOn(document, 'removeEventListener') + }) + + afterEach(() => { + vi.restoreAllMocks() + }) + + it('should call addEventListener with keydown and handleEsc', () => { + const mockOnPressed = vi.fn() + + createKeydownEscListener(mockOnPressed) + + expect(addEventListenerSpy).toHaveBeenCalledWith( + 'keydown', + expect.any(Function), + ) + }) + + it('should trigger onPressed when Escape key is pressed', () => { + const mockOnPressed = vi.fn() + + const removeListener = createKeydownEscListener(mockOnPressed) + + document.dispatchEvent(escapeEvent) + + expect(mockOnPressed).toHaveBeenCalled() + + removeListener() + + expect(removeEventListenerSpy).toHaveBeenCalledWith( + 'keydown', + expect.any(Function), + ) + }) + + it('should not trigger onPressed when a non-Escape key is pressed', () => { + const mockOnPressed = vi.fn() + + createKeydownEscListener(mockOnPressed) + + const enterEvent = new KeyboardEvent('keydown', { key: 'Enter' }) + document.dispatchEvent(enterEvent) + + expect(mockOnPressed).not.toHaveBeenCalled() + }) + + it('should not trigger onPressed when the cleanup function is called', () => { + const mockOnPressed = vi.fn() + const removeListener = createKeydownEscListener(mockOnPressed) + + removeListener() + + expect(removeEventListenerSpy).toHaveBeenCalledWith( + 'keydown', + expect.any(Function), + ) + + document.dispatchEvent(escapeEvent) + + expect(mockOnPressed).not.toHaveBeenCalled() + }) +}) diff --git a/packages/field-plugin/src/createFieldPlugin/createKeydownEscListener.ts b/packages/field-plugin/src/createFieldPlugin/createKeydownEscListener.ts new file mode 100644 index 00000000..6d8ed306 --- /dev/null +++ b/packages/field-plugin/src/createFieldPlugin/createKeydownEscListener.ts @@ -0,0 +1,15 @@ +/** + * @returns function for cleaning up side effects + */ + +export const createKeydownEscListener = (onPressed: () => void) => { + const handleEsc = (event: KeyboardEvent) => { + const key = event.key + if (key === 'Escape') { + onPressed() + } + } + + document.addEventListener('keydown', handleEsc) + return () => document.removeEventListener('keydown', handleEsc) +} diff --git a/packages/field-plugin/src/createFieldPlugin/createPluginActions/createPluginActions.ts b/packages/field-plugin/src/createFieldPlugin/createPluginActions/createPluginActions.ts index 3b5fff17..23b790e7 100644 --- a/packages/field-plugin/src/createFieldPlugin/createPluginActions/createPluginActions.ts +++ b/packages/field-plugin/src/createFieldPlugin/createPluginActions/createPluginActions.ts @@ -35,6 +35,8 @@ export type CreatePluginActions = (options: { messageCallbacks: PluginMessageCallbacks // This function is called whenever the height changes onHeightChange: (height: number) => void + // This function is called whenever the ESC key is pressed + onKeydownEsc: () => void // This initiates the plugin initialize: Initialize } @@ -85,6 +87,10 @@ export const createPluginActions: CreatePluginActions = ({ postToContainer(heightChangeMessage(uid, height)) } + const onKeydownEsc = () => { + postToContainer(modalChangeMessage({ uid, status: false })) + } + return { actions: { setContent: (content) => { @@ -130,6 +136,7 @@ export const createPluginActions: CreatePluginActions = ({ }, messageCallbacks, onHeightChange, + onKeydownEsc, initialize: () => { return new Promise((resolve) => { const callbackId = pushCallback('loaded', (message) =>