mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 09:30:31 +03:00
Migrated first component and test to typescript
This commit is contained in:
parent
72de9d4ff8
commit
524b0a74c6
8 changed files with 91 additions and 56 deletions
|
@ -17,9 +17,9 @@ module.exports = {
|
||||||
testEnvironment: 'jsdom',
|
testEnvironment: 'jsdom',
|
||||||
testURL: 'http://localhost',
|
testURL: 'http://localhost',
|
||||||
transform: {
|
transform: {
|
||||||
'^.+\\.(js|jsx|mjs)$': '<rootDir>/node_modules/babel-jest',
|
'^.+\\.(ts|tsx|js|jsx|mjs)$': '<rootDir>/node_modules/babel-jest',
|
||||||
'^.+\\.css$': '<rootDir>/config/jest/cssTransform.js',
|
'^.+\\.css$': '<rootDir>/config/jest/cssTransform.js',
|
||||||
'^(?!.*\\.(js|jsx|mjs|css|json)$)': '<rootDir>/config/jest/fileTransform.js',
|
'^(?!.*\\.(ts|tsx|js|jsx|mjs|css|json)$)': '<rootDir>/config/jest/fileTransform.js',
|
||||||
},
|
},
|
||||||
transformIgnorePatterns: [
|
transformIgnorePatterns: [
|
||||||
'[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$',
|
'[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$',
|
||||||
|
|
34
package-lock.json
generated
34
package-lock.json
generated
|
@ -2022,12 +2022,31 @@
|
||||||
"@babel/types": "^7.3.0"
|
"@babel/types": "^7.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/cheerio": {
|
||||||
|
"version": "0.22.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.21.tgz",
|
||||||
|
"integrity": "sha512-aGI3DfswwqgKPiEOTaiHV2ZPC9KEhprpgEbJnv0fZl3SGX0cGgEva1126dGrMC6AJM6v/aihlUgJn9M5DbDZ/Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/color-name": {
|
"@types/color-name": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
||||||
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
|
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/enzyme": {
|
||||||
|
"version": "3.10.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/enzyme/-/enzyme-3.10.5.tgz",
|
||||||
|
"integrity": "sha512-R+phe509UuUYy9Tk0YlSbipRpfVtIzb/9BHn5pTEtjJTF5LXvUjrIQcZvNyANNEyFrd2YGs196PniNT1fgvOQA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/cheerio": "*",
|
||||||
|
"@types/react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/eslint-visitor-keys": {
|
"@types/eslint-visitor-keys": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
|
||||||
|
@ -2264,6 +2283,15 @@
|
||||||
"integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==",
|
"integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/ramda": {
|
||||||
|
"version": "0.27.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.27.14.tgz",
|
||||||
|
"integrity": "sha512-vbw/VAtEJeSJ6Z61QT+epirlnBeJiJIO7ndK1BJ0fKswnfbiTNga/jBG6R3OnBaFYx+UJv6Iv7ZfWDFSsSzNqA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ts-toolbelt": "^6.3.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/react": {
|
"@types/react": {
|
||||||
"version": "16.9.46",
|
"version": "16.9.46",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.46.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.46.tgz",
|
||||||
|
@ -19465,6 +19493,12 @@
|
||||||
"integrity": "sha512-1J/vefLC+BWSo+qe8OnJQfWTYRS6ingxjwqmHMqaMxXMj7kFtKLgAaYW3JeX3mktjgUL+etlU8/B4VUAUI9QGw==",
|
"integrity": "sha512-1J/vefLC+BWSo+qe8OnJQfWTYRS6ingxjwqmHMqaMxXMj7kFtKLgAaYW3JeX3mktjgUL+etlU8/B4VUAUI9QGw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"ts-toolbelt": {
|
||||||
|
"version": "6.15.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-6.15.5.tgz",
|
||||||
|
"integrity": "sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"tsconfig-paths": {
|
"tsconfig-paths": {
|
||||||
"version": "3.9.0",
|
"version": "3.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz",
|
||||||
|
|
|
@ -73,6 +73,9 @@
|
||||||
"@stryker-mutator/javascript-mutator": "^3.2.4",
|
"@stryker-mutator/javascript-mutator": "^3.2.4",
|
||||||
"@stryker-mutator/jest-runner": "^3.2.4",
|
"@stryker-mutator/jest-runner": "^3.2.4",
|
||||||
"@svgr/webpack": "^4.3.3",
|
"@svgr/webpack": "^4.3.3",
|
||||||
|
"@types/enzyme": "^3.10.5",
|
||||||
|
"@types/jest": "^26.0.10",
|
||||||
|
"@types/ramda": "^0.27.14",
|
||||||
"@types/react": "^16.9.46",
|
"@types/react": "^16.9.46",
|
||||||
"@types/react-dom": "^16.9.8",
|
"@types/react-dom": "^16.9.8",
|
||||||
"@types/react-redux": "^7.1.9",
|
"@types/react-redux": "^7.1.9",
|
||||||
|
|
44
src/App.js
44
src/App.js
|
@ -1,44 +0,0 @@
|
||||||
import React, { useEffect } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Route, Switch } from 'react-router-dom';
|
|
||||||
import NotFound from './common/NotFound';
|
|
||||||
import './App.scss';
|
|
||||||
|
|
||||||
const propTypes = {
|
|
||||||
fetchServers: PropTypes.func,
|
|
||||||
servers: PropTypes.object,
|
|
||||||
};
|
|
||||||
|
|
||||||
const App = (MainHeader, Home, MenuLayout, CreateServer, EditServer, Settings) => {
|
|
||||||
const AppComp = ({ fetchServers, servers }) => {
|
|
||||||
// On first load, try to fetch the remote servers if the list is empty
|
|
||||||
useEffect(() => {
|
|
||||||
if (Object.keys(servers).length === 0) {
|
|
||||||
fetchServers();
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="container-fluid app-container">
|
|
||||||
<MainHeader />
|
|
||||||
|
|
||||||
<div className="app">
|
|
||||||
<Switch>
|
|
||||||
<Route exact path="/" component={Home} />
|
|
||||||
<Route exact path="/settings" component={Settings} />
|
|
||||||
<Route exact path="/server/create" component={CreateServer} />
|
|
||||||
<Route exact path="/server/:serverId/edit" component={EditServer} />
|
|
||||||
<Route path="/server/:serverId" component={MenuLayout} />
|
|
||||||
<Route component={NotFound} />
|
|
||||||
</Switch>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
AppComp.propTypes = propTypes;
|
|
||||||
|
|
||||||
return AppComp;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default App;
|
|
39
src/App.tsx
Normal file
39
src/App.tsx
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import React, { useEffect, FC } from 'react';
|
||||||
|
import { Route, Switch } from 'react-router-dom';
|
||||||
|
import NotFound from './common/NotFound';
|
||||||
|
import './App.scss';
|
||||||
|
|
||||||
|
interface AppProps {
|
||||||
|
fetchServers: Function;
|
||||||
|
servers: Record<string, object>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const App = (MainHeader: FC, Home: FC, MenuLayout: FC, CreateServer: FC, EditServer: FC, Settings: FC) => (
|
||||||
|
{ fetchServers, servers }: AppProps,
|
||||||
|
) => {
|
||||||
|
// On first load, try to fetch the remote servers if the list is empty
|
||||||
|
useEffect(() => {
|
||||||
|
if (Object.keys(servers).length === 0) {
|
||||||
|
fetchServers();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container-fluid app-container">
|
||||||
|
<MainHeader />
|
||||||
|
|
||||||
|
<div className="app">
|
||||||
|
<Switch>
|
||||||
|
<Route exact path="/" component={Home} />
|
||||||
|
<Route exact path="/settings" component={Settings} />
|
||||||
|
<Route exact path="/server/create" component={CreateServer} />
|
||||||
|
<Route exact path="/server/:serverId/edit" component={EditServer} />
|
||||||
|
<Route path="/server/:serverId" component={MenuLayout} />
|
||||||
|
<Route component={NotFound} />
|
||||||
|
</Switch>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
|
@ -1,4 +1,4 @@
|
||||||
import Bottle from 'bottlejs';
|
import Bottle, { IContainer } from 'bottlejs';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
import { connect as reduxConnect } from 'react-redux';
|
import { connect as reduxConnect } from 'react-redux';
|
||||||
import { pick } from 'ramda';
|
import { pick } from 'ramda';
|
||||||
|
@ -12,17 +12,19 @@ import provideUtilsServices from '../utils/services/provideServices';
|
||||||
import provideMercureServices from '../mercure/services/provideServices';
|
import provideMercureServices from '../mercure/services/provideServices';
|
||||||
import provideSettingsServices from '../settings/services/provideServices';
|
import provideSettingsServices from '../settings/services/provideServices';
|
||||||
|
|
||||||
|
type ActionMap = Record<string, any>;
|
||||||
|
|
||||||
const bottle = new Bottle();
|
const bottle = new Bottle();
|
||||||
const { container } = bottle;
|
const { container } = bottle;
|
||||||
|
|
||||||
const lazyService = (container, serviceName) => (...args) => container[serviceName](...args);
|
const lazyService = (container: IContainer, serviceName: string) => (...args: any[]) => container[serviceName](...args);
|
||||||
const mapActionService = (map, actionName) => ({
|
const mapActionService = (map: ActionMap, actionName: string): ActionMap => ({
|
||||||
...map,
|
...map,
|
||||||
|
|
||||||
// Wrap actual action service in a function so that it is lazily created the first time it is called
|
// Wrap actual action service in a function so that it is lazily created the first time it is called
|
||||||
[actionName]: lazyService(container, actionName),
|
[actionName]: lazyService(container, actionName),
|
||||||
});
|
});
|
||||||
const connect = (propsFromState, actionServiceNames = []) =>
|
const connect = (propsFromState: string[], actionServiceNames: string[] = []) =>
|
||||||
reduxConnect(
|
reduxConnect(
|
||||||
propsFromState ? pick(propsFromState) : null,
|
propsFromState ? pick(propsFromState) : null,
|
||||||
actionServiceNames.reduce(mapActionService, {}),
|
actionServiceNames.reduce(mapActionService, {}),
|
|
@ -1,4 +1,4 @@
|
||||||
.open-map-modal-btn__btn {
|
.open-map-modal-btn__btn.open-map-modal-btn__btn {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin-right: 1rem;
|
margin-right: 1rem;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { shallow } from 'enzyme';
|
import { shallow, ShallowWrapper } from 'enzyme';
|
||||||
import { Route } from 'react-router-dom';
|
import { Route } from 'react-router-dom';
|
||||||
import { identity } from 'ramda';
|
import { identity } from 'ramda';
|
||||||
import appFactory from '../src/App';
|
import appFactory from '../src/App';
|
||||||
|
|
||||||
describe('<App />', () => {
|
describe('<App />', () => {
|
||||||
let wrapper;
|
let wrapper: ShallowWrapper;
|
||||||
const MainHeader = () => '';
|
const MainHeader = () => null;
|
||||||
|
const DummyComponent = () => null;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const App = appFactory(MainHeader, identity, identity, identity, identity);
|
const App = appFactory(MainHeader, DummyComponent, DummyComponent, DummyComponent, DummyComponent, DummyComponent);
|
||||||
|
|
||||||
wrapper = shallow(<App />);
|
wrapper = shallow(<App fetchServers={identity} servers={{}} />);
|
||||||
});
|
});
|
||||||
afterEach(() => wrapper.unmount());
|
afterEach(() => wrapper.unmount());
|
||||||
|
|
Loading…
Reference in a new issue