Convert SideNav to TypeScript

pull/10616/head
Andrew Watkins 2018-03-29 14:40:48 -07:00
parent e3529ee636
commit 10ad7f2ab6
4 changed files with 162 additions and 173 deletions

View File

@ -1,130 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import {Link} from 'react-router'
import classnames from 'classnames'
const {bool, node, string} = PropTypes
const NavListItem = React.createClass({
propTypes: {
link: string.isRequired,
children: node,
location: string,
useAnchor: bool,
isExternal: bool,
},
render() {
const {link, children, location, useAnchor, isExternal} = this.props
const isActive = location.startsWith(link)
return useAnchor ? (
<a
className={classnames('sidebar-menu--item', {active: isActive})}
href={link}
target={isExternal ? '_blank' : '_self'}
>
{children}
</a>
) : (
<Link
className={classnames('sidebar-menu--item', {active: isActive})}
to={link}
>
{children}
</Link>
)
},
})
const NavHeader = React.createClass({
propTypes: {
link: string,
title: string,
useAnchor: bool,
},
render() {
const {link, title, useAnchor} = this.props
// Some nav items, such as Logout, need to hit an external link rather
// than simply route to an internal page. Anchor tags serve that purpose.
return useAnchor ? (
<a className="sidebar-menu--heading" href={link}>
{title}
</a>
) : (
<Link className="sidebar-menu--heading" to={link}>
{title}
</Link>
)
},
})
const NavBlock = React.createClass({
propTypes: {
children: node,
link: string,
icon: string.isRequired,
location: string,
className: string,
},
render() {
const {location, className} = this.props
const isActive = React.Children.toArray(this.props.children).find(child => {
return location.startsWith(child.props.link) // if location is undefined, this will fail silently
})
const children = React.Children.map(this.props.children, child => {
if (child && child.type === NavListItem) {
return React.cloneElement(child, {location})
}
return child
})
return (
<div
className={classnames('sidebar--item', className, {active: isActive})}
>
{this.renderSquare()}
<div className="sidebar-menu">
{children}
<div className="sidebar-menu--triangle" />
</div>
</div>
)
},
renderSquare() {
const {link, icon} = this.props
if (!link) {
return (
<div className="sidebar--square">
<div className={`sidebar--icon icon ${icon}`} />
</div>
)
}
return (
<Link className="sidebar--square" to={link}>
<div className={`sidebar--icon icon ${icon}`} />
</Link>
)
},
})
const NavBar = React.createClass({
propTypes: {
children: node,
},
render() {
const {children} = this.props
return <nav className="sidebar">{children}</nav>
},
})
export {NavBar, NavBlock, NavHeader, NavListItem}

View File

@ -0,0 +1,120 @@
import React, {PureComponent, SFC, ReactNode, ReactElement} from 'react'
import {Link} from 'react-router'
import classnames from 'classnames'
interface NavListItemProps {
link?: string
location?: string
useAnchor?: boolean
isExternal?: boolean
children?: ReactNode
}
const NavListItem: SFC<NavListItemProps> = ({
link,
children,
location,
useAnchor,
isExternal,
}) => {
const isActive = location.startsWith(link)
return useAnchor ? (
<a
className={classnames('sidebar-menu--item', {active: isActive})}
href={link}
target={isExternal ? '_blank' : '_self'}
>
{children}
</a>
) : (
<Link
className={classnames('sidebar-menu--item', {active: isActive})}
to={link}
>
{children}
</Link>
)
}
interface NavHeaderProps {
link?: string
title?: string
useAnchor?: string
}
const NavHeader: SFC<NavHeaderProps> = ({link, title, useAnchor}) => {
// Some nav items, such as Logout, need to hit an external link rather
// than simply route to an internal page. Anchor tags serve that purpose.
return useAnchor ? (
<a className="sidebar-menu--heading" href={link}>
{title}
</a>
) : (
<Link className="sidebar-menu--heading" to={link}>
{title}
</Link>
)
}
interface NavBlockProps {
children?: ReactNode
link?: string
icon: string
location?: string
className?: string
matcher?: string
}
class NavBlock extends PureComponent<NavBlockProps> {
public render() {
const {location, className} = this.props
const isActive = React.Children.toArray(this.props.children).find(
(child: ReactElement<any>) => {
return location.startsWith(child.props.link) // if location is undefined, this will fail silently
}
)
const children = React.Children.map(
this.props.children,
(child: ReactElement<any>) => {
if (child && child.type === NavListItem) {
return React.cloneElement(child, {location})
}
return child
}
)
return (
<div
className={classnames('sidebar--item', className, {active: isActive})}
>
{this.renderSquare()}
<div className="sidebar-menu">
{children}
<div className="sidebar-menu--triangle" />
</div>
</div>
)
}
private renderSquare() {
const {link, icon} = this.props
if (!link) {
return (
<div className="sidebar--square">
<div className={`sidebar--icon icon ${icon}`} />
</div>
)
}
return (
<Link className="sidebar--square" to={link}>
<div className={`sidebar--icon icon ${icon}`} />
</Link>
)
}
}
export {NavBlock, NavHeader, NavListItem}

View File

@ -6,52 +6,13 @@ import Authorized, {ADMIN_ROLE} from 'src/auth/Authorized'
import UserNavBlock from 'src/side_nav/components/UserNavBlock'
import {
NavBar,
NavBlock,
NavHeader,
NavListItem,
} from 'src/side_nav/components/NavItems'
import {DEFAULT_HOME_PAGE} from 'src/shared/constants'
interface Params {
sourceID: string
}
interface Location {
pathname: string
}
interface ExternalLink {
name: string
url: string
}
interface ExternalLinks {
custom: ExternalLink[]
}
interface Links {
me?: string
external?: ExternalLinks
}
interface Organization {
id: string
name: string
}
interface Role {
id: string
name: string
}
interface Me {
name: string
currentOrganization: Organization
organizations: Organization[]
role: Role[]
}
import {Params, Location, Links, Me} from 'src/types/sideNav'
interface Props {
params: Params
@ -85,7 +46,7 @@ class SideNav extends PureComponent<Props> {
const isDefaultPage = location.split('/').includes(DEFAULT_HOME_PAGE)
return isHidden ? null : (
<NavBar location={location}>
<nav className="sidebar">
<div
className={isDefaultPage ? 'sidebar--item active' : 'sidebar--item'}
>
@ -111,7 +72,7 @@ class SideNav extends PureComponent<Props> {
link={`${sourcePrefix}/dashboards`}
location={location}
>
<NavHeader link={`${sourcePrefix}/dashboards`} title={'Dashboards'} />
<NavHeader link={`${sourcePrefix}/dashboards`} title="Dashboards" />
</NavBlock>
<NavBlock
matcher="alerts"
@ -178,7 +139,7 @@ class SideNav extends PureComponent<Props> {
sourcePrefix={sourcePrefix}
/>
) : null}
</NavBar>
</nav>
)
}
}

38
ui/src/types/sideNav.ts Normal file
View File

@ -0,0 +1,38 @@
export interface Params {
sourceID: string
}
export interface Location {
pathname: string
}
export interface ExternalLink {
name: string
url: string
}
export interface ExternalLinks {
custom: ExternalLink[]
}
export interface Links {
me?: string
external?: ExternalLinks
}
export interface Organization {
id: string
name: string
}
export interface Role {
id: string
name: string
}
export interface Me {
name: string
currentOrganization: Organization
organizations: Organization[]
role: Role[]
}