Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
structure
  • Loading branch information
mcasimir committed May 14, 2025
commit cd2e10bba4cc638dc3749393650973be3b7e44b1
26 changes: 25 additions & 1 deletion .cursor/rules/storybook.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,30 @@ alwaysApply: false
- Check the usage of the component in the rest of the app to see how it is used in different contexts.
- Generate stories in MDX format, in a file next to the component file: example: button.tsx -> button.stories.mdx
- Add documentation and examples that clarify how to use the component and its purpose. Avoid generic documentation, focus on the component's purpose and how to use it. Assume an expert user who already knows general concepts of React and UI design.
- Remember mdx already includes "React" as a package, so you don't need to import it.
- DO NOT ADD "import React from 'react'; on MDX files, is already available and causes an error.
- Examples should be interactive, and show the component in action
- Examples should let people view the code

Typical structure of a story

# Component name

Component description, general explanation of its purpose

## Usage

Example of code

## When to use

- list of appropriate use cases

## When not to use

- (if applicable) list of cases where is better to avoid this component, and alternatives

## Examples

...

## Props
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { Meta, Story, Controls } from '@storybook/blocks';
import { DropdownMenuButton } from './dropdown-menu-button';
import { Icon } from '../leafygreen';

<Meta
title="Components/Actions/DropdownMenuButton"
component={DropdownMenuButton}
parameters={{ layout: 'centered' }}
argTypes={{
actions: {
control: 'object',
description: 'Array of menu actions',
},
onAction: {
action: 'action selected',
description: 'Callback when an action is selected',
},
buttonText: {
control: 'text',
description: 'Text to display on the button',
},
buttonProps: {
control: 'object',
description: 'Props to pass to the underlying button component',
},
isVisible: {
control: 'boolean',
description: 'Whether the button is visible',
},
activeAction: {
control: 'text',
description: 'Currently active action',
},
iconSize: {
control: 'select',
options: ['small', 'default', 'large'],
description: 'Size of the action icons',
},
hideOnNarrow: {
control: 'boolean',
description: 'Whether to hide the button text on narrow screens',
},
}}
/>

# DropdownMenuButton

The `DropdownMenuButton` component is a button that opens a dropdown menu of actions. It's useful for grouping related actions under a single button with a label.

## When to use

- When you need to show a labeled button that opens a menu of actions
- When you want to emphasize a primary action while providing access to related actions
- When you need to show actions in a toolbar or header with a clear label
- When you want to maintain a consistent button style while providing multiple options

## When not to use

- When you only need a single action (use a regular button)
- When you want to show actions directly in the interface (use `ItemActionGroup`)
- When you need a more compact menu without a button label (use `ItemActionMenu`)

## Examples

### Basic Usage

<Story name="Basic">
{() => {
const actions = [
{ action: 'edit', label: 'Edit', icon: <Icon glyph="Edit" /> },
{ action: 'duplicate', label: 'Duplicate', icon: <Icon glyph="Copy" /> },
{
action: 'delete',
label: 'Delete',
icon: <Icon glyph="Trash" />,
variant: 'destructive',
},
];
return (
<DropdownMenuButton
buttonText="Actions"
actions={actions}
onAction={(action) => alert(`Action: ${action}`)}
buttonProps={{ size: 'default' }}
/>
);
}}
</Story>

### With Active Action

<Story name="WithActiveAction">
{() => {
const actions = [
{ action: 'view', label: 'View', icon: <Icon glyph="Visibility" /> },
{ action: 'edit', label: 'Edit', icon: <Icon glyph="Edit" /> },
{
action: 'delete',
label: 'Delete',
icon: <Icon glyph="Trash" />,
variant: 'destructive',
},
];
return (
<DropdownMenuButton
buttonText="View Options"
actions={actions}
activeAction="view"
onAction={(action) => alert(`Action: ${action}`)}
buttonProps={{ size: 'default' }}
/>
);
}}
</Story>

### With Custom Button Props

<Story name="WithCustomButtonProps">
{() => {
const actions = [
{ action: 'save', label: 'Save', icon: <Icon glyph="Save" /> },
{ action: 'save-as', label: 'Save As...', icon: <Icon glyph="SaveAs" /> },
{ action: 'export', label: 'Export', icon: <Icon glyph="Download" /> },
];
return (
<DropdownMenuButton
buttonText="Save"
actions={actions}
onAction={(action) => alert(`Action: ${action}`)}
buttonProps={{
size: 'default',
variant: 'primary',
leftGlyph: <Icon glyph="Save" />,
}}
/>
);
}}
</Story>

## Props

<Controls />
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { Meta, Story, Controls } from '@storybook/blocks';
import { ItemActionControls } from './item-action-controls';
import { Icon } from '../leafygreen';

<Meta
title="Components/Actions/ItemActionControls"
component={ItemActionControls}
parameters={{ layout: 'centered' }}
argTypes={{
actions: {
control: 'object',
description: 'Array of actions to display',
},
onAction: {
action: 'action selected',
description: 'Callback when an action is selected',
},
isVisible: {
control: 'boolean',
description: 'Whether the controls are visible',
},
collapseAfter: {
control: 'number',
description:
'Number of actions to show before collapsing others into a menu',
},
collapseToMenuThreshold: {
control: 'number',
description:
'Number of actions that triggers collapsing all actions into a menu',
},
iconSize: {
control: 'select',
options: ['small', 'default', 'large'],
description: 'Size of the action icons',
},
}}
/>

# ItemActionControls

The `ItemActionControls` component is a smart container that automatically manages how actions are displayed based on the number of actions and available space. It can show actions as buttons or collapse them into a menu when needed.

## When to use

- When you have a variable number of actions that might need to be collapsed
- When you want to show some actions prominently while keeping others in a menu
- When you need to adapt the UI based on the number of actions
- When you want to maintain a consistent layout regardless of the number of actions

## When not to use

- When you always want to show all actions as buttons (use `ItemActionGroup`)
- When you always want to show all actions in a menu (use `ItemActionMenu`)
- When you have a fixed, small number of actions

## Examples

### Basic Usage (Shows as Buttons)

<Story name="BasicButtons">
{() => {
const actions = [
{ action: 'edit', label: 'Edit', icon: <Icon glyph="Edit" /> },
{
action: 'delete',
label: 'Delete',
icon: <Icon glyph="Trash" />,
variant: 'destructive',
},
];
return (
<ItemActionControls
actions={actions}
onAction={(action) => alert(`Action: ${action}`)}
collapseToMenuThreshold={3}
/>
);
}}
</Story>

### Collapsed to Menu

<Story name="CollapsedToMenu">
{() => {
const actions = [
{ action: 'edit', label: 'Edit', icon: <Icon glyph="Edit" /> },
{ action: 'duplicate', label: 'Duplicate', icon: <Icon glyph="Copy" /> },
{
action: 'delete',
label: 'Delete',
icon: <Icon glyph="Trash" />,
variant: 'destructive',
},
];
return (
<ItemActionControls
actions={actions}
onAction={(action) => alert(`Action: ${action}`)}
collapseToMenuThreshold={2}
/>
);
}}
</Story>

### Mixed Display (Some Buttons, Some in Menu)

<Story name="MixedDisplay">
{() => {
const actions = [
{ action: 'edit', label: 'Edit', icon: <Icon glyph="Edit" /> },
{ action: 'duplicate', label: 'Duplicate', icon: <Icon glyph="Copy" /> },
{ action: 'share', label: 'Share', icon: <Icon glyph="Share" /> },
{
action: 'delete',
label: 'Delete',
icon: <Icon glyph="Trash" />,
variant: 'destructive',
},
];
return (
<ItemActionControls
actions={actions}
onAction={(action) => alert(`Action: ${action}`)}
collapseAfter={2}
/>
);
}}
</Story>

## Props

<Controls />
Loading