diff --git a/package-lock.json b/package-lock.json index 0cb292a7..26e614cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3056,6 +3056,12 @@ "@types/node": "*" } }, + "@types/classnames": { + "version": "2.2.10", + "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.10.tgz", + "integrity": "sha512-1UzDldn9GfYYEsWWnn/P4wkTlkZDH7lDb0wBMGbtIQc9zXEQq7FlKBdZUn6OBqD8sKZZ2RQO2mAjGpXiDGoRmQ==", + "dev": true + }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", diff --git a/package.json b/package.json index d7fc43da..b21786c2 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "@stryker-mutator/javascript-mutator": "^3.2.4", "@stryker-mutator/jest-runner": "^3.2.4", "@svgr/webpack": "^4.3.3", + "@types/classnames": "^2.2.10", "@types/enzyme": "^3.10.5", "@types/jest": "^26.0.10", "@types/moment": "^2.13.0", diff --git a/src/servers/CreateServer.tsx b/src/servers/CreateServer.tsx index 2dff13d8..2da3e601 100644 --- a/src/servers/CreateServer.tsx +++ b/src/servers/CreateServer.tsx @@ -1,7 +1,9 @@ import React, { FC, useEffect } from 'react'; import { v4 as uuid } from 'uuid'; import { RouterProps } from 'react-router'; +import classNames from 'classnames'; import NoMenuLayout from '../common/NoMenuLayout'; +import { StateFlagTimeout } from '../utils/helpers/hooks'; import { ServerForm } from './helpers/ServerForm'; import { ImportServersBtnProps } from './helpers/ImportServersBtn'; import { NewServerData, RegularServer } from './data'; @@ -14,10 +16,26 @@ interface CreateServerProps extends RouterProps { resetSelectedServer: Function; } -const CreateServer = (ImportServersBtn: FC, useStateFlagTimeout: Function) => ( +const Result: FC<{ type: 'success' | 'error' }> = ({ children, type }) => ( +
+
+
+ {children} +
+
+
+); + +const CreateServer = (ImportServersBtn: FC, useStateFlagTimeout: StateFlagTimeout) => ( { createServer, history: { push }, resetSelectedServer }: CreateServerProps, ) => { const [ serversImported, setServersImported ] = useStateFlagTimeout(false, SHOW_IMPORT_MSG_TIME); + const [ errorImporting, setErrorImporting ] = useStateFlagTimeout(false, SHOW_IMPORT_MSG_TIME); const handleSubmit = (serverData: NewServerData) => { const id = uuid(); @@ -32,19 +50,12 @@ const CreateServer = (ImportServersBtn: FC, useStateFlagT return ( - + - {serversImported && ( -
-
-
- Servers properly imported. You can now select one from the list :) -
-
-
- )} + {serversImported && Servers properly imported. You can now select one from the list :)} + {errorImporting && The servers could not be imported. Make sure the format is correct.}
); }; diff --git a/src/servers/helpers/ImportServersBtn.tsx b/src/servers/helpers/ImportServersBtn.tsx index 53e448b7..86ba43d6 100644 --- a/src/servers/helpers/ImportServersBtn.tsx +++ b/src/servers/helpers/ImportServersBtn.tsx @@ -7,16 +7,20 @@ type Ref = RefObject | MutableRefObject; export interface ImportServersBtnProps { onImport?: () => void; + onImportError?: () => void; } -interface ImportServersBtnConnectProps { +interface ImportServersBtnConnectProps extends ImportServersBtnProps { createServers: (servers: Server[]) => void; fileRef: Ref; } -const ImportServersBtn = ({ importServersFromFile }: ServersImporter) => ( - { createServers, fileRef, onImport = () => {} }: ImportServersBtnConnectProps & ImportServersBtnProps, -) => { +const ImportServersBtn = ({ importServersFromFile }: ServersImporter) => ({ + createServers, + fileRef, + onImport = () => {}, + onImportError = () => {}, +}: ImportServersBtnConnectProps) => { const ref = fileRef ?? useRef(); const onChange = async ({ target }: ChangeEvent) => importServersFromFile(target.files?.[0]) @@ -25,7 +29,8 @@ const ImportServersBtn = ({ importServersFromFile }: ServersImporter) => ( .then(() => { // Reset input after processing file (target as { value: string | null }).value = null; - }); + }) + .catch(onImportError); return ( diff --git a/src/utils/helpers/hooks.js b/src/utils/helpers/hooks.js deleted file mode 100644 index d0852714..00000000 --- a/src/utils/helpers/hooks.js +++ /dev/null @@ -1,26 +0,0 @@ -import { useState, useRef } from 'react'; - -const DEFAULT_DELAY = 2000; - -export const useStateFlagTimeout = (setTimeout, clearTimeout) => (initialValue = false, delay = DEFAULT_DELAY) => { - const [ flag, setFlag ] = useState(initialValue); - const timeout = useRef(undefined); - const callback = () => { - setFlag(!initialValue); - - if (timeout.current) { - clearTimeout(timeout.current); - } - - timeout.current = setTimeout(() => setFlag(initialValue), delay); - }; - - return [ flag, callback ]; -}; - -// Return [ flag, toggle, enable, disable ] -export const useToggle = (initialValue = false) => { - const [ flag, setFlag ] = useState(initialValue); - - return [ flag, () => setFlag(!flag), () => setFlag(true), () => setFlag(false) ]; -}; diff --git a/src/utils/helpers/hooks.ts b/src/utils/helpers/hooks.ts new file mode 100644 index 00000000..6e2c986c --- /dev/null +++ b/src/utils/helpers/hooks.ts @@ -0,0 +1,32 @@ +import { useState, useRef } from 'react'; + +const DEFAULT_DELAY = 2000; + +export type StateFlagTimeout = (initialValue?: boolean, delay?: number) => [ boolean, () => void ]; + +export const useStateFlagTimeout = ( + setTimeout: (callback: Function, timeout: number) => number, + clearTimeout: (timer: number) => void, +): StateFlagTimeout => (initialValue = false, delay = DEFAULT_DELAY) => { + const [ flag, setFlag ] = useState(initialValue); + const timeout = useRef(undefined); + const callback = () => { + setFlag(!initialValue); + + if (timeout.current) { + clearTimeout(timeout.current); + } + + timeout.current = setTimeout(() => setFlag(initialValue), delay); + }; + + return [ flag, callback ]; +}; + +type ToggleResult = [ boolean, (flag: boolean) => void, () => void, () => void ]; + +export const useToggle = (initialValue = false): ToggleResult => { + const [ flag, setFlag ] = useState(initialValue); + + return [ flag, () => setFlag(!flag), () => setFlag(true), () => setFlag(false) ]; +}; diff --git a/test/servers/CreateServer.test.tsx b/test/servers/CreateServer.test.tsx index 4d7b46ed..a1779354 100644 --- a/test/servers/CreateServer.test.tsx +++ b/test/servers/CreateServer.test.tsx @@ -12,8 +12,11 @@ describe('', () => { const createServerMock = jest.fn(); const push = jest.fn(); const historyMock = Mock.of({ push }); - const createWrapper = (serversImported = false) => { - const CreateServer = createServerConstruct(ImportServersBtn, () => [ serversImported, () => '' ]); + const createWrapper = (serversImported = false, importFailed = false) => { + const useStateFlagTimeout = jest.fn() + .mockReturnValueOnce([ serversImported, () => '' ]) + .mockReturnValueOnce([ importFailed, () => '' ]); + const CreateServer = createServerConstruct(ImportServersBtn, useStateFlagTimeout); wrapper = shallow( , @@ -31,13 +34,23 @@ describe('', () => { const wrapper = createWrapper(); expect(wrapper.find(ServerForm)).toHaveLength(1); - expect(wrapper.find('.create-server__import-success-msg')).toHaveLength(0); + expect(wrapper.find('Result')).toHaveLength(0); }); it('shows success message when imported is true', () => { const wrapper = createWrapper(true); + const result = wrapper.find('Result'); - expect(wrapper.find('.create-server__import-success-msg')).toHaveLength(1); + expect(result).toHaveLength(1); + expect(result.prop('type')).toEqual('success'); + }); + + it('shows error message when import failed', () => { + const wrapper = createWrapper(false, true); + const result = wrapper.find('Result'); + + expect(result).toHaveLength(1); + expect(result.prop('type')).toEqual('error'); }); it('creates server and redirects to it when form is submitted', () => {