Skip to content

refactor(app-shell): move ObjectView's renderListView into a sibling hook (slice 11a) - #26492

Merged
objectstack-fleet[bot] merged 25 commits into
mainfrom
claude/project-thread-x004t9
Oct 27, 2026
Merged

objectstack-fleet[bot] merged 25 commits into
mainfrom
claude/project-thread-x004t9

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steve · project thread

Part of #25992 (slice 11a of the move-only split; slices 1 to 10 landed as #26152, #26233, #26310, #26336, #26353, #26374, #26396, #26412 and #26434). Slice 11 is split in two, as the seat agreed: this PR moves renderListView, and slice 11b (the view-config and view-tab CRUD handlers) closes the card.

Clause-②: no
Metadata-protocol: none

What moved

Base 8cac64f2 (slice 10 merged). ui/packages/app-shell/src/views/ObjectView.tsx is 2263 lines there. Three blocks of ObjectViewInner move verbatim into one custom hook, useObjectViewListRenderer, in the new sibling module objectViewListRenderer.tsx. Every input is passed as an argument, typed from its source (no any), and the component destructures renderListView under its original name. No identifier is renamed and no context is added.

  • O: renderListView (lines 1145–1910). The useCallback the plugin ObjectView calls to draw a view. It covers the type: 'chart' face through ObjectChart, the ListView face with the relay of the view's keys onto fullSchema, the toolbar doors that write the user's own list state through listPrefs.patchView, the held options and filter identities, and the dependency list, all kept verbatim.
  • G-columns: the column-declaration flags (lines 783–806). activeTabDeclaresColumns, draftColumnsEdited, activeViewDeclaresColumns and activeViewIsSaved, with their docblocks. Only renderListView reads them.
  • M: the held list identity refs (lines 1109–1119). heldListOptions and heldListFilter, the two useRefs with their docblocks (objectui#10046, fix(showcase): a type: 'modal' target names a page — showcase_new_task becomes a form action (#6739) #7237). Only renderListView reads them.

The hook is called where renderListView stood, after the view tab bar hook. The block it returns is unchanged.

Hook order. The two useRef calls of M used to run before useObjectViewTabBar (whose useViewTabsFit and useDisplayLocale run in between). They now run after it, inside the new hook. Both are independent refs initialised to undefined; nothing between their old and new positions reads them. The seat's ruling allows this shift when stated. G-columns are plain values, not hooks.

The module also takes the lazy ObjectChart declaration (lazy(() => import('@object-ui/plugin-charts')…)), verbatim. Its only mount is the chart face of renderListView, so the chart chunk stays lazy and is split exactly as before. The ImportWizard lazy declaration stays in ObjectView.tsx.

Imports

Each import moves with its only readers: DENSITY_MODE_TO_ROW_HEIGHT, normalizeListViewSchema, buildListFilterKey, writeListFilterState, ListViewPreference, ListView, ListViewSchema, TreeViewConfig, useRef, ReactNode, resolveManagedByEmptyState, warnSuppressedListNav, substituteFilterTokens, keepInlineEditModeForTheSession, and the view-options family entries (timelineViewOptions, calendarViewOptions, ganttViewOptions, galleryViewOptions, kanbanViewOptions, storedLegacyOptions, isSameOptionsValue). The view-options names are still re-exported from ObjectView.tsx, lines unchanged. Names the component still reads elsewhere (useCallback, Suspense, lazy, isSavedViewId, FilterTokenScope) are imported by the module as well. Nothing new is exported from ObjectView.tsx, and the package entry is untouched. ObjectView.tsx goes from 2263 to 1480 lines.

Move-only proof: the multiset of non-blank lines of the base ObjectView.tsx is contained in new ObjectView.tsx plus the module. The only base lines missing are six import lines or entries that lost a moved name: the react, @object-ui/core, ../context/ListPreferencesProvider.js, @object-ui/types and ./objectViewChrome.js imports, and the substituteFilterTokens entry of the helpers list. Each import is replaced by its trimmed form. Removed outright are the ./listFilterStorage.js, @object-ui/plugin-list, ../utils/managedByEmptyState.js and ../utils/warnSuppressedListNav.js imports, the ObjectChart lazy declaration, and the view-options entries of the ./objectViewOptions.js import list. Their lines reappear verbatim in the module. The added lines are the trimmed imports and the new import, the module header and imports, the hook signature and parameter types, the return, and in ObjectView.tsx the call with its destructuring and two-line comment.

Test edits

Six source-scan readers move to the file that now holds what they read. Each edit is a path or file-list change, and no assertion changed:

  1. ObjectView.relayRungCensus-7559.test.ts: RELAY_SOURCE reads objectViewListRenderer.tsx (both candidate paths), the parse name follows, and so does the path in the anchor's error message. The census anchors on const fullSchema = { … } in renderListView.
  2. packages/types/src/__tests__/tree-view-config-readers-8253.test.ts: CONSOLE_COMPOSITION → packages/app-shell/src/views/objectViewListRenderer.tsx. It reads the viewDef tree labelField rung.
  3. packages/types/src/__tests__/object-view-unmirrored-keys-7779.test.ts: APP_SHELL_OBJECT_VIEW → packages/app-shell/src/views/objectViewListRenderer.tsx. Its three reads are the app-shell relay (rowHeight through the fold, rowColor, and the absent allowExport / prefixField / densityMode reads).
  4. ObjectView.titleFieldConvergence.test.tsx: the joined SOURCE list ['ObjectView.tsx', 'objectViewOptions.ts'] gains 'objectViewListRenderer.tsx'. The seams it counts span all three.
  5. ObjectView.overlayPatchOnly.test.ts: objectViewCode reads ObjectView.tsx and objectViewListRenderer.tsx joined. The retired-family absence covers both, handleViewConfigSave stays in ObjectView.tsx, and the objectui#12474 doors (onDensityChange, persistHiddenFields) are in the module.
  6. view-filter-fold.ratchet.test.ts: objectViewSrc reads ObjectView.tsx and objectViewListRenderer.tsx joined. The onFilterChange handlers are in the module, and handleViewConfigSave stays in ObjectView.tsx.

Slice 11b will add its module to the joined lists in 5 and 6, and re-point the writeRefusalCensus-11583 rows and check-i18n-call-site-keys.

Seam table rows this PR changes (measured on 8cac64f2)

The full table (regions A to Q) is in #26336.

# Concern Lines on 8cac64f2 Reads Read by Slice
G-columns Column-declaration flags of the active view 783–806 B (viewDraft), F (baseView, derivedColumnViewIds, savedViews) O 11a, this PR
M Held list identity refs 1109–1119 — O 11a, this PR
O renderListView 1145–1910 A, B, C, D, E, F, G-columns, H, J, L, L-filter, M, N Q 11a, this PR
B View config panel and create dialog state, handleViewConfigSave, handleViewCreate (and handleViewUpdate, 829–836) 231–386 A, C F, G-columns, H, I, N, P, Q 11b
I View switching, mobile switcher, view-tab CRUD callbacks 837–1045 A, B, C, F N, Q 11b (closes the card)

Verification

See the os-dev-report comment on the card for the full readings: app-shell type-check after rebuilding its dependency closure, every ObjectView.* test in views/ (the calendar, gallery, gantt, timeline, kanban and chart bindings, relays, density, column and filter tests among them), the AppContent* tests, every other test that reads ObjectView.tsx as source, including the six re-pointed ones, eslint on the touched files, the derived dispatch gates, and a Console build.

Acceptance notes

  • eslint: 0 errors, no new no-explicit-any. The two files report 108 warnings against 107 for the base ObjectView.tsx alone. The one added warning is react-refresh/only-export-components on objectViewListRenderer.tsx: the module declares the lazy ObjectChart component and exports a hook. The declaration is kept verbatim with its only mount rather than reshaped. The two exhaustive-deps warnings on handleViewChange only moved lines. The six test edits add no warning.

Changeset: .changeset/25992-objectview-list-renderer-split.md (patch, @object-ui/app-shell).


🤖 Generated with Claude Code

https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz

claude added 25 commits October 26, 2026 14:40
…tViewOptions.ts (slice 2)

The timeline/calendar/gantt/gallery/kanban ViewOptions, storedLegacyOptions,
isSameOptionsValue and dispatchViewPatches move verbatim out of
views/ObjectView.tsx into views/objectViewOptions.ts. ObjectView.tsx imports
them back and re-exports every name it exported before.

Test edits forced by the move: the four source scans that read the
ObjectView face (calendarBinding-7029, galleryBinding-7547, ganttBinding-7070,
titleFieldConvergence) now read ObjectView.tsx plus objectViewOptions.ts, and
the writeRefusalCensus-11583 LEDGER row for dispatchViewPatches is keyed by
its new file.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
…ome.tsx (slice 3)

ObjectNotFoundState, the saved-view tab strip's viewport check
(VIEW_TABS_MIN_WIDTH, viewTabsFitNow, useViewTabsFit) and
keepInlineEditModeForTheSession move verbatim out of views/ObjectView.tsx
into views/objectViewChrome.tsx. ObjectView.tsx imports them back and
re-exports ObjectNotFoundState, the one name it exported. No test edits.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
…unt into a sibling hook

Slice 4 of the move-only ObjectView split. The realtime subscription,
the reconnection conflict resolution and the footer record-count fetch
move verbatim from ObjectViewInner into useObjectViewLiveData in
views/objectViewLiveData.ts. The component calls the hook at the same
position (hook order unchanged) and passes its inputs explicitly; the
recordCount state stays in the component. No test edits.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
…igation into sibling hooks

Slices 5 and 6 of the move-only ObjectView split, paired in one PR.

- The identity-import gate, its password-policy state and data source,
  and the managed-environment Users marker move verbatim into
  useObjectViewIdentity in views/objectViewIdentity.tsx.
- The record navigation overlay (detail navigation config, drawer
  recordId URL state, row click, expand to page, list record links)
  moves verbatim into useObjectViewRecordNavigation in
  views/objectViewRecordNavigation.ts. The URL equality filters and the
  user-filter selections between its two blocks stay in the component.

Inputs are passed explicitly; the component destructures the returned
values under their original names. No test edits.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
…m their sources

activeView, objectDef and objects were written as `any` in the new
hook's parameter type. They now name where the component gets each
value: the element of buildViewTabs' result, and
ConsoleObjectViewProps['objects'] and its element. No new
no-explicit-any warning; the moved bodies are unchanged.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
…a sibling hook

Slice 7 of the move-only ObjectView split. The environment list's
entitlements, the list actions and their localized toolbar copies, and
the create and import buttons' toolbar-scope predicates move verbatim
into useObjectViewToolbarAffordances in
views/objectViewToolbarAffordances.ts. The resolveAffordance verdict
memo stays in the component (the affordance grant census reads it
there) and is passed in with the other inputs, typed from their
sources. No test edits.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
…ate into sibling hooks

Slice 8 of the move-only ObjectView split.

- The header's list_toolbar actions (less the record-scoped ones the
  selection bar carries), the nav runAction deep link and the
  action:bar node move verbatim into useObjectViewHeaderToolbar in
  views/objectViewHeaderToolbar.ts.
