proposals/SuggestionCard/decisions.md26 lines · View on GitHub
Decisions
Its own component, not a variant of the agentic panel. The other design in the same set is a full-width investigation report on a page (Panel with an agentic border: title, summary, sections, a code diff, Discard / Approve). This is a decision prompt in the message stream: title, one-line summary, changes, Accept / Refine / Dismiss. Their anatomy and lifecycle differ; the card outlives the decision in the history and has resolved states. They share only small parts (Buttons, check lines, a file-diff row), not a component.
Built on the PDS Panel (default variant, condensed padding) for the border, radius and surface, so no container is redrawn. Everything sits in one inner wrapper so the Panel's own first-child heading rules don't resize the title.
Props:title (required), description, children (the changes), actions (slot), state, statusLabel, headingLevel. No text is hardcoded; the buttons and labels all come from the screen.
state: pending | accepted.pending shows actions. accepted replaces them with a result line (success-colored check plus statusLabel), inside a role='status' region that is always in the DOM, so the change is announced. The changes stay visible. Erika's call: there is an accepted state; its final look is to be refined later.
Open: dismissed and expired states. Not designed. Does a dismissed card disappear, collapse, or show "Dismissed"?
Open: Refine. The card only holds the button (in actions). What it does (focus the chat input, open a field) is the screen's.
SuggestionChange: a field label over a before and after pair, as a p plus a dl (dt for the heading, dd for the value). beforeLabel and afterLabel are required strings because the words are translatable. An empty value is passed as a dash by the consumer. Before is in --pds-color-status-critical-foreground, after in --pds-color-status-success-foreground (semibold), headings uppercased with CSS (--pds-typography-ls-l). The text labels carry the meaning, not only color. Columns are 1fr / 2fr (a guess from the screenshot).
Open: Accept style. Accept is a plain primary button here; the panel's Approve has the agentic gradient. Gradient only when applying something to the site?
Open: many changes. No height cap or scroll; the chat scrolls.
Open: text-only changes. A code-diff change (the panel's file row with CodeBlock) is not built here.
Heading is a h3 by default; headingLevel changes it. The card is meant to sit inside an agent ChatMessage; the InChat story composes them.
Storybook: Proposals/Suggestion Card, new-pattern. Uses the local Icon wrapper (for check) and, in stories, the local Button and ChatMessage.
The content area is a slot, shown as a placeholder in Storybook (the same dashed children box the PDS stories use, via the story-only proposals/_story-helpers/SlotPlaceholder; its class has no pds- prefix so Annotate ignores it). Erika's call: what goes in the card is the screen's. SuggestionChange stays as an optional helper, shown in the WithChanges story; drop it if the before and after rows are not part of this proposal. Open: should the card still own spacing between its children, or only the frame?
padding: the same options as Panel (none, condensed, standard, spacious), passed straight to the Panel. Default is condensed (Panel's own default is standard) because the card sits in a chat column. The type comes from Panel's props, so the port follows if Panel's options change.
Footer wrapper for the live region. The empty role='status' region has to stay in the DOM so the accepted message is announced. As a direct child of the flex column body it added an invisible gap below the actions (extra bottom space). The actions and the status region now share one __footer block, so there is one gap, not two.
Fills its container, like a Panel. It is a Panel, so it is a block at full width and sets no width of its own; the chat column decides. The stories no longer cap the width, except InChat, which uses a narrow column (22rem) to imitate a chat panel.
Space above the buttons is --pds-spacing-m (16px), larger than the --pds-spacing-s (12px) between the other sections, so the decision row reads as its own area. Set with component variables --suggestion-card-gap and --suggestion-card-actions-gap; the footer margin makes up the difference. Tried on Erika's request; revert by setting the actions gap to s.
buttonSize: xs | s (default s) for the whole button set. The buttons are consumer content in a slot, so the card sets size on every Button in actions (looking inside fragments) with cloneElement, overriding any size on them; other elements are left alone. The stories no longer set sizes on their buttons. Heights: xs 28px, s 32px. Trade-off: the card inspects its slot, which is unusual for PDS; the port can instead pass the size down through context. Open: is m ever needed?
Title is Heading M: the PDS pds-heading-m utility class (16px, semibold, compact heading font, 130% line height) on the heading element, not custom font rules. Erika's call; it was 14px before. headingLevel still only changes the tag.
Description is Copy S: the PDS pds-copy-s utility class (14px, regular, default font, 150% line height) on the description, kept in the secondary text color. Erika's call. Before, it used the same values written out by hand.
In a chat, the card sits 20px (--pds-spacing-l) below the agent's text, the same as the gap between turns. ChatMessage doesn't space the blocks inside an agent message (the markdown renderer does), so the InChat story sets it with a FlexContainer.
buttonSize defaults to xs (Erika's call; it was s). s (32px) is the option for a roomier card; xs is 28px. It is a prop with a control in Storybook, so there is no separate story for it.
headingSize: m | l (default m), the PDS heading classes pds-heading-m (16px) and pds-heading-l (18px). Separate from headingLevel (the tag). PDS has no heading class smaller than M, and xl and up are too large for a card in a chat, so only these two.