Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 22 additions & 10 deletions src/components/Accordion/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,27 @@ import { MdFavorite } from 'react-icons/md';
</BBBAccordion>
```

### Accordion with a right-aligned button header

```jsx
import { BBBAccordion } from 'bbb-ui-components-react';
import { MdEdit } from 'react-icons/md';

<BBBAccordion title="Right-aligned" buttonHeader={<MdEdit />} buttonHeaderPosition="right">
<p>Content for the accordion.</p>
</BBBAccordion>
```

## Props

| Property | Type | Default | Description |
| ------------------ | -------------------------------------- | ----------- | ------------------------------------------------------------------------------ |
| `title` | `string` | | The text to be displayed in the accordion header. |
| `tooltipLabel` | `string` | `null` | An optional label for the tooltip that appears on hover. |
| `tooltipPlacement` | `import('@tippyjs/react').Placement` | `'bottom'` | The placement of the tooltip. |
| `ariaLabel` | `string` | | The accessible name for the expand button. |
| `ariaLabelledBy` | `string` | | The ID of the element that labels the expand button. |
| `ariaDescribedBy` | `string` | | The ID of the element that describes the expand button. |
| `buttonHeader` | `React.ReactNode` | `null` | Optional content to be rendered inside the button header. |
| `children` | `React.ReactNode` | | The content to be displayed when the accordion is expanded. |
| Property | Type | Default | Description |
| ----------------------- | -------------------------------------- | ----------- | ------------------------------------------------------------------------------ |
| `title` | `string` | | The text to be displayed in the accordion header. |
| `tooltipLabel` | `string` | `null` | An optional label for the tooltip that appears on hover. |
| `tooltipPlacement` | `import('@tippyjs/react').Placement` | `'bottom'` | The placement of the tooltip. |
| `ariaLabel` | `string` | | The accessible name for the expand button. |
| `ariaLabelledBy` | `string` | | The ID of the element that labels the expand button. |
| `ariaDescribedBy` | `string` | | The ID of the element that describes the expand button. |
| `buttonHeader` | `React.ReactNode` | `null` | Optional content to be rendered inside the button header. |
| `buttonHeaderPosition` | `'left' \| 'right'` | `'left'` | Position of `buttonHeader` within the header row. |
| `children` | `React.ReactNode` | | The content to be displayed when the accordion is expanded. |
31 changes: 30 additions & 1 deletion src/components/Accordion/component.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import BBBAccordion from './component';
import { TOOLTIP_PLACEMENT_VALUES, DEFAULT_TOOLTIP_PLACEMENT } from './constants';
import {
TOOLTIP_PLACEMENT_VALUES,
DEFAULT_TOOLTIP_PLACEMENT,
BUTTON_HEADER_POSITIONS,
BUTTON_HEADER_POSITION_VALUES,
DEFAULT_BUTTON_HEADER_POSITION,
} from './constants';
import { MdEdit } from 'react-icons/md';
import Typography from '../Typography/component';

const meta = {
Expand Down Expand Up @@ -44,6 +51,14 @@ const meta = {
control: false,
description: 'Optional React node rendered inside the button header.',
},
buttonHeaderPosition: {
control: 'select',
options: BUTTON_HEADER_POSITION_VALUES,
description: 'Position of `buttonHeader` within the header row.',
table: {
defaultValue: { summary: `${DEFAULT_BUTTON_HEADER_POSITION}` },
},
},
children: {
control: false,
description: 'Content shown when the accordion is expanded.',
Expand Down Expand Up @@ -79,3 +94,17 @@ export const WithTooltip: Story = {
),
},
};

/** Shows `buttonHeaderPosition="right"` pushing the button header to the far edge of the header row. */
export const WithRightAlignedButtonHeader: Story = {
args: {
title: 'Right-aligned Button Header',
buttonHeader: <MdEdit aria-label="Edit" />,
buttonHeaderPosition: BUTTON_HEADER_POSITIONS.RIGHT,
children: (
<div style={{ padding: '1rem' }}>
<Typography>Accordion content goes here.</Typography>
</div>
),
},
};
7 changes: 5 additions & 2 deletions src/components/Accordion/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as Styled from './styles';
import { MdExpandMore } from 'react-icons/md';
import Tippy from '@tippyjs/react';
import 'tippy.js/dist/tippy.css';
import { DEFAULT_TOOLTIP_PLACEMENT } from './constants';
import { DEFAULT_TOOLTIP_PLACEMENT, DEFAULT_BUTTON_HEADER_POSITION } from './constants';

/**
* A customizable Accordion component that allows expanding and collapsing content.
Expand All @@ -21,6 +21,7 @@ function Accordion({
ariaLabelledBy,
ariaDescribedBy,
buttonHeader = null,
buttonHeaderPosition = DEFAULT_BUTTON_HEADER_POSITION,
children,
}: AccordionProps): JSX.Element {
const [isExpanded, setIsExpanded] = useState(false);
Expand All @@ -38,7 +39,9 @@ function Accordion({
<MdExpandMore />
</Styled.ExpandIcon>
<Styled.TitleText>{title}</Styled.TitleText>
{buttonHeader}
<Styled.ButtonHeaderWrapper $position={buttonHeaderPosition}>
{buttonHeader}
</Styled.ButtonHeaderWrapper>
</Styled.ButtonContent>
</Styled.ExpandButton>
);
Expand Down
10 changes: 10 additions & 0 deletions src/components/Accordion/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,18 @@ const TOOLTIP_PLACEMENTS = {
const TOOLTIP_PLACEMENT_VALUES = Object.values(TOOLTIP_PLACEMENTS);
const DEFAULT_TOOLTIP_PLACEMENT = TOOLTIP_PLACEMENTS.TOP;

const BUTTON_HEADER_POSITIONS = {
LEFT: 'left',
RIGHT: 'right',
} as const;
const BUTTON_HEADER_POSITION_VALUES = Object.values(BUTTON_HEADER_POSITIONS);
const DEFAULT_BUTTON_HEADER_POSITION = BUTTON_HEADER_POSITIONS.LEFT;

export {
TOOLTIP_PLACEMENTS,
TOOLTIP_PLACEMENT_VALUES,
DEFAULT_TOOLTIP_PLACEMENT,
BUTTON_HEADER_POSITIONS,
BUTTON_HEADER_POSITION_VALUES,
DEFAULT_BUTTON_HEADER_POSITION,
}
8 changes: 7 additions & 1 deletion src/components/Accordion/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import styled from 'styled-components';
import { colorBackgroundLight, colorBrand1, colorTextDefault, colorWhite } from '../../stylesheets/palette';
import { fontSizeDefault } from '../../stylesheets/typography';
import { borderRadiusDefault, spacingMedium, spacingSmall } from '../../stylesheets/sizing';
import { StyledAccordionContent, StyledExpandIcon } from './types';
import { StyledAccordionContent, StyledExpandIcon, StyledButtonHeaderWrapper } from './types';

export const ExpandButton = styled.button`
display: flex;
Expand Down Expand Up @@ -51,6 +51,12 @@ export const ExpandIcon = styled.div<StyledExpandIcon>`
}
`;

