Merge pull request #489 from mz8i/feature/generate-prop-types
Auto-generate propTypes
This commit is contained in:
commit
6a42961eaf
13
app/.babelrc
Normal file
13
app/.babelrc
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
"@babel/plugin-syntax-jsx",
|
||||||
|
"@babel/plugin-syntax-typescript",
|
||||||
|
[
|
||||||
|
"babel-plugin-typescript-to-proptypes",
|
||||||
|
{
|
||||||
|
"implicitChildren": true,
|
||||||
|
"typeCheck": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
28
app/package-lock.json
generated
28
app/package-lock.json
generated
@ -425,6 +425,15 @@
|
|||||||
"@babel/helper-plugin-utils": "^7.0.0"
|
"@babel/helper-plugin-utils": "^7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@babel/plugin-syntax-typescript": {
|
||||||
|
"version": "7.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.3.3.tgz",
|
||||||
|
"integrity": "sha512-dGwbSMA1YhVS8+31CnPR7LB4pcbrzcV99wQzby4uAfrkZPYZlQ7ImwdpzLqi6Z6IL02b8IAL379CaMwo0x5Lag==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/helper-plugin-utils": "^7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@babel/plugin-transform-arrow-functions": {
|
"@babel/plugin-transform-arrow-functions": {
|
||||||
"version": "7.2.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz",
|
||||||
@ -2324,6 +2333,17 @@
|
|||||||
"integrity": "sha512-f49NsaohQ1ByY20nUrpc30QFdbeT4ntV4PAL2vSZe6uCB5nqAcqXS/qzU+aI6ZfYhWASx5eIsTFvFrs1B2ffGg==",
|
"integrity": "sha512-f49NsaohQ1ByY20nUrpc30QFdbeT4ntV4PAL2vSZe6uCB5nqAcqXS/qzU+aI6ZfYhWASx5eIsTFvFrs1B2ffGg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"babel-plugin-typescript-to-proptypes": {
|
||||||
|
"version": "0.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/babel-plugin-typescript-to-proptypes/-/babel-plugin-typescript-to-proptypes-0.17.1.tgz",
|
||||||
|
"integrity": "sha512-yREUfvDlmn6QjM0QbywXUkXBQMD/iFfLVTl+jig4X7ZLUg9lq8ZLuex8HIM2SQ4X3vcjGnWPFowodlMcXhwxdQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/helper-module-imports": "^7.0.0",
|
||||||
|
"@babel/helper-plugin-utils": "^7.0.0",
|
||||||
|
"@babel/plugin-syntax-typescript": "^7.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"babel-preset-jest": {
|
"babel-preset-jest": {
|
||||||
"version": "23.2.0",
|
"version": "23.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-23.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-23.2.0.tgz",
|
||||||
@ -9509,7 +9529,7 @@
|
|||||||
},
|
},
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
||||||
},
|
},
|
||||||
"minipass": {
|
"minipass": {
|
||||||
@ -13639,7 +13659,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -17045,7 +17065,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
||||||
"dev": true
|
"dev": true
|
||||||
}
|
}
|
||||||
@ -17886,7 +17906,7 @@
|
|||||||
},
|
},
|
||||||
"wrap-ansi": {
|
"wrap-ansi": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
|
"resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
|
||||||
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
|
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -37,6 +37,8 @@
|
|||||||
"sharp": "^0.22.1"
|
"sharp": "^0.22.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/plugin-syntax-jsx": "^7.2.0",
|
||||||
|
"@babel/plugin-syntax-typescript": "^7.3.3",
|
||||||
"@types/express": "^4.17.0",
|
"@types/express": "^4.17.0",
|
||||||
"@types/express-session": "^1.15.13",
|
"@types/express-session": "^1.15.13",
|
||||||
"@types/jest": "^24.0.17",
|
"@types/jest": "^24.0.17",
|
||||||
@ -51,6 +53,7 @@
|
|||||||
"@types/sharp": "^0.22.2",
|
"@types/sharp": "^0.22.2",
|
||||||
"@types/webpack-env": "^1.14.0",
|
"@types/webpack-env": "^1.14.0",
|
||||||
"babel-eslint": "^10.0.2",
|
"babel-eslint": "^10.0.2",
|
||||||
|
"babel-plugin-typescript-to-proptypes": "^0.17.1",
|
||||||
"eslint": "^5.16.0",
|
"eslint": "^5.16.0",
|
||||||
"eslint-plugin-jest": "^22.15.0",
|
"eslint-plugin-jest": "^22.15.0",
|
||||||
"eslint-plugin-react": "^7.14.3",
|
"eslint-plugin-react": "^7.14.3",
|
||||||
|
@ -9,6 +9,17 @@ module.exports = {
|
|||||||
})
|
})
|
||||||
config.module.rules = rules;
|
config.module.rules = rules;
|
||||||
|
|
||||||
|
// find module rule that runs ts-loader for TS(X) files
|
||||||
|
const tsRule = config.module.rules.find(r =>
|
||||||
|
new RegExp(r.test).test('test.tsx') && Array.isArray(r.use) && r.use.some(u => u.loader.includes('ts-loader')));
|
||||||
|
|
||||||
|
// run babel-loader before ts-loader to generate propTypes
|
||||||
|
tsRule.use.push({
|
||||||
|
loader: 'babel-loader',
|
||||||
|
options: {
|
||||||
|
babelrc: true
|
||||||
|
}
|
||||||
|
})
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import { Route, Switch, Link } from 'react-router-dom';
|
import { Route, Switch, Link } from 'react-router-dom';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import '../../node_modules/bootstrap/dist/css/bootstrap.min.css';
|
import '../../node_modules/bootstrap/dist/css/bootstrap.min.css';
|
||||||
import './app.css';
|
import './app.css';
|
||||||
@ -22,15 +21,21 @@ import ContactPage from './pages/contact';
|
|||||||
import DataAccuracyPage from './pages/data-accuracy';
|
import DataAccuracyPage from './pages/data-accuracy';
|
||||||
import OrdnanceSurveyLicencePage from './pages/ordnance-survey-licence';
|
import OrdnanceSurveyLicencePage from './pages/ordnance-survey-licence';
|
||||||
import OrdnanceSurveyUprnPage from './pages/ordnance-survey-uprn';
|
import OrdnanceSurveyUprnPage from './pages/ordnance-survey-uprn';
|
||||||
|
import { Building } from './models/building';
|
||||||
|
import { User } from './models/user';
|
||||||
|
|
||||||
|
|
||||||
interface AppProps {
|
interface AppProps {
|
||||||
user?: any;
|
user?: User;
|
||||||
building?: any;
|
building?: Building;
|
||||||
building_like?: boolean;
|
building_like?: boolean;
|
||||||
revisionId: number;
|
revisionId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface AppState {
|
||||||
|
user?: User;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* App component
|
* App component
|
||||||
*
|
*
|
||||||
@ -43,13 +48,7 @@ interface AppProps {
|
|||||||
* map or other pages are rendered, based on the URL. Use a react-router-dom <Link /> in
|
* map or other pages are rendered, based on the URL. Use a react-router-dom <Link /> in
|
||||||
* child components to navigate without a full page reload.
|
* child components to navigate without a full page reload.
|
||||||
*/
|
*/
|
||||||
class App extends React.Component<AppProps, any> { // TODO: add proper types
|
class App extends React.Component<AppProps, AppState> {
|
||||||
static propTypes = { // TODO: generate propTypes from TS
|
|
||||||
user: PropTypes.object,
|
|
||||||
building: PropTypes.object,
|
|
||||||
building_like: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
static mapAppPaths = ['/', '/:mode(view|edit|multi-edit)/:category/:building(\\d+)?/(history)?'];
|
static mapAppPaths = ['/', '/:mode(view|edit|multi-edit)/:category/:building(\\d+)?/(history)?'];
|
||||||
|
|
||||||
constructor(props: Readonly<AppProps>) {
|
constructor(props: Readonly<AppProps>) {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import InfoBox from '../components/info-box';
|
import InfoBox from '../components/info-box';
|
||||||
@ -18,8 +17,4 @@ const BuildingNotFound: React.FunctionComponent<BuildingNotFoundProps> = (props)
|
|||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
|
||||||
BuildingNotFound.propTypes = {
|
|
||||||
mode: PropTypes.string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default BuildingNotFound;
|
export default BuildingNotFound;
|
||||||
|
@ -20,9 +20,9 @@ import { Building } from '../models/building';
|
|||||||
interface BuildingViewProps {
|
interface BuildingViewProps {
|
||||||
cat: string;
|
cat: string;
|
||||||
mode: 'view' | 'edit';
|
mode: 'view' | 'edit';
|
||||||
building: Building;
|
building?: Building;
|
||||||
building_like: boolean;
|
building_like?: boolean;
|
||||||
user: any;
|
user?: any;
|
||||||
selectBuilding: (building: Building) => void
|
selectBuilding: (building: Building) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import './categories.css'
|
import './categories.css'
|
||||||
|
|
||||||
const Categories = (props) => (
|
interface CategoriesProps {
|
||||||
|
mode: 'view' | 'edit' | 'multi-edit';
|
||||||
|
building_id?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Categories: React.FC<CategoriesProps> = (props) => (
|
||||||
<ol className="data-category-list">
|
<ol className="data-category-list">
|
||||||
<Category
|
<Category
|
||||||
title="Location"
|
title="Location"
|
||||||
@ -117,12 +121,17 @@ const Categories = (props) => (
|
|||||||
</ol>
|
</ol>
|
||||||
)
|
)
|
||||||
|
|
||||||
Categories.propTypes = {
|
interface CategoryProps {
|
||||||
mode: PropTypes.string,
|
mode: 'view' | 'edit' | 'multi-edit';
|
||||||
building_id: PropTypes.number
|
building_id?: number;
|
||||||
|
slug: string;
|
||||||
|
title: string;
|
||||||
|
desc: string;
|
||||||
|
help: string;
|
||||||
|
inactive: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Category = (props) => {
|
const Category: React.FC<CategoryProps> = (props) => {
|
||||||
let categoryLink = `/${props.mode}/${props.slug}`;
|
let categoryLink = `/${props.mode}/${props.slug}`;
|
||||||
if (props.building_id != undefined) categoryLink += `/${props.building_id}`;
|
if (props.building_id != undefined) categoryLink += `/${props.building_id}`;
|
||||||
|
|
||||||
@ -145,14 +154,4 @@ const Category = (props) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Category.propTypes = {
|
|
||||||
title: PropTypes.string,
|
|
||||||
desc: PropTypes.string,
|
|
||||||
slug: PropTypes.string,
|
|
||||||
help: PropTypes.string,
|
|
||||||
inactive: PropTypes.bool,
|
|
||||||
mode: PropTypes.string,
|
|
||||||
building_id: PropTypes.number
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Categories;
|
export default Categories;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React from 'react';
|
||||||
import { Link, NavLink } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { BackIcon, EditIcon, ViewIcon }from '../components/icons';
|
import { BackIcon }from '../components/icons';
|
||||||
|
|
||||||
interface ContainerHeaderProps {
|
interface ContainerHeaderProps {
|
||||||
cat?: string;
|
cat?: string;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { DataTitleCopyable } from './data-title';
|
import { DataTitleCopyable } from './data-title';
|
||||||
import { BaseDataEntryProps } from './data-entry';
|
import { BaseDataEntryProps } from './data-entry';
|
||||||
@ -8,7 +7,6 @@ interface CheckboxDataEntryProps extends BaseDataEntryProps {
|
|||||||
value: boolean;
|
value: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const CheckboxDataEntry: React.FunctionComponent<CheckboxDataEntryProps> = (props) => {
|
const CheckboxDataEntry: React.FunctionComponent<CheckboxDataEntryProps> = (props) => {
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
@ -37,18 +35,4 @@ const CheckboxDataEntry: React.FunctionComponent<CheckboxDataEntryProps> = (prop
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckboxDataEntry.propTypes = {
|
|
||||||
title: PropTypes.string,
|
|
||||||
slug: PropTypes.string,
|
|
||||||
tooltip: PropTypes.string,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
value: PropTypes.any,
|
|
||||||
onChange: PropTypes.func,
|
|
||||||
copy: PropTypes.shape({
|
|
||||||
copying: PropTypes.bool,
|
|
||||||
copyingKey: PropTypes.func,
|
|
||||||
toggleCopyAttribute: PropTypes.func
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default CheckboxDataEntry;
|
export default CheckboxDataEntry;
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { DataTitleCopyable } from './data-title';
|
import { DataTitleCopyable } from './data-title';
|
||||||
|
import { CopyProps } from '../data-containers/category-view-props';
|
||||||
|
|
||||||
interface BaseDataEntryProps {
|
interface BaseDataEntryProps {
|
||||||
slug: string;
|
slug: string;
|
||||||
title: string;
|
title: string;
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
copy?: any; // CopyProps clashes with propTypes
|
copy?: CopyProps; // CopyProps clashes with propTypes
|
||||||
mode?: 'view' | 'edit' | 'multi-edit';
|
mode?: 'view' | 'edit' | 'multi-edit';
|
||||||
onChange?: (key: string, value: any) => void;
|
onChange?: (key: string, value: any) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DataEntryProps extends BaseDataEntryProps {
|
interface DataEntryProps extends BaseDataEntryProps {
|
||||||
value: string;
|
value?: string;
|
||||||
maxLength?: number;
|
maxLength?: number;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
valueTransform?: (string) => string
|
valueTransform?: (string) => string
|
||||||
@ -49,22 +49,6 @@ const DataEntry: React.FunctionComponent<DataEntryProps> = (props) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
DataEntry.propTypes = {
|
|
||||||
title: PropTypes.string,
|
|
||||||
slug: PropTypes.string,
|
|
||||||
tooltip: PropTypes.string,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
value: PropTypes.any,
|
|
||||||
placeholder: PropTypes.string,
|
|
||||||
maxLength: PropTypes.number,
|
|
||||||
onChange: PropTypes.func,
|
|
||||||
copy: PropTypes.shape({
|
|
||||||
copying: PropTypes.bool,
|
|
||||||
copyingKey: PropTypes.func,
|
|
||||||
toggleCopyAttribute: PropTypes.func
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DataEntry;
|
export default DataEntry;
|
||||||
export {
|
export {
|
||||||
BaseDataEntryProps
|
BaseDataEntryProps
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import Tooltip from '../../components/tooltip';
|
import Tooltip from '../../components/tooltip';
|
||||||
|
import { CopyProps } from '../data-containers/category-view-props';
|
||||||
|
|
||||||
|
|
||||||
interface DataTitleProps {
|
interface DataTitleProps {
|
||||||
@ -18,18 +18,13 @@ const DataTitle: React.FunctionComponent<DataTitleProps> = (props) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
DataTitle.propTypes = {
|
|
||||||
title: PropTypes.string,
|
|
||||||
tooltip: PropTypes.string
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
interface DataTitleCopyableProps {
|
interface DataTitleCopyableProps {
|
||||||
title: string;
|
title: string;
|
||||||
tooltip: string;
|
tooltip?: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
copy?: any; // TODO: type should be CopyProps, but that clashes with propTypes in some obscure way
|
copy?: CopyProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DataTitleCopyable: React.FunctionComponent<DataTitleCopyableProps> = (props) => {
|
const DataTitleCopyable: React.FunctionComponent<DataTitleCopyableProps> = (props) => {
|
||||||
@ -55,18 +50,5 @@ const DataTitleCopyable: React.FunctionComponent<DataTitleCopyableProps> = (prop
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
DataTitleCopyable.propTypes = {
|
|
||||||
title: PropTypes.string,
|
|
||||||
tooltip: PropTypes.string,
|
|
||||||
slug: PropTypes.string,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
copy: PropTypes.shape({
|
|
||||||
copying: PropTypes.bool,
|
|
||||||
copyingKey: PropTypes.func,
|
|
||||||
toggleCopyAttribute: PropTypes.func,
|
|
||||||
toggleCopying: PropTypes.func
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DataTitle;
|
export default DataTitle;
|
||||||
export { DataTitleCopyable }
|
export { DataTitleCopyable }
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import Tooltip from '../../components/tooltip';
|
import Tooltip from '../../components/tooltip';
|
||||||
|
|
||||||
@ -49,11 +48,4 @@ const LikeDataEntry: React.FunctionComponent<LikeDataEntryProps> = (props) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
LikeDataEntry.propTypes = {
|
|
||||||
// mode: PropTypes.string,
|
|
||||||
userLike: PropTypes.bool,
|
|
||||||
totalLikes: PropTypes.number,
|
|
||||||
onLike: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LikeDataEntry;
|
export default LikeDataEntry;
|
||||||
|
@ -1,23 +1,16 @@
|
|||||||
import React, { Component, Fragment } from 'react';
|
import React, { Component, Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { sanitiseURL } from '../../helpers';
|
import { sanitiseURL } from '../../helpers';
|
||||||
import { DataTitleCopyable } from './data-title';
|
import { DataTitleCopyable } from './data-title';
|
||||||
|
import { BaseDataEntryProps } from './data-entry';
|
||||||
|
|
||||||
|
|
||||||
class MultiDataEntry extends Component<any, any> { // TODO: add proper types
|
interface MultiDataEntryProps extends BaseDataEntryProps {
|
||||||
static propTypes = { // TODO: generate propTypes from TS
|
value: string[];
|
||||||
slug: PropTypes.string,
|
placeholder: string;
|
||||||
title: PropTypes.string,
|
}
|
||||||
tooltip: PropTypes.string,
|
|
||||||
value: PropTypes.arrayOf(PropTypes.string),
|
class MultiDataEntry extends Component<MultiDataEntryProps> {
|
||||||
placeholder: PropTypes.string,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
onChange: PropTypes.func,
|
|
||||||
copy: PropTypes.bool,
|
|
||||||
toggleCopyAttribute: PropTypes.func,
|
|
||||||
copying: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { DataTitleCopyable } from './data-title';
|
import { DataTitleCopyable } from './data-title';
|
||||||
import { BaseDataEntryProps } from './data-entry';
|
import { BaseDataEntryProps } from './data-entry';
|
||||||
@ -45,22 +44,4 @@ const NumericDataEntry: React.FunctionComponent<NumericDataEntryProps> = (props)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
NumericDataEntry.propTypes = {
|
|
||||||
title: PropTypes.string,
|
|
||||||
slug: PropTypes.string,
|
|
||||||
tooltip: PropTypes.string,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
value: PropTypes.any,
|
|
||||||
placeholder: PropTypes.string,
|
|
||||||
max: PropTypes.number,
|
|
||||||
min: PropTypes.number,
|
|
||||||
step: PropTypes.number,
|
|
||||||
onChange: PropTypes.func,
|
|
||||||
copy: PropTypes.shape({
|
|
||||||
copying: PropTypes.bool,
|
|
||||||
copyingKey: PropTypes.func,
|
|
||||||
toggleCopyAttribute: PropTypes.func
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NumericDataEntry;
|
export default NumericDataEntry;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { DataTitleCopyable } from './data-title';
|
import { DataTitleCopyable } from './data-title';
|
||||||
import { BaseDataEntryProps } from './data-entry';
|
import { BaseDataEntryProps } from './data-entry';
|
||||||
@ -44,20 +43,4 @@ const SelectDataEntry: React.FunctionComponent<SelectDataEntryProps> = (props) =
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectDataEntry.propTypes = {
|
|
||||||
title: PropTypes.string,
|
|
||||||
slug: PropTypes.string,
|
|
||||||
tooltip: PropTypes.string,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
value: PropTypes.any,
|
|
||||||
placeholder: PropTypes.string,
|
|
||||||
options: PropTypes.arrayOf(PropTypes.string),
|
|
||||||
onChange: PropTypes.func,
|
|
||||||
copy: PropTypes.shape({
|
|
||||||
copying: PropTypes.bool,
|
|
||||||
copyingKey: PropTypes.func,
|
|
||||||
toggleCopyAttribute: PropTypes.func
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SelectDataEntry;
|
export default SelectDataEntry;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { DataTitleCopyable } from './data-title';
|
import { DataTitleCopyable } from './data-title';
|
||||||
import { BaseDataEntryProps } from './data-entry';
|
import { BaseDataEntryProps } from './data-entry';
|
||||||
@ -42,20 +41,4 @@ const TextboxDataEntry: React.FunctionComponent<TextboxDataEntryProps> = (props)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextboxDataEntry.propTypes = {
|
|
||||||
title: PropTypes.string,
|
|
||||||
slug: PropTypes.string,
|
|
||||||
tooltip: PropTypes.string,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
value: PropTypes.any,
|
|
||||||
placeholder: PropTypes.string,
|
|
||||||
maxLength: PropTypes.number,
|
|
||||||
onChange: PropTypes.func,
|
|
||||||
copy: PropTypes.shape({
|
|
||||||
copying: PropTypes.bool,
|
|
||||||
copyingKey: PropTypes.func,
|
|
||||||
toggleCopyAttribute: PropTypes.func
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TextboxDataEntry;
|
export default TextboxDataEntry;
|
||||||
|
@ -1,10 +1,18 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import Tooltip from '../../components/tooltip';
|
|
||||||
import DataTitle from './data-title';
|
import DataTitle from './data-title';
|
||||||
|
|
||||||
const UPRNsDataEntry = (props) => {
|
|
||||||
|
interface UPRNsDataEntryProps {
|
||||||
|
title: string;
|
||||||
|
tooltip: string;
|
||||||
|
value: {
|
||||||
|
uprn: string;
|
||||||
|
parent_uprn?: string;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const UPRNsDataEntry: React.FC<UPRNsDataEntryProps> = (props) => {
|
||||||
const uprns = props.value || [];
|
const uprns = props.value || [];
|
||||||
const noParent = uprns.filter(uprn => uprn.parent_uprn == null);
|
const noParent = uprns.filter(uprn => uprn.parent_uprn == null);
|
||||||
const withParent = uprns.filter(uprn => uprn.parent_uprn != null);
|
const withParent = uprns.filter(uprn => uprn.parent_uprn != null);
|
||||||
@ -46,15 +54,6 @@ const UPRNsDataEntry = (props) => {
|
|||||||
</dd>
|
</dd>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
)
|
||||||
}
|
};
|
||||||
|
|
||||||
UPRNsDataEntry.propTypes = {
|
|
||||||
title: PropTypes.string,
|
|
||||||
tooltip: PropTypes.string,
|
|
||||||
value: PropTypes.arrayOf(PropTypes.shape({
|
|
||||||
uprn: PropTypes.string.isRequired,
|
|
||||||
parent_uprn: PropTypes.string
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
export default UPRNsDataEntry;
|
export default UPRNsDataEntry;
|
||||||
|
@ -1,23 +1,19 @@
|
|||||||
import React, { Component, Fragment } from 'react';
|
import React, { Component, Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import NumericDataEntry from './numeric-data-entry';
|
import NumericDataEntry from './numeric-data-entry';
|
||||||
import { dataFields } from '../../data_fields';
|
import { dataFields } from '../../data_fields';
|
||||||
|
import { CopyProps } from '../data-containers/category-view-props';
|
||||||
|
|
||||||
class YearDataEntry extends Component<any, any> { // TODO: add proper types
|
interface YearDataEntryProps {
|
||||||
static propTypes = { // TODO: generate propTypes from TS
|
year: number;
|
||||||
year: PropTypes.number,
|
upper: number;
|
||||||
upper: PropTypes.number,
|
lower: number;
|
||||||
lower: PropTypes.number,
|
copy?: CopyProps;
|
||||||
mode: PropTypes.string,
|
mode?: 'view' | 'edit' | 'multi-edit';
|
||||||
onChange: PropTypes.func,
|
onChange?: (key: string, value: any) => void;
|
||||||
copy: PropTypes.shape({
|
}
|
||||||
copying: PropTypes.bool,
|
|
||||||
copyingKey: PropTypes.func,
|
|
||||||
toggleCopyAttribute: PropTypes.func
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
|
class YearDataEntry extends Component<YearDataEntryProps, any> {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Redirect, NavLink } from 'react-router-dom';
|
import { Redirect, NavLink } from 'react-router-dom';
|
||||||
|
|
||||||
import ContainerHeader from './container-header';
|
import ContainerHeader from './container-header';
|
||||||
@ -19,10 +18,10 @@ interface DataContainerProps {
|
|||||||
help: string;
|
help: string;
|
||||||
inactive?: boolean;
|
inactive?: boolean;
|
||||||
|
|
||||||
user: User;
|
user?: User;
|
||||||
mode: 'view' | 'edit';
|
mode: 'view' | 'edit';
|
||||||
building: Building;
|
building?: Building;
|
||||||
building_like: boolean;
|
building_like?: boolean;
|
||||||
selectBuilding: (building: Building) => void
|
selectBuilding: (building: Building) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,15 +44,6 @@ interface DataContainerState {
|
|||||||
*/
|
*/
|
||||||
const withCopyEdit = (WrappedComponent: React.ComponentType<CategoryViewProps>) => {
|
const withCopyEdit = (WrappedComponent: React.ComponentType<CategoryViewProps>) => {
|
||||||
return class DataContainer extends React.Component<DataContainerProps, DataContainerState> {
|
return class DataContainer extends React.Component<DataContainerProps, DataContainerState> {
|
||||||
static propTypes = { // TODO: generate propTypes from TS
|
|
||||||
title: PropTypes.string,
|
|
||||||
slug: PropTypes.string,
|
|
||||||
intro: PropTypes.string,
|
|
||||||
help: PropTypes.string,
|
|
||||||
inactive: PropTypes.bool,
|
|
||||||
children: PropTypes.node
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
@ -1,16 +1,23 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React from 'react';
|
||||||
import { Link, Redirect } from 'react-router-dom';
|
import { Link, Redirect, RouteComponentProps } from 'react-router-dom';
|
||||||
import { parse } from 'query-string';
|
import { parse } from 'query-string';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import Sidebar from './sidebar';
|
import Sidebar from './sidebar';
|
||||||
import InfoBox from '../components/info-box';
|
import InfoBox from '../components/info-box';
|
||||||
import { BackIcon }from '../components/icons';
|
import { BackIcon }from '../components/icons';
|
||||||
import DataEntry from './data-components/data-entry';
|
import DataEntry from './data-components/data-entry';
|
||||||
import { dataFields } from '../data_fields';
|
import { dataFields } from '../data_fields';
|
||||||
|
import { User } from '../models/user';
|
||||||
|
|
||||||
|
interface MultiEditRouteParams {
|
||||||
|
cat: string;
|
||||||
|
}
|
||||||
|
|
||||||
const MultiEdit = (props) => {
|
interface MultiEditProps extends RouteComponentProps<MultiEditRouteParams> {
|
||||||
|
user?: User;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MultiEdit: React.FC<MultiEditProps> = (props) => {
|
||||||
if (!props.user){
|
if (!props.user){
|
||||||
return <Redirect to="/sign-up.html" />
|
return <Redirect to="/sign-up.html" />
|
||||||
}
|
}
|
||||||
@ -85,10 +92,4 @@ const MultiEdit = (props) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
MultiEdit.propTypes = {
|
|
||||||
user: PropTypes.object,
|
|
||||||
match: PropTypes.object,
|
|
||||||
location: PropTypes.object
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MultiEdit;
|
export default MultiEdit;
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import './sidebar.css';
|
import './sidebar.css';
|
||||||
|
|
||||||
const Sidebar = (props) => (
|
const Sidebar: React.FC<{}> = (props) => (
|
||||||
<div id="sidebar" className="info-container">
|
<div id="sidebar" className="info-container">
|
||||||
{ props.children }
|
{ props.children }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
Sidebar.propTypes = {
|
|
||||||
children: PropTypes.node
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Sidebar;
|
export default Sidebar;
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
function ErrorBox(props){
|
interface ErrorBoxProps {
|
||||||
|
msg: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ErrorBox: React.FC<ErrorBoxProps> = (props) => {
|
||||||
if (props.msg) {
|
if (props.msg) {
|
||||||
console.error(props.msg);
|
console.error(props.msg);
|
||||||
}
|
}
|
||||||
@ -12,7 +15,7 @@ function ErrorBox(props){
|
|||||||
(
|
(
|
||||||
<div className="alert alert-danger" role="alert">
|
<div className="alert alert-danger" role="alert">
|
||||||
{
|
{
|
||||||
(typeof props.msg === 'string' || props.msg instanceof String)?
|
typeof props.msg === 'string' ?
|
||||||
props.msg
|
props.msg
|
||||||
: 'Unexpected error'
|
: 'Unexpected error'
|
||||||
}
|
}
|
||||||
@ -21,10 +24,6 @@ function ErrorBox(props){
|
|||||||
}
|
}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
ErrorBox.propTypes = {
|
|
||||||
msg: PropTypes.string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ErrorBox;
|
export default ErrorBox;
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
const InfoBox = (props) => (
|
interface InfoBoxProps {
|
||||||
|
msg: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const InfoBox: React.FC<InfoBoxProps> = (props) => (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{
|
{
|
||||||
(props.msg || props.children)?
|
(props.msg || props.children)?
|
||||||
(
|
(
|
||||||
<div className="alert alert-info" role="alert">
|
<div className="alert alert-info" role="alert">
|
||||||
{
|
{
|
||||||
(typeof props.msg === 'string' || props.msg instanceof String)?
|
typeof props.msg === 'string' ?
|
||||||
props.msg
|
props.msg
|
||||||
: 'Enjoy the colouring! Usual service should resume shortly.'
|
: 'Enjoy the colouring! Usual service should resume shortly.'
|
||||||
}
|
}
|
||||||
@ -21,9 +24,4 @@ const InfoBox = (props) => (
|
|||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
|
||||||
InfoBox.propTypes = {
|
|
||||||
msg: PropTypes.string,
|
|
||||||
children: PropTypes.node
|
|
||||||
}
|
|
||||||
|
|
||||||
export default InfoBox;
|
export default InfoBox;
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import './tooltip.css';
|
import './tooltip.css';
|
||||||
import { InfoIcon } from './icons';
|
import { InfoIcon } from './icons';
|
||||||
|
|
||||||
class Tooltip extends Component<any, any> { // TODO: add proper types
|
interface TooltipProps {
|
||||||
static propTypes = { // TODO: generate propTypes from TS
|
text: string;
|
||||||
text: PropTypes.string
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
interface TooltipState {
|
||||||
|
active: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Tooltip extends Component<TooltipProps, TooltipState> {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { Logo } from './components/logo';
|
import { Logo } from './components/logo';
|
||||||
|
import { User } from './models/user';
|
||||||
|
|
||||||
import './header.css';
|
import './header.css';
|
||||||
|
|
||||||
|
|
||||||
interface HeaderProps {
|
interface HeaderProps {
|
||||||
user: any;
|
user: User;
|
||||||
animateLogo: boolean;
|
animateLogo: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,14 +19,7 @@ interface HeaderState {
|
|||||||
/**
|
/**
|
||||||
* Render the main header using a responsive design
|
* Render the main header using a responsive design
|
||||||
*/
|
*/
|
||||||
class Header extends React.Component<HeaderProps, HeaderState> { // TODO: add proper types
|
class Header extends React.Component<HeaderProps, HeaderState> {
|
||||||
static propTypes = { // TODO: generate propTypes from TS
|
|
||||||
user: PropTypes.shape({
|
|
||||||
username: PropTypes.string
|
|
||||||
}),
|
|
||||||
animateLogo: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {collapseMenu: true};
|
this.state = {collapseMenu: true};
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import { Switch, Route, RouteComponentProps, Redirect } from 'react-router-dom';
|
import { Switch, Route, RouteComponentProps, Redirect } from 'react-router-dom';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import Welcome from './pages/welcome';
|
import Welcome from './pages/welcome';
|
||||||
import Sidebar from './building/sidebar';
|
import Sidebar from './building/sidebar';
|
||||||
@ -19,10 +18,10 @@ interface MapAppRouteParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface MapAppProps extends RouteComponentProps<MapAppRouteParams> {
|
interface MapAppProps extends RouteComponentProps<MapAppRouteParams> {
|
||||||
building: Building;
|
building?: Building;
|
||||||
building_like: boolean;
|
building_like?: boolean;
|
||||||
user: any;
|
user?: any;
|
||||||
revisionId: number;
|
revisionId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MapAppState {
|
interface MapAppState {
|
||||||
@ -33,13 +32,6 @@ interface MapAppState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MapApp extends React.Component<MapAppProps, MapAppState> {
|
class MapApp extends React.Component<MapAppProps, MapAppState> {
|
||||||
static propTypes = {
|
|
||||||
category: PropTypes.string,
|
|
||||||
revision_id: PropTypes.number,
|
|
||||||
building: PropTypes.object,
|
|
||||||
building_like: PropTypes.bool,
|
|
||||||
user: PropTypes.object
|
|
||||||
};
|
|
||||||
constructor(props: Readonly<MapAppProps>) {
|
constructor(props: Readonly<MapAppProps>) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
@ -235,7 +227,7 @@ class MapApp extends React.Component<MapAppProps, MapAppState> {
|
|||||||
</Route>
|
</Route>
|
||||||
<Route exact path="/:mode/categories/:building?">
|
<Route exact path="/:mode/categories/:building?">
|
||||||
<Sidebar>
|
<Sidebar>
|
||||||
<Categories mode={mode} building_id={building_id} />
|
<Categories mode={mode || 'view'} building_id={building_id} />
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
</Route>
|
</Route>
|
||||||
<Route exact path="/multi-edit/:cat" render={(props) => (
|
<Route exact path="/multi-edit/:cat" render={(props) => (
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import './legend.css';
|
import './legend.css';
|
||||||
import { Logo } from '../components/logo';
|
import { Logo } from '../components/logo';
|
||||||
@ -114,13 +113,15 @@ const LEGEND_CONFIG = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class Legend extends React.Component<any, any> { // TODO: add proper types
|
interface LegendProps {
|
||||||
static propTypes = { // TODO: generate propTypes from TS
|
slug: string;
|
||||||
slug: PropTypes.string,
|
}
|
||||||
color: PropTypes.string,
|
|
||||||
text: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
|
interface LegendState {
|
||||||
|
collapseList: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Legend extends React.Component<LegendProps, LegendState> {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {collapseList: false};
|
this.state = {collapseList: false};
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component, Fragment } from 'react';
|
import React, { Component, Fragment } from 'react';
|
||||||
import { Map, TileLayer, ZoomControl, AttributionControl, GeoJSON } from 'react-leaflet-universal';
|
import { Map, TileLayer, ZoomControl, AttributionControl, GeoJSON } from 'react-leaflet-universal';
|
||||||
import { GeoJsonObject } from 'geojson';
|
import { GeoJsonObject } from 'geojson';
|
||||||
@ -15,12 +14,12 @@ import { Building } from '../models/building';
|
|||||||
const OS_API_KEY = 'NVUxtY5r8eA6eIfwrPTAGKrAAsoeI9E9';
|
const OS_API_KEY = 'NVUxtY5r8eA6eIfwrPTAGKrAAsoeI9E9';
|
||||||
|
|
||||||
interface ColouringMapProps {
|
interface ColouringMapProps {
|
||||||
building: Building;
|
building?: Building;
|
||||||
mode: 'basic' | 'view' | 'edit' | 'multi-edit';
|
mode: 'basic' | 'view' | 'edit' | 'multi-edit';
|
||||||
category: string;
|
category: string;
|
||||||
revision_id: number;
|
revision_id: number;
|
||||||
selectBuilding: any;
|
selectBuilding: (building: Building) => void;
|
||||||
colourBuilding: any;
|
colourBuilding: (building: Building) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ColouringMapState {
|
interface ColouringMapState {
|
||||||
@ -34,15 +33,6 @@ interface ColouringMapState {
|
|||||||
* Map area
|
* Map area
|
||||||
*/
|
*/
|
||||||
class ColouringMap extends Component<ColouringMapProps, ColouringMapState> {
|
class ColouringMap extends Component<ColouringMapProps, ColouringMapState> {
|
||||||
static propTypes = { // TODO: generate propTypes from TS
|
|
||||||
building: PropTypes.object,
|
|
||||||
mode: PropTypes.string,
|
|
||||||
category: PropTypes.string,
|
|
||||||
revision_id: PropTypes.number,
|
|
||||||
selectBuilding: PropTypes.func,
|
|
||||||
colourBuilding: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -1,16 +1,35 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import './search-box.css';
|
import './search-box.css';
|
||||||
import { SearchIcon } from '../components/icons';
|
import { SearchIcon } from '../components/icons';
|
||||||
|
import { Point } from 'geojson';
|
||||||
|
|
||||||
|
interface SearchResult {
|
||||||
|
type: string;
|
||||||
|
attributes: {
|
||||||
|
label: string;
|
||||||
|
zoom: number;
|
||||||
|
};
|
||||||
|
geometry: Point
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface SearchBoxProps {
|
||||||
|
onLocate: (lat: number, lng: number, zoom: number) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SearchBoxState {
|
||||||
|
q: string;
|
||||||
|
results: SearchResult[];
|
||||||
|
fetching: boolean;
|
||||||
|
collapsedSearch: boolean;
|
||||||
|
smallScreen: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search for location
|
* Search for location
|
||||||
*/
|
*/
|
||||||
class SearchBox extends Component<any, any> { // TODO: add proper types
|
class SearchBox extends Component<SearchBoxProps, SearchBoxState> {
|
||||||
static propTypes = { // TODO: generate propTypes from TS
|
|
||||||
onLocate: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import './theme-switcher.css';
|
import './theme-switcher.css';
|
||||||
|
|
||||||
const ThemeSwitcher = (props) => (
|
interface ThemeSwitcherProps {
|
||||||
|
currentTheme: string;
|
||||||
|
onSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ThemeSwitcher: React.FC<ThemeSwitcherProps> = (props) => (
|
||||||
<form className={`theme-switcher ${props.currentTheme}`} onSubmit={props.onSubmit}>
|
<form className={`theme-switcher ${props.currentTheme}`} onSubmit={props.onSubmit}>
|
||||||
<button className="btn btn-outline btn-outline-dark"
|
<button className="btn btn-outline btn-outline-dark"
|
||||||
type="submit">
|
type="submit">
|
||||||
@ -12,9 +16,4 @@ const ThemeSwitcher = (props) => (
|
|||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|
||||||
ThemeSwitcher.propTypes = {
|
|
||||||
currentTheme: PropTypes.string,
|
|
||||||
onSubmit: PropTypes.func.isRequired
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ThemeSwitcher;
|
export default ThemeSwitcher;
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
interface User {
|
interface User {
|
||||||
username: string;
|
username?: string;
|
||||||
|
email?: string;
|
||||||
|
registered?: Date;
|
||||||
|
api_key?: string;
|
||||||
|
error?: string;
|
||||||
// TODO: add other fields as needed
|
// TODO: add other fields as needed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Redirect, Link } from 'react-router-dom';
|
import { Redirect, Link } from 'react-router-dom';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import ErrorBox from '../components/error-box';
|
import ErrorBox from '../components/error-box';
|
||||||
import InfoBox from '../components/info-box';
|
import InfoBox from '../components/info-box';
|
||||||
import SupporterLogos from '../components/supporter-logos';
|
import SupporterLogos from '../components/supporter-logos';
|
||||||
|
import { User } from '../models/user';
|
||||||
|
|
||||||
class Login extends Component<any, any> { // TODO: add proper types
|
interface LoginProps {
|
||||||
static propTypes = { // TODO: generate propTypes from TS
|
user: User,
|
||||||
login: PropTypes.func,
|
login: (user: User) => void;
|
||||||
user: PropTypes.object
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
class Login extends Component<LoginProps, any> {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
import React, { Component, FormEvent } from 'react';
|
import React, { Component, FormEvent } from 'react';
|
||||||
import { Link, Redirect } from 'react-router-dom';
|
import { Link, Redirect } from 'react-router-dom';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import ConfirmationModal from '../components/confirmation-modal';
|
import ConfirmationModal from '../components/confirmation-modal';
|
||||||
import ErrorBox from '../components/error-box';
|
import ErrorBox from '../components/error-box';
|
||||||
|
import { User } from '../models/user';
|
||||||
|
|
||||||
class MyAccountPage extends Component<any, any> { // TODO: add proper types
|
interface MyAccountPageProps {
|
||||||
static propTypes = { // TODO: generate propTypes from TS
|
user: User;
|
||||||
user: PropTypes.shape({
|
updateUser: (user: User) => void;
|
||||||
username: PropTypes.string,
|
logout: () => void;
|
||||||
email: PropTypes.string,
|
}
|
||||||
registered: PropTypes.instanceOf(Date), // TODO: check if fix correct
|
|
||||||
api_key: PropTypes.string,
|
|
||||||
error: PropTypes.object
|
|
||||||
}),
|
|
||||||
updateUser: PropTypes.func,
|
|
||||||
logout: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
|
interface MyAccountPageState {
|
||||||
|
showDeleteConfirm: boolean;
|
||||||
|
error: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MyAccountPage extends Component<MyAccountPageProps, MyAccountPageState> {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { ChangeEvent, FormEvent } from 'react';
|
import React, { FormEvent } from 'react';
|
||||||
import { RouteComponentProps, Redirect } from 'react-router';
|
import { RouteComponentProps, Redirect } from 'react-router';
|
||||||
|
|
||||||
import ErrorBox from '../components/error-box';
|
import ErrorBox from '../components/error-box';
|
||||||
|
@ -1,17 +1,27 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Redirect, Link } from 'react-router-dom';
|
import { Redirect, Link } from 'react-router-dom';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import ErrorBox from '../components/error-box';
|
import ErrorBox from '../components/error-box';
|
||||||
import InfoBox from '../components/info-box';
|
import InfoBox from '../components/info-box';
|
||||||
import SupporterLogos from '../components/supporter-logos';
|
import SupporterLogos from '../components/supporter-logos';
|
||||||
|
import { User } from '../models/user';
|
||||||
|
|
||||||
class SignUp extends Component<any, any> { // TODO: add proper types
|
interface SignUpProps {
|
||||||
static propTypes = { // TODO: generate propTypes from TS
|
login: (user: User) => void;
|
||||||
login: PropTypes.func.isRequired,
|
user: User;
|
||||||
user: PropTypes.object
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
interface SignUpState {
|
||||||
|
username: string;
|
||||||
|
email: string;
|
||||||
|
confirm_email: string;
|
||||||
|
show_password: boolean;
|
||||||
|
password: string;
|
||||||
|
confirm_conditions: boolean;
|
||||||
|
error: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SignUp extends Component<SignUpProps, SignUpState> {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
@ -19,7 +29,7 @@ class SignUp extends Component<any, any> { // TODO: add proper types
|
|||||||
email: '',
|
email: '',
|
||||||
confirm_email: '',
|
confirm_email: '',
|
||||||
password: '',
|
password: '',
|
||||||
show_password: '',
|
show_password: false,
|
||||||
confirm_conditions: false,
|
confirm_conditions: false,
|
||||||
error: undefined
|
error: undefined
|
||||||
};
|
};
|
||||||
@ -31,11 +41,11 @@ class SignUp extends Component<any, any> { // TODO: add proper types
|
|||||||
handleChange(event) {
|
handleChange(event) {
|
||||||
const target = event.target;
|
const target = event.target;
|
||||||
const value = target.type === 'checkbox' ? target.checked : target.value;
|
const value = target.type === 'checkbox' ? target.checked : target.value;
|
||||||
const name = target.name;
|
const name: keyof SignUpState = target.name;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
[name]: value
|
[name]: value
|
||||||
});
|
} as Pick<SignUpState, keyof SignUpState>);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit(event) {
|
handleSubmit(event) {
|
||||||
|
Loading…
Reference in New Issue
Block a user