import type { Meta, StoryObj } from '@storybook/nextjs-vite'; import { useState } from 'react'; import { UnsavedChangesChip, useGuardedClose } from '../components/ui/modal-guarded-close'; import { Button } from '../components/ui/button'; /** * Stories for `modal-guarded-close` — the unified dirty-state contract for * modals. The chip is the visual affordance; the `useConfirm` / * `useGuardedClose` hooks are exercised by the interactive story below (the * chip story file covers all three exports of the module). */ const meta = { title: 'UI/UnsavedChangesChip', component: UnsavedChangesChip, } satisfies Meta; export default meta; type Story = StoryObj; /** * The standing "Unsaved changes" signal for a dirty modal's footer — * `role="status"`, warning color, with the WHAT-is-dirty detail on hover. */ export const Default: Story = { args: { detail: '2 rules changed, 1 rule removed', }, }; /** * In its natural habitat: a form-modal footer between the status text and the * Cancel/Save buttons. */ export const InAFooterRow: Story = { args: { detail: 'Name and threshold edited', }, render: args => (
3 rules selected
), }; /** * `useGuardedClose` end-to-end: while dirty, Close asks the house confirm * (`useConfirm` + ModalV2) before discarding; while clean, it closes * immediately. Toggle the checkbox and hit Close to see both paths. */ export const GuardedCloseFlow: Story = { args: {}, render: () => , }; function GuardedCloseDemo() { const [dirty, setDirty] = useState(true); const [lastOutcome, setLastOutcome] = useState('never closed'); const { guardedClose, dialog } = useGuardedClose(dirty, () => setLastOutcome('closed (edits discarded)')); return (
{dirty && }
Last outcome: {lastOutcome} {dialog}
); }