export const ButtonHeaderWrapper = styled.span<StyledButtonHeaderWrapper>`
display: flex;
align-items: center;
margin-left: ${({ $position }) => ($position === 'right' ? 'auto' : '0')};
`;

export const TitleText = styled.span`
font-size: ${fontSizeDefault};
font-weight: 400;
Expand Down
10 changes: 9 additions & 1 deletion src/components/Accordion/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TOOLTIP_PLACEMENT_VALUES } from './constants';
import { TOOLTIP_PLACEMENT_VALUES, BUTTON_HEADER_POSITION_VALUES } from './constants';
import * as React from 'react';

export interface StyledExpandIcon {
Expand All @@ -10,7 +10,12 @@ export interface StyledAccordionContent {
$scrollHeight: number;
}

export interface StyledButtonHeaderWrapper {
$position: ButtonHeaderPositionType;
}

type TooltipPlacementType = typeof TOOLTIP_PLACEMENT_VALUES[number];
type ButtonHeaderPositionType = typeof BUTTON_HEADER_POSITION_VALUES[number];

export interface AccordionProps {
/** The text to be displayed in the accordion header. */
Expand All @@ -34,6 +39,9 @@ export interface AccordionProps {
/** Optional React node rendered inside the button header, alongside the title. @default null */
buttonHeader?: React.ReactNode;

/** Position of `buttonHeader` within the header row. @default 'left' */
buttonHeaderPosition?: ButtonHeaderPositionType;

/** Content shown when the accordion is expanded. */
children?: React.ReactNode;
};
Loading