Skip to content
This repository has been archived by the owner on Aug 21, 2023. It is now read-only.

Commit

Permalink
Merge 147e19a into 9088529
Browse files Browse the repository at this point in the history
  • Loading branch information
plbabin committed Apr 11, 2019
2 parents 9088529 + 147e19a commit b8a89b4
Show file tree
Hide file tree
Showing 10 changed files with 386 additions and 3 deletions.
8 changes: 5 additions & 3 deletions src/components/Nav/Nav.css.ts
Expand Up @@ -6,6 +6,7 @@ import baseStyles from '../../styles/resets/baseStyles.css'

export const config = {
indicatorTransition: 'opacity 100ms ease',
padding: '10px',
}

export const NavUI = styled('nav', { pure: false })`
Expand All @@ -24,7 +25,7 @@ export const ListUI = styled('ul', { pure: false })`
`

export const ItemUI = styled('li', { pure: false })`
padding: 0 10px;
padding: 0;
transform: translateZ(0);
&.is-disabled {
Expand All @@ -36,6 +37,7 @@ export const ItemUI = styled('li', { pure: false })`
color: ${getColor('charcoal.200')};
display: block;
text-decoration: none !important;
padding: 0 ${config.padding};
&:hover {
color: ${getColor('charcoal.300')};
Expand Down Expand Up @@ -81,13 +83,13 @@ export const ErrorWrapperUI = styled(Flexy.Item)`
export const IndicatorUI = styled('div')`
background: ${getColor('blue.500')};
border-radius: 9999px;
bottom: 0;
bottom: -1px;
height: 2px;
opacity: 0;
left: 0;
right: 0;
position: absolute;
transition: ${config.indicatorTransition};
width: 100%;
will-change: opacity;
${({ isActive }) =>
Expand Down
28 changes: 28 additions & 0 deletions src/components/TabBar/README.md
@@ -0,0 +1,28 @@
# TabBar

This component is provides a Tab-like component with support for [react-router](https://github.com/ReactTraining/react-router). The [TabBar.Item](./Item.md) component is a simple link to the [Nav.Item](../Nav/docs/Item.md) component.

TabBar is a wrapper containing a [Toolbar](../Toolbar/README.md), a [Nav](../Nav/README.md) and a secondary (left or right aligned) content placeholder.

## Example

```jsx
<TabBar secContent="13,333 items">
<TabBar.Item exact to="/">
Home
</TabBar.Item>
<TabBar.Item exact to="/about">
About
</TabBar.Item>
<TabBar.Item exact to="/contact">
Contact
</TabBar.Item>
</TabBar>
```

## Props

| Prop | Type | Default | Description |
| ---------- | -------- | ------- | --------------------------------------------------------------------------------------------------- |
| className | `string` | | The className of the component. |
| secContent | `any` | | A right or left aligned placeholder that will be render inside the toolbar as the secondary content |
69 changes: 69 additions & 0 deletions src/components/TabBar/TabBar.css.ts
@@ -0,0 +1,69 @@
import styled from '../styled'
import Toolbar from '../Toolbar'

import { getColor } from '../../styles/utilities/color'
import baseStyles from '../../styles/resets/baseStyles.css'

const getAlignment = align => {
switch (align) {
case 'center':
return 'center'
default:
return 'flex-start'
}
}

const getDirection = align => {
switch (align) {
case 'right':
return 'row-reverse'
default:
return 'row'
}
}

export const TabBarUI = styled('nav')`
--BlueConfigGlobalFontSize: 14px;
${baseStyles};
display: flex;
margin: 0 auto;
.c-Toolbar {
justify-content: ${props => getAlignment(props.align)};
flex-direction: ${props => getDirection(props.align)};
}
.c-ToolbarWrapper {
width: 100%;
}
`

export const SecContentUI = styled(Toolbar.Item)`
font-size: 14px;
color: ${getColor('charcoal.200')};
display: flex;
align-items: center;
margin-bottom: calc(8px * -1);
&.is-defaultItem {
margin-left: ${props => (props.align !== 'right' ? 'auto' : '0')};
margin-right: ${props => (props.align === 'right' ? 'auto' : '0')};
}
b,
strong {
font-weight: 500;
color: ${getColor('charcoal.600')};
}
`

// adjust margin to have to active bar hover the toolbar border
export const ToolbarUI = styled(Toolbar)`
width: 100%;
padding-left: 0;
padding-right: 0;
&.is-placement-top.is-size-sm .c-Nav {
/* margin-bottom: calc(9px * -1); */
}
`
60 changes: 60 additions & 0 deletions src/components/TabBar/TabBar.tsx
@@ -0,0 +1,60 @@
import * as React from 'react'
import propConnect from '../PropProvider/propConnect'
import getValidProps from '@helpscout/react-utils/dist/getValidProps'
import Nav from '../Nav'
import Toolbar from '../Toolbar'
import { classNames } from '../../utilities/classNames'
import { noop } from '../../utilities/other'
import { TabBarUI, SecContentUI, ToolbarUI } from './TabBar.css'
import { COMPONENT_KEY } from './TabBar.utils'

export interface Props {
className?: string
children?: any
innerRef: (node: HTMLElement) => void
secContent?: any
align?: 'left' | 'center' | 'right'
}

export class TabBar extends React.Component<Props> {
static className = 'c-TabBar'
static defaultProps = {
innerRef: noop,
align: 'left',
}

static Item = Nav.Item

getClassName() {
const { className } = this.props
return classNames(TabBar.className, className)
}

render() {
const { children, innerRef, secContent, align, ...rest } = this.props

return (
<TabBarUI
{...getValidProps(rest)}
className={this.getClassName()}
innerRef={innerRef}
align={align}
>
<ToolbarUI placement="top">
<Toolbar.Item>
<Nav>{children}</Nav>
</Toolbar.Item>
{secContent && (
<SecContentUI align={align}>{secContent}</SecContentUI>
)}
</ToolbarUI>
</TabBarUI>
)
}
}

const PropConnectedComponent = propConnect(COMPONENT_KEY, { pure: false })(
TabBar
)

export default PropConnectedComponent
1 change: 1 addition & 0 deletions src/components/TabBar/TabBar.utils.ts
@@ -0,0 +1 @@
export const COMPONENT_KEY = 'TabBar'
68 changes: 68 additions & 0 deletions src/components/TabBar/__tests__/TabBar.test.tsx
@@ -0,0 +1,68 @@
import * as React from 'react'
import { mount, render } from 'enzyme'
import { TabBar } from '../TabBar'
import { SecContentUI, TabBarUI } from '../TabBar.css'
import Item from '../../Nav/Nav.Item'
import Toolbar from '../../Toolbar'

describe('className', () => {
test('Has default className', () => {
const wrapper = render(<TabBar />)

expect(wrapper.hasClass('c-TabBar')).toBeTruthy()
})

test('Can render custom className', () => {
const customClassName = 'blue'
const wrapper = render(<TabBar className={customClassName} />)

expect(wrapper.hasClass(customClassName)).toBeTruthy()
})
})

describe('Sub-components', () => {
test('Has Item sub-component', () => {
expect(TabBar.Item).toBe(Item)
})
})

describe('Render', () => {
test('Has a Toolbar component', () => {
const wrapper = mount(<TabBar />)
expect(wrapper.find(Toolbar).length).toBeTruthy()
})
})

describe('Align', () => {
test('Sets align prop to TabBarUI', () => {
const align = 'right'
const wrapper = mount(<TabBar align={align} />)
expect(wrapper.find(TabBarUI).prop('align')).toBe(align)
})

test('Sets align prop to childrens', () => {
const align = 'right'
const wrapper = mount(<TabBar align={align} secContent="test" />)
expect(wrapper.find(TabBarUI).prop('align')).toBe(align)
expect(wrapper.find(SecContentUI).prop('align')).toBe(align)
})
})

describe('Secondary content', () => {
test('Renders nothing if prop is empty', () => {
const wrapper = mount(<TabBar />)
expect(wrapper.find(SecContentUI).length).toBeFalsy()
})
test('Renders secondary content text', () => {
const text = 'sec content text'
const wrapper = mount(<TabBar secContent={text} />)
expect(wrapper.find(SecContentUI).text()).toBe(text)
})

test('Renders secondary content child', () => {
const text = 'this is a test'
const node = <span>{text}</span>
const wrapper = mount(<TabBar secContent={node} />)
expect(wrapper.find(SecContentUI).text()).toBe(text)
})
})
25 changes: 25 additions & 0 deletions src/components/TabBar/docs/TabBar.md
@@ -0,0 +1,25 @@
# TabBar

This component is provides navigation interactions with support for [react-router](https://github.com/ReactTraining/react-router). The [TabBar.Item](./Item.md) component is a simple link to the [Nav.Item](../Nav/docs/Item.md) component.

## Example

```jsx
<TabBar>
<TabBar.Item exact to="/">
Home
</TabBar.Item>
<TabBar.Item exact to="/about">
About
</TabBar.Item>
<TabBar.Item exact to="/contact">
Contact
</TabBar.Item>
</TabBar>
```

## Props

| Prop | Type | Default | Description |
| --------- | -------- | ------- | ------------------------------- |
| className | `string` | | The className of the component. |
3 changes: 3 additions & 0 deletions src/components/TabBar/index.ts
@@ -0,0 +1,3 @@
import TabBar from './TabBar'

export default TabBar
1 change: 1 addition & 0 deletions src/components/index.js
Expand Up @@ -107,6 +107,7 @@ export { default as Text } from './Text'
export { default as ThemeProvider } from './ThemeProvider'
export { default as Timeline } from './Timeline'
export { default as Timestamp } from './Timestamp'
export { default as TabBar } from './TabBar'
export { default as Toolbar } from './Toolbar'
export { default as Tooltip } from './Tooltip'
export { default as Truncate } from './Truncate'
Expand Down

0 comments on commit b8a89b4

Please sign in to comment.