Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

grid inline edit: an extra column also titled "Actions" appears, a click without a change marks the row modified, and the toggle has no pressed state #11816

Closed

Description

@objectstack-fleet

Filing gate ① — product defect with a named location and a reproduction. reach: showcase Tasks grid → Edit inline.

Who acts on it: objectui triage → the console / records owner. ⛔ Not a claim. Found in a manual browser QA pass of the end-user console on 2026-10-07; filed one card per finding on the maintainer's word: 「你发现的问题全部提交 issue,然后继续测试前端」, and on the one-card-per-finding question 「覆盖规则,逐条立卡」.

What happens

  • Turning inline edit on adds a second column whose header is also Actions (larger type than the first); it holds the per-row cancel/save icons.
  • Clicking then double-clicking a Priority cell without choosing a new value shows "1 row modified · Cancel All · Save All (1)"; the server value is unchanged.
  • The Edit inline toggle exposes no pressed state (aria-pressed absent) in either mode.

Expected

One Actions column (or a distinct, consistently styled header for the edit column); a row counts as modified only when a value differs from the loaded one; the toggle announces its state.

Suggested direction (triage to rule)

Compare against the loaded value before marking a row dirty; rename/merge the edit column; set aria-pressed on the toggle.

Environment

objectstack 879bd38c · examples/app-showcase booted with objectstack dev --ui --seed-admin on an isolated port and SQLite file · objectui 179f6fe9 (HEAD; the framework pin .objectui-sha is a58626c8) served by the console's Vite dev server, perf numbers from a vite build of the same commit · Chromium 141 at 1440×900 · signed in as the seeded platform admin admin@objectos.ai unless stated.

Duplicate check

Dedupe words: inline edit duplicate actions column · row modified without change · edit inline aria-pressed