- The list's uf_ URL state (seed, write-back, Reset view) moves into
  useObjectViewListUrlState, and the filter[...] equality filters with
  the restored user-filter selections into useObjectViewUrlFilters,
  both in views/objectViewListUrlState.ts.

Hook parameters are typed from their sources. One test edit, path
only: ObjectView.urlFilterSuffix-9196 reads objectViewListUrlState.ts,
where the scanned block now lives.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
…and header chrome into sibling hooks

Move-only split of ObjectViewInner: the view tab items, ViewTabBar and
its own-row face go to objectViewTabBar.tsx; the merged views and the
plugin ObjectViewSchema go to objectViewPluginSchema.ts; the create
button, folded header actions menu and refused-view face go to
objectViewHeaderChrome.tsx. Each hook is called where its block stood.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
… into sibling sub-components

Move-only split of ObjectViewInner's JSX: the PageHeader block goes to
ObjectViewPageHeader in objectViewPageHeader.tsx, and the non-split
NavigationOverlay with its RecordDetailView goes to
ObjectViewRecordOverlay in objectViewRecordOverlay.tsx. Every value is
passed in as a prop; the rendered DOM is unchanged.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
…hook

Move-only split of ObjectViewInner: renderListView, the column-declaration
flags and the held list identity refs only it reads, and the lazy
ObjectChart it mounts go to useObjectViewListRenderer in
objectViewListRenderer.tsx, called where renderListView stood. Six
source-scan tests read the new file (path or file-list changes only).

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation tests tooling labels Oct 27, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · seat domain:ui#1 · 2026-10-27T05:35Z · #25992 slice 11a

