From c400e6fe2db4f7a6634551eae0fcec0e7bd1e63c Mon Sep 17 00:00:00 2001 From: thefringeninja Date: Wed, 24 Oct 2018 21:41:04 +1000 Subject: [PATCH 1/4] relicon sorted --- src/components/{RelIcon.js => RelIcon.ts} | 25 ++++++++++++++--------- 1 file changed, 15 insertions(+), 10 deletions(-) rename src/components/{RelIcon.js => RelIcon.ts} (74%) diff --git a/src/components/RelIcon.js b/src/components/RelIcon.ts similarity index 74% rename from src/components/RelIcon.js rename to src/components/RelIcon.ts index fcb6fc0..9f33431 100644 --- a/src/components/RelIcon.js +++ b/src/components/RelIcon.ts @@ -1,20 +1,21 @@ -import { createElement } from 'react'; +import { SvgIconProps } from '@material-ui/core/SvgIcon'; +import { createElement, ReactElement } from 'react'; +import { rels } from '../stream-store'; import { - Publish, - Settings, + ChevronLeft, + ChevronRight, DeleteForever, FirstPage, + Help, LastPage, - ChevronLeft, - ChevronRight, - RssFeed, + List, + Publish, Refresh, + RssFeed, Search, + Settings, SqlStreamStore, - List, - Help, } from './Icons'; -import { rels } from '../stream-store'; const fontIconByRel = { [rels.first]: FirstPage, @@ -32,7 +33,11 @@ const fontIconByRel = { [rels.curies]: Help, }; -const RelIcon = ({ rel, ...props }) => +interface RelIconProps extends SvgIconProps { + rel: string; +} + +const RelIcon = ({ rel, ...props }: RelIconProps): ReactElement => createElement(fontIconByRel[rel] || SqlStreamStore, props); export default RelIcon; From a5a16661cb1f5f2e3ce9e4921decf52a4d06155f Mon Sep 17 00:00:00 2001 From: thefringeninja Date: Wed, 24 Oct 2018 22:26:05 +1000 Subject: [PATCH 2/4] hocs sorted --- ...nProvider.js => AuthorizationProvider.tsx} | 11 +++++-- ...tionProvider.js => NavigationProvider.tsx} | 17 ++++++++-- src/components/getDisplayName.js | 4 --- src/components/getDisplayName.ts | 9 +++++ src/components/mount.js | 26 --------------- src/components/mount.ts | 33 +++++++++++++++++++ src/types/index.d.ts | 4 ++- 7 files changed, 67 insertions(+), 37 deletions(-) rename src/components/{AuthorizationProvider.js => AuthorizationProvider.tsx} (65%) rename src/components/{NavigationProvider.js => NavigationProvider.tsx} (53%) delete mode 100644 src/components/getDisplayName.js create mode 100644 src/components/getDisplayName.ts delete mode 100644 src/components/mount.js create mode 100644 src/components/mount.ts diff --git a/src/components/AuthorizationProvider.js b/src/components/AuthorizationProvider.tsx similarity index 65% rename from src/components/AuthorizationProvider.js rename to src/components/AuthorizationProvider.tsx index 73b1d58..e99f1e5 100644 --- a/src/components/AuthorizationProvider.js +++ b/src/components/AuthorizationProvider.tsx @@ -1,9 +1,14 @@ -import React from 'react'; +import React, { ReactNode, StatelessComponent } from 'react'; import getDisplayName from './getDisplayName'; -const { Consumer, Provider } = React.createContext(); +const { Consumer, Provider } = React.createContext( + undefined, +); -const AuthorizationProvider = ({ authorization, children }) => ( +const AuthorizationProvider: StatelessComponent<{ + authorization: string | undefined; + children: ReactNode; +}> = ({ authorization, children }) => ( {children} ); diff --git a/src/components/NavigationProvider.js b/src/components/NavigationProvider.tsx similarity index 53% rename from src/components/NavigationProvider.js rename to src/components/NavigationProvider.tsx index 639ddaa..f2d8935 100644 --- a/src/components/NavigationProvider.js +++ b/src/components/NavigationProvider.tsx @@ -1,8 +1,19 @@ -import React from 'react'; +import React, { ReactNode, StatelessComponent } from 'react'; +import { NavigationHandler } from '../types'; import getDisplayName from './getDisplayName'; -const { Consumer, Provider } = React.createContext(); -const NavigationProvider = ({ onNavigate, children }) => ( +const defaultNavigationHandler: NavigationHandler = (link, authorization) => { + return; +}; + +const { Consumer, Provider } = React.createContext( + defaultNavigationHandler, +); + +const NavigationProvider: StatelessComponent<{ + onNavigate: NavigationHandler; + children: ReactNode; +}> = ({ onNavigate, children }) => ( {children} ); diff --git a/src/components/getDisplayName.js b/src/components/getDisplayName.js deleted file mode 100644 index 3870255..0000000 --- a/src/components/getDisplayName.js +++ /dev/null @@ -1,4 +0,0 @@ -export default (hocName, WrappedComponent) => - `${hocName}(${WrappedComponent.displayName || - WrappedComponent.name || - 'Component'})`; diff --git a/src/components/getDisplayName.ts b/src/components/getDisplayName.ts new file mode 100644 index 0000000..a3f8b1a --- /dev/null +++ b/src/components/getDisplayName.ts @@ -0,0 +1,9 @@ +import { ComponentType } from 'react'; + +export default ( + hocName: string, + WrappedComponent: ComponentType, +): string => + `${hocName}(${WrappedComponent.displayName || + WrappedComponent.name || + 'Component'})`; diff --git a/src/components/mount.js b/src/components/mount.js deleted file mode 100644 index be4385d..0000000 --- a/src/components/mount.js +++ /dev/null @@ -1,26 +0,0 @@ -import React, { PureComponent } from 'react'; -import getDisplayName from './getDisplayName'; - -const mount = (didMount, willUnmount = () => {}) => WrappedComponent => { - class Mount extends PureComponent { - componentDidMount() { - didMount(this.props); - } - - componentWillUnmount() { - willUnmount(this.props); - } - - render() { - return React.createElement(WrappedComponent, { ...this.props }); - } - } - - Mount.displayName = getDisplayName('Mount', WrappedComponent); - - return Mount; -}; - -mount.displayName = 'Mount'; - -export default mount; diff --git a/src/components/mount.ts b/src/components/mount.ts new file mode 100644 index 0000000..3d0349c --- /dev/null +++ b/src/components/mount.ts @@ -0,0 +1,33 @@ +import React, { ComponentType, PureComponent } from 'react'; +import getDisplayName from './getDisplayName'; + +type DidMount = (props: T) => void; +type WillUnmount = (props: T) => void; + +const mount = ( + didMount: DidMount, + willUnmount?: WillUnmount, +) => (WrappedComponent: ComponentType) => { + class Mount extends PureComponent { + componentDidMount() { + didMount(this.props); + } + + componentWillUnmount() { + if (!willUnmount) { + return; + } + willUnmount(this.props); + } + + render() { + return React.createElement(WrappedComponent, this.props); + } + } + + return Mount; +}; + +mount.displayName = 'Mount'; + +export default mount; diff --git a/src/types/index.d.ts b/src/types/index.d.ts index 1271ebf..ec6186d 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -21,7 +21,9 @@ export interface EmbeddedResources { [rel: string]: HalResource | HalResource[]; } +export type NavigationHandler = (link: HalLink, authorization?: string) => void; + export interface NavigatableProps { - onNavigate: (link: HalLink, authorization?: string) => void; + onNavigate: NavigationHandler; authorization?: string; } From 8425394808721d3a5aafa1227bc6a991b4ba8d02 Mon Sep 17 00:00:00 2001 From: thefringeninja Date: Wed, 24 Oct 2018 23:56:51 +1000 Subject: [PATCH 3/4] balance of components sorted --- .../{SqlStreamStore.js => SqlStreamStore.tsx} | 5 +- src/components/Icons/{index.js => index.ts} | 0 src/components/{Loading.js => Loading.tsx} | 19 +--- .../{Notifications.js => Notifications.tsx} | 103 ++++++++++-------- src/components/StreamBrowser.js | 39 ------- src/components/StreamBrowser.tsx | 54 +++++++++ .../StripeyTable/{index.js => index.tsx} | 23 ++-- src/components/{index.js => index.ts} | 0 src/types/index.d.ts | 15 +++ 9 files changed, 154 insertions(+), 104 deletions(-) rename src/components/Icons/{SqlStreamStore.js => SqlStreamStore.tsx} (90%) rename src/components/Icons/{index.js => index.ts} (100%) rename src/components/{Loading.js => Loading.tsx} (50%) rename src/components/{Notifications.js => Notifications.tsx} (78%) delete mode 100644 src/components/StreamBrowser.js create mode 100644 src/components/StreamBrowser.tsx rename src/components/StripeyTable/{index.js => index.tsx} (63%) rename src/components/{index.js => index.ts} (100%) diff --git a/src/components/Icons/SqlStreamStore.js b/src/components/Icons/SqlStreamStore.tsx similarity index 90% rename from src/components/Icons/SqlStreamStore.js rename to src/components/Icons/SqlStreamStore.tsx index a4d197b..4dd44d7 100644 --- a/src/components/Icons/SqlStreamStore.js +++ b/src/components/Icons/SqlStreamStore.tsx @@ -1,7 +1,8 @@ -import React from 'react'; import { SvgIcon } from '@material-ui/core'; +import { SvgIconProps } from '@material-ui/core/SvgIcon'; +import React, { ComponentType } from 'react'; /* eslint-disable max-len */ -const SqlStreamStore = props => ( +const SqlStreamStore: ComponentType = props => ( ( +const Loading = ({ open }: { open: boolean }) => (
- + diff --git a/src/components/Notifications.js b/src/components/Notifications.tsx similarity index 78% rename from src/components/Notifications.js rename to src/components/Notifications.tsx index 73806ec..42542a3 100644 --- a/src/components/Notifications.js +++ b/src/components/Notifications.tsx @@ -1,32 +1,40 @@ -import React, { createElement } from 'react'; -import { Observable as obs } from 'rxjs'; import { IconButton, Snackbar, SnackbarContent, + WithStyles, withStyles, } from '@material-ui/core'; -import { Close, CheckCircle, Warning, Error, Info } from '../components/Icons'; -import { green, amber, blue, red } from '@material-ui/core/colors'; +import { amber, blue, green, red } from '@material-ui/core/colors'; import classNames from 'classnames'; +import React, {ComponentType, createElement, ReactNode, StatelessComponent} from 'react'; +import { Observable as obs } from 'rxjs'; import uuid from 'uuid'; -import { actions } from '../stream-store'; +import { CheckCircle, Close, Error, Info, Warning } from '../components/Icons'; import { connect, createAction, createState } from '../reactive'; +import { actions } from '../stream-store'; +import { HttpProblemDetailsResponse, HttpResponse } from '../types'; const iconsByVariant = { - success: CheckCircle, - warning: Warning, error: Error, info: Info, + success: CheckCircle, + warning: Warning, }; -const formatTitle = ({ status, statusText }) => `${status} ${statusText}`; +const formatTitle = ({ + status, + statusText, +}: { + status: number; + statusText: string; +}) => `${status} ${statusText}`; -const formatSubheader = ({ title, type }) => +const formatSubheader = ({ title, type }: { title: string; type: string }) => title ? `${title} (${type})` : null; -const formatContent = ({ detail }) => - detail +const formatContent = ({ detail }: { detail?: string }): ReactNode[] => + !!detail ? detail .split(/\r|\n/) .filter(x => x.length) @@ -38,7 +46,7 @@ const formatContent = ({ detail }) => ], [], ) - : null; + : []; const responses$ = obs.merge( ...Object.keys(actions).map(verb => actions[verb].response), @@ -46,20 +54,20 @@ const responses$ = obs.merge( const clientError$ = responses$ .filter(({ status }) => status >= 400 && status < 500) - .map(({ body, ...response }) => ({ - variant: 'warning', - title: formatTitle(response), - subheader: formatSubheader(body), + .map(({ body, ...response }: HttpProblemDetailsResponse) => ({ content: formatContent(body), + subheader: formatSubheader(body), + title: formatTitle(response), + variant: 'warning', })); const serverError$ = responses$ .filter(({ status }) => status >= 500) - .map(({ body, ...response }) => ({ - variant: 'error', - title: formatTitle(response), - subheader: formatSubheader(body), + .map(({ body, ...response }: HttpProblemDetailsResponse) => ({ content: formatContent(body), + subheader: formatSubheader(body), + title: formatTitle(response), + variant: 'error', })); const unsafe = Object.keys(actions) @@ -69,10 +77,10 @@ const unsafe = Object.keys(actions) const success$ = obs .merge(...unsafe) .filter(({ status }) => status < 400) - .map(response => ({ - variant: 'success', - title: formatTitle(response), + .map((response: HttpResponse) => ({ autoHideDuration: 2000, + title: formatTitle(response), + variant: 'success', })); const dismiss = createAction(); @@ -102,48 +110,58 @@ const state$ = createState( ); const styles = theme => ({ - success: { - backgroundColor: green[600], - }, error: { backgroundColor: red[500], }, - info: { - backgroundColor: blue[500], - }, - warning: { - backgroundColor: amber[700], - }, icon: { fontSize: 20, }, iconVariant: { - opacity: 0.9, marginRight: theme.spacing.unit, + opacity: 0.9, + }, + info: { + backgroundColor: blue[500], }, message: { - display: 'flex', alignItems: 'center', + display: 'flex', + }, + success: { + backgroundColor: green[600], + }, + warning: { + backgroundColor: amber[700], }, }); -const Notification = withStyles(styles)( +interface NotificationProps { + autoHideDuration: number | undefined; + className: string; + content: ReactNode; + messageId: string; + subheader: string; + title: string; + variant: string; +} + +const Notification: ComponentType = withStyles(styles)( ({ - classes, + autoHideDuration, className, + classes, + content, messageId, - title, subheader, - content, + title, variant, - autoHideDuration, ...other - }) => ( + }: NotificationProps & WithStyles) => ( @@ -166,7 +184,6 @@ const Notification = withStyles(styles)( dismiss.next(messageId)} > @@ -178,7 +195,7 @@ const Notification = withStyles(styles)( ), ); -const Notifications = ({ notifications }) => ( +const Notifications: StatelessComponent<{ notifications: NotificationProps[] }> = ({ notifications }) => (
{notifications.map(notification => ( diff --git a/src/components/StreamBrowser.js b/src/components/StreamBrowser.js deleted file mode 100644 index 2e97c55..0000000 --- a/src/components/StreamBrowser.js +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; -import { - List, - ListItem, - ListItemText, - LinearProgress, - Typography, - withStyles, -} from '@material-ui/core'; -import Hyperlink from './Hyperlink'; - -const StreamBrowser = withStyles(theme => ({ - browser: { - padding: theme.spacing.unit * 2.5, - }, -}))(({ streams, classes, loading }) => ( -
- Stream Browser - {loading ? ( - - ) : streams.length ? ( - - {streams.map(link => ( - - - {link.title} - - - ))} - - ) : ( - - No matching streams found. - - )} -
-)); - -export default StreamBrowser; diff --git a/src/components/StreamBrowser.tsx b/src/components/StreamBrowser.tsx new file mode 100644 index 0000000..12c6315 --- /dev/null +++ b/src/components/StreamBrowser.tsx @@ -0,0 +1,54 @@ +import { + LinearProgress, + List, + ListItem, + ListItemText, + Typography, + WithStyles, + withStyles, +} from '@material-ui/core'; +import React, { ComponentType } from 'react'; +import { HalLink } from '../types'; +import Hyperlink from './Hyperlink'; + +const styles = theme => ({ + browser: { + padding: theme.spacing.unit * 2.5, + }, +}); + +interface StreamBrowserProps { + loading: boolean; + streams: HalLink[]; +} + +const StreamBrowser: ComponentType = withStyles(styles)( + ({ + streams, + classes, + loading, + }: StreamBrowserProps & WithStyles) => ( +
+ Stream Browser + {loading ? ( + + ) : streams.length ? ( + + {streams.map(link => ( + + + {link.title} + + + ))} + + ) : ( + + No matching streams found. + + )} +
+ ), +); + +export default StreamBrowser; diff --git a/src/components/StripeyTable/index.js b/src/components/StripeyTable/index.tsx similarity index 63% rename from src/components/StripeyTable/index.js rename to src/components/StripeyTable/index.tsx index abdb826..bfe544e 100644 --- a/src/components/StripeyTable/index.js +++ b/src/components/StripeyTable/index.tsx @@ -1,13 +1,15 @@ -import React from 'react'; import { Table, TableBody, - TableRow as MaterialTableRow, TableCell as MaterialTableCell, - TableHead, TableFooter, + TableHead, + TableRow as MaterialTableRow, + WithStyles, withStyles, } from '@material-ui/core'; +import { TableRowProps } from '@material-ui/core/TableRow'; +import React from 'react'; const TableCell = withStyles(theme => ({ head: { @@ -16,14 +18,21 @@ const TableCell = withStyles(theme => ({ }, }))(MaterialTableCell); -const TableRow = withStyles(theme => ({ +const tableRowStyles = theme => ({ row: { '&:nth-of-type(odd)': { backgroundColor: theme.palette.background.paper, }, }, -}))(({ classes, ...props }) => ( - -)); +}); + +const TableRow = withStyles(tableRowStyles)( + ({ + classes, + ...props + }: TableRowProps & WithStyles) => ( + + ), +); export { Table, TableBody, TableHead, TableCell, TableRow, TableFooter }; diff --git a/src/components/index.js b/src/components/index.ts similarity index 100% rename from src/components/index.js rename to src/components/index.ts diff --git a/src/types/index.d.ts b/src/types/index.d.ts index ec6186d..308006c 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -27,3 +27,18 @@ export interface NavigatableProps { onNavigate: NavigationHandler; authorization?: string; } + +export interface HttpResponse { + body: { [key: string]: any } | string | undefined; + status: number; + statusText: string; + ok: boolean; +} + +export interface HttpProblemDetailsResponse extends HttpResponse { + body: { + detail?: any; + title: string; + type: string; + }; +} From 32f201cba9951f735a4ac053d40b3940e85fe362 Mon Sep 17 00:00:00 2001 From: thefringeninja Date: Thu, 25 Oct 2018 16:08:27 +1000 Subject: [PATCH 4/4] fixed typo --- src/stream-store/Viewer/HalViewer/Home.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream-store/Viewer/HalViewer/Home.js b/src/stream-store/Viewer/HalViewer/Home.js index f8e8f6a..0b1937d 100644 --- a/src/stream-store/Viewer/HalViewer/Home.js +++ b/src/stream-store/Viewer/HalViewer/Home.js @@ -32,7 +32,7 @@ const Links = ({ provider, versions }) => ( - {'Server Information'} + {'Server Information'}