mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2026-05-27 23:56:19 +00:00
Migrated to TS first component where some dependency was being injected
This commit is contained in:
10
package-lock.json
generated
10
package-lock.json
generated
@@ -3410,6 +3410,16 @@
|
|||||||
"@types/react-router": "*"
|
"@types/react-router": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/reactstrap": {
|
||||||
|
"version": "8.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/reactstrap/-/reactstrap-8.5.1.tgz",
|
||||||
|
"integrity": "sha512-oEedcEGoX8EqDymsjrjzTnmaf3FuDY9qKLZMA9cH1ZkkqBc2V4i2sJ6ssXEod+GHQ5XH2r52uvbMkjEkjEZHDQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"popper.js": "^1.14.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/stack-utils": {
|
"@types/stack-utils": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
|
||||||
|
|||||||
@@ -83,6 +83,7 @@
|
|||||||
"@types/react-dom": "^16.9.8",
|
"@types/react-dom": "^16.9.8",
|
||||||
"@types/react-redux": "^7.1.9",
|
"@types/react-redux": "^7.1.9",
|
||||||
"@types/react-router-dom": "^5.1.5",
|
"@types/react-router-dom": "^5.1.5",
|
||||||
|
"@types/reactstrap": "^8.5.1",
|
||||||
"adm-zip": "^0.4.13",
|
"adm-zip": "^0.4.13",
|
||||||
"autoprefixer": "^9.6.3",
|
"autoprefixer": "^9.6.3",
|
||||||
"babel-core": "7.0.0-bridge.0",
|
"babel-core": "7.0.0-bridge.0",
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
import React, { useRef } from 'react';
|
|
||||||
import { UncontrolledTooltip } from 'reactstrap';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
const propTypes = {
|
|
||||||
onImport: PropTypes.func,
|
|
||||||
createServers: PropTypes.func,
|
|
||||||
fileRef: PropTypes.oneOfType([ PropTypes.object, PropTypes.node ]),
|
|
||||||
};
|
|
||||||
|
|
||||||
// FIXME Replace with typescript: (ServersImporter)
|
|
||||||
const ImportServersBtn = ({ importServersFromFile }) => {
|
|
||||||
const ImportServersBtnComp = ({ createServers, fileRef, onImport = () => '' }) => {
|
|
||||||
const ref = fileRef || useRef();
|
|
||||||
const onChange = ({ target }) =>
|
|
||||||
importServersFromFile(target.files[0])
|
|
||||||
.then(createServers)
|
|
||||||
.then(onImport)
|
|
||||||
.then(() => {
|
|
||||||
// Reset input after processing file
|
|
||||||
target.value = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="btn btn-outline-secondary mr-2"
|
|
||||||
id="importBtn"
|
|
||||||
onClick={() => ref.current.click()}
|
|
||||||
>
|
|
||||||
Import from file
|
|
||||||
</button>
|
|
||||||
<UncontrolledTooltip placement="top" target="importBtn">
|
|
||||||
You can create servers by importing a CSV file with columns <b>name</b>, <b>apiKey</b> and <b>url</b>.
|
|
||||||
</UncontrolledTooltip>
|
|
||||||
|
|
||||||
<input type="file" accept="text/csv" className="create-server__csv-select" ref={ref} onChange={onChange} />
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
ImportServersBtnComp.propTypes = propTypes;
|
|
||||||
|
|
||||||
return ImportServersBtnComp;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ImportServersBtn;
|
|
||||||
46
src/servers/helpers/ImportServersBtn.tsx
Normal file
46
src/servers/helpers/ImportServersBtn.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import React, { useRef, RefObject, ChangeEvent, MutableRefObject } from 'react';
|
||||||
|
import { UncontrolledTooltip } from 'reactstrap';
|
||||||
|
import ServersImporter from '../services/ServersImporter';
|
||||||
|
import { Server } from '../data';
|
||||||
|
|
||||||
|
type Ref<T> = RefObject<T> | MutableRefObject<T>;
|
||||||
|
|
||||||
|
interface ImportServersBtnProps {
|
||||||
|
createServers: (servers: Server[]) => void;
|
||||||
|
fileRef: Ref<HTMLInputElement>;
|
||||||
|
onImport?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImportServersBtn = ({ importServersFromFile }: ServersImporter) => (
|
||||||
|
{ createServers, fileRef, onImport = () => {} }: ImportServersBtnProps,
|
||||||
|
) => {
|
||||||
|
const ref = fileRef ?? useRef<HTMLInputElement>();
|
||||||
|
const onChange = async ({ target }: ChangeEvent<HTMLInputElement>) =>
|
||||||
|
importServersFromFile(target.files?.[0])
|
||||||
|
.then(createServers)
|
||||||
|
.then(onImport)
|
||||||
|
.then(() => {
|
||||||
|
// Reset input after processing file
|
||||||
|
(target as { value: string | null }).value = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-outline-secondary mr-2"
|
||||||
|
id="importBtn"
|
||||||
|
onClick={() => ref.current?.click()}
|
||||||
|
>
|
||||||
|
Import from file
|
||||||
|
</button>
|
||||||
|
<UncontrolledTooltip placement="top" target="importBtn">
|
||||||
|
You can create servers by importing a CSV file with columns <b>name</b>, <b>apiKey</b> and <b>url</b>.
|
||||||
|
</UncontrolledTooltip>
|
||||||
|
|
||||||
|
<input type="file" accept="text/csv" className="create-server__csv-select" ref={ref} onChange={onChange} />
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ImportServersBtn;
|
||||||
@@ -6,7 +6,7 @@ const CSV_MIME_TYPE = 'text/csv';
|
|||||||
export default class ServersImporter {
|
export default class ServersImporter {
|
||||||
public constructor(private readonly csvjson: CsvJson, private readonly fileReaderFactory: () => FileReader) {}
|
public constructor(private readonly csvjson: CsvJson, private readonly fileReaderFactory: () => FileReader) {}
|
||||||
|
|
||||||
public importServersFromFile = async (file?: File): Promise<RegularServer[]> => {
|
public importServersFromFile = async (file?: File | null): Promise<RegularServer[]> => {
|
||||||
if (!file || file.type !== CSV_MIME_TYPE) {
|
if (!file || file.type !== CSV_MIME_TYPE) {
|
||||||
throw new Error('No file provided or file is not a CSV');
|
throw new Error('No file provided or file is not a CSV');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { shallow } from 'enzyme';
|
import { shallow, ShallowWrapper } from 'enzyme';
|
||||||
import { UncontrolledTooltip } from 'reactstrap';
|
import { UncontrolledTooltip } from 'reactstrap';
|
||||||
|
import { Mock } from 'ts-mockery';
|
||||||
import importServersBtnConstruct from '../../../src/servers/helpers/ImportServersBtn';
|
import importServersBtnConstruct from '../../../src/servers/helpers/ImportServersBtn';
|
||||||
|
import ServersImporter from '../../../src/servers/services/ServersImporter';
|
||||||
|
|
||||||
describe('<ImportServersBtn />', () => {
|
describe('<ImportServersBtn />', () => {
|
||||||
let wrapper;
|
let wrapper: ShallowWrapper;
|
||||||
const onImportMock = jest.fn();
|
const onImportMock = jest.fn();
|
||||||
const createServersMock = jest.fn();
|
const createServersMock = jest.fn();
|
||||||
const serversImporterMock = {
|
const serversImporterMock = Mock.of<ServersImporter>({
|
||||||
importServersFromFile: jest.fn().mockResolvedValue([]),
|
importServersFromFile: jest.fn().mockResolvedValue([]),
|
||||||
};
|
});
|
||||||
|
const click = jest.fn();
|
||||||
const fileRef = {
|
const fileRef = {
|
||||||
current: { click: jest.fn() },
|
current: Mock.of<HTMLInputElement>({ click }),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
onImportMock.mockReset();
|
jest.clearAllMocks();
|
||||||
createServersMock.mockReset();
|
|
||||||
serversImporterMock.importServersFromFile.mockClear();
|
|
||||||
fileRef.current.click.mockReset();
|
|
||||||
|
|
||||||
const ImportServersBtn = importServersBtnConstruct(serversImporterMock);
|
const ImportServersBtn = importServersBtnConstruct(serversImporterMock);
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ describe('<ImportServersBtn />', () => {
|
|||||||
|
|
||||||
btn.simulate('click');
|
btn.simulate('click');
|
||||||
|
|
||||||
expect(fileRef.current.click).toHaveBeenCalledTimes(1);
|
expect(click).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('imports servers when file input changes', (done) => {
|
it('imports servers when file input changes', (done) => {
|
||||||
Reference in New Issue
Block a user