Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/ui foundation #22

Merged
merged 6 commits into from
Feb 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/components/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './general';
export * from './list';
export * from './navigation';
1 change: 1 addition & 0 deletions packages/components/src/navigation/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './navigationBar.component';
75 changes: 75 additions & 0 deletions packages/components/src/navigation/navigationBar.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import * as React from 'react';
import { AppBar, Toolbar, makeStyles, IconButton, Theme, createStyles, Typography, Button } from '@material-ui/core';
import MenuIcon from '@material-ui/icons/Menu';
import { RouteDrawer } from './routeDrawer.component';
import { AppRoute } from 'elite-types';

const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
flexGrow: 1,
},
menuButton: {
marginRight: theme.spacing(2),
},
title: {
flexGrow: 1,
},
}),
);

export interface NavigationBarProps {
/** Title displayed by in navigation bar */
readonly title: string;

/** Callback used by navigation bar to navigate to a route */
readonly onNavigateTo: (route: AppRoute) => void;

/** Routes that the user may navigate to */
readonly routes: AppRoute[];
}

/**
* NavigationBar displays a material-ui AppBar in combination with a Drawer
* component which the User can use to navigate the available routes
*
* @param props See NavigationBarProps
*/
export const NavigationBar = (props: NavigationBarProps) => {
const classes = useStyles();
const [routeDrawerOpen, setRouteDrawerOpen] = React.useState(false);

const title = props.title;

return (
<>
<div className={classes.root}>
<AppBar position={'static'}>
<Toolbar>
<IconButton
edge={'start'}
className={classes.menuButton}
color={'inherit'}
aria-label={'open drawer'}
onClick={() => setRouteDrawerOpen(true)}
>
<MenuIcon />
</IconButton>
<Typography className={classes.title} variant={'h6'} noWrap>
{title}
</Typography>
<Button color={'inherit'}>Login</Button>
</Toolbar>
</AppBar>
</div>

<RouteDrawer
routes={props.routes}
onNavigateTo={props.onNavigateTo}
isOpen={routeDrawerOpen}
onOpen={() => setRouteDrawerOpen(true)}
onClose={() => setRouteDrawerOpen(false)}
/>
</>
);
};
58 changes: 58 additions & 0 deletions packages/components/src/navigation/routeDrawer.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import {
createStyles,
List,
ListItem,
ListItemIcon,
ListItemText,
makeStyles,
SwipeableDrawer,
Theme,
} from '@material-ui/core';
import { AppRoute, getDisplayNameForRoute, getLinkForRoute } from 'elite-types';
import * as React from 'react';

export interface RouteDrawerProps {
/** Routes displayed as options in the drawer list */
readonly routes: AppRoute[];

/** Callback used by route drawer to navigate to a route */
readonly onNavigateTo: (route: AppRoute) => void;

/** Whether or not the drawer is open */
readonly isOpen: boolean;

/** Callback used by the drawer to signify it should open */
readonly onOpen: () => void;

/** Callback used by the drawer to signify it should close */
readonly onClose: () => void;
}

const useStyles = makeStyles((theme: Theme) =>
createStyles({
list: {
width: 250,
},
fullList: {
width: 'auto',
},
}),
);

export const RouteDrawer = (props: RouteDrawerProps) => {
const classes = useStyles();
return (
<SwipeableDrawer anchor={'bottom'} open={props.isOpen} onClose={props.onClose} onOpen={props.onOpen}>
<div className={classes.fullList} role={'presentation'} onClick={props.onClose} onKeyDown={props.onClose}>
<List>
{props.routes.map(route => (
<ListItem button={true} key={getLinkForRoute(route)} onClick={() => props.onNavigateTo(route)}>
{route.icon && <ListItemIcon>{route.icon}</ListItemIcon>}
<ListItemText primary={getDisplayNameForRoute(route)} />
</ListItem>
))}
</List>
</div>
</SwipeableDrawer>
);
};
29 changes: 19 additions & 10 deletions packages/frontend/src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,40 @@
import { ErrorBoundaryComponent, FeatureFlagsProvider, NavigationBar } from 'elite-components';
import { getConfiguration } from 'elite-configuration';
import { AppPath, Configuration } from 'elite-types';
import { AppPath, Configuration, getDisplayNameForRoute, getLinkForRoute } from 'elite-types';
import * as React from 'react';
import { hot } from 'react-hot-loader';
import { Redirect, Route, Switch } from 'react-router';
import { Router } from 'react-router-dom';
import history from './util/history';
import { APP_ROUTES } from './util/routes';
import { ErrorBoundaryComponent, FeatureFlagsProvider } from 'elite-components';
import { Divider } from '@material-ui/core';
import { LinkDirectory } from './util/linkDirectory';

// Global bootstrap: install subsystems and load configuration
// Global bootstrap: load configuration
const configuration: Configuration = getConfiguration();

export const AppComponent = () => (
<FeatureFlagsProvider value={configuration.featureMap}>
<Router history={history}>
<ErrorBoundaryComponent>
<Switch>
{APP_ROUTES.map((routeProps, index) => (
<Route key={index} {...routeProps} />
{APP_ROUTES.map((route, index) => (
<Route
key={index}
{...route}
render={props => (
<>
<NavigationBar
routes={APP_ROUTES}
title={getDisplayNameForRoute(route)}
onNavigateTo={r => history.push(getLinkForRoute(r))}
/>
{route.render(props)}
</>
)}
/>
))}
{/* Error 404 Fallback */}
<Redirect to={AppPath.HOME} />
<Route path={AppPath.ERROR} render={() => <Redirect to={AppPath.HOME} />} />
</Switch>
<Divider />
<LinkDirectory />
</ErrorBoundaryComponent>
</Router>
</FeatureFlagsProvider>
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
<title>Elite-SE | Sexy</title>
</head>

<body>
<body style="margin: 0">
<!-- React root container dom element -->
<div id="root" />
<noscript>You need to enable javascript to be able to use this WebApp</noscript>
Expand Down
13 changes: 0 additions & 13 deletions packages/frontend/src/util/linkDirectory.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,20 +1,9 @@
import * as React from 'react';
import { HOME_ROUTE } from 'elite-home';
import { LINK_ROUTE } from 'elite-link';
import { AppPath, AppRoute, LinkType } from 'elite-types';
import { AppPath, AppRoute, LinkType, getLinkForRoute, getDisplayNameForRoute } from 'elite-types';

export const APP_ROUTES: AppRoute[] = [HOME_ROUTE, LINK_ROUTE];

/**
* Retrieves the url which other pages can use to link to a certain
* app path
*
* @param route
*/
export function getLinkForRoute(route: AppRoute): LinkType {
return route.link || route.path;
}

/**
* Retrieves the url which other pages can use to link to a certain
* app path
Expand All @@ -28,16 +17,6 @@ export function getLinkForPath(path: AppPath): LinkType {
return getLinkForRoute(route);
}

/**
* Retrieves the human readable link title/displayed name for
* a given route
*
* @param route
*/
export function getDisplayNameForRoute(route: AppRoute): string {
return route.displayName || getLinkForRoute(route);
}

/**
* Retrieves the human readable link title/displayed name for
* a given path
Expand Down
13 changes: 7 additions & 6 deletions packages/home/src/home.page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { FeatureFlag } from 'elite-components';
import { AppPath, AppRoute } from 'elite-types';
import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import { AppPath, AppRoute } from 'elite-types';
import HomeIcon from '@material-ui/icons/Home';

export interface HomePageProps extends RouteComponentProps {}

Expand All @@ -11,14 +12,14 @@ export interface HomePageProps extends RouteComponentProps {}
export const HOME_ROUTE: AppRoute = {
path: AppPath.HOME,
displayName: 'Home',
icon: <HomeIcon />,
render: props => <HomePage {...props} />,
};

export const HomePage = (props: HomePageProps) => (
<>
<h1>Main Page</h1>
export const HomePage = (props: HomePageProps) => {
return (
<FeatureFlag featureName="under-construction-message">
Elite Sexyz is currently under construction. See discord main channel for more information
</FeatureFlag>
</>
);
);
};
23 changes: 9 additions & 14 deletions packages/link/src/link.page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { List } from '@material-ui/core';
import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import { LinkListItem } from 'elite-components';
import { AppPath, AppRoute } from 'elite-types';
import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import LinkIcon from '@material-ui/icons/Link';

export interface LinkPageProps extends RouteComponentProps {}

Expand All @@ -12,19 +13,13 @@ export interface LinkPageProps extends RouteComponentProps {}
export const LINK_ROUTE: AppRoute = {
path: AppPath.LINK,
displayName: 'Useful Links',
render: props => (
<>
<LinkPage {...props} />
</>
),
icon: <LinkIcon />,
render: props => <LinkPage {...props} />,
};

export const LinkPage = (props: LinkPageProps) => (
<>
<h1>Useful Links List</h1>
<List>
<LinkListItem href={'https://elite-se.informatik.uni-augsburg.de'} title={'Main Webpage'} />
<LinkListItem href={'https://github.com/elite-se/elite-se.protokolle'} title={'Exam Protocols'} />
</List>
</>
<List>
<LinkListItem href={'https://elite-se.informatik.uni-augsburg.de'} title={'Main Webpage'} />
<LinkListItem href={'https://github.com/elite-se/elite-se.protokolle'} title={'Exam Protocols'} />
</List>
);
22 changes: 16 additions & 6 deletions packages/types/src/routes/appRoute.type.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
import { LinkType } from './link.type';
import { AppPath } from './appPath.type';
import { RouteProps } from 'react-router';
import { RouteProps, RouteComponentProps } from 'react-router';

/**
* Each Approute can have a specific link (i.e., path with filled parameter placeholders),
* a display Name, i.e., text of the link and a nonoptional (!) path
*/
export interface AppRoute extends RouteProps {
// Use this if the link target differs from the path specification,
// i.e., if the path url contains paramter specifications etc
/**
* Use this if the link target differs from the path specification,
* i.e., if the path url contains paramter specifications etc
*/
readonly link?: LinkType;

// link text (Human readable!)
/** link text (Human readable!) */
readonly displayName?: string;

// AppRoutes must have a path - deoptionalize this property
/** optional icon displayed next to the link name */
readonly icon?: JSX.Element;

/** AppRoutes must have a path - deoptionalize this property */
readonly path: AppPath;

render(props: any): any;
/** render is required for AppRoutes */
readonly render: (props: RouteComponentProps<any>) => React.ReactNode;

/** prevent usage of component/children props, i.e., AppRoutes must use render! */
readonly component?: never;
readonly children?: never;
}
22 changes: 22 additions & 0 deletions packages/types/src/routes/appRoute.util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { AppRoute } from './appRoute.type';
import { LinkType } from './link.type';

/**
* Retrieves the url which other pages can use to link to a certain
* app path
*
* @param route
*/
export function getLinkForRoute(route: AppRoute): LinkType {
return route.link || route.path;
}

/**
* Retrieves the human readable link title/displayed name for
* a given route
*
* @param route
*/
export function getDisplayNameForRoute(route: AppRoute): string {
return route.displayName || getLinkForRoute(route);
}
1 change: 1 addition & 0 deletions packages/types/src/routes/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './appPath.type';
export * from './appRoute.type';
export * from './appRoute.util';
export * from './link.type';