From 478209f50dea49adf675c9c449db3b8f64e0882e Mon Sep 17 00:00:00 2001 From: Alejandro Celaya <alejandro@alejandrocelaya.com> Date: Fri, 22 Oct 2021 18:53:00 +0200 Subject: [PATCH] Improvements on ManageServers --- src/servers/CreateServer.tsx | 3 +- src/servers/EditServer.tsx | 4 +-- src/servers/ManageServers.tsx | 24 +++++++------ src/servers/ManageServersRow.tsx | 2 +- src/servers/helpers/ImportServersBtn.tsx | 4 ++- test/servers/EditServer.test.tsx | 6 ++-- .../servers/helpers/ImportServersBtn.test.tsx | 35 ++++++++++++++----- 7 files changed, 52 insertions(+), 26 deletions(-) diff --git a/src/servers/CreateServer.tsx b/src/servers/CreateServer.tsx index 2ee329cd..e24e548c 100644 --- a/src/servers/CreateServer.tsx +++ b/src/servers/CreateServer.tsx @@ -27,7 +27,7 @@ const ImportResult = ({ type }: { type: 'error' | 'success' }) => ( ); const CreateServer = (ImportServersBtn: FC<ImportServersBtnProps>, useStateFlagTimeout: StateFlagTimeout) => ( - { servers, createServer, history: { push } }: CreateServerProps, + { servers, createServer, history: { push, goBack } }: CreateServerProps, ) => { const hasServers = !!Object.keys(servers).length; const [ serversImported, setServersImported ] = useStateFlagTimeout(false, SHOW_IMPORT_MSG_TIME); @@ -44,6 +44,7 @@ const CreateServer = (ImportServersBtn: FC<ImportServersBtnProps>, useStateFlagT <ServerForm title={<h5 className="mb-0">Add new server</h5>} onSubmit={handleSubmit}> {!hasServers && <ImportServersBtn tooltipPlacement="top" onImport={setServersImported} onImportError={setErrorImporting} />} + {hasServers && <Button outline onClick={goBack}>Cancel</Button>} <Button outline color="primary" className="ml-2">Create server</Button> </ServerForm> diff --git a/src/servers/EditServer.tsx b/src/servers/EditServer.tsx index f18c788d..f6576066 100644 --- a/src/servers/EditServer.tsx +++ b/src/servers/EditServer.tsx @@ -10,7 +10,7 @@ interface EditServerProps { } export const EditServer = (ServerError: FC) => withSelectedServer<EditServerProps>(( - { editServer, selectedServer, history: { push, goBack } }, + { editServer, selectedServer, history: { goBack } }, ) => { if (!isServerWithId(selectedServer)) { return null; @@ -18,7 +18,7 @@ export const EditServer = (ServerError: FC) => withSelectedServer<EditServerProp const handleSubmit = (serverData: ServerData) => { editServer(selectedServer.id, serverData); - push(`/server/${selectedServer.id}`); + goBack(); }; return ( diff --git a/src/servers/ManageServers.tsx b/src/servers/ManageServers.tsx index 78d1259b..3f69e43a 100644 --- a/src/servers/ManageServers.tsx +++ b/src/servers/ManageServers.tsx @@ -1,5 +1,5 @@ import { FC, useEffect, useState } from 'react'; -import { Button } from 'reactstrap'; +import { Button, Row } from 'reactstrap'; import { faFileDownload as exportIcon, faPlus as plusIcon } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Link } from 'react-router-dom'; @@ -41,15 +41,19 @@ export const ManageServers = ( <NoMenuLayout> <SearchField className="mb-3" onChange={filterServers} /> - <div className="mb-3 d-flex flex-row"> - <ImportServersBtn onImportError={setErrorImporting} /> - <Button outline className="ml-2" onClick={async () => serversExporter.exportServers()}> - <FontAwesomeIcon icon={exportIcon} fixedWidth /> Export servers - </Button> - <Button outline color="primary" className="ml-auto" tag={Link} to="/server/create"> - <FontAwesomeIcon icon={plusIcon} fixedWidth /> Add a server - </Button> - </div> + <Row className="mb-3"> + <div className="col-md-6 d-flex d-md-block mb-2 mb-md-0"> + <ImportServersBtn className="flex-fill" onImportError={setErrorImporting} /> + <Button outline className="ml-2 flex-fill" onClick={async () => serversExporter.exportServers()}> + <FontAwesomeIcon icon={exportIcon} fixedWidth /> Export servers + </Button> + </div> + <div className="col-md-6 text-md-right d-flex d-md-block"> + <Button outline color="primary" className="flex-fill" tag={Link} to="/server/create"> + <FontAwesomeIcon icon={plusIcon} fixedWidth /> Add a server + </Button> + </div> + </Row> <SimpleCard title="Shlink servers"> <table className="table table-hover mb-0"> diff --git a/src/servers/ManageServersRow.tsx b/src/servers/ManageServersRow.tsx index 306ef719..03ca9cae 100644 --- a/src/servers/ManageServersRow.tsx +++ b/src/servers/ManageServersRow.tsx @@ -54,7 +54,7 @@ export const ManageServersRow = ( <FontAwesomeIcon icon={editIcon} fixedWidth /> Edit server </DropdownItem> <DropdownItem> - <FontAwesomeIcon icon={autoConnectIcon} fixedWidth /> {isAutoConnect ? 'Unset' : 'Set'} auto-connect + <FontAwesomeIcon icon={autoConnectIcon} fixedWidth /> {isAutoConnect ? 'Do not a' : 'A'}uto-connect </DropdownItem> <DropdownItem divider /> <DropdownItem className="dropdown-item--danger" onClick={showModal}> diff --git a/src/servers/helpers/ImportServersBtn.tsx b/src/servers/helpers/ImportServersBtn.tsx index 869b661f..c9e150a5 100644 --- a/src/servers/helpers/ImportServersBtn.tsx +++ b/src/servers/helpers/ImportServersBtn.tsx @@ -12,6 +12,7 @@ export interface ImportServersBtnProps { onImport?: () => void; onImportError?: (error: Error) => void; tooltipPlacement?: 'top' | 'bottom'; + className?: string; } interface ImportServersBtnConnectProps extends ImportServersBtnProps { @@ -25,6 +26,7 @@ const ImportServersBtn = ({ importServersFromFile }: ServersImporter) => ({ onImport = () => {}, onImportError = () => {}, tooltipPlacement = 'bottom', + className = '', }: ImportServersBtnConnectProps) => { const ref = fileRef ?? useRef<HTMLInputElement>(); const onChange = async ({ target }: ChangeEvent<HTMLInputElement>) => @@ -39,7 +41,7 @@ const ImportServersBtn = ({ importServersFromFile }: ServersImporter) => ({ return ( <> - <Button outline id="importBtn" onClick={() => ref.current?.click()}> + <Button outline id="importBtn" className={className} onClick={() => ref.current?.click()}> <FontAwesomeIcon icon={importIcon} fixedWidth /> Import from file </Button> <UncontrolledTooltip placement={tooltipPlacement} target="importBtn"> diff --git a/test/servers/EditServer.test.tsx b/test/servers/EditServer.test.tsx index c74cb72c..4f6a65da 100644 --- a/test/servers/EditServer.test.tsx +++ b/test/servers/EditServer.test.tsx @@ -10,8 +10,8 @@ describe('<EditServer />', () => { let wrapper: ReactWrapper; const ServerError = jest.fn(); const editServerMock = jest.fn(); - const push = jest.fn(); - const historyMock = Mock.of<History>({ push }); + const goBack = jest.fn(); + const historyMock = Mock.of<History>({ goBack }); const match = Mock.of<match<{ serverId: string }>>({ params: { serverId: 'abc123' }, }); @@ -50,6 +50,6 @@ describe('<EditServer />', () => { form.simulate('submit', {}); expect(editServerMock).toHaveBeenCalledTimes(1); - expect(push).toHaveBeenCalledTimes(1); + expect(goBack).toHaveBeenCalledTimes(1); }); }); diff --git a/test/servers/helpers/ImportServersBtn.test.tsx b/test/servers/helpers/ImportServersBtn.test.tsx index da5b4941..dd8471be 100644 --- a/test/servers/helpers/ImportServersBtn.test.tsx +++ b/test/servers/helpers/ImportServersBtn.test.tsx @@ -15,25 +15,43 @@ describe('<ImportServersBtn />', () => { const fileRef = { current: Mock.of<HTMLInputElement>({ click }), }; - - beforeEach(() => { - jest.clearAllMocks(); - - const ImportServersBtn = importServersBtnConstruct(serversImporterMock); - + const ImportServersBtn = importServersBtnConstruct(serversImporterMock); + const createWrapper = (className?: string) => { wrapper = shallow( - <ImportServersBtn createServers={createServersMock} fileRef={fileRef} onImport={onImportMock} />, + <ImportServersBtn + createServers={createServersMock} + className={className} + fileRef={fileRef} + onImport={onImportMock} + />, ); - }); + + return wrapper; + }; + + afterEach(jest.clearAllMocks); afterEach(() => wrapper.unmount()); it('renders a button, a tooltip and a file input', () => { + const wrapper = createWrapper(); + expect(wrapper.find('#importBtn')).toHaveLength(1); expect(wrapper.find(UncontrolledTooltip)).toHaveLength(1); expect(wrapper.find('.import-servers-btn__csv-select')).toHaveLength(1); }); + it.each([ + [ undefined, '' ], + [ 'foo', 'foo' ], + [ 'bar', 'bar' ], + ])('allows a class name to be provided', (providedClassName, expectedClassName) => { + const wrapper = createWrapper(providedClassName); + + expect(wrapper.find('#importBtn').prop('className')).toEqual(expectedClassName); + }); + it('triggers click on file ref when button is clicked', () => { + const wrapper = createWrapper(); const btn = wrapper.find('#importBtn'); btn.simulate('click'); @@ -42,6 +60,7 @@ describe('<ImportServersBtn />', () => { }); it('imports servers when file input changes', (done) => { + const wrapper = createWrapper(); const file = wrapper.find('.import-servers-btn__csv-select'); file.simulate('change', { target: { files: [ '' ] } });