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
14 changes: 11 additions & 3 deletions packages/react/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright IBM Corp. 2016, 2025
* Copyright IBM Corp. 2016, 2026
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
Expand All @@ -16,17 +16,25 @@ import { mergeConfig } from 'vite';

const configDir = fileURLToPath(new URL('.', import.meta.url));

// We can't use .mdx files in conjuction with `storyStoreV7`, which we are using to preload stories for CI purposes only.
// MDX files are fine to ignore in CI mode since they don't make a difference for VRT testing
// Keep top level MDX docs pages in Storybook, but exclude component level docs
// directories from story discovery because those pages are referenced through
// colocated component docs instead of the main stories index.
//
// TODO: Remove `*.js` story support once React stories have been migrated to
// TypeScript.
const storyGlobs = [
'./Welcome/Welcome.mdx',
'../src/**/*.stories.js',
'../src/**/*.stories.tsx',
'../src/**/*.mdx',
'../src/components/Tile/Tile.mdx',
'../src/**/next/*.stories.js',
'../src/**/next/*.stories.tsx',
'../src/**/next/**/*.stories.js',
'../src/**/next/**/*.stories.tsx',
'../src/**/next/*.mdx',
'../src/**/*-story.js',
'../src/**/*-story.tsx',
'./Preview/Preview.mdx',
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@
* LICENSE file in the root directory of this source tree.
*/

/* eslint-disable no-console */

import React from 'react';
import React, { useState } from 'react';
import { action } from 'storybook/actions';
import './story.scss';
import { default as Accordion, AccordionItem, AccordionSkeleton } from '.';
import type { AccordionItemProps } from './AccordionItem';
import Button from '../Button';
import ButtonSet from '../ButtonSet';
import mdx from './Accordion.mdx';
import { WithLayer } from '../../../.storybook/templates/WithLayer';

type OnHeadingClickPayload = Parameters<
NonNullable<AccordionItemProps['onHeadingClick']>
>[0];

export default {
title: 'Components/Accordion',
component: Accordion,
Expand Down Expand Up @@ -72,7 +75,7 @@ const sharedArgs = {
isFlush: false,
ordered: false,
size: 'md',
onHeadingClick: ({ isOpen, event }) => {
onHeadingClick: ({ isOpen, event }: OnHeadingClickPayload) => {
action('onHeadingClick')({
isOpen,
type: event.type,
Expand Down Expand Up @@ -125,62 +128,83 @@ Default.args = { ...sharedArgs };
Default.argTypes = { ...sharedArgTypes };

export const Controlled = (args) => {
const [expandAll, setExpandAll] = React.useState(false);
const { onHeadingClick, ...restArgs } = args;
const accordionItemIds = ['plan', 'members', 'payment', 'review'] as const;
type AccordionItemId = (typeof accordionItemIds)[number];

const [openItems, setOpenItems] = useState(() => new Set<AccordionItemId>());

const handleHeadingClick =
(id: AccordionItemId) =>
({ isOpen, event }: OnHeadingClickPayload) => {
setOpenItems((prev) => {
const nextOpenItems = new Set(prev);

if (isOpen) {
nextOpenItems.add(id);
} else {
nextOpenItems.delete(id);
}

return nextOpenItems;
});

if (onHeadingClick) {
onHeadingClick({ isOpen, event });
}
};

return (
<>
<ButtonSet className={'controlled-accordion-btnset'}>
<Button
className={'controlled-accordion-btn'}
onClick={() => {
expandAll === true ? setExpandAll(1) : setExpandAll(true);
setOpenItems(new Set(accordionItemIds));
}}>
Click to expand all
Open all
</Button>
<Button
className={'controlled-accordion-btn'}
onClick={() => {
expandAll || expandAll === null
? setExpandAll(false)
: setExpandAll(null);
setOpenItems(new Set());
}}>
Click to collapse all
Close all
</Button>
</ButtonSet>

<Accordion {...restArgs}>
<AccordionItem
title="Choose your plan"
open={expandAll}
onHeadingClick={onHeadingClick}>
open={openItems.has('plan')}
onHeadingClick={handleHeadingClick('plan')}>
<p>
Compare plan features and select the option that best matches your
team&apos;s expected usage.
</p>
</AccordionItem>
<AccordionItem
title="Add team members"
open={expandAll}
onHeadingClick={onHeadingClick}>
open={openItems.has('members')}
onHeadingClick={handleHeadingClick('members')}>
<p>
Invite collaborators by email and assign their workspace roles
before launch.
</p>
</AccordionItem>
<AccordionItem
title="Set payment details"
open={expandAll}
onHeadingClick={onHeadingClick}>
open={openItems.has('payment')}
onHeadingClick={handleHeadingClick('payment')}>
<p>
Add billing information and choose whether to receive invoices by
email.
</p>
</AccordionItem>
<AccordionItem
title="Review and confirm"
open={expandAll}
onHeadingClick={onHeadingClick}>
open={openItems.has('review')}
onHeadingClick={handleHeadingClick('review')}>
<p>
Check your setup summary, then confirm to create the workspace for
your team.
Expand Down
Loading