Create Accordion

This commit is contained in:
Angelos Chalaris
2019-03-02 10:46:34 +02:00
parent b5db1acc4b
commit c0177c01c4
3 changed files with 189 additions and 3 deletions

View File

@ -91,13 +91,14 @@ import ReactDOM from 'react-dom';
<details>
<summary>View contents</summary>
* [Accordion](#accordion)
* [Carousel](#carousel)
* [Collapse](#collapse)
* [CountDown](#countdown)
* [FileDrop](#filedrop)
* [Mailto](#mailto)
* [StarRating](#starrating)
* [Tab](#tab)
* [Tabs](#tabs)
* [Ticker](#ticker)
* [Toggle](#toggle)
* [Tooltip](#tooltip)
@ -771,6 +772,92 @@ ReactDOM.render(
## Visual
### Accordion
Renders an accordion menu with multiple collapsible content components.
Define an `AccordionItem` component, pass it to the `Accordion` and remove unnecessary nodes expect for `AccordionItem` by identifying the function's name in `props.children`.
Each `AccordionItem` component renders a `<button>` that is used to update the `Accordion` via the `props.handleClick` callback and the content of the component, passed down via `props.children`, while its appearance is determined by `props.isCollapsed` and based on `style`.
In the `Accordion` component, use the `React.useState()` hook to initialize the value of the `bindIndex` state variable to `props.defaultIndex`.
Use `Array.prototype.map` on the collected nodes to render the individual collapsiple elements.
Define `changeItem`, which will be executed when clicking an `AccordionItem`'s `<button>`.
`changeItem` executes the passed callback, `onItemClick` and updates `bindIndex` based on the clicked element.
```jsx
function AccordionItem(props) {
const style = {
collapsed: {
display: 'none'
},
expanded: {
display: 'block'
},
buttonStyle: {
display: 'block',
width: '100%'
}
};
return (
<div>
<button style={style.buttonStyle} onClick={() => props.handleClick()}>
{props.label}
</button>
<div
className="collapse-content"
style={props.isCollapsed ? style.collapsed : style.expanded}
aria-expanded={props.isCollapsed}
>
{props.children}
</div>
</div>
);
}
function Accordion(props) {
const [bindIndex, setBindIndex] = React.useState(props.defaultIndex);
const changeItem = itemIndex => {
if (typeof props.onItemClick === 'function') props.onItemClick(itemIndex);
if (itemIndex !== bindIndex) setBindIndex(itemIndex);
};
const items = props.children.filter(item => item.type.name === 'AccordionItem');
return (
<div className="wrapper">
{items.map(({ props }) => (
<AccordionItem
isCollapsed={bindIndex === props.index}
label={props.label}
handleClick={() => changeItem(props.index)}
children={props.children}
/>
))}
</div>
);
}
```
<details>
<summary>Examples</summary>
```jsx
ReactDOM.render(
<Accordion defaultIndex="1" onItemClick={console.log}>
<AccordionItem label="A" index="1">
Lorem ipsum
</AccordionItem>
<AccordionItem label="B" index="2">
Dolor sit amet
</AccordionItem>
</Accordion>,
document.getElementById('root')
);
```
</details>
<br>[⬆ Back to top](#table-of-contents)
### Carousel
Renders a carousel component.
@ -1177,7 +1264,7 @@ ReactDOM.render(<StarRating rating={2} />, document.getElementById('root'));
<br>[⬆ Back to top](#table-of-contents)
### Tab
### Tabs
Renders a tabbed menu and view component.

View File

@ -1,4 +1,20 @@
[
{
"name": "Accordion.md",
"title": "Accordion",
"text": "Renders an accordion menu with multiple collapsible content components.\n\nDefine an `AccordionItem` component, pass it to the `Accordion` and remove unnecessary nodes expect for `AccordionItem` by identifying the function's name in `props.children`.\nEach `AccordionItem` component renders a `<button>` that is used to update the `Accordion` via the `props.handleClick` callback and the content of the component, passed down via `props.children`, while its appearance is determined by `props.isCollapsed` and based on `style`.\nIn the `Accordion` component, use the `React.useState()` hook to initialize the value of the `bindIndex` state variable to `props.defaultIndex`.\nUse `Array.prototype.map` on the collected nodes to render the individual collapsiple elements.\nDefine `changeItem`, which will be executed when clicking an `AccordionItem`'s `<button>`.\n`changeItem` executes the passed callback, `onItemClick` and updates `bindIndex` based on the clicked element.\n\n",
"codeBlocks": [
"```jsx\nfunction AccordionItem(props) {\n const style = {\n collapsed: {\n display: 'none'\n },\n expanded: {\n display: 'block'\n },\n buttonStyle: {\n display: 'block',\n width: '100%'\n }\n };\n\n return (\n <div>\n <button style={style.buttonStyle} onClick={() => props.handleClick()}>\n {props.label}\n </button>\n <div\n className=\"collapse-content\"\n style={props.isCollapsed ? style.collapsed : style.expanded}\n aria-expanded={props.isCollapsed}\n >\n {props.children}\n </div>\n </div>\n );\n}\n\nfunction Accordion(props) {\n const [bindIndex, setBindIndex] = React.useState(props.defaultIndex);\n\n const changeItem = itemIndex => {\n if (typeof props.onItemClick === 'function') props.onItemClick(itemIndex);\n if (itemIndex !== bindIndex) setBindIndex(itemIndex);\n };\n const items = props.children.filter(item => item.type.name === 'AccordionItem');\n\n return (\n <div className=\"wrapper\">\n {items.map(({ props }) => (\n <AccordionItem\n isCollapsed={bindIndex === props.index}\n label={props.label}\n handleClick={() => changeItem(props.index)}\n children={props.children}\n />\n ))}\n </div>\n );\n}\n```",
"```jsx\nReactDOM.render(\n <Accordion defaultIndex=\"1\" onItemClick={console.log}>\n <AccordionItem label=\"A\" index=\"1\">\n Lorem ipsum\n </AccordionItem>\n <AccordionItem label=\"B\" index=\"2\">\n Dolor sit amet\n </AccordionItem>\n </Accordion>,\n document.getElementById('root')\n);\n```"
],
"expertise": 2,
"tags": [
"visual",
"children",
"state"
],
"notes": []
},
{
"name": "AutoLink.md",
"title": "AutoLink",
@ -266,7 +282,7 @@
},
{
"name": "Tabs.md",
"title": "Tab",
"title": "Tabs",
"text": "Renders a tabbed menu and view component.\n\nDefine a `TabItem` component, pass it to the `Tab` and remove unnecessary nodes expect for `TabItem` by identifying the function's name in `props.children`.\nUse the `React.useState()` hook to initialize the value of the `bindIndex` state variable to `props.defaultIndex`.\nUse `Array.prototype.map` on the collected nodes to render the `tab-menu` and `tab-view`.\nDefine `changeTab`, which will be executed when clicking a `<button>` from the `tab-menu`.\n`changeTab` executes the passed callback, `onTabClick` and updates `bindIndex`, which in turn causes a re-render, evaluating the `style` and `className` of the `tab-view` items and `tab-menu` buttons according to their `index`.\n\n",
"codeBlocks": [
"```css\n.tab-menu > button {\n cursor: pointer;\n padding: 8px 16px;\n border: 0;\n border-bottom: 2px solid transparent;\n background: none;\n}\n.tab-menu > button.focus {\n border-bottom: 2px solid #007bef;\n}\n.tab-menu > button:hover {\n border-bottom: 2px solid #007bef;\n}\n```",

83
snippets/Accordion.md Normal file
View File

@ -0,0 +1,83 @@
### Accordion
Renders an accordion menu with multiple collapsible content components.
Define an `AccordionItem` component, pass it to the `Accordion` and remove unnecessary nodes expect for `AccordionItem` by identifying the function's name in `props.children`.
Each `AccordionItem` component renders a `<button>` that is used to update the `Accordion` via the `props.handleClick` callback and the content of the component, passed down via `props.children`, while its appearance is determined by `props.isCollapsed` and based on `style`.
In the `Accordion` component, use the `React.useState()` hook to initialize the value of the `bindIndex` state variable to `props.defaultIndex`.
Use `Array.prototype.map` on the collected nodes to render the individual collapsiple elements.
Define `changeItem`, which will be executed when clicking an `AccordionItem`'s `<button>`.
`changeItem` executes the passed callback, `onItemClick` and updates `bindIndex` based on the clicked element.
```jsx
function AccordionItem(props) {
const style = {
collapsed: {
display: 'none'
},
expanded: {
display: 'block'
},
buttonStyle: {
display: 'block',
width: '100%'
}
};
return (
<div>
<button style={style.buttonStyle} onClick={() => props.handleClick()}>
{props.label}
</button>
<div
className="collapse-content"
style={props.isCollapsed ? style.collapsed : style.expanded}
aria-expanded={props.isCollapsed}
>
{props.children}
</div>
</div>
);
}
function Accordion(props) {
const [bindIndex, setBindIndex] = React.useState(props.defaultIndex);
const changeItem = itemIndex => {
if (typeof props.onItemClick === 'function') props.onItemClick(itemIndex);
if (itemIndex !== bindIndex) setBindIndex(itemIndex);
};
const items = props.children.filter(item => item.type.name === 'AccordionItem');
return (
<div className="wrapper">
{items.map(({ props }) => (
<AccordionItem
isCollapsed={bindIndex === props.index}
label={props.label}
handleClick={() => changeItem(props.index)}
children={props.children}
/>
))}
</div>
);
}
```
```jsx
ReactDOM.render(
<Accordion defaultIndex="1" onItemClick={console.log}>
<AccordionItem label="A" index="1">
Lorem ipsum
</AccordionItem>
<AccordionItem label="B" index="2">
Dolor sit amet
</AccordionItem>
</Accordion>,
document.getElementById('root')
);
```
<!-- tags: visual,children,state -->
<!-- expertise: 2 -->