diff --git a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/LoadPluginFromUrl.component.test.tsx b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/LoadPluginFromUrl.component.test.tsx index 1b347d6eec569ab743acd711450aa4479f0cb808..1aec9dda6b880da0174461f9c0225c70a693f1e5 100644 --- a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/LoadPluginFromUrl.component.test.tsx +++ b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/LoadPluginFromUrl.component.test.tsx @@ -226,5 +226,50 @@ describe('LoadPluginFromUrl - component', () => { }); }); }); + it('should load plugin from url after pressing enter key', async () => { + const pluginUrl = 'http://example.com/plugin.js'; + const pluginScript = `function init() {} init()`; + mockedAxiosClient.onGet(pluginUrl).reply(HttpStatusCode.Ok, pluginScript); + + global.URL.canParse = jest.fn().mockReturnValue(true); + + const { store } = renderComponent(); + + const dispatchSpy = jest.spyOn(store, 'dispatch'); + + const input = screen.getByTestId('load-plugin-input-url'); + + act(() => { + fireEvent.change(input, { target: { value: pluginUrl } }); + fireEvent.keyDown(input, { key: 'Enter', code: 'Enter', charCode: 13 }); + }); + + await waitFor(() => { + expect(dispatchSpy).toHaveBeenCalledWith({ + payload: 'e008fb2ceb97e3d6139ffe38a1b39d5d', + type: 'plugins/setCurrentDrawerPluginHash', + }); + }); + }); + it('should not load plugin from url after pressing enter key if url is not correct', async () => { + global.URL.canParse = jest.fn().mockReturnValue(false); + const { store } = renderComponent(); + + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const input = screen.getByTestId('load-plugin-input-url'); + expect(input).toBeVisible(); + + act(() => { + fireEvent.change(input, { target: { value: 'abcd' } }); + fireEvent.keyDown(input, { key: 'Enter', code: 'Enter', charCode: 13 }); + }); + + const button = screen.getByTestId('load-plugin-button'); + expect(button).toBeDisabled(); + + await waitFor(() => { + expect(dispatchSpy).not.toHaveBeenCalled(); + }); + }); }); }); diff --git a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/LoadPluginFromUrl.component.tsx b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/LoadPluginFromUrl.component.tsx index f57224d048c4af8fd407c8c38cff19f111e8dccf..20268f2ecbe5cbe3588377499363f554aba9687e 100644 --- a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/LoadPluginFromUrl.component.tsx +++ b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/LoadPluginFromUrl.component.tsx @@ -3,7 +3,8 @@ import { Input } from '@/shared/Input'; import { useLoadPluginFromUrl } from './hooks/useLoadPluginFromUrl'; export const LoadPluginFromUrl = (): JSX.Element => { - const { handleChangePluginUrl, handleLoadPlugin, isPending, pluginUrl } = useLoadPluginFromUrl(); + const { handleChangePluginUrl, handleLoadPlugin, isPending, pluginUrl, handleKeyPress } = + useLoadPluginFromUrl(); return ( <div className="flex w-full"> @@ -13,6 +14,7 @@ export const LoadPluginFromUrl = (): JSX.Element => { className="h-10 w-full flex-none bg-cultured p-3" type="url" value={pluginUrl} + onKeyDown={handleKeyPress} onChange={handleChangePluginUrl} data-testid="load-plugin-input-url" /> diff --git a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/hooks/useLoadPluginFromUrl.ts b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/hooks/useLoadPluginFromUrl.ts index 1b7c935f9365a150d9c0abfff44f20643d4c0d0f..bc1cf35d906640f7d5e6d2a57b9a597e2798e7cc 100644 --- a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/hooks/useLoadPluginFromUrl.ts +++ b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/hooks/useLoadPluginFromUrl.ts @@ -5,8 +5,9 @@ import { setCurrentDrawerPluginHash } from '@/redux/plugins/plugins.slice'; import { PluginsManager } from '@/services/pluginsManager'; import { showToast } from '@/utils/showToast'; import axios from 'axios'; -import { ChangeEvent, useMemo, useState } from 'react'; +import { ChangeEvent, useMemo, useState, KeyboardEvent } from 'react'; import { getErrorMessage } from '@/utils/getErrorMessage'; +import { ENTER_KEY_CODE } from '@/constants/common'; import { PLUGIN_LOADING_ERROR_PREFIX } from '../../AvailablePluginsDrawer.constants'; type UseLoadPluginReturnType = { @@ -14,6 +15,7 @@ type UseLoadPluginReturnType = { handleLoadPlugin: () => Promise<void>; isPending: boolean; pluginUrl: string; + handleKeyPress: (event: KeyboardEvent<HTMLInputElement>) => Promise<void>; }; export const useLoadPluginFromUrl = (): UseLoadPluginReturnType => { @@ -64,6 +66,13 @@ export const useLoadPluginFromUrl = (): UseLoadPluginReturnType => { setIsLoading(false); } }; + + const handleKeyPress = async (event: KeyboardEvent<HTMLInputElement>): Promise<void> => { + if (event.code === ENTER_KEY_CODE && !isPending) { + await handleLoadPlugin(); + } + }; + const handleChangePluginUrl = (event: ChangeEvent<HTMLInputElement>): void => { setPluginUrl(event.target.value); }; @@ -71,6 +80,7 @@ export const useLoadPluginFromUrl = (): UseLoadPluginReturnType => { return { handleChangePluginUrl, handleLoadPlugin, + handleKeyPress, isPending, pluginUrl, }; diff --git a/src/constants/common.ts b/src/constants/common.ts index cc5db42a582c323623f57455b262fba83529b98f..33920fa680973ef0fb027bef64903a93af288750 100644 --- a/src/constants/common.ts +++ b/src/constants/common.ts @@ -19,3 +19,5 @@ export const ONE_HUNDRED = 100; export const EMPTY_ARRAY_STRING = '[]'; export const ZOOM_FACTOR = 2.0; // Zoom factor indicating doubling the distance for each zoom level + +export const ENTER_KEY_CODE = 'Enter';