Skip to content

The record page's lifecycle path looks like a control and is inert: stageField renders as a segmented button group with no handler, no cursor, and no tab stop #11243

Closed as not planned

Description

@os-zhuang

The record page's lifecycle path looks like a control and is inert: it renders stageField as a segmented button group with no handler, no cursor, and no tab stop

Measured on 17.1.0 (open edition, objectstack dev), HotCRM crm_quote — but it is the generic record chrome, so it applies to every object that declares a stageField.

What renders

The record page draws the object's lifecycle across the top:

[ 草稿 ] [ 审核中 ] [ 已提交 ] [ 已接受 ] [ 已拒绝 ] [ 已过期 ]
   ▲ current, filled

Pill-shaped segments, the current one filled in the accent colour, the rest in a resting state — visually a segmented button group, and positioned exactly where a CRM user expects the stage control to be.

What it is

document.querySelectorAll('main [role="listitem"]')   // ← listitem, not button/tab
getComputedStyle(el).cursor        // "auto"
el.getAttribute('tabindex')        // null
el.closest('button,[role=button],[role=tab],a')   // null

A full pointer sequence (pointerdown → mousedown → pointerup → mouseup → click) on the target segment changes nothing; the record's status is still draft afterwards. Advancing the record needs 编辑 → the status <select> → 更新, or a bulk action.

Why it is worth changing

The path is the affordance every CRM user reaches for first — in the products this chrome is modelled on, clicking the next segment is how you advance a record. Rendering it as buttons and wiring nothing is the shape that costs a user two or three clicks before they conclude it is decoration, and it teaches them to distrust the rest of the chrome.

Two directions, either defensible:

  1. Make it a control. Clicking a segment writes stageField, subject to the same permission/validation path an edit takes (so a locked or unauthorised record refuses with the normal envelope). This is what the visual promises.
  2. Make it look like a readout. If advancing must stay in the form, drop the button styling — a progress rail without pill-and-fill reads as status, not as a control, and nobody loses a click to it.

What it should not keep doing is looking like (1) and behaving like (2).

Accessibility note

The segments carry role="listitem" with no tabindex, so a keyboard user cannot reach them at all — which is correct for a readout and wrong for a control. Whichever direction is taken, the role should follow: listitem for a readout, tab/button with a tab stop for a control.

Activity

  1. added
    enhancementNew feature or request
    repo:objectuiSeam card: cross-repo ordering with objectui is the substance (pure objectui fixes live in objectui)
    on Aug 23, 2026
  2. os-zhuang commented on Aug 23, 2026

    @os-zhuang
    ContributorAuthor

    Triage: closed as moved → objectstack-ai/objectui#5768 (file-at-destination rule: generic record chrome, the body stands with objectstack removed — console/UI defects land in objectui). Graded there as a decision card (needs-user-decision, domain:ui) with the four-facet block; nothing further tracks here.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestrepo:objectuiSeam card: cross-repo ordering with objectui is the substance (pure objectui fixes live in objectui)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions