move wrapper / shared components out of App component

This commit is contained in:
Jake McDermott
2019-01-02 01:30:08 -05:00
parent f975f9fa75
commit 6efd523db2
3 changed files with 267 additions and 272 deletions

View File

@@ -2,10 +2,8 @@ import React, { Fragment } from 'react';
import { ConfigContext } from './context'; import { ConfigContext } from './context';
import { import {
HashRouter as Router,
Redirect, Redirect,
Switch, Switch,
withRouter,
Route, Route,
} from 'react-router-dom'; } from 'react-router-dom';
import { import {
@@ -19,30 +17,16 @@ import {
ToolbarItem ToolbarItem
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import { global_breakpoint_md as breakpointMd } from '@patternfly/react-tokens'; import { global_breakpoint_md as breakpointMd } from '@patternfly/react-tokens';
import { I18nProvider, I18n } from '@lingui/react';
import { t } from '@lingui/macro';
import api from './api'; import api from './api';
import { API_LOGOUT, API_CONFIG } from './endpoints'; import { API_LOGOUT, API_CONFIG } from './endpoints';
import ja from '../build/locales/ja/messages';
import en from '../build/locales/en/messages';
import Login from './pages/Login'; import Login from './pages/Login';
import Background from './components/Background';
import HelpDropdown from './components/HelpDropdown'; import HelpDropdown from './components/HelpDropdown';
import LogoutButton from './components/LogoutButton'; import LogoutButton from './components/LogoutButton';
import TowerLogo from './components/TowerLogo'; import TowerLogo from './components/TowerLogo';
import NavExpandableGroup from './components/NavExpandableGroup'; import NavExpandableGroup from './components/NavExpandableGroup';
const catalogs = { en, ja };
// Derive the language and the region from global user agent data. Example: es-US
// https://developer.mozilla.org/en-US/docs/Web/API/Navigator
const language = (navigator.languages && navigator.languages[0])
|| navigator.language
|| navigator.userLanguage;
const languageWithoutRegionCode = language.toLowerCase().split(/[_-]+/)[0];
class App extends React.Component { class App extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
@@ -83,94 +67,80 @@ class App extends React.Component {
render () { render () {
const { config, isNavOpen } = this.state; const { config, isNavOpen } = this.state;
// extract a flattened array of all routes from the provided route config const { logo, loginInfo, navLabel, routeGroups = [] } = this.props;
const { logo, loginInfo, routeGroups = [] } = this.props;
return ( return (
<Router> api.isAuthenticated () ? (
<I18nProvider <Switch>
language={languageWithoutRegionCode} <Route path="/login" render={() => <Redirect to='/home' />} />
catalogs={catalogs} <Route exact path="/" render={() => <Redirect to='/home' />} />
> <Route render={() => (
<I18n> <ConfigContext.Provider value={config}>
{({ i18n }) => ( <Page
api.isAuthenticated () ? ( usecondensed="True"
<ConfigContext.Provider value={config}> header={(
<Switch> <PageHeader
<Route path="/login" render={() => <Redirect to='/home' />} /> showNavToggle
<Route exact path="/" render={() => <Redirect to='/home' />} /> onNavToggle={() => this.onNavToggle()}
<Route render={() => ( logo={(
<Background> <TowerLogo
<Page onClick={this.onLogoClick}
usecondensed="True" />
header={( )}
<PageHeader toolbar={(
showNavToggle <Toolbar>
onNavToggle={() => this.onNavToggle()} <ToolbarGroup>
logo={( <ToolbarItem>
<TowerLogo <HelpDropdown />
onClick={this.onLogoClick} </ToolbarItem>
/> <ToolbarItem>
)} <LogoutButton
toolbar={( onDevLogout={() => this.onDevLogout()}
<Toolbar>
<ToolbarGroup>
<ToolbarItem>
<HelpDropdown />
</ToolbarItem>
<ToolbarItem>
<LogoutButton
onDevLogout={() => this.onDevLogout()}
/>
</ToolbarItem>
</ToolbarGroup>
</Toolbar>
)}
/> />
)} </ToolbarItem>
sidebar={( </ToolbarGroup>
<PageSidebar </Toolbar>
isNavOpen={isNavOpen} )}
nav={( />
<Nav aria-label={i18n._("Primary Navigation")}> )}
<NavList> sidebar={(
{ <PageSidebar
routeGroups.map(params => <NavExpandableGroup key={params.groupId} {...params} />) isNavOpen={isNavOpen}
} nav={(
</NavList> <Nav aria-label={navLabel}>
</Nav> <NavList>
)}
/>
)}
>
{ {
// routeGroups.map(params => <NavExpandableGroup key={params.groupId} {...params} />)
// Extract a flattened array of all route params from the provided route groups
// and use it to create the route components.
//
// [{ routes }, { routes }] -> [route, route, route] -> (<Route/><Route/><Route/>)
//
routeGroups
.reduce((allRoutes, { routes }) => allRoutes.concat(routes), [])
.map(({ component: Component, path }) => (
<Route key={path} path={path} render={params => <Component {...params } />} />
))
} }
</Page> </NavList>
</Background> </Nav>
)} /> )}
</Switch> />
</ConfigContext.Provider> )}
) : ( >
<Switch> {
<Route path="/login" render={() => <Login logo={logo} loginInfo={loginInfo} />} /> //
<Redirect to="/login" /> // Extract a flattened array of all route params from the provided route config
</Switch> // and use it to render route components.
) //
)} // [{ routes }, { routes }] -> [route, route, route] -> (<Route/><Route/><Route/>)
</I18n> //
</I18nProvider> routeGroups
</Router> .reduce((allRoutes, { routes }) => allRoutes.concat(routes), [])
.map(({ component: Component, path }) => (
<Route key={path} path={path} render={params => <Component {...params } />} />
))
}
</Page>
</ConfigContext.Provider>
)} />
</Switch>
) : (
<Switch>
<Route path="/login" render={() => <Login logo={logo} loginInfo={loginInfo} />} />
<Redirect to="/login" />
</Switch>
)
); );
} }
} }

View File

@@ -1,16 +1,24 @@
import React from 'react'; import React from 'react';
import { render } from 'react-dom'; import { render } from 'react-dom';
import {
import App from './App'; HashRouter as Router,
import api from './api'; Switch,
} from 'react-router-dom';
import {
I18n,
I18nProvider,
} from '@lingui/react';
import { t } from '@lingui/macro';
import '@patternfly/react-core/dist/styles/base.css'; import '@patternfly/react-core/dist/styles/base.css';
import '@patternfly/patternfly-next/patternfly.css'; import '@patternfly/patternfly-next/patternfly.css';
import './app.scss'; import './app.scss';
import './components/Pagination/styles.scss'; import './components/Pagination/styles.scss';
import './components/DataListToolbar/styles.scss'; import './components/DataListToolbar/styles.scss';
import api from './api';
import App from './App';
import Background from './components/Background';
import Applications from './pages/Applications'; import Applications from './pages/Applications';
import Credentials from './pages/Credentials'; import Credentials from './pages/Credentials';
import CredentialTypes from './pages/CredentialTypes'; import CredentialTypes from './pages/CredentialTypes';
@@ -34,150 +42,16 @@ import License from './pages/License';
import Teams from './pages/Teams'; import Teams from './pages/Teams';
import Templates from './pages/Templates'; import Templates from './pages/Templates';
import Users from './pages/Users'; import Users from './pages/Users';
import ja from '../build/locales/ja/messages';
import en from '../build/locales/en/messages';
const routeGroups = [ const catalogs = { en, ja };
{ // Derive the language and region from global user agent data. Example: es-US
groupId: 'views_group', // https://developer.mozilla.org/en-US/docs/Web/API/Navigator
title: 'Views', const language = (navigator.languages && navigator.languages[0])
routes: [ || navigator.language
{ || navigator.userLanguage;
path: '/home', const languageWithoutRegionCode = language.toLowerCase().split(/[_-]+/)[0];
title: 'Dashboard',
component: Dashboard
},
{
path: '/jobs',
title: 'Jobs',
component: Jobs
},
{
path: '/schedules',
title: 'Schedules',
component: Schedules
},
{
path: '/portal',
title: 'Portal Mode',
component: Portal
},
],
},
{
groupId: 'resources_group',
title: "Resources",
routes: [
{
path: '/templates',
title: 'Templates',
component: Templates
},
{
path: '/credentials',
title: 'Credentials',
component: Credentials
},
{
path: '/projects',
title: 'Projects',
component: Projects
},
{
path: '/inventories',
title: 'Inventories',
component: Inventories
},
{
path: '/inventory_scripts',
title: 'Inventory Scripts',
component: InventoryScripts
},
],
},
{
groupId: 'access_group',
title: 'Access',
routes: [
{
path: '/organizations',
title: 'Organizations',
component: Organizations
},
{
path: '/users',
title: 'Users',
component: Users
},
{
path: '/teams',
title: 'Teams',
component: Teams
},
],
},
{
groupId: 'administration_group',
title: 'Administration',
routes: [
{
path: '/credential_types',
title: 'Credential Types',
component: CredentialTypes
},
{
path: '/notification_templates',
title: 'Notifications',
component: NotificationTemplates
},
{
path: '/management_jobs',
title: 'Management Jobs',
component: ManagementJobs
},
{
path: '/instance_groups',
title: 'Instance Groups',
component: InstanceGroups
},
{
path: '/applications',
title: 'Integrations',
component: Applications
},
],
},
{
groupId: 'settings_group',
title: 'Settings',
routes: [
{
path: '/auth_settings',
title: 'Authentication',
component: AuthSettings
},
{
path: '/jobs_settings',
title: 'Jobs',
component: JobsSettings
},
{
path: '/system_settings',
title: 'System',
component: SystemSettings
},
{
path: '/ui_settings',
title: 'User Interface',
component: UISettings
},
{
path: '/license',
title: 'License',
component: License
},
],
},
];
export async function main () { export async function main () {
const el = document.getElementById('app'); const el = document.getElementById('app');
@@ -186,11 +60,166 @@ export async function main () {
const { custom_logo, custom_login_info } = data; const { custom_logo, custom_login_info } = data;
render( render(
<App <Router>
logo={custom_logo} <I18nProvider
loginInfo={custom_login_info} language={languageWithoutRegionCode}
routeGroups={routeGroups} catalogs={catalogs}
/>, el); >
<I18n>
{({ i18n }) => (
<Background>
<App
logo={custom_logo}
loginInfo={custom_login_info}
navLabel={i18n._(t`Primary Navigation`)}
routeGroups={[
{
title: i18n._(t`Views`),
groupId: 'views_group',
routes: [
{
title: i18n._(t`Dashboard`),
path: '/home',
component: Dashboard
},
{
title: i18n._(t`Jobs`),
path: '/jobs',
component: Jobs
},
{
title: i18n._(t`Schedules`),
path: '/schedules',
component: Schedules
},
{
title: i18n._(t`Portal Mode`),
path: '/portal',
component: Portal
},
],
},
{
title: i18n._(t`Resources`),
groupId: 'resources_group',
routes: [
{
title: i18n._(t`Templates`),
path: '/templates',
component: Templates
},
{
title: i18n._(t`Credentials`),
path: '/credentials',
component: Credentials
},
{
title: i18n._(t`Projects`),
path: '/projects',
component: Projects
},
{
title: i18n._(t`Inventories`),
path: '/inventories',
component: Inventories
},
{
title: i18n._(t`Inventory Scripts`),
path: '/inventory_scripts',
component: InventoryScripts
},
],
},
{
title: i18n._(t`Access`),
groupId: 'access_group',
routes: [
{
title: i18n._(t`Organizations`),
path: '/organizations',
component: Organizations
},
{
title: i18n._(t`Users`),
path: '/users',
component: Users
},
{
title: i18n._(t`Teams`),
path: '/teams',
component: Teams
},
],
},
{
title: i18n._(t`Administration`),
groupId: 'administration_group',
routes: [
{
title: i18n._(t`Credential Types`),
path: '/credential_types',
component: CredentialTypes
},
{
title: i18n._(t`Notifications`),
path: '/notification_templates',
component: NotificationTemplates
},
{
title: i18n._(t`Management Jobs`),
path: '/management_jobs',
component: ManagementJobs
},
{
title: i18n._(t`Instance Groups`),
path: '/instance_groups',
component: InstanceGroups
},
{
title: i18n._(t`Integrations`),
path: '/applications',
component: Applications
},
],
},
{
title: i18n._(t`Settings`),
groupId: 'settings_group',
routes: [
{
title: i18n._(t`Authentication`),
path: '/auth_settings',
component: AuthSettings
},
{
title: i18n._(t`Jobs`),
path: '/jobs_settings',
component: JobsSettings
},
{
title: i18n._(t`System`),
path: '/system_settings',
component: SystemSettings
},
{
title: i18n._(t`User Interface`),
path: '/ui_settings',
component: UISettings
},
{
title: i18n._(t`License`),
path: '/license',
component: License
},
],
},
]}
/>
</Background>
)}
</I18n>
</I18nProvider>
</Router>, el);
}; };
export default main(); export default main();

View File

@@ -10,8 +10,6 @@ import {
import towerLogo from '../../images/tower-logo-header.svg'; import towerLogo from '../../images/tower-logo-header.svg';
import api from '../api'; import api from '../api';
import Background from '../components/Background';
class AtLogin extends Component { class AtLogin extends Component {
constructor (props) { constructor (props) {
super(props); super(props);
@@ -66,25 +64,23 @@ class AtLogin extends Component {
return ( return (
<I18n> <I18n>
{({ i18n }) => ( {({ i18n }) => (
<Background> <LoginPage
<LoginPage brandImgSrc={logoSrc}
brandImgSrc={logoSrc} brandImgAlt={alt || 'Ansible Tower'}
brandImgAlt={alt || 'Ansible Tower'} loginTitle={i18n._(t`Welcome to Ansible Tower! Please Sign In.`)}
loginTitle={i18n._(t`Welcome to Ansible Tower! Please Sign In.`)} >
> <LoginForm
<LoginForm usernameLabel={i18n._(t`Username`)}
usernameLabel={i18n._(t`Username`)} usernameValue={username}
usernameValue={username} onChangeUsername={this.handleUsernameChange}
onChangeUsername={this.handleUsernameChange} passwordLabel={i18n._(t`Password`)}
passwordLabel={i18n._(t`Password`)} passwordValue={password}
passwordValue={password} onChangePassword={this.handlePasswordChange}
onChangePassword={this.handlePasswordChange} isValidPassword={isValidPassword}
isValidPassword={isValidPassword} passwordHelperTextInvalid={i18n._(t`Invalid username or password. Please try again.`)}
passwordHelperTextInvalid={i18n._(t`Invalid username or password. Please try again.`)} onLoginButtonClick={this.handleSubmit}
onLoginButtonClick={this.handleSubmit} />
/> </LoginPage>
</LoginPage>
</Background>
)} )}
</I18n> </I18n>
); );