Head ed7146bf (read from the PR object). Base 8cac64f2 (slice 10 merged).

  • Shape. 9 files, +975 / −831. renderListView (O), the column-declaration flags (G-columns), the held list refs (M) and the lazy ObjectChart move into one hook, useObjectViewListRenderer, in views/objectViewListRenderer.tsx. ObjectView.tsx goes from 2263 to 1480 lines. Part of [maintainer] refactor(ui): split ObjectView.tsx (4414 lines) into smaller modules, one move-only slice per PR, so concurrent ui cards stop colliding in it #25992; slice 11b closes it.
  • Move-only, re-measured by the seat. The multiset of non-blank lines of the base ObjectView.tsx is contained in the new ObjectView.tsx plus the module, except 6 lines. Those six are the trimmed react, @object-ui/core, ListPreferencesProvider, @object-ui/types and objectViewChrome imports, and the substituteFilterTokens entry. Each one reappears in its trimmed form.
  • Test edits, re-read by the seat. The 6 reader edits change only a path or a file list (relayRungCensus-7559, tree-view-config-readers-8253, object-view-unmirrored-keys-7779, titleFieldConvergence, overlayPatchOnly, view-filter-fold.ratchet). No assertion changed.
  • Stated hook-order shift. heldListOptions and heldListFilter (two independent useRefs) now run after useObjectViewTabBar. Nothing between the old and new positions reads them. This is allowed by the slice ruling.
  • Governed surface. scripts/pm/check-governed-merges.mjs --pr 26492: not governed.
  • Checks pinned to the head. All check runs completed: 0 failing, 0 pending. The seat's landing script checked this before posting.
  • Evidence (dev). app-shell type-check exits 0. 203 files / 3358 tests pass. The console build exits 0. eslint shows 0 errors and +1 warning (react-refresh/only-export-components on the module, stated in the body). Gates: 34/34.
  • Serial constraints. ObjectView.tsx is held one slice at a time. 11b starts from main after this PR merges.

Landing: ready + auto-merge through the relay.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 27, 2026 05:36
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit 73683ee Oct 27, 2026
55 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-x004t9 branch October 27, 2026 06:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants