Render Admin NavBlock based on authorization

Require location prop as propType in SideNav to prevent silent fail.
Add guard clause on isUsingAuth to prevent DOM break in Authorized.
Consolidate return on Authorized render.
Clean up Authorized.

Signed-off-by: Alex Paxton <thealexpaxton@gmail.com>
pull/10616/head
Jared Scheib 2017-10-25 16:22:00 -07:00 committed by Alex Paxton
parent c8163b68aa
commit 95917837e3
3 changed files with 30 additions and 16 deletions

View File

@ -33,19 +33,28 @@ export const isUserAuthorized = (meRole, requiredRole) => {
const getRoleName = ({roles: [{name}, ..._]}) => name const getRoleName = ({roles: [{name}, ..._]}) => name
const Authorized = ({children, me, isUsingAuth, requiredRole, replaceWith}) => { const Authorized = ({
if (!isUsingAuth) { children,
return React.isValidElement(children) ? children : children[0] me,
isUsingAuth,
requiredRole,
replaceWith,
...additionalProps
}) => {
// if me response has not been received yet, render nothing
if (typeof isUsingAuth !== 'boolean') {
return null
} }
const meRole = getRoleName(me) const meRole = getRoleName(me)
if (isUserAuthorized(meRole, requiredRole)) { if (!isUsingAuth || isUserAuthorized(meRole, requiredRole)) {
return React.cloneElement( return React.cloneElement(
React.isValidElement(children) ? children : children[0] React.isValidElement(children) ? children : children[0],
) {...additionalProps}
) // guards against multiple children wrapped by Authorized
} }
return replaceWith ? React.cloneElement(replaceWith) : null return replaceWith ? replaceWith : null
// if you want elements to be disabled instead of hidden: // if you want elements to be disabled instead of hidden:
// return React.cloneElement(clonedElement, {disabled: !isAuthorized}) // return React.cloneElement(clonedElement, {disabled: !isAuthorized})
@ -54,7 +63,7 @@ const Authorized = ({children, me, isUsingAuth, requiredRole, replaceWith}) => {
const {arrayOf, bool, node, shape, string} = PropTypes const {arrayOf, bool, node, shape, string} = PropTypes
Authorized.propTypes = { Authorized.propTypes = {
isUsingAuth: bool.isRequired, isUsingAuth: bool,
replaceWith: node, replaceWith: node,
children: node.isRequired, children: node.isRequired,
me: shape({ me: shape({

View File

@ -2,13 +2,15 @@ import React, {PropTypes} from 'react'
import {Link} from 'react-router' import {Link} from 'react-router'
import classnames from 'classnames' import classnames from 'classnames'
import Authorized from 'src/auth/Authorized'
const {bool, node, string} = PropTypes const {bool, node, string} = PropTypes
const NavListItem = React.createClass({ const NavListItem = React.createClass({
propTypes: { propTypes: {
link: string.isRequired, link: string.isRequired,
children: node, children: node,
location: string, location: string.isRequired,
useAnchor: bool, useAnchor: bool,
isExternal: bool, isExternal: bool,
}, },
@ -60,15 +62,14 @@ const NavBlock = React.createClass({
children: node, children: node,
link: string, link: string,
icon: string.isRequired, icon: string.isRequired,
location: string, location: string.isRequired,
className: string, className: string,
}, },
render() { render() {
const {location, className} = this.props const {location, className} = this.props
const isActive = React.Children.toArray(this.props.children).find(child => { const isActive = React.Children.toArray(this.props.children).find(child => {
return location.startsWith(child.props.link) return location.startsWith(child.props.link) // if location is undefined, this will fail silently
}) })
const children = React.Children.map(this.props.children, child => { const children = React.Children.map(this.props.children, child => {
@ -119,7 +120,7 @@ const NavBar = React.createClass({
render() { render() {
const children = React.Children.map(this.props.children, child => { const children = React.Children.map(this.props.children, child => {
if (child && child.type === NavBlock) { if (child && (child.type === NavBlock || child.type === Authorized)) {
return React.cloneElement(child, { return React.cloneElement(child, {
location: this.props.location, location: this.props.location,
}) })

View File

@ -2,6 +2,8 @@ import React, {PropTypes} from 'react'
import {withRouter, Link} from 'react-router' import {withRouter, Link} from 'react-router'
import {connect} from 'react-redux' import {connect} from 'react-redux'
import Authorized, {ADMIN_ROLE} from 'src/auth/Authorized'
import { import {
NavBar, NavBar,
NavBlock, NavBlock,
@ -108,9 +110,11 @@ const SideNav = React.createClass({
Create Create
</NavListItem> </NavListItem>
</NavBlock> </NavBlock>
<Authorized requiredRole={ADMIN_ROLE}>
<NavBlock icon="crown2" link={`${sourcePrefix}/admin`}> <NavBlock icon="crown2" link={`${sourcePrefix}/admin`}>
<NavHeader link={`${sourcePrefix}/admin`} title="Admin" /> <NavHeader link={`${sourcePrefix}/admin`} title="Admin" />
</NavBlock> </NavBlock>
</Authorized>
<NavBlock icon="cog-thick" link={`${sourcePrefix}/manage-sources`}> <NavBlock icon="cog-thick" link={`${sourcePrefix}/manage-sources`}>
<NavHeader <NavHeader
link={`${sourcePrefix}/manage-sources`} link={`${sourcePrefix}/manage-sources`}