Skip to content

Commit

Permalink
Merge pull request #231 from oaknational/fix/chevron-accordian-size
Browse files Browse the repository at this point in the history
Fix/chevron accordian size
  • Loading branch information
kimon-satan authored Jul 17, 2024
2 parents 6343341 + 8d17f24 commit 9b77142
Show file tree
Hide file tree
Showing 13 changed files with 253 additions and 257 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,32 @@ import { StoryObj, Meta } from "@storybook/react";
import {
InternalAccordionButton,
InternalAccordionContent,
InternalAccordion,
} from "./InternalAccordion";
import { InternalAccordionProvider } from "./InternalAccordionProvider";

const meta: Meta<typeof InternalAccordion> = {
import { OakFlex } from "@/components/atoms/OakFlex";

const meta: Meta<typeof InternalAccordionProvider> = {
title: "Components/atoms/InternalAccordion",
component: InternalAccordion,
component: InternalAccordionProvider,
tags: ["autodocs"],
argTypes: {},
parameters: {
controls: {
include: ["type"],
},
},
};

export default meta;

type Story = StoryObj<typeof InternalAccordion>;
type Story = StoryObj<typeof InternalAccordionProvider>;

export const Default: Story = {
render: (args) => (
<InternalAccordionProvider isInitialOpen={true}>
<InternalAccordion {...args}>
<InternalAccordionButton {...args}>
render: () => (
<InternalAccordionProvider isInitialOpen={false}>
<OakFlex $flexDirection={"column"}>
<InternalAccordionButton id={"generic-accordion"}>
accordion button
</InternalAccordionButton>
<InternalAccordionContent {...args}>
<InternalAccordionContent aria-labelledby={"generic-accordion"}>
accordion content
</InternalAccordionContent>
</InternalAccordion>
</OakFlex>
</InternalAccordionProvider>
),
args: {
id: "generic-accordion",
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,26 @@ import "@testing-library/jest-dom";
import { create } from "react-test-renderer";

import {
InternalAccordion,
InternalAccordionButton,
InternalAccordionContent,
} from "./InternalAccordion";
import AccordionProvider from "./InternalAccordionProvider";

import renderWithTheme from "@/test-helpers/renderWithTheme";
import { OakFlex } from "@/components/atoms/OakFlex";

describe("InternalAccordion", () => {
it("renders", () => {
const { getByTestId } = renderWithTheme(
<AccordionProvider isInitialOpen={true}>
<InternalAccordion id={"internal-accordion"} data-testid="test">
<OakFlex id={"internal-accordion"} data-testid="test">
<InternalAccordionButton id={"internal-accordion"}>
accordion button
</InternalAccordionButton>
<InternalAccordionContent id={"internal-accordion"}>
<InternalAccordionContent aria-labelledby={"internal-accordion"}>
accordion content
</InternalAccordionContent>
</InternalAccordion>
</OakFlex>
</AccordionProvider>,
);
expect(getByTestId("test")).toBeInTheDocument();
Expand All @@ -31,14 +31,14 @@ describe("InternalAccordion", () => {
it("matches snapshot", () => {
const tree = create(
<AccordionProvider isInitialOpen={true}>
<InternalAccordion id={"internal-accordion"} data-testid="test">
<OakFlex id={"internal-accordion"} data-testid="test">
<InternalAccordionButton id={"internal-accordion"}>
accordion button
</InternalAccordionButton>
<InternalAccordionContent id={"internal-accordion"}>
<InternalAccordionContent aria-labelledby={"internal-accordion"}>
accordion content
</InternalAccordionContent>
</InternalAccordion>
</OakFlex>
</AccordionProvider>,
).toJSON();
expect(tree).toMatchSnapshot();
Expand Down
64 changes: 16 additions & 48 deletions src/components/atoms/InternalAccordion/InternalAccordion.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,11 @@
import React, { ReactNode } from "react";
import React from "react";
import styled from "styled-components";

import useAccordionContext from "./useAccordionContext";

import { OakBox, OakBoxProps, OakFlex, OakFlexProps } from "@/components/atoms";

export type InternalAccordionProps = {
/**
* The content of the accordion
*/
children: ReactNode;
/**
* The id of the accordion
*/
id: string;
};

const StyledButton = styled(OakFlex)`
const FlexWithReset = styled(OakFlex)`
font: inherit;
color: inherit;
border: none;
Expand All @@ -28,52 +17,41 @@ const StyledButton = styled(OakFlex)`

/**
*
* An accordion component that can be used to show/hide content
*
*
* Must call this coponent inside the AccordionProvider where you can also access the useAccordionContext hook
*
* Must use the InternalAccordionButton and InternalAccordionContent components as direct children of InternalAccordion
* Content which will appear and disappear
*
* Must appear as a sibling of InternalAccordionButton
*
*/

export const InternalAccordion = styled(OakBox)``;

const UnstyledAccordionContent = ({
const AccordionContent = ({
children,
id,
...styleProps
}: InternalAccordionProps & OakBoxProps) => {
...rest
}: OakBoxProps & { "aria-labelledby": string }) => {
const { isOpen } = useAccordionContext();

return (
<OakBox hidden={!isOpen} aria-labelledby={id} role="region" {...styleProps}>
<OakBox hidden={!isOpen} role="region" {...rest}>
{children}
</OakBox>
);
};

export const InternalAccordionContent = styled(AccordionContent)``;

/**
*
* An accordion component that can be used to show/hide content
*
* InternalAccordionContent is a child component of InternalAccordion and sibling of InternalAccordionButton
* User interface to toggle visibility of InternalAccordionContent
*
* The children of InternalAccordionContent will be hidden or shown based on the state of the InternalAccordionButton
* Must appear as a sibling of InternalAccordionContent
*
*/

export const InternalAccordionContent = styled(UnstyledAccordionContent)``;

const UnstyledAccordionButton = (
props: InternalAccordionProps & OakFlexProps & OakBoxProps,
) => {
const AccordionButton = (props: { id: string } & OakFlexProps) => {
const { children, id, ...rest } = props;
const { isOpen, setOpen } = useAccordionContext();

return (
<StyledButton
<FlexWithReset
as="button"
type="button"
onClick={() => setOpen(!isOpen)}
Expand All @@ -83,18 +61,8 @@ const UnstyledAccordionButton = (
{...rest}
>
{children}
</StyledButton>
</FlexWithReset>
);
};

/**
*
* An accordion component that can be used to show/hide content
*
* InternalAccordionButton is a child component of InternalAccordion and sibling of InternalAccordionContent
*
* The children of InternalAccordianButton will be used as the button to toggle the visibility of the InternalAccordionContent
*
*/

export const InternalAccordionButton = styled(UnstyledAccordionButton)``;
export const InternalAccordionButton = styled(AccordionButton)``;
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ type AccordionContext = {
};

export const accordionContext = createContext<AccordionContext | null>(null);
/**
*
* Decomposed component comprising InternalAccordionButton and InternalAccordionContent wrapped by InternalAccordionProvider
*
* - InternalAccordionButton will toggle InternalAccordionContent visibility
* - It is up to the user to arrange the InternalAccordionButton and InternalAccordionContent components in the desired order
*
*/

export const InternalAccordionProvider: FC<{
isInitialOpen: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,22 @@ exports[`InternalAccordion matches snapshot 1`] = `
font-family: Lexend,sans-serif;
}
.c1 {
.c2 {
font-family: Lexend,sans-serif;
}
.c1:hover {
.c2:hover {
cursor: pointer;
}
.c2 {
.c1 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
.c3 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
Expand All @@ -24,7 +31,7 @@ exports[`InternalAccordion matches snapshot 1`] = `
flex-grow: 1;
}
.c3 {
.c4 {
font: inherit;
color: inherit;
border: none;
Expand All @@ -37,13 +44,13 @@ exports[`InternalAccordion matches snapshot 1`] = `
}
<div
className="c0"
className="c0 c1"
data-testid="test"
id="internal-accordion"
>
<button
aria-expanded={true}
className="c1 c2 c3 "
className="c2 c3 c4 "
id="internal-accordion"
onClick={[Function]}
type="button"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import { useEffect, useRef } from "react";
import { createPortal } from "react-dom";
type ClientPortalInterface = {

type InternalClientPortalInterface = {
children: React.ReactNode;
show?: boolean;
onClose?: () => void;
};
const ClientPortal = ({ children, show }: ClientPortalInterface) => {

export const InternalClientPortal = ({
children,
show,
}: InternalClientPortalInterface) => {
const ref = useRef<Element | null>(null);

useEffect(() => {
ref.current = document.body;
}, []);
return show && ref.current ? createPortal(children, ref.current) : null;
};
export default ClientPortal;
1 change: 1 addition & 0 deletions src/components/atoms/InternalClientPortal/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./InternalClientPortal";
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import styled from "styled-components";

import { OakHandDrawnFocusUnderline } from "../OakHandDrawnFocusUnderline";

import { OakBoxProps, OakIcon, oakBoxCss } from "@/components/atoms";
import { OakBoxProps, OakFlex, OakIcon, oakBoxCss } from "@/components/atoms";
import {
InternalAccordion,
InternalAccordionButton,
InternalAccordionContent,
} from "@/components/atoms/InternalAccordion";
Expand Down Expand Up @@ -55,14 +54,13 @@ export const StyledAccordionButton = styled(
}
`;

const StyledAccordion = styled(InternalAccordion)<FlexStyleProps>`
const StyledContainer = styled(OakFlex)`
${StyledAccordionUnderline} {
visibility: hidden;
}
${StyledAccordionButton}:focus-visible ~ ${StyledAccordionUnderline} {
visibility: visible;
}
${oakBoxCss}
${flexStyle}
`;
Expand All @@ -80,10 +78,9 @@ const Accordion = ({
const { isOpen } = useAccordionContext();

return (
<StyledAccordion
<StyledContainer
$position={"relative"}
$pv={"inner-padding-s"}
$display={"flex"}
$flexDirection={"column"}
$gap={"all-spacing-1"}
{...styleProps}
Expand All @@ -92,31 +89,30 @@ const Accordion = ({
id={id}
$width={"100%"}
$justifyContent={"space-between"}
$alignItems={"center"}
>
{header}
<OakIcon
iconName="chevron-down"
$width="all-spacing-6"
$height="all-spacing-6"
alt=""
$width="all-spacing-7"
$height="all-spacing-7"
alt="An arrow to indicate whether the item is open or closed"
style={{
transform: isOpen ? "rotate(180deg)" : "none",
transition: "all 0.3s ease 0s",
}}
/>
</StyledAccordionButton>
<InternalAccordionContent id={id}>{children}</InternalAccordionContent>
<InternalAccordionContent aria-labelledby={id}>
{children}
</InternalAccordionContent>
<StyledAccordionUnderline $fill={"border-decorative5-stronger"} />
</StyledAccordion>
</StyledContainer>
);
};

/**
* An Internal accordion component that can be used to show/hide content
*
* It has a shadow and a hand-drawn underline effect and a chevron icon that rotates when the accordion is open
*
* other flex, box and color style proops can be passed to the accordion as props
* InternalChevronAccordion has a chevron icon that rotates when the accordion is open
*/

export const InternalChevronAccordion = (
Expand Down
Loading

0 comments on commit 9b77142

Please sign in to comment.