Skip to content

Feat/action panel component#60

Open
itsprade wants to merge 10 commits intomainfrom
feat/action-panel-component
Open

Feat/action panel component#60
itsprade wants to merge 10 commits intomainfrom
feat/action-panel-component

Conversation

@itsprade
Copy link
Contributor

@itsprade itsprade commented Mar 9, 2026

ActionPanel – PR summary

What it is

ActionPanel is a presentational card component that displays a title and a vertical list of actions (icon + label). Each action is a button (onClick) or a link (href). The parent decides which actions to show and what they do (e.g. from the backend or a registry).

In this PR

  • Loading state: Optional loading per action; shows a spinner in the icon slot and makes the row non-interactive (for backend-driven flows).
  • Empty state: When actions.length === 0, shows “No actions available” with alignment and padding consistent with the title.
  • Layout: 320px min-width; reduced horizontal padding (px-4); title aligned with action row icon; no gap between rows; spacing handled via container padding.
  • Docs: README in the component folder with design rationale, API, usage, and backend-driven pattern.
  • Example: 2-column layout includes an ActionPanel with a loading demo (“Create invoice” shows loading for 1.5s) and a second panel with empty actions to demonstrate the empty state.

Usage

Parent passes title and actions: ActionItem[] (each with key, label, icon, and onClick or href; optional disabled, loading). Suitable for backend-driven UIs where the API returns available action keys and the app resolves them with a registry and sets loading from mutation state.

@itsprade itsprade requested a review from IzumiSy March 9, 2026 10:27
@IzumiSy
Copy link
Contributor

IzumiSy commented Mar 10, 2026

/review

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generated by API Design Review for issue #60

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 10, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@tailor-platform/app-shell@60
npm i https://pkg.pr.new/@tailor-platform/app-shell-vite-plugin@60

commit: d92e362

@itsprade
Copy link
Contributor Author

/review

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generated by API Design Review for issue #60

const iconSlotClasses = "astw:flex astw:size-4 astw:items-center astw:justify-center";

const ActionSpinner = () => (
<span className={iconSlotClasses} aria-hidden data-testid="action-panel-spinner">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[2/2 — Medium] Double-nested iconSlotClasses span when loading

ActionSpinner wraps the spinner SVG in its own iconSlotClasses span (astw:flex astw:size-4 astw:items-center astw:justify-center):

const ActionSpinner = () => (
  <span className={iconSlotClasses} >   {/* ← size-4 container */}
    (svg className="astw:size-4 …" /)
  </span>
);

But in ActionRow, the spinner is rendered inside another iconSlotClasses span:

<span className={iconSlotClasses} aria-hidden>   {/* ← another size-4 container */}
  {loading ? (ActionSpinner /) : icon}
</span>

When loading is true you get two nested 16×16 flex containers. This is redundant and will cause subtle alignment differences vs. the non-loading state (the icon is placed directly inside one container; the spinner sits two levels deep).

Fix by making ActionSpinner return only the SVG (no wrapping span), since the outer iconSlotClasses span in ActionRow already provides the sizing/centering:

const ActionSpinner = () => (
  (svg
    className="astw:size-4 astw:animate-spin"
    viewBox="0 0 16 16"
    fill="none"
    xmlns="(www.w3.org/redacted)
    aria-hidden
    data-testid="action-panel-spinner"
  )
    
  (/svg)
);

@IzumiSy
Copy link
Contributor

IzumiSy commented Mar 12, 2026

@itsprade need rebasing this branch with main 🙏

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file needs converging into docs dir 🙏

Introduces a new ActionPanel component for displaying a card with a
title and a vertical list of clickable/linkable action rows, each with
an icon and label. Supports both onClick handlers and href navigation.

Usage:
  import { ActionPanel } from "@tailor-platform/app-shell";

  <ActionPanel
    title="Actions"
    actions={[
      { key: "create", label: "Create invoice", icon: <Icon />, onClick: () => {} },
      { key: "docs", label: "View docs", icon: <Icon />, href: "/docs" },
    ]}
  />

Made-with: Cursor
- Add optional loading prop to ActionItem; show spinner and disable row when loading
- Reduce horizontal padding (px-6 to px-4), align title with action row icon (pl-3)
- Reduce gap between actions to 0; increase top padding (pt-6), add title mb-2
- Add loading example on 2-column layout (Create invoice shows loading for 1.5s)
- Update docs and tests for loading support

Made-with: Cursor
- Add 320px min-width to ActionPanel card
- Empty state: align message with title (pl-3), add py-2; title spacing via container pb-4
- 2-column layout: second ActionPanel with empty actions to demonstrate empty state
- Add README.md with design rationale, API, usage, and backend-driven pattern
- README: replace 'dumb' with 'presentational component with no internal business logic'

Made-with: Cursor
- Use ============ section comments and ALL CAPS labels
- Inline card/header/list classes in JSX; keep row/icon constants
- Add EXPORTS section; match repo style

Made-with: Cursor
- ActionItem: only onClick (no href); use useNavigate() in callback for navigation
- Remove ActionItem from public API (consumers infer from ActionPanelProps)
- Always render button; remove link branch and href+disabled/loading edge case
- Update demo and 2-column example to use navigate() for link-like actions
- Remove test for 'renders as link when href provided'

Made-with: Cursor
Remove the extra spinner wrapper to keep icon-slot alignment consistent, and update ActionPanel docs to reflect the onClick-only action contract.

Made-with: Cursor
@itsprade itsprade force-pushed the feat/action-panel-component branch from 5a9c1da to acc9853 Compare March 13, 2026 07:21
Relocate ActionPanel documentation from the component folder into the central docs/components structure so it follows the same format and discoverability as other component docs.

Made-with: Cursor
Apply oxfmt output to docs, examples, and tests to keep formatting consistent.

Made-with: Cursor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants