mirror of
https://github.com/ascribe/onion.git
synced 2025-01-03 10:25:08 +01:00
Control expanded state of Navbar.Collapse
Controlling the expanded state allows us to close the collapse after items are selected (as recommended by react-bootstrap maintainers). Also makes changes to the nav components to pass through props (required by react-bootstrap to correctly wire everything together).
This commit is contained in:
parent
f775222768
commit
f9dcdb0318
@ -21,6 +21,7 @@ import NavRoutesLinks from './nav_routes_links';
|
|||||||
import { currentUserShape, whitelabelShape } from './prop_types';
|
import { currentUserShape, whitelabelShape } from './prop_types';
|
||||||
|
|
||||||
import { constructHead } from '../utils/dom';
|
import { constructHead } from '../utils/dom';
|
||||||
|
import { safeMerge } from '../utils/general';
|
||||||
import { getLangText } from '../utils/lang';
|
import { getLangText } from '../utils/lang';
|
||||||
|
|
||||||
|
|
||||||
@ -35,7 +36,12 @@ let Header = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return PieceListStore.getState();
|
return safeMerge(
|
||||||
|
PieceListStore.getState(),
|
||||||
|
{
|
||||||
|
expandedCollapse: false
|
||||||
|
}
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@ -52,7 +58,17 @@ let Header = React.createClass({
|
|||||||
this.setState(state);
|
this.setState(state);
|
||||||
},
|
},
|
||||||
|
|
||||||
getLogo() {
|
collapseNav() {
|
||||||
|
if (this.state.expandedCollapse) {
|
||||||
|
this.onToggleCollapse(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onToggleCollapse(expandedCollapse) {
|
||||||
|
this.setState({ expandedCollapse });
|
||||||
|
},
|
||||||
|
|
||||||
|
renderLogo() {
|
||||||
const { whitelabel } = this.props;
|
const { whitelabel } = this.props;
|
||||||
|
|
||||||
if (whitelabel.head) {
|
if (whitelabel.head) {
|
||||||
@ -74,7 +90,7 @@ let Header = React.createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getPoweredBy() {
|
renderPoweredBy() {
|
||||||
return (
|
return (
|
||||||
<a className="pull-left ascribe-powered-by" href="https://www.ascribe.io/" target="_blank">
|
<a className="pull-left ascribe-powered-by" href="https://www.ascribe.io/" target="_blank">
|
||||||
<span>{getLangText('powered by')} </span>
|
<span>{getLangText('powered by')} </span>
|
||||||
@ -129,6 +145,7 @@ let Header = React.createClass({
|
|||||||
navbar
|
navbar
|
||||||
pullRight
|
pullRight
|
||||||
hasPieces={!!unfilteredPieceListCount}
|
hasPieces={!!unfilteredPieceListCount}
|
||||||
|
onSelect={this.collapseNav}
|
||||||
routes={routes}
|
routes={routes}
|
||||||
userAcl={currentUser.acl} />
|
userAcl={currentUser.acl} />
|
||||||
);
|
);
|
||||||
@ -154,20 +171,22 @@ let Header = React.createClass({
|
|||||||
<Navbar
|
<Navbar
|
||||||
ref="navbar"
|
ref="navbar"
|
||||||
fixedTop
|
fixedTop
|
||||||
className="hidden-print">
|
className="hidden-print"
|
||||||
|
expanded={this.state.expandedCollapse}
|
||||||
|
onToggle={this.onToggleCollapse}>
|
||||||
<Navbar.Header>
|
<Navbar.Header>
|
||||||
<Navbar.Brand>
|
<Navbar.Brand>
|
||||||
{this.getLogo()}
|
{this.renderLogo()}
|
||||||
</Navbar.Brand>
|
</Navbar.Brand>
|
||||||
<AclProxy
|
<AclProxy
|
||||||
aclName="acl_view_powered_by"
|
aclName="acl_view_powered_by"
|
||||||
aclObject={whitelabel}>
|
aclObject={whitelabel}>
|
||||||
{this.getPoweredBy()}
|
{this.renderPoweredBy()}
|
||||||
</AclProxy>
|
</AclProxy>
|
||||||
<Navbar.Toggle />
|
<Navbar.Toggle />
|
||||||
</Navbar.Header>
|
</Navbar.Header>
|
||||||
<Navbar.Collapse>
|
<Navbar.Collapse>
|
||||||
<Nav navbar pullRight>
|
<Nav navbar pullRight onSelect={this.collapseNav}>
|
||||||
<HeaderNotificationDebug show={false} />
|
<HeaderNotificationDebug show={false} />
|
||||||
<HeaderNotifications />
|
<HeaderNotifications />
|
||||||
{account}
|
{account}
|
||||||
|
@ -12,6 +12,7 @@ import NotificationStore from '../stores/notification_store';
|
|||||||
import withContext from './context/with_context';
|
import withContext from './context/with_context';
|
||||||
import { currentUserShape } from './prop_types';
|
import { currentUserShape } from './prop_types';
|
||||||
|
|
||||||
|
import { omitFromObject } from '../utils/general';
|
||||||
import { getLangText } from '../utils/lang';
|
import { getLangText } from '../utils/lang';
|
||||||
|
|
||||||
|
|
||||||
@ -98,6 +99,8 @@ const HeaderNotifications = React.createClass({
|
|||||||
// Injected through HOCs
|
// Injected through HOCs
|
||||||
currentUser: currentUserShape.isRequired,
|
currentUser: currentUserShape.isRequired,
|
||||||
isLoggedIn: bool.isRequired
|
isLoggedIn: bool.isRequired
|
||||||
|
|
||||||
|
// All other props are passed down to the backing NavDropdown
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
@ -133,6 +136,8 @@ const HeaderNotifications = React.createClass({
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { editionListNotifications, pieceListNotifications } = this.state;
|
const { editionListNotifications, pieceListNotifications } = this.state;
|
||||||
|
const dropdownProps = omitFromObject(this.props, ['currentUser'], ['isLoggedIn']);
|
||||||
|
|
||||||
if (pieceListNotifications.length || editionListNotifications.length) {
|
if (pieceListNotifications.length || editionListNotifications.length) {
|
||||||
let numNotifications = 0;
|
let numNotifications = 0;
|
||||||
|
|
||||||
@ -145,6 +150,7 @@ const HeaderNotifications = React.createClass({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<NavDropdown
|
<NavDropdown
|
||||||
|
{...dropdownProps}
|
||||||
ref="dropdownButton"
|
ref="dropdownButton"
|
||||||
className="notification-menu"
|
className="notification-menu"
|
||||||
id="header-notification-dropdown"
|
id="header-notification-dropdown"
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import Nav from 'react-bootstrap/lib/Nav';
|
import Nav from 'react-bootstrap/lib/Nav';
|
||||||
@ -12,26 +10,30 @@ import { sanitizeList } from '../utils/general';
|
|||||||
|
|
||||||
const DISABLE_ENUM = ['hasPieces', 'noPieces'];
|
const DISABLE_ENUM = ['hasPieces', 'noPieces'];
|
||||||
|
|
||||||
let NavRoutesLinks = React.createClass({
|
const NavRoutesLinks = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
hasPieces: React.PropTypes.bool,
|
hasPieces: React.PropTypes.bool,
|
||||||
routes: React.PropTypes.arrayOf(React.PropTypes.object),
|
routes: React.PropTypes.arrayOf(React.PropTypes.object),
|
||||||
userAcl: React.PropTypes.object
|
userAcl: React.PropTypes.object
|
||||||
|
|
||||||
|
// All other props are passed to the backing Nav
|
||||||
},
|
},
|
||||||
|
|
||||||
isRouteDisabled(disableOn) {
|
isRouteDisabled(disableOn) {
|
||||||
const { hasPieces } = this.props;
|
const { hasPieces } = this.props;
|
||||||
|
|
||||||
if (disableOn) {
|
if (disableOn && !DISABLE_ENUM.includes(disableOn)) {
|
||||||
if (!DISABLE_ENUM.includes(disableOn)) {
|
throw new Error(
|
||||||
throw new Error(`"disableOn" must be one of: [${DISABLE_ENUM.join(', ')}] got "${disableOn}" instead`);
|
`"disableOn" must be one of: [${DISABLE_ENUM.join(', ')}] got "${disableOn}" instead`
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (disableOn === 'hasPieces') {
|
if (disableOn === 'hasPieces') {
|
||||||
return hasPieces;
|
return hasPieces;
|
||||||
} else if (disableOn === 'noPieces') {
|
} else if (disableOn === 'noPieces') {
|
||||||
return !hasPieces;
|
return !hasPieces;
|
||||||
}
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -48,11 +50,11 @@ let NavRoutesLinks = React.createClass({
|
|||||||
*/
|
*/
|
||||||
extractLinksFromRoutes(node, userAcl, i) {
|
extractLinksFromRoutes(node, userAcl, i) {
|
||||||
if (!node) {
|
if (!node) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const links = node.childRoutes.map((child, j) => {
|
const links = node.childRoutes.map((child, j) => {
|
||||||
const { aclName, disableOn, headerTitle, path, childRoutes } = child;
|
const { aclName, disableOn, headerTitle, path } = child;
|
||||||
|
|
||||||
// We validate if the user has set the title correctly,
|
// We validate if the user has set the title correctly,
|
||||||
// otherwise we're not going to render his route
|
// otherwise we're not going to render his route
|
||||||
@ -88,15 +90,12 @@ let NavRoutesLinks = React.createClass({
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<NavRoutesLinksLink
|
<NavRoutesLinksLink key={j} {...navLinkProps} />
|
||||||
key={j}
|
|
||||||
{...navLinkProps} />
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// remove all nulls from the list of generated links
|
// remove all nulls from the list of generated links
|
||||||
@ -104,10 +103,15 @@ let NavRoutesLinks = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {routes, userAcl} = this.props;
|
const {
|
||||||
|
routes,
|
||||||
|
userAcl,
|
||||||
|
hasPieces: ignoredHasPieces,
|
||||||
|
...props
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Nav {...this.props}>
|
<Nav {...props}>
|
||||||
{this.extractLinksFromRoutes(routes[0], userAcl, 0)}
|
{this.extractLinksFromRoutes(routes[0], userAcl, 0)}
|
||||||
</Nav>
|
</Nav>
|
||||||
);
|
);
|
||||||
|
@ -1,46 +1,45 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import DropdownButton from 'react-bootstrap/lib/DropdownButton';
|
|
||||||
import MenuItem from 'react-bootstrap/lib/MenuItem';
|
import MenuItem from 'react-bootstrap/lib/MenuItem';
|
||||||
import NavItem from 'react-bootstrap/lib/NavItem';
|
import NavItem from 'react-bootstrap/lib/NavItem';
|
||||||
|
import NavDropdown from 'react-bootstrap/lib/NavDropdown';
|
||||||
|
|
||||||
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
|
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
|
||||||
|
|
||||||
|
|
||||||
let NavRoutesLinksLink = React.createClass({
|
const NavRoutesLinksLink = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
children: React.PropTypes.oneOfType([
|
children: React.PropTypes.node,
|
||||||
React.PropTypes.arrayOf(React.PropTypes.element),
|
|
||||||
React.PropTypes.element
|
|
||||||
]),
|
|
||||||
disabled: React.PropTypes.bool,
|
|
||||||
depth: React.PropTypes.number,
|
depth: React.PropTypes.number,
|
||||||
|
disabled: React.PropTypes.bool,
|
||||||
headerTitle: React.PropTypes.string,
|
headerTitle: React.PropTypes.string,
|
||||||
routePath: React.PropTypes.string
|
routePath: React.PropTypes.string
|
||||||
|
|
||||||
|
// All other props are passed through to the backing NavItem, or NavDropdown
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { children, headerTitle, depth, disabled, routePath } = this.props;
|
const { children, headerTitle, depth, disabled, routePath, ...props } = this.props;
|
||||||
|
|
||||||
// if the route has children, we're returning a DropdownButton that will get filled
|
// if the route has children, we're returning a DropdownButton that will get filled
|
||||||
// with MenuItems
|
// with MenuItems
|
||||||
if (children) {
|
if (children) {
|
||||||
return (
|
return (
|
||||||
<DropdownButton
|
<NavDropdown
|
||||||
|
{...props}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
id={`nav-route-${headerTitle.toLowerCase()}-dropdown`}
|
id={`nav-route-${headerTitle.toLowerCase()}-dropdown`}
|
||||||
title={headerTitle}>
|
title={headerTitle}>
|
||||||
{children}
|
{children}
|
||||||
</DropdownButton>
|
</NavDropdown>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if (depth === 1) {
|
if (depth === 1) {
|
||||||
// if the node's child is actually a node of level one (a child of a node), we're
|
// if the node's child is actually a node of level one (a child of a node), we're
|
||||||
// returning a DropdownButton matching MenuItem
|
// returning a MenuItem for the containing NavDropdown
|
||||||
return (
|
return (
|
||||||
<LinkContainer
|
<LinkContainer
|
||||||
|
{...props}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
to={routePath}>
|
to={routePath}>
|
||||||
<MenuItem>{headerTitle}</MenuItem>
|
<MenuItem>{headerTitle}</MenuItem>
|
||||||
@ -49,6 +48,7 @@ let NavRoutesLinksLink = React.createClass({
|
|||||||
} else if (depth === 0) {
|
} else if (depth === 0) {
|
||||||
return (
|
return (
|
||||||
<LinkContainer
|
<LinkContainer
|
||||||
|
{...props}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
to={routePath}>
|
to={routePath}>
|
||||||
<NavItem>{headerTitle}</NavItem>
|
<NavItem>{headerTitle}</NavItem>
|
||||||
|
Loading…
Reference in New Issue
Block a user