Skip to content

[Web UX] 示例任务分组无法独立展开或收起 #162

Description

@zzzbitz

问题

新建对话页的两个示例任务分组「搭建网页应用」和「搭建和优化智能体」被实现成了单选式手风琴:始终恰好有一个分组展开。

这导致用户无法:

  • 点击当前已展开的分组将其收起;
  • 同时展开两个分组;
  • 同时收起两个分组。

界面使用了展开/收起箭头和 aria-expanded,视觉与无障碍语义都更像独立 disclosure control,但实际行为更接近 tab,容易让人误以为点击没有生效。

复现步骤

  1. 打开 Penguin Harness Web UI 的新建对话页。
  2. 默认「搭建网页应用」处于展开状态。
  3. 再次点击「搭建网页应用」。
  4. 点击「搭建和优化智能体」。

实际结果:

  • 第 3 步没有任何变化,当前分组无法收起;
  • 第 4 步会展开第二组并自动收起第一组;
  • 任意时刻都只能有一个分组展开。

代码确认

该行为在当前 origin/main(aeffc8a)中被显式写死,并非偶发渲染问题:

  • openFolder 的类型是不可空的单个 ExampleFolderId,并默认取第一个分组;代码注释也明确写着 ALWAYS exactly one:
    /**
    * The open example folder — bookmark-style, and ALWAYS exactly one: selecting another closes
    * the previous, and clicking the open one is a no-op rather than collapsing it. Never
    * nullable on purpose. With every folder the same length, "one open" is what makes the
    * block's height a constant: the examples area can neither collapse to bare folder rows nor
    * grow, so nothing below it shifts as folders are switched.
    */
    const [openFolder, setOpenFolder] = useState<ExampleFolderId>(EXAMPLE_FOLDERS[0].id);
  • 每个分组是否展开由 folder.id === openFolder 决定;点击只执行 setOpenFolder(folder.id),因此点击当前分组是 no-op,点击其他分组必然替换前一个:
    {EXAMPLE_FOLDERS.map((folder) => {
    const open = folder.id === openFolder;
    return (
    <div key={folder.id}>
    {/* A tab, not a disclosure: the open folder stays open (clicking it is a
    no-op) and carries the selected fill, so the block always shows one
    folder's examples and its height never changes. */}
    <button
    type="button"
    aria-expanded={open}
    onClick={() => setOpenFolder(folder.id)}
  • 分组清单注释同样规定 exactly ONE folder is open at a time:
    /**
    * Draft-screen example cards, filed into collapsible folders in display order.
    *
    * Folders are what lets the showcase grow past a flat list: exactly ONE folder is open at a
    * time (bookmark-style — opening one closes the other), so the block's height is one row per
    * folder plus the open folder's own rows, never the whole catalog. Adding an example means
    * appending it to the folder it belongs to, not lengthening the page.
    *
    * Keep the folders similarly sized: the draft page reserves no scroll area for this block, so
    * a folder much longer than its siblings is what would make the height jump between them.

这一设计由 PR #130 引入,动机是保持示例区高度恒定:
#130

期望行为

将两个分组作为彼此独立的 disclosure controls:

  • 点击已展开分组,只收起该分组;
  • 点击已收起分组,只展开该分组;
  • 支持全部展开、全部收起,以及任意组合;
  • aria-expanded 始终与各分组的真实状态一致。

实现上可以将单值 openFolder 改为 Set 或等价的逐组布尔状态。新建对话页外层已经是 overflow-y-auto,全部展开导致的高度增长可以由页面滚动承接。

建议增加一个前端测试,覆盖默认状态、点击当前组收起、两组同时展开、两组同时收起四种状态。

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions