From 9d08b623be7e8af76c854509ab4a23b7e81f75a8 Mon Sep 17 00:00:00 2001 From: IanLondon Date: Wed, 26 Sep 2018 11:17:53 -0400 Subject: [PATCH 1/2] feat(components): create new tab-styled vertical nav bar Closes #1923 --- components/src/index.js | 1 + components/src/tabbedNav/NavTab.js | 73 ++++++++++++++++ components/src/tabbedNav/NavTab.md | 46 ++++++++++ components/src/tabbedNav/OutsideLinkTab.js | 65 +++++++++++++++ components/src/tabbedNav/TabbedNavBar.js | 27 ++++++ components/src/tabbedNav/index.js | 11 +++ components/src/tabbedNav/navbar.css | 83 +++++++++++++++++++ .../src/containers/ConnectedNav.js | 59 ++++++------- protocol-designer/src/containers/NavBar.css | 22 ----- 9 files changed, 337 insertions(+), 50 deletions(-) create mode 100644 components/src/tabbedNav/NavTab.js create mode 100644 components/src/tabbedNav/NavTab.md create mode 100644 components/src/tabbedNav/OutsideLinkTab.js create mode 100644 components/src/tabbedNav/TabbedNavBar.js create mode 100644 components/src/tabbedNav/index.js create mode 100644 components/src/tabbedNav/navbar.css delete mode 100644 protocol-designer/src/containers/NavBar.css diff --git a/components/src/index.js b/components/src/index.js index c1e43c4a7d6..d3735d6d009 100644 --- a/components/src/index.js +++ b/components/src/index.js @@ -21,6 +21,7 @@ export * from './interaction-enhancers' export * from './lists' export * from './modals' export * from './nav' +export * from './tabbedNav' export * from './structure' export * from './tooltips' diff --git a/components/src/tabbedNav/NavTab.js b/components/src/tabbedNav/NavTab.js new file mode 100644 index 00000000000..07bda7c23f5 --- /dev/null +++ b/components/src/tabbedNav/NavTab.js @@ -0,0 +1,73 @@ +// @flow +import * as React from 'react' +import {NavLink} from 'react-router-dom' +import classnames from 'classnames' + +import styles from './navbar.css' +import {Button} from '../buttons' +import {NotificationIcon, type IconName} from '../icons' + +type NavTabProps= { + /** optional click event for nav button */ + onClick?: (event: SyntheticEvent<>) => void, + /** optional url for nav button route */ + url?: string, + /** position a single button on the bottom of the page */ + isBottom?: boolean, + /** classes to apply */ + className?: string, + /** disabled attribute (setting disabled removes onClick) */ + disabled?: boolean, + /** optional title to display below the icon */ + title?: string, + /** Icon name for button's icon */ + iconName: IconName, + /** Display a notification dot */ + notification?: boolean, + /** selected styling (can also use react-router & `activeClassName`) */ + selected?: boolean, +} + +export default function NavTab (props: NavTabProps) { + const {url} = props + const className = classnames( + props.className, + styles.tab, + { + [styles.disabled]: props.disabled, + [styles.bottom]: props.isBottom, + [styles.selected]: props.selected, + } + ) + + let buttonProps = { + className: className, + disabled: props.disabled, + onClick: props.onClick, + } + + if (url) { + buttonProps = { + ...buttonProps, + Component: NavLink, + to: url, + activeClassName: styles.selected, + } + } + + return ( + + ) +} diff --git a/components/src/tabbedNav/NavTab.md b/components/src/tabbedNav/NavTab.md new file mode 100644 index 00000000000..c0f74e40e06 --- /dev/null +++ b/components/src/tabbedNav/NavTab.md @@ -0,0 +1,46 @@ +Basic Usage: + +```js +
+ alert('you clicked me')} + iconName='ot-connect' + /> +
+``` +Disabled: + +```js +
+ alert('you clicked me')} + iconName='ot-connect' + disabled + /> +
+``` + +Currently Selected: + +```js +
+ alert('you clicked me')} + iconName='ot-connect' + selected + /> +
+``` + +Optional Title: + +```js +
+ alert('you clicked me')} + title='connect' + iconName='ot-connect' + title='connect' + /> +
+``` diff --git a/components/src/tabbedNav/OutsideLinkTab.js b/components/src/tabbedNav/OutsideLinkTab.js new file mode 100644 index 00000000000..faf6adab475 --- /dev/null +++ b/components/src/tabbedNav/OutsideLinkTab.js @@ -0,0 +1,65 @@ +// @flow +import * as React from 'react' +import cx from 'classnames' + +import styles from './navbar.css' +import {Button} from '../buttons' +import {NotificationIcon, type IconName} from '../icons' + +type Props = { + /** optional click event for nav button */ + onClick?: (event: SyntheticEvent<>) => mixed, + /** link to outside URL */ + to: string, + /** position a single button on the bottom of the page */ + isBottom?: boolean, + /** classes to apply */ + className?: string, + /** disabled attribute (setting disabled removes onClick) */ + disabled?: boolean, + /** optional title to display below the icon */ + title?: string, + /** Icon name for button's icon */ + iconName: IconName, + /** Display a notification dot */ + notification?: boolean, + /** selected styling (can also use react-router & `activeClassName`) */ + selected?: boolean, +} + +/** Very much like NavTab, but used for opening external links in a new tab/window */ +export default function OutsideLinkTab (props: Props) { + const className = cx( + props.className, + styles.tab, + styles.no_link, + { + [styles.disabled]: props.disabled, + [styles.bottom]: props.isBottom, + [styles.selected]: props.selected, + } + ) + return ( + + ) +} diff --git a/components/src/tabbedNav/TabbedNavBar.js b/components/src/tabbedNav/TabbedNavBar.js new file mode 100644 index 00000000000..c2dfa348ea6 --- /dev/null +++ b/components/src/tabbedNav/TabbedNavBar.js @@ -0,0 +1,27 @@ +// @flow +import * as React from 'react' +import cx from 'classnames' +import styles from './navbar.css' + +type Props= { + className?: string, + topChildren?: React.Node, + bottomChildren?: React.Node, +} + +export default function TabbedNavBar (props: Props) { + const className = cx(styles.navbar, props.className) + return ( + + ) +} diff --git a/components/src/tabbedNav/index.js b/components/src/tabbedNav/index.js new file mode 100644 index 00000000000..06545e18eaf --- /dev/null +++ b/components/src/tabbedNav/index.js @@ -0,0 +1,11 @@ +// @flow +// navigational components +import TabbedNavBar from './TabbedNavBar' +import NavTab from './NavTab' +import OutsideLinkTab from './OutsideLinkTab' + +export { + TabbedNavBar, + NavTab, + OutsideLinkTab, +} diff --git a/components/src/tabbedNav/navbar.css b/components/src/tabbedNav/navbar.css new file mode 100644 index 00000000000..21a74f8f6fb --- /dev/null +++ b/components/src/tabbedNav/navbar.css @@ -0,0 +1,83 @@ +@import '..'; + +.navbar { + flex: none; + width: 4rem; + display: flex; + flex-direction: column; +} + +.navbar > * { + flex: 1; +} + +/* TODO(mc, 2018-03-21): @apply --button-default? */ +.tab { + display: inline-block; + cursor: pointer; + background-color: var(--c-white); + color: var(--c-med-gray); + outline: none; /* button reset? */ + border: none; /* button reset >:( */ + border-right: var(--bd-light); +} + +.top_section > *, +.bottom_section > *, +.filler { + width: 100%; + border-right: var(--bd-light); +} + +.top_section > .tab { + border-bottom: var(--bd-light); +} + +.bottom_section > .tab { + border-top: var(--bd-light); +} + +.filler { + flex-grow: 9999; +} + +.title { + display: block; + color: var(--c-font-dark); + font-size: var(--fs-caption); + font-weight: var(--fw-semibold); + text-transform: uppercase; + text-align: center; + padding-bottom: 0.5rem; +} + +.disabled { + cursor: default; + pointer-events: none; + opacity: 0.75; +} + +.selected { + background-color: var(--c-bg-light); + border-right-color: transparent; + + & > svg { + fill: var(--c-dark-gray); + } +} + +.icon { + height: 100%; + width: 100%; + padding: 0.5rem 0.5rem 0; + color: var(--c-charcoal); +} + +.notification { + color: var(--c-orange); +} + +.no_link { + text-decoration: none; + color: inherit; +} diff --git a/protocol-designer/src/containers/ConnectedNav.js b/protocol-designer/src/containers/ConnectedNav.js index de339b4e05d..e42f3efad33 100644 --- a/protocol-designer/src/containers/ConnectedNav.js +++ b/protocol-designer/src/containers/ConnectedNav.js @@ -4,10 +4,9 @@ import type {ThunkDispatch, BaseState} from '../types' import {connect} from 'react-redux' import {KNOWLEDGEBASE_ROOT_URL} from '../components/KnowledgeBaseLink' -import {NavButton, VerticalNavBar, OutsideLinkButton} from '@opentrons/components' +import {NavTab, TabbedNavBar, OutsideLinkTab} from '@opentrons/components' import i18n from '../localization' import {type Page, actions, selectors} from '../navigation' -import styles from './NavBar.css' type Props = { currentPage: Page, @@ -16,32 +15,36 @@ type Props = { function Nav (props: Props) { return ( - -
- - -
-
- - -
-
+ + + + + } + bottomChildren={ + + + + + } + /> ) } diff --git a/protocol-designer/src/containers/NavBar.css b/protocol-designer/src/containers/NavBar.css deleted file mode 100644 index f9cd783f2d5..00000000000 --- a/protocol-designer/src/containers/NavBar.css +++ /dev/null @@ -1,22 +0,0 @@ -@import '@opentrons/components'; - -.nav_bar { - display: flex; - flex-direction: column; - justify-content: space-between; - height: 100vh; -} - -.top_buttons > * { - border-bottom: 2px solid var(--c-light-gray); - border-top: 0; - border-left: 0; - border-right: 0; -} - -.bottom_buttons > * { - border-top: 2px solid var(--c-light-gray); - border-bottom: 0; - border-left: 0; - border-right: 0; -} From f3c5311c4617556975bdf56fe61c28db25598987 Mon Sep 17 00:00:00 2001 From: IanLondon Date: Thu, 27 Sep 2018 14:45:50 -0400 Subject: [PATCH 2/2] fixup: add tabbedNav to component library --- components/styleguide.config.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/styleguide.config.js b/components/styleguide.config.js index 6587228c95e..a552ed8f489 100644 --- a/components/styleguide.config.js +++ b/components/styleguide.config.js @@ -36,9 +36,13 @@ module.exports = { components: 'src/alerts/[A-Z]*.js', }, { - name: 'Nav', + name: 'Nav (buttons)', components: 'src/nav/[A-Z]*.js', }, + { + name: 'Tabbed Nav', + components: 'src/tabbedNav/[A-Z]*.js', + }, { name: 'Buttons', components: 'src/buttons/[A-Z]*.js',