Chore/convert to typescript (#60)

* Convert to using TypeScript

Next.js v.9 allows converting an existing project to TS by simply renaming the files as TS files

* Fix type errors: Type 'string' is not assignable to type 'number'.

* Add mention about missing typings for javascript-time-ago

* Add GA_INITIALIZED to window

* Fix type error in feed transformers

* Model MembershipInfoForm state and props with TS

* Silence the typescript warning in MembershipInfoForm

* Add threshold to Fader props

* Fix Warning: Each child in a list should have a unique "key" prop.

Sponsors don't have ids – name is probably unique and can be used as a key

* Allow key as prop for SponsorLink

* Add type of the props for SponsorLink
This commit is contained in:
Olavi Haapala
2019-11-07 07:28:02 +02:00
committed by Riku Rouvila
parent bcc6619aee
commit f4474523ad
20 changed files with 125 additions and 26 deletions

View File

@@ -1,10 +1,14 @@
import React from "react";
type Props = {
threshold: number;
};
function clamp(min, max, value) {
return Math.min(Math.max(value, min), max);
}
export default class Fader extends React.Component {
export default class Fader extends React.Component<Props> {
static defaultProps = {
threshold: 100,
};

View File

@@ -8,7 +8,8 @@ import ReactTimeAgo from "react-time-ago";
import JavascriptTimeAgo from "javascript-time-ago";
import timeagoFi from "javascript-time-ago/locale/fi";
JavascriptTimeAgo.locale(timeagoFi);
// TODO: Add type definitions for javascript-time-ago
(JavascriptTimeAgo as any).locale(timeagoFi);
export default class Feed extends React.Component {
state = {
@@ -41,7 +42,7 @@ export default class Feed extends React.Component {
target="_blank"
href={message.imageLink}
rel="noopener noreferrer"
tabIndex="-1"
tabIndex={-1}
>
{image}
</a>

View File

@@ -1,7 +1,14 @@
import React from "react";
import EmailComponent from "./EmailComponent";
import sponsors from "../data/sponsors";
const SponsorLink = ({ href, name }) => (
type Props = {
href: string;
name: string;
key: string;
};
const SponsorLink = ({ href, name }: Props) => (
<a href={href} target="_blank" rel="noopener noreferrer">
<img
src={`/static/images/sponsors/${name.toLowerCase()}.svg`}

View File

@@ -33,7 +33,7 @@ export default class Members extends React.Component {
href="https://github.com/koodiklinikka"
target="_blank"
rel="noopener noreferrer"
tabIndex="-1"
tabIndex={-1}
>
{members}
</a>

View File

@@ -16,7 +16,8 @@ export default {
return items.filter(isVisibleGithubEvent).map(item => {
const template = lodashTemplate(
githubEvent.parse(item).text,
templateSettings
templateSettings,
false
);
const repository = `https://github.com/${item.repo.name}`;

View File

@@ -1,10 +1,28 @@
import pick from "lodash/pick";
import request from "axios";
import React from "react";
import classSet from "classnames";
import api from "../api";
import Loader from "../Loader";
type Props = {
onSignupSuccess: () => void;
};
type State = {
error: boolean;
errors: string[];
fields: {
name: string;
email: string;
handle: string;
address: string;
postcode: string;
city: string;
};
sending: boolean;
pristineFields: string[];
};
const fieldNameTranslations = {
address: { fi: "Osoite" },
city: { fi: "Paikkakunta" },
@@ -20,23 +38,26 @@ function validateEmail(email) {
return mailValidateRe.test(email);
}
const fieldNames = ["name", "email", "handle", "address", "postcode", "city"];
function getUserInfo(state) {
return pick(state, fieldNames);
return state.fields;
}
export default class MembershipInfoForm extends React.Component {
state = {
address: "",
city: "",
email: "",
handle: "",
name: "",
postcode: "",
sending: false,
pristineFields: fieldNames,
};
export default class MembershipInfoForm extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.setState({
fields: {
address: "",
city: "",
email: "",
handle: "",
name: "",
postcode: "",
},
sending: false,
pristineFields: Object.keys(this.state.fields),
});
}
onSubmit = async () => {
this.setState({
@@ -62,7 +83,10 @@ export default class MembershipInfoForm extends React.Component {
}
this.setState({
[e.target.name]: e.target.value,
fields: {
...this.state.fields,
[name]: e.target.value,
},
pristineFields: this.state.pristineFields.filter(
fieldName => fieldName !== name
),
@@ -73,13 +97,13 @@ export default class MembershipInfoForm extends React.Component {
getDataErrors = () => {
const foundErrors = [];
fieldNames.forEach(fieldName => {
Object.keys(this.state.fields).forEach(fieldName => {
if (!this.state[fieldName]) {
foundErrors.push({ field: fieldName, type: "missing" });
}
});
if (this.state.email && !validateEmail(this.state.email)) {
if (this.state.fields.email && !validateEmail(this.state.fields.email)) {
foundErrors.push({ field: "email", type: "invalid" });
}
@@ -119,7 +143,7 @@ export default class MembershipInfoForm extends React.Component {
const fieldsWithErrors = visibleErrors.map(({ field }) => field);
const inputFields = fieldNames.map(fieldName => {
const inputFields = Object.keys(this.state.fields).map(fieldName => {
const inputClasses = classSet({
input: true,
"has-error": fieldsWithErrors.includes(fieldName),

2
next-env.d.ts vendored Normal file
View File

@@ -0,0 +1,2 @@
/// <reference types="next" />
/// <reference types="next/types/global" />

34
package-lock.json generated
View File

@@ -1089,6 +1089,28 @@
"resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz",
"integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw=="
},
"@types/node": {
"version": "12.12.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.5.tgz",
"integrity": "sha512-KEjODidV4XYUlJBF3XdjSH5FWoMCtO0utnhtdLf1AgeuZLOrRbvmU/gaRCVg7ZaQDjVf3l84egiY0mRNe5xE4A==",
"dev": true
},
"@types/prop-types": {
"version": "15.7.3",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==",
"dev": true
},
"@types/react": {
"version": "16.9.11",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.11.tgz",
"integrity": "sha512-UBT4GZ3PokTXSWmdgC/GeCGEJXE5ofWyibCcecRLUVN2ZBpXQGVgQGtG2foS7CrTKFKlQVVswLvf7Js6XA/CVQ==",
"dev": true,
"requires": {
"@types/prop-types": "*",
"csstype": "^2.2.0"
}
},
"@webassemblyjs/ast": {
"version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
@@ -2942,6 +2964,12 @@
}
}
},
"csstype": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.7.tgz",
"integrity": "sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ==",
"dev": true
},
"cyclist": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
@@ -10318,6 +10346,12 @@
"is-typedarray": "^1.0.0"
}
},
"typescript": {
"version": "3.6.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz",
"integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==",
"dev": true
},
"ua-parser-js": {
"version": "0.7.20",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.20.tgz",

View File

@@ -33,12 +33,15 @@
"twitter-text": "^3.0.0"
},
"devDependencies": {
"@types/node": "12.12.5",
"@types/react": "16.9.11",
"babel-eslint": "^10.0.3",
"eslint": "^6.6.0",
"eslint-config-prettier": "^6.5.0",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-react": "^7.16.0",
"prettier": "^1.18.2"
"prettier": "^1.18.2",
"typescript": "3.6.4"
},
"prettier": {
"trailingComma": "es5"

20
tsconfig.json Normal file
View File

@@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"typeRoots": ["./node_modules/@types", "./typings"],
"exclude": ["node_modules"],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "./typings/*"]
}

3
typings/global.d.ts vendored Normal file
View File

@@ -0,0 +1,3 @@
interface Window {
GA_INITIALIZED: boolean;
}