diff --git a/package.json b/package.json index 0e40ec0..b5d69e1 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,9 @@ "scripts": { "analyze": "source-map-explorer build/static/js/main.*", "start": "react-scripts-ts start", - "build": "prettier --list-different \"src/**/*.js\" \"src/**/*.ts\" \"src/**/*.tsx\" && react-scripts-ts build", + "build": "prettier --list-different \"src/**/*.js\" \"src/**/*.ts\" \"src/**/*.tsx\" && tslint --project ./ && react-scripts-ts build", "eject": "react-scripts-ts eject", - "format": "prettier --write \"src/**/*.js\" \"src/**/*.ts\" \"src/**/*.tsx\"" + "format": "prettier --write \"src/**/*.js\" \"src/**/*.ts\" \"src/**/*.tsx\" && tslint --project ./ --fix" }, "main": "src/index.tsx", "private": false, diff --git a/src/components/HyperMediaControls/Dialog.js b/src/components/HyperMediaControls/Dialog.tsx similarity index 76% rename from src/components/HyperMediaControls/Dialog.js rename to src/components/HyperMediaControls/Dialog.tsx index 1b45357..adbf916 100644 --- a/src/components/HyperMediaControls/Dialog.js +++ b/src/components/HyperMediaControls/Dialog.tsx @@ -1,29 +1,48 @@ -import React, { PureComponent } from 'react'; import { Button, Dialog, - DialogTitle, DialogActions, DialogContent, + DialogTitle, Slide, withStyles, + WithStyles, } from '@material-ui/core'; +import React, { + FormEventHandler, + PureComponent, + StatelessComponent, +} from 'react'; +import { SlideProps } from '@material-ui/core/Slide'; +import { HalLink } from '../../types'; import RelIcon from '../RelIcon'; -import RelButton from './RelButton'; import HelpButton from './HelpButton'; +import RelButton from './RelButton'; const styles = theme => ({ button: { margin: theme.spacing.unit, }, }); -const SlideUp = props => ; +const SlideUp: StatelessComponent = props => ( + +); + +const isStandardRel = (rel: string) => rel.indexOf(':') === -1; -const isStandardRel = rel => rel.indexOf(':') === -1; +interface HyperMediaDialogProps { + rel: string; + title?: string; + curies: HalLink[]; + onSubmit: () => void; +} export default withStyles(styles)( - class HyperMediaDialog extends PureComponent { + class HyperMediaDialog extends PureComponent< + HyperMediaDialogProps & WithStyles, + { open: boolean } + > { state = { open: false, }; @@ -49,18 +68,16 @@ export default withStyles(styles)( }; render() { - const { label, rel, title, classes, children, curies } = this.props; + const { rel, title, classes, children, curies } = this.props; const { open } = this.state; return ( { @@ -22,8 +23,28 @@ const getValue = value => { return value; }; -class FormButton extends PureComponent { - state = {}; +interface FormButtonProps { + rel: string; + link: HalLink; + actions; + curies: HalLink[]; + schema; + title: string; +} + +interface FormButtonState { + model: { + [key: string]: any; + }; +} + +class FormButton extends PureComponent< + FormButtonProps & NavigatableProps, + FormButtonState +> { + state = { + model: {}, + }; _onSubmit = () => { const { rel, link, actions, authorization } = this.props; const { model: body } = this.state; @@ -31,10 +52,10 @@ class FormButton extends PureComponent { if (actions[rel]) { actions[rel].request.next({ body, - link, headers: { authorization, }, + link, }); } }; @@ -51,11 +72,10 @@ class FormButton extends PureComponent { }; render() { - const { schema, rel, title, curies } = this.props; + const { schema, rel, curies } = this.props; const { model } = this.state; return ( { +const getCurie = (rel: string, curies: HalLink[]): HalLink => { const [prefix, rest] = rel.split(':', 2); return !rest || rest.indexOf(':') !== -1 @@ -22,62 +28,90 @@ const getCurie = (rel, curies) => { }))[0] || { href: rel }; }; +interface DocumentationProps { + readonly open: boolean; + readonly onClose: () => void; +} + const Documentation = withStyles(theme => ({ drawerPaper: { - width: '45%', padding: theme.spacing.unit * 2, + width: '45%', }, -}))(({ open, children, onClose, classes }) => ( - - - - {children} - - - -)); +}))( + ({ + open, + children, + onClose, + classes, + }: DocumentationProps & WithStyles & { children: ReactNode }) => ( + + + + {children} + + + + ), +); -class HelpButton extends PureComponent { - state = { - open: false, - href: null, - type: null, - disabled: true, - documentation: null, - }; +interface HelpButtonProps { + rel: string; + curies: HalLink[]; + disabled?: boolean; + authorization?: string; +} + +interface HelpButtonState extends HalLink { + open: boolean; + documentation: string; + disabled: boolean; +} - static getDerivedStateFromProps = ({ rel, curies }, state) => ({ +class HelpButton extends PureComponent { + static getDerivedStateFromProps = ( + { rel, curies }: HelpButtonProps, + state: HelpButtonState, + ): HelpButtonState => ({ ...state, disabled: false, ...getCurie(rel, curies), }); + state = { + curies: [], + disabled: true, + documentation: '', + href: '', + open: false, + type: '', + }; _handleOnClick = async () => { const { authorization } = this.props; const { href, type } = this.state; const { body: documentation } = await http.get({ - link: { href, type }, headers: { authorization }, + link: { href, type }, }); this.setState({ ...this.state, - open: true, documentation: typeof documentation === 'string' ? documentation : JSON.stringify(documentation), + open: true, }); }; diff --git a/src/components/HyperMediaControls/LinkButton.js b/src/components/HyperMediaControls/LinkButton.tsx similarity index 56% rename from src/components/HyperMediaControls/LinkButton.js rename to src/components/HyperMediaControls/LinkButton.tsx index b57fe64..04e7b02 100644 --- a/src/components/HyperMediaControls/LinkButton.js +++ b/src/components/HyperMediaControls/LinkButton.tsx @@ -1,13 +1,30 @@ -import React, { PureComponent } from 'react'; -import { TextField } from '@material-ui/core'; +import { PropTypes, TextField } from '@material-ui/core'; +import React, { PureComponent, StatelessComponent } from 'react'; import uriTemplate from 'uri-template'; +import { HalLink, NavigatableProps } from '../../types'; +import { preventDefault } from '../../utils'; import { withNavigation } from '../NavigationProvider'; import Dialog from './Dialog'; import RelButton from './RelButton'; -import { preventDefault } from '../../utils'; +import Color = PropTypes.Color; + +interface LinkButtonProps { + readonly rel: string; + readonly link: HalLink; + color: Color; +} + +interface TemplatedLinkButtonProps extends LinkButtonProps { + readonly curies: HalLink[]; +} -const TemplatedLinkButton = withNavigation()( - class TemplatedLinkButton extends PureComponent { +const TemplatedLinkButton: StatelessComponent< + TemplatedLinkButtonProps +> = withNavigation()( + class extends PureComponent< + TemplatedLinkButtonProps & NavigatableProps, + { [variable: string]: string } + > { state = {}; _onChange = variable => ({ target }) => @@ -23,7 +40,6 @@ const TemplatedLinkButton = withNavigation()( return ( ( +interface NonTemplatedLinkButtonProps extends LinkButtonProps { + readonly title: string; +} + +const NonTemplatedLinkButton: StatelessComponent< + NonTemplatedLinkButtonProps +> = withNavigation()( + ({ + link, + rel, + authorization, + onNavigate, + }: NonTemplatedLinkButtonProps & NavigatableProps) => ( onNavigate(link, authorization))} /> ), ); -export default ({ link, ...props }) => +export default ({ + link, + ...props +}: TemplatedLinkButtonProps | NonTemplatedLinkButtonProps) => link.templated === true ? ( - + ) : ( - + ); diff --git a/src/components/HyperMediaControls/RelButton.js b/src/components/HyperMediaControls/RelButton.js deleted file mode 100644 index 1857ea4..0000000 --- a/src/components/HyperMediaControls/RelButton.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; -import { Button } from '@material-ui/core'; -import RelIcon from '../RelIcon'; - -const RelButton = ({ rel, onClick, title, color, ...props }) => ( - -); - -export default RelButton; diff --git a/src/components/HyperMediaControls/RelButton.tsx b/src/components/HyperMediaControls/RelButton.tsx new file mode 100644 index 0000000..2b75f51 --- /dev/null +++ b/src/components/HyperMediaControls/RelButton.tsx @@ -0,0 +1,24 @@ +import { Button } from '@material-ui/core'; +import { ButtonProps } from '@material-ui/core/Button'; +import { SvgIconProps } from '@material-ui/core/SvgIcon'; +import React, { ReactNode, StatelessComponent } from 'react'; +import RelIcon from '../RelIcon'; + +interface RelButtonProps { + rel: string; + title?: string; +} + +const RelButton: StatelessComponent = ({ + rel, + onClick, + title, + ...props +}) => ( + +); + +export default RelButton; diff --git a/src/components/HyperMediaControls/TextAreaField.js b/src/components/HyperMediaControls/TextAreaField.tsx similarity index 62% rename from src/components/HyperMediaControls/TextAreaField.js rename to src/components/HyperMediaControls/TextAreaField.tsx index 8a8a42a..5127c07 100644 --- a/src/components/HyperMediaControls/TextAreaField.js +++ b/src/components/HyperMediaControls/TextAreaField.tsx @@ -1,8 +1,14 @@ -import React from 'react'; import { TextField } from '@material-ui/core'; +import React, { ComponentType, StatelessComponent } from 'react'; import { ComposedComponent } from 'react-schema-form'; +import { FormInputProps } from './reactSchemaForms'; -const TextAreaField = ({ form, error, onChangeValidate, value }) => ( +const TextAreaField: StatelessComponent = ({ + form, + error, + onChangeValidate, + value, +}) => (
(
); -export default ComposedComponent.default(TextAreaField); +export default ComposedComponent.default(TextAreaField) as ComponentType< + FormInputProps +>; diff --git a/src/components/HyperMediaControls/UuidField.js b/src/components/HyperMediaControls/UuidField.tsx similarity index 92% rename from src/components/HyperMediaControls/UuidField.js rename to src/components/HyperMediaControls/UuidField.tsx index 3aceba5..c4f04ed 100644 --- a/src/components/HyperMediaControls/UuidField.js +++ b/src/components/HyperMediaControls/UuidField.tsx @@ -1,10 +1,11 @@ -import React from 'react'; import { Button, TextField } from '@material-ui/core'; -import { SpaceBar } from '../Icons'; -import uuid from 'uuid'; +import React from 'react'; import { ComposedComponent } from 'react-schema-form'; +import uuid from 'uuid'; +import { SpaceBar } from '../Icons'; +import { FormInputProps } from './reactSchemaForms'; -class UuidField extends React.PureComponent { +class UuidField extends React.PureComponent { constructor(props) { super(props); diff --git a/src/components/HyperMediaControls/index.js b/src/components/HyperMediaControls/index.tsx similarity index 71% rename from src/components/HyperMediaControls/index.js rename to src/components/HyperMediaControls/index.tsx index 7458746..8c0e706 100644 --- a/src/components/HyperMediaControls/index.js +++ b/src/components/HyperMediaControls/index.tsx @@ -1,17 +1,29 @@ -import React from 'react'; import { Card, CardActions } from '@material-ui/core'; +import React, { StatelessComponent } from 'react'; import { connect, createState } from '../../reactive'; -import { navigation, store } from '../../stream-store'; -import LinkButton from './LinkButton'; +import { navigation, rels, store } from '../../stream-store'; +import { HalLinks } from '../../types'; import FormButton from './FormButton'; -import { rels } from '../../stream-store'; +import LinkButton from './LinkButton'; -const isNotSelf = (rel, links) => +const isNotSelf = (rel: string, links: HalLinks): boolean => links[rels.self] && links[rel][0].href !== links[rels.self][0].href; const state$ = createState(store.url$.map(href => ['href', () => href])); -const HyperMediaControls = ({ forms, href, actions, links }) => ( +interface HyperMediaControlsProps { + actions: any; + href: string; + links: HalLinks; + forms: { [rel: string]: any }; +} + +const HyperMediaControls: StatelessComponent = ({ + forms, + href, + actions, + links, +}) => (
@@ -23,12 +35,10 @@ const HyperMediaControls = ({ forms, href, actions, links }) => ( key={rel} rel={rel} link={links[rel][0]} - color={'active'} + color={'primary'} curies={links[rels.curies]} /> ))} -
-
{Object.keys(forms).map(rel => ( void; + setDefault: ( + key: string, + model: { [key: string]: any }, + form: Form, + value: string, + ) => void; + value: string; +} diff --git a/src/components/Hyperlink.js b/src/components/Hyperlink.tsx similarity index 54% rename from src/components/Hyperlink.js rename to src/components/Hyperlink.tsx index 5785ef3..9ace7a6 100644 --- a/src/components/Hyperlink.js +++ b/src/components/Hyperlink.tsx @@ -1,28 +1,43 @@ -import React from 'react'; -import { withStyles } from '@material-ui/core'; +import { WithStyles, withStyles } from '@material-ui/core'; +import React, { ReactNode, StatelessComponent } from 'react'; +import theme from '../theme'; +import { HalLink, NavigatableProps } from '../types'; import { preventDefault } from '../utils'; import { withNavigation } from './NavigationProvider'; -import theme from '../theme'; const color = theme.palette.action.active; const styles = { hyperlink: { - color, - '&:hover': { + '&:active': { color, }, - '&:active': { + '&:hover': { color, }, '&:visited': { color, }, + color, }, }; -const Hyperlink = withNavigation()( + +interface HyperlinkProps { + link: HalLink; +} + +const Hyperlink: StatelessComponent< + HyperlinkProps & { children?: ReactNode } +> = withNavigation()( withStyles(styles)( - ({ classes, link, children, authorization, onNavigate }) => ( + ({ + classes, + link, + children, + authorization, + onNavigate, + }: HyperlinkProps & { children?: ReactNode } & NavigatableProps & + WithStyles) => ( { +const FeedNavigationLink: StatelessComponent< + FeedNavigationLinkProps +> = withNavigation()( + class What extends PureComponent< + FeedNavigationLinkProps & NavigatableProps + > { + _handleOnClick: FormEventHandler = e => { const { onNavigate, authorization, link } = this.props; e.preventDefault(); @@ -21,19 +35,20 @@ const FeedNavigationLink = withNavigation()( render() { const { rel, link } = this.props; return ( - + ); } }, ); +interface NavigationLinksProps { + links: HalLinks; +} -const NavigationLinks = ({ links }) => ( +const NavigationLinks: StatelessComponent = ({ + links, +}) => (