Navigation Menu

Skip to content

Commit

Permalink
feat(bs5): add accordion (#2105)
Browse files Browse the repository at this point in the history
* feat(bs5): add accordion

* chore(dep): upgrade react to 16.8.0

* refactor(accordion): use uceContext hook

* refactor(AccordionBody): change id to accordionId

* refactor(AccordionBody): make Tag only affect accordion-body element

* feature(Accordion): add UncontrolledAccordion
  • Loading branch information
phwebi committed Oct 27, 2021
1 parent f8b415f commit 1c09448
Show file tree
Hide file tree
Showing 24 changed files with 713 additions and 7 deletions.
149 changes: 149 additions & 0 deletions docs/lib/Components/AccordionPage.js
@@ -0,0 +1,149 @@
/* eslint react/no-multi-comp: 0, react/prop-types: 0 */
import React from 'react';
import { PrismCode } from 'react-prism';
import PageTitle from '../UI/PageTitle';
import SectionTitle from '../UI/SectionTitle';

import AccordionExample from '../examples/Accordion';
const AccordionExampleSource = require('!!raw-loader!../examples/Accordion');

import UncontrolledAccordionExample from '../examples/UncontrolledAccordion';
const UncontrolledAccordionExampleSource = require('!!raw-loader!../examples/UncontrolledAccordion');

export default class AccordionPage extends React.Component {
render() {
return (
<div>
<PageTitle title="Accordion" />
<div className="docs-example">
<AccordionExample />
</div>
<pre>
<PrismCode className="language-jsx">
{ AccordionExampleSource}
</PrismCode>
</pre>
<SectionTitle>Properties</SectionTitle>
<pre>
<PrismCode className="language-jsx">
{`Accordion.propTypes = {
openId: Proptypes.string.isRequired,
toggle: Proptypes.func.isRequired,
tag: tagPropType,
className: PropTypes.string,
cssModule: PropTypes.object,
innerRef: PropTypes.oneOfType([
PropTypes.object,
PropTypes.string,
PropTypes.func,
]),
children: PropTypes.node,
};
AccordionBody.propTypes = {
tag: tagPropType,
className: PropTypes.string,
cssModule: PropTypes.object,
innerRef: PropTypes.oneOfType([
PropTypes.object,
PropTypes.string,
PropTypes.func,
]),
children: PropTypes.node,
accordionId: PropTypes.string.isRequired,
};
AccordionHeader.propTypes = {
tag: tagPropType,
className: PropTypes.string,
cssModule: PropTypes.object,
innerRef: PropTypes.oneOfType([
PropTypes.object,
PropTypes.string,
PropTypes.func,
]),
children: PropTypes.node,
targetId: PropTypes.string.isRequired,
};
AccordionItem.propTypes = {
tag: tagPropType,
className: PropTypes.string,
cssModule: PropTypes.object,
innerRef: PropTypes.oneOfType([
PropTypes.object,
PropTypes.string,
PropTypes.func,
]),
children: PropTypes.node,
};
`}
</PrismCode>
</pre>
<div className="docs-example">
<UncontrolledAccordionExample />
</div>
<pre>
<PrismCode className="language-jsx">
{ UncontrolledAccordionExampleSource }
</PrismCode>
</pre>
<SectionTitle>Properties</SectionTitle>
<pre>
<PrismCode className="language-jsx">
{`UncontrolledAccordion.propTypes = {
tag: tagPropType,
className: PropTypes.string,
cssModule: PropTypes.object,
innerRef: PropTypes.oneOfType([
PropTypes.object,
PropTypes.string,
PropTypes.func,
]),
children: PropTypes.node,
};
AccordionBody.propTypes = {
tag: tagPropType,
className: PropTypes.string,
cssModule: PropTypes.object,
innerRef: PropTypes.oneOfType([
PropTypes.object,
PropTypes.string,
PropTypes.func,
]),
children: PropTypes.node,
accordionId: PropTypes.string.isRequired,
};
AccordionHeader.propTypes = {
tag: tagPropType,
className: PropTypes.string,
cssModule: PropTypes.object,
innerRef: PropTypes.oneOfType([
PropTypes.object,
PropTypes.string,
PropTypes.func,
]),
children: PropTypes.node,
targetId: PropTypes.string.isRequired,
};
AccordionItem.propTypes = {
tag: tagPropType,
className: PropTypes.string,
cssModule: PropTypes.object,
innerRef: PropTypes.oneOfType([
PropTypes.object,
PropTypes.string,
PropTypes.func,
]),
children: PropTypes.node,
};
`}
</PrismCode>
</pre>
</div>
);
}
}
4 changes: 4 additions & 0 deletions docs/lib/Components/index.js
Expand Up @@ -2,6 +2,10 @@ import React from 'react';
import Content from '../UI/Content';

const items = [
{
name: 'Accordion',
to: '/components/accordion/'
},
{
name: 'Alerts',
to: '/components/alerts/'
Expand Down
45 changes: 45 additions & 0 deletions docs/lib/examples/Accordion.js
@@ -0,0 +1,45 @@
import React, { useState } from 'react';
import { Accordion, AccordionBody, AccordionHeader, AccordionItem } from 'reactstrap';

const Example = (props) => {
const [openId, setOpenId] = useState();
const toggle = (id) => {
openId === id ? setOpenId(undefined) : setOpenId(id);
};

return (
<div>
<Accordion openId={openId} toggle={toggle}>
<AccordionItem>
<AccordionHeader targetId="1">
Accordion Item 1
</AccordionHeader>
<AccordionBody accordionId="1">
<strong>This is the first item's accordion body.</strong>
You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow.
</AccordionBody>
</AccordionItem>
<AccordionItem>
<AccordionHeader targetId="2">
Accordion Item 2
</AccordionHeader>
<AccordionBody accordionId="2">
<strong>This is the second item's accordion body.</strong>
You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow.
</AccordionBody>
</AccordionItem>
<AccordionItem>
<AccordionHeader targetId="3">
Accordion Item 3
</AccordionHeader>
<AccordionBody accordionId="3">
<strong>This is the third item's accordion body.</strong>
You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow.
</AccordionBody>
</AccordionItem>
</Accordion>
</div>
);
};

export default Example;
40 changes: 40 additions & 0 deletions docs/lib/examples/UncontrolledAccordion.js
@@ -0,0 +1,40 @@
import React from 'react';
import { UncontrolledAccordion, AccordionBody, AccordionHeader, AccordionItem } from 'reactstrap';

const Example = (props) => {
return (
<div>
<UncontrolledAccordion>
<AccordionItem>
<AccordionHeader targetId="1">
Accordion Item 1
</AccordionHeader>
<AccordionBody accordionId="1">
<strong>This is the first item's accordion body.</strong>
You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow.
</AccordionBody>
</AccordionItem>
<AccordionItem>
<AccordionHeader targetId="2">
Accordion Item 2
</AccordionHeader>
<AccordionBody accordionId="2">
<strong>This is the second item's accordion body.</strong>
You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow.
</AccordionBody>
</AccordionItem>
<AccordionItem>
<AccordionHeader targetId="3">
Accordion Item 3
</AccordionHeader>
<AccordionBody accordionId="3">
<strong>This is the third item's accordion body.</strong>
You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow.
</AccordionBody>
</AccordionItem>
</UncontrolledAccordion>
</div>
);
};

export default Example;
4 changes: 3 additions & 1 deletion docs/lib/routes.js
Expand Up @@ -17,6 +17,7 @@ import PopoversPage from './Components/PopoversPage';
import ProgressPage from './Components/ProgressPage';
import TooltipsPage from './Components/TooltipsPage';
import BadgePage from './Components/BadgePage';
import AccordionPage from './Components/AccordionPage';
import MediaPage from './Components/MediaPage';
import ModalsPage from './Components/ModalsPage';
import CardPage from './Components/CardPage';
Expand All @@ -41,7 +42,8 @@ const routes = (
<Route path="/" component={UI.Layout}>
<IndexRoute component={Home} />
<Route path="/components/" component={Components}>
<IndexRedirect to="alerts/" />
<IndexRedirect to="accordion/" />
<Route path="accordion/" component={AccordionPage} />
<Route path="breadcrumbs/" component={BreadcrumbsPage} />
<Route path="buttons/" component={ButtonsPage} />
<Route path="button-group/" component={ButtonGroupPage} />
Expand Down
4 changes: 2 additions & 2 deletions package.json
Expand Up @@ -267,7 +267,7 @@
"react-transition-group": "^3.0.0"
},
"peerDependencies": {
"react": ">=16.3.0",
"react": ">=16.8.0",
"react-dom": ">=16.3.0"
},
"devDependencies": {
Expand Down Expand Up @@ -306,7 +306,7 @@
"mini-css-extract-plugin": "^1.4.0",
"ncp": "^2.0.0",
"raw-loader": "^1.0.0",
"react": "^16.3.2",
"react": "^16.8.0",
"react-app-rewired": "^1.6.2",
"react-dom": "^16.3.2",
"react-helmet": "^5.0.3",
Expand Down
55 changes: 55 additions & 0 deletions src/Accordion.js
@@ -0,0 +1,55 @@
import React, { useMemo } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { mapToCssModules, tagPropType } from './utils';
import { AccordionContext } from './AccordionContext';

const propTypes = {
tag: tagPropType,
className: PropTypes.string,
cssModule: PropTypes.object,
innerRef: PropTypes.oneOfType([
PropTypes.object,
PropTypes.string,
PropTypes.func,
]),
children: PropTypes.node,
openId: PropTypes.string.isRequired,
toggle: PropTypes.func.isRequired,
};

const defaultProps = {
tag: 'div'
};

const Accordion = (props) => {
const {
openId,
toggle,
className,
cssModule,
tag: Tag,
innerRef,
...attributes
} = props;
const classes = mapToCssModules(classNames(
className,
'accordion',
), cssModule);

const accordionContext = useMemo(() => ({
openId,
toggle,
}));

return (
<AccordionContext.Provider value={accordionContext}>
<Tag {...attributes} className={classes} ref={innerRef} />
</AccordionContext.Provider>
);
};

Accordion.propTypes = propTypes;
Accordion.defaultProps = defaultProps;

export default Accordion;
53 changes: 53 additions & 0 deletions src/AccordionBody.js
@@ -0,0 +1,53 @@
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { mapToCssModules, tagPropType } from './utils';
import Collapse from './Collapse';
import { AccordionContext } from './AccordionContext';

const propTypes = {
tag: tagPropType,
className: PropTypes.string,
cssModule: PropTypes.object,
innerRef: PropTypes.oneOfType([
PropTypes.object,
PropTypes.string,
PropTypes.func,
]),
children: PropTypes.node,
accordionId: PropTypes.string.isRequired,
};

const defaultProps = {
tag: 'div'
};

const AccordionItem = (props) => {
const {
className,
cssModule,
tag: Tag,
innerRef,
children,
accordionId,
...attributes
} = props;

const { openId } = useContext(AccordionContext);

const classes = mapToCssModules(classNames(
className,
'accordion-collapse',
), cssModule);

return (
<Collapse {...attributes} className={classes} ref={innerRef} isOpen={openId === accordionId}>
<Tag className="accordion-body">{children}</Tag>
</Collapse>
);
};

AccordionItem.propTypes = propTypes;
AccordionItem.defaultProps = defaultProps;

export default AccordionItem;

0 comments on commit 1c09448

Please sign in to comment.