Created
October 9, 2018 17:45
-
-
Save latviancoder/a5054fe3d97594fa4c215c491d3e1b81 to your computer and use it in GitHub Desktop.
Accordion
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component } from 'react'; | |
import scrollIntoView from 'scroll-into-view-if-needed'; | |
import classNames from 'classnames'; | |
import { Icon } from 'office-ui-fabric-react'; | |
import ContentBlock from '../contentBlock/ContentBlock.component'; | |
import s from './accordion.scss'; | |
type AccordionRenderParams = { | |
currentOpenedPanelIndex?: number; | |
onAccordionPanelClicked?: (ref: React.RefObject<HTMLDivElement>, panelIndex: number) => void; | |
}; | |
interface AccordionProps { | |
children: (params: AccordionRenderParams) => JSX.Element | Array<JSX.Element>; | |
defaultOpenedPanelIndex?: number; | |
} | |
interface AccordionState { | |
currentOpenedPanelIndex?: number; | |
} | |
class Accordion extends Component<AccordionProps, AccordionState> { | |
state: AccordionState = { | |
// We can initialize accordion with one panel already opened | |
currentOpenedPanelIndex: this.props.defaultOpenedPanelIndex || undefined | |
}; | |
onAccordionPanelClicked = (ref: React.RefObject<HTMLDivElement>, panelIndex: number) => { | |
this.setState( | |
{ | |
currentOpenedPanelIndex: (panelIndex === this.state.currentOpenedPanelIndex) | |
? undefined | |
: panelIndex | |
}, | |
() => { | |
// Scroll to opened accordion panel if it is not visible on screen and try to position it on the top of the screen | |
if (ref.current) { | |
scrollIntoView(ref.current, { block: 'nearest', scrollMode: 'if-needed' }); | |
} | |
} | |
); | |
} | |
render() { | |
return <div> | |
{this.props.children({ | |
currentOpenedPanelIndex: this.state.currentOpenedPanelIndex, | |
onAccordionPanelClicked: this.onAccordionPanelClicked | |
})} | |
</div>; | |
} | |
} | |
interface AccordionPanelProps { | |
title: string; | |
panelIndex: number; | |
isInvalid?: boolean; | |
onAccordionPanelClicked?: (ref: React.RefObject<HTMLDivElement>, panelIndex: number) => void; | |
currentOpenedPanelIndex?: number; | |
} | |
export class AccordionPanel extends Component<AccordionPanelProps> { | |
containerRef: React.RefObject<HTMLDivElement>; | |
constructor(props: AccordionPanelProps) { | |
super(props); | |
// Create reference to underlying DOM element | |
this.containerRef = React.createRef(); | |
} | |
render() { | |
const isOpen = this.props.currentOpenedPanelIndex === this.props.panelIndex; | |
return <div ref={this.containerRef}> | |
<ContentBlock | |
className={s.panel} | |
padding={false} | |
innerClassName={(!isOpen && this.props.isInvalid) ? s.invalidBlock : undefined} | |
> | |
<div | |
className={classNames( | |
s.title, | |
isOpen && s.titleIsOpened | |
)} | |
// Pass reference to DOM element to onClick callback | |
onClick={() => this.props.onAccordionPanelClicked | |
&& this.props.onAccordionPanelClicked(this.containerRef, this.props.panelIndex)} | |
> | |
{this.props.title} | |
<Icon iconName={isOpen ? 'ChevronUp' : 'ChevronDown'}/> | |
</div> | |
{isOpen && <div className={s.content}> | |
{this.props.children} | |
</div>} | |
</ContentBlock> | |
</div>; | |
} | |
} | |
export default Accordion; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment