---
name: Accordion
menu: Components
---

import PropsTable from 'website-src/components/PropsTable'
import { livePreviewStyle } from '../helpers/constants'
import Accordion from './Accordion'
import Text from '../Text/Text'
import cactusTheme from '@repay/cactus-theme'
import { LiveProvider, LiveEditor, LiveError, LivePreview } from 'react-live'

# Accordion

### Try it out

export const code = `<Accordion>
  <Accordion.Header>
    <Text as="h3"> My header</Text>
  </Accordion.Header>
  <Accordion.Body>Accordion Content Goes Here</Accordion.Body>
</Accordion>`

<LiveProvider code={code} scope={{ Accordion, Text }}>
  <LiveEditor style={livePreviewStyle} />
  <LiveError />
  <LivePreview />
</LiveProvider>

## Best practices

- If you need to group accordions together, make sure to place them inside the `Accordion.Provider` component, as it provides the keyboard accessibility features
  for groups of accordions.

## Basic usage

You can use a single Accordion by itself:

```jsx
import React from 'react'
import { Accordion, Text } from '@repay/cactus-web'

const SingleAccordion = props => {
  return (
    <Accordion>
      <Accordion.Header>
        <Text as="h3">My Header</Text>
      </Accordion.Header>
      <Accordion.Body>Accordion Content Goes Here</Accordion.Body>
    </Accordion>
  )
}
```

### Accordion Provider

You can also use `Accordion.Provider` to display multiple Accordions at the same time.  The provider
will manage the focus for you in an accessible way and allows for both controlled and uncontrolled
management of open/closed state.

To use a controlled provider, be sure to give each of your Accordions an `id` prop.  You can
then pass in an array of IDs to the `openId` prop of the provider.  You'll also want to pass an
`onChange` handler to respond to the toggling of Accordions.

If you'd rather use an uncontrolled provider, you won't need to pass in an `openId` prop, as the
provider will manage the open/closed state for you.  You can set the maximum number of Accordions
that can be open at the same time using the `maxOpen` prop, which defaults to 1.  You can also pass
`defaultOpen={true}` to any `Accordion` that you'd like to initialize in an open state.

Controlled Example:

```jsx
const ControlledAccordions = () => {
  const [open, setOpen] = useState<string[]>(['accordion2'])
  const toggleAccordion = (toggleId: string) => {
    setOpen((currentOpen) => {
      if (currentOpen.includes(toggleId)) {
        return currentOpen.filter((id) => id !== toggleId)
      }
      return [...currentOpen, toggleId]
    })
  }

  return (
    <Accordion.Provider openId={open} onChange={toggleAccordion}>
      <Accordion id="accordion1">
        <Accordion.Header>
          <Text as="h3">My Header 1</Text>
        </Accordion.Header>
        <Accordion.Body>Accordion Content Goes Here</Accordion.Body>
      </Accordion>
      <Accordion id="accordion2">
        <Accordion.Header>
          <Text as="h3">My Header 2</Text>
        </Accordion.Header>
        <Accordion.Body>I default to open because "openId" was initialized with my ID.</Accordion.Body>
      </Accordion>
      <Accordion id="accordion3">
        <Accordion.Header>
          <Text as="h3">My Header 3</Text>
        </Accordion.Header>
        <Accordion.Body>Accordion Content Goes Here</Accordion.Body>
      </Accordion>
    </Accordion.Provider>
  )
}
```

Uncontrolled Example:

```jsx
import React from 'react'
import { Accordion, Text } from '@repay/cactus-web'

const MultipleAccordions = props => {
  return (
    <Accordion.Provider maxOpen={2}>
      <Accordion>
        <Accordion.Header>
          <Text as="h3">My Header 1</Text>
        </Accordion.Header>
        <Accordion.Body>Accordion Content Goes Here</Accordion.Body>
      </Accordion>
      <Accordion defaultOpen={true}>
        <Accordion.Header>
          <Text as="h3">My Header 2</Text>
        </Accordion.Header>
        <Accordion.Body>I default to open because "defaultOpen" was set to true.</Accordion.Body>
      </Accordion>
      <Accordion>
        <Accordion.Header>
          <Text as="h3">My Header 3</Text>
        </Accordion.Header>
        <Accordion.Body>Accordion Content Goes Here</Accordion.Body>
      </Accordion>
    </Accordion.Provider>
  )
}
```

### Outline Variant

We have included an `outline` variant which can be used to better depict hierarchical structures as well as separations in the page.

Usage:

```jsx
import React from 'react'
import { Accordion, Text } from '@repay/cactus-web'

const OutlineAccordion = props => {
  return (
    <Accordion variant="outline">
      <Accordion.Header>
        <Text as="h3">Example Header</Text>
      </Accordion.Header>
      <Accordion.Body>Example Content</Accordion.Body>
    </Accordion>
  )
}
```

### Render Prop

To allow further customization of the accordions, we've provided the option to pass a render function to the header. This function receives an object
which includes a variable indicating whether or not the accordion is open, as well as an id that can be placed on whichever element describes the accordion.

Usage:

```jsx
import React, { useState } from 'react'
import { Accordion, IconButton, Text } from '@repay/cactus-web'
import { ActionsDelete, NavigationCircleDown, NavigationCircleUp } from '@repay/cactus-icons'

const initialAccordions = [
  {
    header: 'Header 1',
    body: 'Body 1'
  },
  {
    header: 'Header 2'
    body: 'Body 2'
  }
]

const CustomAccordions = props => {
  const [accordions, setAccordions] = useState(initialAccordions)

  const handleUpClick = index => {
    accordionsCopy = [...accordions]
    const temp = accordionsCopy[index - 1]
    accordionsCopy[index - 1] = accordionsCopy[index]
    accordionsCopy[index] = temp
    setAccordions(accordionsCopy)
  }

  const handleDownClick = index => {
    accordionsCopy = [...accordions]
    const temp = accordionsCopy[index + 1]
    accordionsCopy[index + 1] = accordionsCopy[index]
    accordionsCopy[index] = temp
    setAccordions(accordionsCopy)
  }

  const handleDelete = index => {
    const accordionsCopy = [...accordions]
    accordionsCopy.splice(index, 1)
    setAccordions(accordionsCopy)
  }

  return (
    <Accordion.Provider>
      {accordions.map((acc, index) => (
        <Accordion variant="outline" key={acc.header}> render={({ isOpen, headerId }) => (
          <Flex alignItems="center" width="100%>
            <Text as="h3" id={headerId}>{acc.header}</Text>
            {isOpen && (
              <IconButton
                iconSize="medium"
                variant="danger"
                ml="auto"
                mr={4}
                label={`Delete ${acc.header}`}
                onClick={e => {
                  handleDelete(index)
                  e.stopPropagation()
                }}
              >
                <ActionsDelete aria-hidden="true" />
              </IconButton>
            )}
            <Flex
              alignItems="center"
              ml={isOpen ? 0 : 'auto'}
              pl={4}
              borderLeft="1px solid"
              borderLeftColor="lightContrast"
            >
              <IconButton
                iconSize="medium"
                mr={1}
                label={`Move ${acc.header} down`}
                disabled={index === accordions.length - 1}
                onClick={e => {
                  handleDownClick(index)
                  e.stopPropagation()
                }}
              >
                <NavigationCircleDown aria-hidden="true" />
              </IconButton>
              <IconButton
                iconSize="medium"
                label={`Move ${acc.header} up`}
                disabled={index === accordions.length - 1}
                onClick={e => {
                  handleUpClick(index)
                  e.stopPropagation()
                }}
              >
                <NavigationCircleUp aria-hidden="true" />
              </IconButton>
            </Flex>
          </Flex>
        )} />
      ))}
    </Accordion.Provider>
  )
}
```

## Properties

## Accordion

<PropsTable of={Accordion} />

## Accordion Header

<PropsTable of={Accordion} staticProp="AccordionHeader" />

## Accordion Body

<PropsTable of={Accordion} staticProp="AccordionBody" />

## Accordion Provider

<PropsTable of={Accordion} staticProp="AccordionProvider" />