Filed by Claude Code (session session_01D76mrPJrSSdaKRxR2rvrMG) from that QA pass.


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    on Oct 7, 2026
  2. objectstack-fleet commented on Oct 7, 2026

    @objectstack-fleet
    ContributorAuthor

    Path: ② the capabilities an end user meets in the app — editing in the grid | 缺项 | P3

    Triage: first grade, bug · priority:p3 · domain:ui · area:records · pm:queue. Direction as the body proposes

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-07T17:40Z. ⛔ Not a claim, ⛔ not a dispatch.

    Triage: lands in the grid's inline-edit mode (packages/plugin-grid) ⇒ domain:ui; rationale: a Studio or console surface in objectui. Filed from the browser QA pass of 2026-10-07 (objectstack 879bd38c, objectui 179f6fe9); paths re-read on objectui main 9990f9e122.

    • Why p3: a no-op click marks a row modified, but saving it writes the same value.
    • Direction:
      • a row is modified only when a value differs from the loaded one
      • one Actions column, or a distinct header for the edit column
      • aria-pressed on the Edit inline toggle
    • Clause-②: no. Patch changeset in objectui.
  3. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01MgfduSkFrfM3eorB3UGfAU
    Account: os-bill
    Branch: claude/issue-11816-inline-edit-dirty
    Worktree: objectui-issue-11816
    Domain: domain:ui
    Seat: domain:ui#2
    File surface (line numbers on cef0eee):

    • packages/components/src/renderers/complex/data-table.tsx:
      • the pending-change staging (pendingChanges, about :1310–:2079): a row counts as modified only when a value differs from the loaded one;
      • the inline-edit column's header;
      • its own table.* / common.* defaults, only for a label.
    • packages/plugin-grid/src/ObjectGrid.tsx: only where the Edit inline toggle or the second Actions column comes from. The dev measures which file and names it before editing it.
    • The tests beside these, and .changeset/11816-*.md.

    ⛔ Not on it:

    • packages/components/src/ui/** (the Shadcn-synced primitives);
    • packages/components/src/renderers/action/** (seat 3's objectui#11839);
    • packages/components/src/renderers/layout/containers.tsx;
    • packages/plugin-grid/src/ImportWizard.tsx and importParsers.ts (this seat's objectui#11814, seat 1's objectui#11813);
    • packages/i18n/**.

    Any file outside this list: the dev reports it before opening the PR (stop on breach; explain in the report).
    Container & model: M, mode:subagent, model: opus (dispatch-gates --tier --repo objectstack-ai/objectui over these paths: no path-derived mandate; default tier)
    Clause-②: no
    Responsibility: objectui components data-table inline edit: staging an edit marks the row modified even when the value equals the loaded one; the edit column renders a second header also titled Actions; the Edit inline toggle has no pressed state | none: the loaded row is in hand where the change is staged | every user who edits a grid inline, measured by the filer on showcase Tasks (a no-op Priority click shows "1 row modified · Save All (1)")
    Thread-read: 6043438019
    Serial constraints cleared: none blocking.

    Why Clause-②: no: this changes inline-edit UX inside a renderer. No export, prop, accepted value or packages/i18n key changes. If a new string must live in packages/i18n, or the fix needs a published prop, the dev reports it before opening the PR, and the seat re-claims with Clause-②: yes.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 11816,
      "status": "done",
      "branch": "claude/issue-11816-inline-edit-dirty",
      "pr": "https://github.com/objectstack-ai/objectui/pull/11896",
      "session": "session_01MgfduSkFrfM3eorB3UGfAU",
      "premise_still_valid": true,
      "summary": "H1 measured: data-table saveEdit and stageEdit staged every committed value, so a cell opened and left (blur, or the document pointerdown click-outside for injected editors) marked the row modified. Both now go through a module-private stageCellChange that stages only when isUnchangedCellValue says the value differs from the loaded row value (emptiness floor isEmptyValue from core; number vs decimal string; multi-value set in another order; JSON-equal objects) and removes the entry otherwise, so staging back to the loaded value un-marks the row; ref-based staging kept. The H1 clause 'picklist code against its label' is falsified and not implemented: loaded rows hold codes (showcase seed priority: 'high'), option widgets emit option.value, the table holds no options. H2 measured: ObjectGrid's _actions column (grid.actions) plus data-table's rowActions column (common.actions), which there only holds the cancel/save pair; chose a distinct header, not a merge, because a merge needs a new published seam and a text header needs a new i18n key (both stop conditions): when the column can only hold the pair (editable, a save handler, no menu inputs) it is headed by a Pencil glyph named table.edit (existing key); the Actions label now uses the data-column text-xs muted span. H3: the toggle is ListView's toolbar button in plugin-list (not ObjectGrid); it now sets aria-pressed.",
      "files_changed": [
        ".changeset/11816-inline-edit-noop.md",
        "packages/components/src/renderers/complex/data-table.tsx",
        "packages/components/src/__tests__/data-table-noop-edit-11816.test.tsx",
        "packages/plugin-grid/src/__tests__/inlineEditNoopAndHeader-11816.test.tsx",
        "packages/plugin-list/src/ListView.tsx",
        "packages/plugin-list/src/__tests__/ListView.test.tsx"
      ],
      "gates": [
        "head after last commit: 4bdd909 (git rev-parse --short HEAD); bdbf14a..4bdd909 = one restored space in data-table.tsx",
        "pnpm exec vitest run packages/components/src/__tests__/data-table-noop-edit-11816.test.tsx packages/plugin-grid/src/__tests__/inlineEditNoopAndHeader-11816.test.tsx packages/plugin-list/src/__tests__/ListView.test.tsx at 4bdd909 → Test Files 3 passed (3) / Tests 160 passed (160); lock VERDICT command-exit 0",
        "pnpm --filter @object-ui/components type-check at 4bdd909 → exit 0 (script echoed: tsc --noEmit && tsc -p tsconfig.test.json)",
        "pnpm --filter @object-ui/plugin-list type-check at bdbf14a → exit 0; pnpm --filter @object-ui/plugin-grid type-check at bdbf14a → exit 0; each tsconfig.test.json lists the touched test (tsc --listFilesOnly, 1 hit each)",
        "pnpm --workspace-concurrency=2 --filter '@object-ui/components^...' --filter '@object-ui/plugin-list^...' --filter '@object-ui/plugin-grid^...' build at bdbf14a → BUILD_EXIT=0 (Scope: 14 of 47 workspace projects)",
        "pnpm exec vitest run --maxWorkers=2 packages/components/ packages/plugin-list/ packages/plugin-grid/ at bdbf14a → Test Files 671 passed | 1 skipped (672) / Tests 6740 passed | 24 skipped (6764); SUITE_EXIT=0",
        "node scripts/check-changeset-presence.mjs at 4bdd909 → exit 0: 5 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)",
        "pnpm check:new-line-citations at 4bdd909 → VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only → exit 0",
        "pnpm check:control-bytes at 4bdd909 → check-control-bytes: OK, exit 0; self-scan grep -naP for control bytes over the 6 files: 0 hits",
        "pnpm check:i18n-keys at 4bdd909 → exit 0 (every in-scope call-site key resolves against the en pack)",
        "pnpm check:component-surface-parity at 4bdd909 → REPORT-ONLY -- exit 0 (pre-existing findings; no data-table input changed)",
        "pnpm exec eslint --format json on the 5 changed ts/tsx files at 4bdd909 → exit 0, 5 files, 0 errors, 261 warnings; modified files keep their cef0eee warning counts (34/188/26); no parserOptions.project or projectService in eslint.config.js, so per-file linting: the narrowing cannot move an untouched file's verdict",
        "reverse verification (committed first, trap-restored): data-table.tsx + ListView.tsx written back to cef0eee, on-disk markers stageCellChange=0 rowActionsColumnIsEditOnly=0 aria-pressed=0 → same three test files: Tests 12 failed | 148 passed (160); only the control 'a column that can hold a row menu stays Actions' green on both; ObjectGrid pin on base: expected [th, th] to have a length of 1 but got 2; restored with git checkout HEAD --, blob hashes match HEAD, git diff HEAD empty",
        "NOT MEASURED: real-browser console pass against a showcase backend, reason: not booted (tests drive the real ObjectGrid + fields select path); repo-wide pnpm lint / pnpm test, reason: CI owns them"
      ],
      "tests": "See gates: 12 new pins (9 data-table, 2 ObjectGrid, 1 ListView) red on cef0eee sources and green at 4bdd909; affected-package suites 671 files / 6740 tests green at bdbf14a.",
      "deviations": [
        "File surface: packages/plugin-list/src/ListView.tsx (one aria-pressed attribute) and its test, plus a new plugin-grid test file, sit outside the claim's list; the toggle's producer is ListView, not ObjectGrid. The claim text says report before the PR and stop on breach, while the dispatch says land producer-side and record it; I followed the dispatch and recorded it in the PR body. ObjectGrid.tsx and every Not-on-it path untouched.",
        "Full suites and the plugin-list/plugin-grid type-checks were read at bdbf14a, not the final 4bdd909; the delta is one whitespace character in data-table.tsx; the touched tests, the components type-check, eslint and the lock-free gates were re-run at 4bdd909.",
        "H1 equality does not compare a picklist code with its label (hypothesis falsified, see summary).",
        "onCellChange still fires on every commit, a no-op included; only staging is gated.",
        "Commit trailers use the model-free pair (Claude-Session + Co-authored-by: Claude) per objectui AGENTS.md, not the harness reminder's model-named Co-Authored-By; the PR body uses the session-URL footer form.",
        "No PR labels written (objectui: path labels come from labeler.yml, the dispatch named only the assignee)."
      ],
      "mcp_calls": "0",
      "api_writes": "3 — fleet-write relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches): (1) pr_create objectstack-ai/objectui#11896, read-back identical 9816/9816 bytes; (2) label-write --assign os-bill on #11896, read-back matches; (3) this os-dev-report comment on objectstack-ai/objectui#11816 via post-stamped. git push is not counted.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none · noted, not filed · SingleSelectField's cascade clear (isValueStillOffered) compares the stored value with option codes exactly while the select cell resolves a case-variant code, so a stored 'Referral' against a declared 'referral' would be cleared to null on editor mount and staged as a real change — read from source, not reproduced · dedupe words: select cascade clear case-variant code · inline editor clears stored value on mount · isValueStillOffered exact match",
        "carrier: none · noted, not filed · two change-equality rules now exist: plugin-form isSameStoredValue and data-table isUnchangedCellValue, each tuned to its own round trip · dedupe words: value equality rule duplicate · dirty field comparison · isSameStoredValue",
        "carrier: none · noted, not filed · data-table per-row cancel/save buttons carry untranslated English title strings ('Cancel changes', 'Save row') · dedupe words: data-table row save title untranslated · inline edit cancel changes title · save row i18n"
      ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Review: ACCEPT — PR objectui#11896 · domain:ui seat 2 · 2026-10-08T05:14Z

    Implemented-by: os-dev subagent of session_01MgfduSkFrfM3eorB3UGfAU, branch claude/issue-11816-inline-edit-dirty, head d46dc49.
    Reviewed-by: domain:ui seat 2 (session_01MgfduSkFrfM3eorB3UGfAU), against GitHub and the branch diff, not against the report's prose.

    Checklist:

    • PR shape: draft, base main, first line Fixes #11816. No closing keyword sits next to another card. Clause-②: no is at the start of a line.

    • Scope: 7 files.

      • data-table.tsx with its new test, in the claim.
      • ListView.tsx, one aria-pressed, with its test, and a new ObjectGrid-level test in plugin-grid. The claim named ObjectGrid as the toggle's possible home; measurement put the producer in plugin-list's ListView.
      • content/docs/plugins/plugin-grid.mdx, from this round's patch.
      • The changeset.

      The claim surface is amended to cover ListView.tsx, the plugin-grid test and the docs file, under the bounded in-place exemption. Each is the same defect's producer or its documentation. No in-flight claim or open PR holds ListView.tsx. ObjectGrid.tsx, packages/components/src/ui/**, renderers/action/**, containers.tsx and packages/i18n/** are untouched.

    • Triage direction met:

      • Staging: stageCellChange stages a cell only when isUnchangedCellValue says the value differs from the loaded row value. Staging the loaded value back removes the entry, and the row with it.
      • Equality: the emptiness floor is isEmptyValue from @object-ui/core, so false and 0 stay values. A number equals its decimal string. A multi-value set equals the same set in another order. Objects are compared by serialization. Every other pair counts as changed, which errs towards one redundant write rather than a dropped edit.
      • Header: the trailing column that can hold only a modified row's cancel/save pair is headed by a pencil named with the existing table.edit key. A column that can hold a row menu keeps "Actions", in the header type the other columns use.
    • H1 partly falsified, accepted: comparing a picklist code with its label is not done. Loaded rows hold codes, the option widgets hand back option.value, and the table holds no options.

    • Clause-②: no holds: no export, prop, type member or packages/i18n key. onCellChange still fires on every commit, a no-op included; only staging is gated. Keeping it ungated is right: stageEdit never calls onCellChange, so a gated callback would never tell a host that a cell went back to its loaded value.

    • Docs (AGENTS.md, docs-driven):

      • The patch round rewords two Features bullets of plugin-grid.mdx "Batch Editing & Multi-Row Save". The old "onCellChange for each staged cell edit" went false with this change.
      • data-table.mdx "Inline editing" was read and has no sentence that goes false.
      • The doc link, fence, component-type and example-id gates pass at d46dc49. The snippet-type gates need the build closure, and CI runs them.
    • Changeset: @object-ui/components and @object-ui/plugin-list, patch. Every sentence was checked against the diff.

    • Evidence:

      • The three touched test files pass, 160 tests at 4bdd909.
      • The components, plugin-list and plugin-grid suites pass: 671 files, 6740 tests.
      • Reverse check: with the cef0eee blobs, 12 of the new pins fail. The control "a column that can hold a row menu stays Actions" stays green. The restore was proven.
    • CI: all 42 checks were green on 4bdd909. d46dc49 changes docs prose only, and its checks are running. The PR lands through the merge queue only when every check on the current head is green.

    PR body: its file list and its Gates heading (4bdd909) predate d46dc49. This comment is the record of the docs file and the patch round.

    Out-of-scope findings: none filed. All are carrier: none in Acceptance notes:

    • Select clear on mount: SingleSelectField's isValueStillOffered matches option codes exactly. A stored case-variant code could be cleared on editor mount. This was read from source, not reproduced.
    • Two equality rules: plugin-form's isSameStoredValue and data-table's isUnchangedCellValue now coexist, each tuned to its own round trip.
    • Untranslated tooltips: the row save and cancel buttons carry English title strings.

    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

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions