Convert SideNav to TypeScript
parent
e3529ee636
commit
10ad7f2ab6
|
@ -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}
|
|
|
@ -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}
|
|
@ -6,52 +6,13 @@ import Authorized, {ADMIN_ROLE} from 'src/auth/Authorized'
|
||||||
|
|
||||||
import UserNavBlock from 'src/side_nav/components/UserNavBlock'
|
import UserNavBlock from 'src/side_nav/components/UserNavBlock'
|
||||||
import {
|
import {
|
||||||
NavBar,
|
|
||||||
NavBlock,
|
NavBlock,
|
||||||
NavHeader,
|
NavHeader,
|
||||||
NavListItem,
|
NavListItem,
|
||||||
} from 'src/side_nav/components/NavItems'
|
} from 'src/side_nav/components/NavItems'
|
||||||
|
|
||||||
import {DEFAULT_HOME_PAGE} from 'src/shared/constants'
|
import {DEFAULT_HOME_PAGE} from 'src/shared/constants'
|
||||||
|
import {Params, Location, Links, Me} from 'src/types/sideNav'
|
||||||
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[]
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
params: Params
|
params: Params
|
||||||
|
@ -85,7 +46,7 @@ class SideNav extends PureComponent<Props> {
|
||||||
const isDefaultPage = location.split('/').includes(DEFAULT_HOME_PAGE)
|
const isDefaultPage = location.split('/').includes(DEFAULT_HOME_PAGE)
|
||||||
|
|
||||||
return isHidden ? null : (
|
return isHidden ? null : (
|
||||||
<NavBar location={location}>
|
<nav className="sidebar">
|
||||||
<div
|
<div
|
||||||
className={isDefaultPage ? 'sidebar--item active' : 'sidebar--item'}
|
className={isDefaultPage ? 'sidebar--item active' : 'sidebar--item'}
|
||||||
>
|
>
|
||||||
|
@ -111,7 +72,7 @@ class SideNav extends PureComponent<Props> {
|
||||||
link={`${sourcePrefix}/dashboards`}
|
link={`${sourcePrefix}/dashboards`}
|
||||||
location={location}
|
location={location}
|
||||||
>
|
>
|
||||||
<NavHeader link={`${sourcePrefix}/dashboards`} title={'Dashboards'} />
|
<NavHeader link={`${sourcePrefix}/dashboards`} title="Dashboards" />
|
||||||
</NavBlock>
|
</NavBlock>
|
||||||
<NavBlock
|
<NavBlock
|
||||||
matcher="alerts"
|
matcher="alerts"
|
||||||
|
@ -178,7 +139,7 @@ class SideNav extends PureComponent<Props> {
|
||||||
sourcePrefix={sourcePrefix}
|
sourcePrefix={sourcePrefix}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</NavBar>
|
</nav>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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[]
|
||||||
|
}
|
Loading…
Reference in New Issue