Skip to content

Commit

Permalink
feat(accordion): ✨ add accordion components
Browse files Browse the repository at this point in the history
  • Loading branch information
navin-moorthy committed Aug 19, 2020
1 parent 11c1346 commit 1b4b61e
Show file tree
Hide file tree
Showing 10 changed files with 870 additions and 320 deletions.
72 changes: 38 additions & 34 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,40 +23,44 @@
"lint:package": "sort-package-json",
"storybook": "start-storybook -p 6006"
},
"dependencies": {},
"dependencies": {
"reakit": "^1.2.2",
"reakit-system": "^0.14.2",
"reakit-utils": "^0.14.2"
},
"devDependencies": {
"@babel/core": "^7.11.1",
"@commitlint/cli": "^9.1.2",
"@commitlint/config-conventional": "^9.1.2",
"@storybook/addon-a11y": "^6.0.10",
"@storybook/addon-actions": "^6.0.10",
"@storybook/addon-essentials": "^6.0.10",
"@storybook/addon-links": "^6.0.10",
"@storybook/addon-storysource": "^6.0.10",
"@storybook/react": "^6.0.10",
"@types/react": "^16.9.46",
"@types/react-dom": "^16.9.8",
"@typescript-eslint/eslint-plugin": "^3.9.0",
"@typescript-eslint/parser": "^3.9.0",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0",
"eslint": "^7.7.0",
"eslint-config-prettier": "^6.11.0",
"eslint-config-react-app": "^5.2.1",
"eslint-plugin-flowtype": "^5.2.0",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-jsx-a11y": "^6.3.1",
"eslint-plugin-prettier": "^3.1.4",
"eslint-plugin-react": "^7.20.6",
"eslint-plugin-react-hooks": "^4.0.8",
"gacp": "^2.10.0",
"husky": "^4.2.5",
"lint-staged": "^10.2.11",
"prettier": "^2.0.5",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-is": "^16.13.1",
"sort-package-json": "^1.44.0",
"typescript": "^3.9.7"
"@babel/core": "7.11.1",
"@commitlint/cli": "9.1.2",
"@commitlint/config-conventional": "9.1.2",
"@storybook/addon-a11y": "6.0.12",
"@storybook/addon-actions": "6.0.12",
"@storybook/addon-essentials": "6.0.12",
"@storybook/addon-links": "6.0.12",
"@storybook/addon-storysource": "6.0.12",
"@storybook/react": "6.0.12",
"@types/react": "16.9.46",
"@types/react-dom": "16.9.8",
"@typescript-eslint/eslint-plugin": "3.9.1",
"@typescript-eslint/parser": "3.9.1",
"babel-eslint": "10.1.0",
"babel-loader": "8.1.0",
"eslint": "7.7.0",
"eslint-config-prettier": "6.11.0",
"eslint-config-react-app": "5.2.1",
"eslint-plugin-flowtype": "5.2.0",
"eslint-plugin-import": "2.22.0",
"eslint-plugin-jsx-a11y": "6.3.1",
"eslint-plugin-prettier": "3.1.4",
"eslint-plugin-react": "7.20.6",
"eslint-plugin-react-hooks": "4.1.0",
"gacp": "2.10.0",
"husky": "4.2.5",
"lint-staged": "10.2.11",
"prettier": "2.0.5",
"react": "16.13.1",
"react-dom": "16.13.1",
"react-is": "16.13.1",
"sort-package-json": "1.44.0",
"typescript": "3.9.7"
}
}
66 changes: 66 additions & 0 deletions src/accordion/Accordion.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React from "react";
import { Meta } from "@storybook/react";

import { useAccordionState } from "./AccordionState";
import { Accordion } from "./Accordion";
import { AccordionItem } from "./AccordionItem";
import { AccordionTrigger } from "./AccordionTrigger";
import { AccordionPanel } from "./AccordionPanel";

export default {
title: "Component/Accordion",
} as Meta;

export const Default = () => {
const state = useAccordionState();

return (
<Accordion {...state}>
<AccordionItem {...state}>
<h3>
<AccordionTrigger {...state}>Trigger 1</AccordionTrigger>
</h3>
<AccordionPanel {...state}>Panel 1</AccordionPanel>
</AccordionItem>
<AccordionItem {...state}>
<h3>
<AccordionTrigger {...state}>Trigger 2</AccordionTrigger>
</h3>
<AccordionPanel {...state}>Panel 2</AccordionPanel>
</AccordionItem>
<AccordionItem {...state}>
<h3>
<AccordionTrigger {...state}>Trigger 3</AccordionTrigger>
</h3>
<AccordionPanel {...state}>Panel 4</AccordionPanel>
</AccordionItem>
</Accordion>
);
};

