Skip to content

Commit

Permalink
feat(expandcollapse): Add support for subtext and arbitrary header co…
Browse files Browse the repository at this point in the history
…ntent
  • Loading branch information
ryanoglesby08 committed Nov 6, 2017
1 parent 52c52ec commit 3c5f4c8
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 78 deletions.
3 changes: 2 additions & 1 deletion src/components/ExpandCollapse/ExpandCollapse.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,13 @@ class ExpandCollapse extends React.Component {
return (
<div {...safeRest(rest)}>
{React.Children.map(children, (panel, index) => {
const { id, header, onToggle } = panel.props
const { id, header, subtext, onToggle } = panel.props

return (
<PanelWrapper
panelId={id}
panelHeader={header}
panelSubtext={subtext}
panelOnToggle={onToggle}
open={this.state.panels.has(id)}
last={index === React.Children.count(children) - 1}
Expand Down
4 changes: 3 additions & 1 deletion src/components/ExpandCollapse/Panel/Panel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ const Panel = ({ children }) => children

Panel.propTypes = {
id: PropTypes.string.isRequired,
header: PropTypes.string.isRequired,
header: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired,
subtext: PropTypes.string,
onToggle: PropTypes.func,
children: PropTypes.node.isRequired,
}

Panel.defaultProps = {
subtext: undefined,
onToggle: undefined
}

Expand Down
16 changes: 0 additions & 16 deletions src/components/ExpandCollapse/Panel/Panel.modules.scss

This file was deleted.

27 changes: 23 additions & 4 deletions src/components/ExpandCollapse/PanelWrapper/PanelWrapper.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import Reveal from '../../Animation/Reveal'
import Translate from '../../Animation/Translate'
import Panel from '../Panel/Panel'

import styles from '../Panel/Panel.modules.scss'
import styles from './PanelWrapper.modules.scss'

class PanelWrapper extends React.Component {
constructor(props) {
Expand Down Expand Up @@ -47,8 +47,25 @@ class PanelWrapper extends React.Component {
this.setState({ hover: false })
}

renderHeader(header, subtext) {
if (typeof header !== 'string') {
return header
}

// FIXME: Use between here!
return (
<Flexbox direction="column">
<Box spacing="margin" bottom={2}>
<Text size="medium">{header}</Text>
</Box>

{subtext && <Text size="small">{subtext}</Text>}
</Flexbox>
)
}

render() {
const { panelId, panelHeader, last, onClick, children } = this.props
const { panelId, panelHeader, panelSubtext, last, onClick, children } = this.props

return (
<div data-testid={panelId}>
Expand All @@ -71,7 +88,7 @@ class PanelWrapper extends React.Component {
</Translate>
</Box>

<Text size="medium">{panelHeader}</Text>
{this.renderHeader(panelHeader, panelSubtext)}
</Flexbox>
</Box>
</Clickable>
Expand Down Expand Up @@ -103,7 +120,8 @@ class PanelWrapper extends React.Component {
}
PanelWrapper.propTypes = {
panelId: PropTypes.string.isRequired,
panelHeader: PropTypes.string.isRequired,
panelHeader: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired,
panelSubtext: PropTypes.string,
panelOnToggle: PropTypes.func,
open: PropTypes.bool,
last: PropTypes.bool.isRequired,
Expand All @@ -112,6 +130,7 @@ PanelWrapper.propTypes = {
}

PanelWrapper.defaultProps = {
panelSubtext: undefined,
panelOnToggle: undefined,
open: false,
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.header {
width: 100%;
text-align: left;
}
108 changes: 65 additions & 43 deletions src/components/ExpandCollapse/__tests__/ExpandCollapse.spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { shallow, mount } from 'enzyme'

import Reveal from '../../Animation/Reveal'
import Translate from '../../Animation/Translate'
import Text from '../../Typography/Text/Text'
import ExpandCollapse from '../ExpandCollapse'

describe('ExpandCollapse', () => {
Expand All @@ -24,34 +25,73 @@ describe('ExpandCollapse', () => {
return {
expandCollapse,
findPanel,
findPanelHeader,
hoverPanel: id => findPanelHeader(id).simulate('mouseEnter'),
unHoverPanel: id => findPanelHeader(id).simulate('mouseLeave'),
togglePanel: id => findPanelHeader(id).simulate('click'),
clickPanel: id => findPanelHeader(id).simulate('click'),
}
}

const expectPanelToBeOpen = panel => expect(panel.find(Reveal)).toHaveProp('in', true)
const expectPanelToBeClosed = panel => expect(panel.find(Reveal)).toHaveProp('in', false)

const defaultProps = {
id: 'a-panel',
header: 'Panel title',
}
const defaultChildren = 'Panel content'
const aPanel = ({ ...props }, children = defaultChildren) => (
<ExpandCollapse.Panel {...defaultProps} {...props}>
{children}
</ExpandCollapse.Panel>
)

it('renders', () => {
const { expandCollapse } = doMount(
<ExpandCollapse open={['panel-1']}>
<ExpandCollapse.Panel id="panel-1" header="First panel title">
First panel
<ExpandCollapse.Panel id="panel-1" header="Panel title">
Panel content
</ExpandCollapse.Panel>
</ExpandCollapse>
)

expect(expandCollapse).toMatchSnapshot()
})

describe('the panel header', () => {
it('can be a simple string', () => {
const { findPanelHeader } = doMount(
<ExpandCollapse>{aPanel({ id: 'panel-1', header: 'Panel title' })}</ExpandCollapse>
)

expect(findPanelHeader('panel-1')).toContainReact(<Text size="medium">Panel title</Text>)
})

it('can have additional subtext', () => {
const { findPanelHeader } = doMount(
<ExpandCollapse>
{aPanel({ id: 'panel-1', header: 'Panel title', subtext: 'Some subtext' })}
</ExpandCollapse>
)

expect(findPanelHeader('panel-1')).toContainReact(<Text size="medium">Panel title</Text>)
expect(findPanelHeader('panel-1')).toContainReact(<Text size="small">Some subtext</Text>)
})

it('can be arbitrary components', () => {
const Header = () => <div>A complex header</div>

const { findPanelHeader } = doMount(
<ExpandCollapse>{aPanel({ id: 'panel-1', header: <Header /> })}</ExpandCollapse>
)

expect(findPanelHeader('panel-1')).toContainReact(<div>A complex header</div>)
})
})

it('translates the caret icon on hover', () => {
const { findPanel, hoverPanel, unHoverPanel } = doMount(
<ExpandCollapse>
<ExpandCollapse.Panel id="panel-1" header="First panel title">
First panel
</ExpandCollapse.Panel>
</ExpandCollapse>
<ExpandCollapse>{aPanel({ id: 'panel-1' })}</ExpandCollapse>
)

hoverPanel('panel-1')
Expand All @@ -61,16 +101,14 @@ describe('ExpandCollapse', () => {
expect(findPanel('panel-1').find(Translate)).toHaveProp('in', false)
})

// TODO: it flips the caret when it opens. Need internet to see the icon.

describe('panel interactions', () => {
it('can have some panels open and some panels closed by default', () => {
const { findPanel } = doMount(
<ExpandCollapse open={['panel-1']}>
<ExpandCollapse.Panel id="panel-1" header="First panel title">
First panel
</ExpandCollapse.Panel>
<ExpandCollapse.Panel id="panel-2" header="Second panel title">
Second panel
</ExpandCollapse.Panel>
{aPanel({ id: 'panel-1' })}
{aPanel({ id: 'panel-2' })}
</ExpandCollapse>
)

Expand All @@ -79,28 +117,20 @@ describe('ExpandCollapse', () => {
})

it('opens and closes panels by clicking them', () => {
const { findPanel, togglePanel } = doMount(
<ExpandCollapse>
<ExpandCollapse.Panel id="panel-1" header="First panel title">
First panel
</ExpandCollapse.Panel>
</ExpandCollapse>
const { findPanel, clickPanel } = doMount(
<ExpandCollapse>{aPanel({ id: 'panel-1' })}</ExpandCollapse>
)

togglePanel('panel-1')
clickPanel('panel-1')
expectPanelToBeOpen(findPanel('panel-1'))

togglePanel('panel-1')
clickPanel('panel-1')
expectPanelToBeClosed(findPanel('panel-1'))
})

it('lets a parent component control the open and closed panels', () => {
const { expandCollapse, findPanel } = doMount(
<ExpandCollapse>
<ExpandCollapse.Panel id="panel-1" header="First panel title">
First panel
</ExpandCollapse.Panel>
</ExpandCollapse>
<ExpandCollapse>{aPanel({ id: 'panel-1' })}</ExpandCollapse>
)

expandCollapse.setProps({ open: ['panel-1'] })
Expand All @@ -113,15 +143,11 @@ describe('ExpandCollapse', () => {
it('triggers individual panel callbacks when panels are opened and closed', () => {
const onPanelToggle = jest.fn()

const { expandCollapse, togglePanel } = doMount(
<ExpandCollapse>
<ExpandCollapse.Panel id="panel-1" header="First panel title" onToggle={onPanelToggle}>
First panel
</ExpandCollapse.Panel>
</ExpandCollapse>
const { expandCollapse, clickPanel } = doMount(
<ExpandCollapse>{aPanel({ id: 'panel-1', onToggle: onPanelToggle })}</ExpandCollapse>
)

togglePanel('panel-1')
clickPanel('panel-1')
expect(onPanelToggle).toHaveBeenCalledWith(true)

expandCollapse.setProps({ open: [] })
Expand All @@ -133,25 +159,21 @@ describe('ExpandCollapse', () => {
it('triggers a callback when any panel is opened or closed', () => {
const onToggle = jest.fn()

const { togglePanel } = doMount(
const { clickPanel } = doMount(
<ExpandCollapse onToggle={onToggle}>
<ExpandCollapse.Panel id="panel-1" header="First panel title">
First panel
</ExpandCollapse.Panel>
<ExpandCollapse.Panel id="panel-2" header="Second panel title">
Second panel
</ExpandCollapse.Panel>
{aPanel({ id: 'panel-1' })}
{aPanel({ id: 'panel-2' })}
</ExpandCollapse>
)

togglePanel('panel-1')
clickPanel('panel-1')
// TODO: Can I convert back to an array?
expect(onToggle).toHaveBeenCalledWith(new Set(['panel-1']))

// TODO: Can I just clear the onToggle mock?
jest.clearAllMocks()

togglePanel('panel-2')
clickPanel('panel-2')
expect(onToggle).toHaveBeenCalledWith(new Set(['panel-1', 'panel-2']))
})
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ exports[`ExpandCollapse renders 1`] = `
last={true}
onClick={[Function]}
open={true}
panelHeader="First panel title"
panelHeader="Panel title"
panelId="panel-1"
panelOnToggle={undefined}
panelSubtext={undefined}
>
<div
data-testid="panel-1"
Expand Down Expand Up @@ -138,18 +139,44 @@ exports[`ExpandCollapse renders 1`] = `
</Translate>
</div>
</Box>
<Text
block={false}
bold={false}
invert={false}
size="medium"
<Flexbox
dangerouslyAddClassName={undefined}
direction="column"
>
<span
className="medium mediumFont color"
<div
className="column"
>
First panel title
</span>
</Text>
<Box
all={undefined}
bottom={2}
dangerouslyAddClassName={undefined}
horizontal={undefined}
inline={false}
left={undefined}
right={undefined}
spacing="margin"
top={undefined}
vertical={undefined}
>
<div
className="bottomMargin-2"
>
<Text
block={false}
bold={false}
invert={false}
size="medium"
>
<span
className="medium mediumFont color"
>
Panel title
</span>
</Text>
</div>
</Box>
</div>
</Flexbox>
</div>
</Flexbox>
</div>
Expand Down Expand Up @@ -216,11 +243,12 @@ exports[`ExpandCollapse renders 1`] = `
className="base baseFont color"
>
<Panel
header="First panel title"
header="Panel title"
id="panel-1"
onToggle={undefined}
subtext={undefined}
>
First panel
Panel content
</Panel>
</div>
</Text>
Expand Down

0 comments on commit 3c5f4c8

Please sign in to comment.