export const AllowMultiple = () => {
const state = useAccordionState({ allowMultiple: true });

return (
<Accordion {...state}>
<AccordionItem {...state}>
<h3>
<AccordionTrigger {...state}>Trigger 1</AccordionTrigger>
</h3>
<AccordionPanel {...state}>Panel 1</AccordionPanel>
</AccordionItem>
<AccordionItem {...state}>
<h3>
<AccordionTrigger {...state}>Trigger 2</AccordionTrigger>
</h3>
<AccordionPanel {...state}>Panel 2</AccordionPanel>
</AccordionItem>
<AccordionItem {...state}>
<h3>
<AccordionTrigger {...state}>Trigger 3</AccordionTrigger>
</h3>
<AccordionPanel {...state}>Panel 4</AccordionPanel>
</AccordionItem>
</Accordion>
);
};
13 changes: 13 additions & 0 deletions src/accordion/Accordion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { createComponent, createHook } from "reakit-system";
import { ACCORDION_KEYS } from "./__keys";

export const useAccordion = createHook<{}, {}>({
name: "AccordionPanel",
keys: ACCORDION_KEYS,
});

export const Accordion = createComponent({
as: "div",
memo: true,
useHook: useAccordion,
});
49 changes: 49 additions & 0 deletions src/accordion/AccordionItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import * as React from "react";
import { createComponent, createHook } from "reakit-system";
import {
unstable_IdHTMLProps,
unstable_IdOptions,
unstable_useId,
} from "reakit";
import { useForkRef } from "reakit-utils";

import { AccordionStateReturn } from "./AccordionState";
import { ACCORDION_KEYS } from "./__keys";

export type AccordionItemOptions = unstable_IdOptions & AccordionStateReturn;

export type AccordionItemHTMLProps = unstable_IdHTMLProps;

export const useAccordionItem = createHook<
AccordionItemOptions,
AccordionItemHTMLProps
>({
name: "AccordionItem",
compose: [unstable_useId],
keys: ACCORDION_KEYS,

useProps(options, { ref: htmlRef, ...htmlProps }) {
const ref = React.useRef<HTMLElement>(null);
const { id } = options;

React.useLayoutEffect(() => {
if (!id) return undefined;

options.registerItem?.({ id, ref });

/* eslint-disable-next-line react-hooks/exhaustive-deps */
}, [id]);

return { ref: useForkRef(ref, htmlRef), ...htmlProps };
},

useComposeProps(_, htmlProps) {
return { ...htmlProps };
},
});

export const AccordionItem = createComponent({
as: "div",
memo: true,
useHook: useAccordionItem,
});
60 changes: 60 additions & 0 deletions src/accordion/AccordionPanel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/* eslint-disable no-unused-expressions */
/* eslint-disable react-hooks/exhaustive-deps */

import * as React from "react";
import { createComponent, createHook } from "reakit-system";
import {
unstable_IdHTMLProps,
unstable_IdOptions,
unstable_useId,
} from "reakit";
import { useForkRef } from "reakit-utils";

import { AccordionStateReturn } from "./AccordionState";
import { ACCORDION_KEYS } from "./__keys";

export type AccordionPanelOptions = unstable_IdOptions & AccordionStateReturn;

export type AccordionPanelHTMLProps = unstable_IdHTMLProps;

export const useAccordionPanel = createHook<
AccordionPanelOptions,
AccordionPanelHTMLProps
>({
name: "AccordionPanel",
keys: ACCORDION_KEYS,
compose: [unstable_useId],

useProps(options, { ref: htmlRef, style: htmlStyle, ...htmlProps }) {
const ref = React.useRef<HTMLElement>(null);

const { id } = options;
const item = options.items.find(({ panel }) => panel?.id === id);
const buttonId = item?.button?.id;
const isOpen = item ? options.activeItems.includes(item.id) : false;

React.useEffect(() => {
if (!id) return undefined;

options.registerPanel?.({ id, ref });
}, [id]);

const style = {
display: `${isOpen ? "block" : "none"}`,
...htmlStyle,
};

return {
ref: useForkRef(ref, htmlRef),
role: "region",
"aria-labelledby": `${buttonId ? buttonId : undefined}`,
style,
...htmlProps,
};
},
});

export const AccordionPanel = createComponent({
as: "div",
useHook: useAccordionPanel,
});
Loading

0 comments on commit 1b4b61e

Please sign in to comment.