Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .changeset/6874-retire-titleformat.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
---
'@object-ui/plugin-grid': patch
---

`ObjectGrid` no longer copies `titleFormat` onto a relational column's `fieldMeta`
(objectui#6874).

`RELATIONAL_META_KEYS` listed eight keys that `applyRelationalMeta` copies off the
object-schema field def onto the built `fieldMeta`, at all three of `generateColumns`'s
column-building call sites. `titleFormat` was one of them and had **zero FIELD-meta
readers**.

This is a zero of a different kind from objectui#6711's, and a stronger one: `titleFormat`
is a real, live key with plenty of readers — it just has none on a field meta. The sweep
did not fail to find readers. It found every member read of the identifier across
`packages/` and `apps/` (tests included) and classified each by its receiver:

- `objectDef` / `objectSchema` / `objSchema` — `core/utils/record-title.ts`,
`components/renderers/layout/containers.tsx`, `plugin-detail/DetailView.tsx`,
`plugin-kanban/ObjectKanban.tsx`, `plugin-calendar/ObjectCalendar.tsx`,
`react/hooks/useRecordSearch.ts`. An OBJECT schema, every one.
- `refObjectSchema?.titleFormat` — `fields/widgets/LookupField.tsx`: the REFERENCED
object's schema, fetched by `getSchema(referenceTo)`. Also an OBJECT schema, and the one
that decides this case — it is what the grid's own inline picker reads.
- `param.titleFormat` — `app-shell/utils/paramToField.ts`, off a resolved `ActionParamDef`.
The field-def read beside it is `field.title_format`, a different spelling on a different
surface.

`RecordPickerDialog` and `lookupColumnDisplay` receive it as a PROP, and the repo's single
`titleFormat=` pass is `titleFormat={refTitleFormat}` — object-schema sourced. So copying
`reference_to` is what makes `titleFormat` work on this path, and copying `titleFormat`
onto the meta reached nothing.

Nothing renders differently, and the argument does not rest on the member sweep alone: the
only computed access to the meta bag anywhere in `@object-ui/fields` or `plugin-grid` is
`applyRelationalMeta`'s own write, so no consumer can pick the key up dynamically. The key
is also not a member of any declared type on this path — `applyRelationalMeta` writes into
a `Record<string, any>` and the bag reaches cell renderers through an `as any` cast.

`plugin-dashboard/src/recordFields.tsx` had already recorded this exact measurement as its
reason for not copying the key into that seam, so it was a measured no-op in two seams and
retired from only one. Same defect class as objectui#6625 (`FieldMeta.decimals`),
objectui#6597 (`FieldMeta.referenceTo`) and objectui#6711 (`reference_to_field`), and the
same disposition as objectui#6711 on this very list.

⚠️ **What the measurement bounds.** The sweep covers this repo and the producer repo. A
host application outside them could still be reading `titleFormat` off the `fieldMeta` a
cell renderer receives; that was never a declared promise this renderer made, and this
repo's own contract is what the retirement is about — but the world was not measured, and a
host reading the key off a field meta gets `undefined` after this change. The supported
source is unchanged and unaffected: the referenced object's schema.

Because the key had no readers on this path, the suite stays green whether or not the
removal is correct, so the absence is pinned directly instead
(`__tests__/relationalMetaCopySet-6874.test.tsx`): all three call sites, each with a
presence assertion on the seven surviving keys as the control against a fixture that passes
by never reaching the copy path.
Original file line numberDiff line numberDiff line change
Expand Up@@ -237,15 +237,18 @@ describe('objectui#6694 — RecordDetailDrawer lookup rows carry their reference
/**
* The copy-set boundary.
*
* `ObjectGrid`'s `applyRelationalMeta` copies NINE keys; this seam copies THREE,
* `ObjectGrid`'s `applyRelationalMeta` copies SEVEN keys — it copied NINE until
* objectui#6711 and objectui#6874 retired `reference_to_field` and `titleFormat`
* from its list, both on the reader measurement this seam had already recorded.
* This seam copies THREE,
* and the difference is measured rather than preferred: the grid's cells are
* EDITABLE, so its extra keys feed the inline picker (`LookupField` / `UserField`
* read `id_field`, `description_field`, `lookup_filters`, `lookupFilters`).
* These two widgets are read-only — their only render path ends at a CELL
* renderer — and `packages/fields/src/index.tsx` reads exactly three relational
* keys off a cell's `field` prop.
*
* ⛔ This is what stops the omitted six from being added back "for parity": a
* ⛔ This is what stops the omitted keys from being added back "for parity": a
* `FieldMeta` member written on every call and read by nothing is precisely what
* objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`) retired from this
* same file. If these widgets ever gain inline editing, that is the event that
Expand All@@ -257,7 +260,11 @@ describe('objectui#6694 — buildFieldMeta copies the cell-read relational keys
reference_to: 'project',
reference: 'project',
display_field: 'project_code',
// The six the grid also copies, which have no reader on this path:
// Six keys with no reader on this path. FOUR of them the grid still copies
// (its picker-only keys); the other two it has since retired as well —
// `reference_to_field` (objectui#6711) and `titleFormat` (objectui#6874).
// All six stay on the fixture on purpose: the assertion below pins THIS
// seam's boundary, which does not move when the grid's list does.
reference_to_field: 'x',
id_field: 'x',
description_field: 'x',
Expand Down
20 changes: 13 additions & 7 deletions packages/plugin-dashboard/src/recordFields.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -92,11 +92,15 @@ export const NUMERIC_FIELD_TYPES = new Set([
* widgets funnel through, which is what this module exists for (see the file
* header: the two surfaces must never drift).
*
* ## ⚠️ The copy set is DELIBERATELY 3 of the grid's 9 — measured, per key
* ## ⚠️ The copy set is DELIBERATELY 3 of the grid's 7 — measured, per key
*
* `RELATIONAL_META_KEYS` is `reference_to`, `reference`, `reference_to_field`,
* `display_field`, `id_field`, `description_field`, `lookup_filters`,
* `lookupFilters`, `titleFormat`. The grid needs all nine because its cells are
* `RELATIONAL_META_KEYS` is `reference_to`, `reference`, `display_field`,
* `id_field`, `description_field`, `lookup_filters`, `lookupFilters`. It listed
* NINE until the two keys this file had already measured as reader-less were
* retired from it as well — `reference_to_field` (objectui#6711) and
* `titleFormat` (objectui#6874).
*
* The grid needs the remaining seven because its cells are
* EDITABLE — its own docblock says the extra keys "drive the inline picker's
* query (LookupField reads reference_to/reference, display_field, id_field,
* description_field, lookup_filters)", and the defect that earned them was an
Expand All@@ -114,15 +118,17 @@ export const NUMERIC_FIELD_TYPES = new Set([
* mentions in that module; read only by `fields/src/widgets/LookupField.tsx`
* and `UserField.tsx`, both EDITORS. ⛔ NOT copied.
* - `reference_to_field` — ZERO member reads anywhere in the repo. ⛔ NOT
* copied.
* copied. ⭐ The grid has since retired it from its own list too
* (objectui#6711); this measurement is what that retirement acted on.
* - `titleFormat` — never read off a FIELD meta at all; every reader takes it
* off the OBJECT schema (`getRecordDisplayName` in `@object-ui/core`,
* `containers.tsx`). On this path that object schema arrives through
* `useRefObjectSchema(reference_to)` — so copying `reference_to` is what
* makes `titleFormat` work, and copying `titleFormat` here would reach
* nothing. ⛔ NOT copied.
* nothing. ⛔ NOT copied. ⭐ The grid has since retired it too
* (objectui#6874), on exactly this reading.
*
* ⛔ Do not "restore parity" by widening this to the grid's nine. A member
* ⛔ Do not "restore parity" by widening this to the grid's seven. A member
* written from the schema def on every call and read by nothing is exactly what
* objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`) retired from this
* very file. Add a key when a reader on THIS path is measured, not before; if
Expand Down
74 changes: 53 additions & 21 deletions packages/plugin-grid/src/ObjectGrid.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -430,42 +430,74 @@ function getDataConfig(schema: ObjectGridSchema): ViewData | null {
* raw id after moving to another row. Copy them from the object-schema field
* definition onto the built `fieldMeta` for every column-building path.
*
* ## ⛔ `reference_to_field` was in this list and is RETIRED (objectui#6711)
* ## ⛔ Two keys were in this list and are RETIRED
*
* Every key here has to have a measured reader on this grid's own render path —
* the cell renderers and inline editors in `@object-ui/fields` that
* `getCellRenderer` dispatches into. `reference_to_field` had none: swept across
* `packages/` and `apps/` (and again across the producer repo), the only
* occurrences of the identifier anywhere were this array literal — the write —
* and prose recording that nothing reads it. No member access, no destructuring,
* no bracket read. `@objectstack/spec`'s FieldSchema does not declare it either,
* so nothing authorable produces it.
* `getCellRenderer` dispatches into. Two keys had none, for two different
* reasons, and each retirement was its own adjudication.
*
* The control that makes that zero a reading, not an artefact of how the sweep
* was written: the same sweep over its list-mates finds real readers for each of
* them — `reference_to` / `reference` / `display_field` in `LookupCellRenderer`,
* `id_field` / `description_field` / `lookup_filters` / `lookupFilters` in
* `LookupField` / `UserField`. ⚠️ One exception, measured and deliberately NOT
* acted on here: `titleFormat` has no FIELD-meta reader either — every reader
* takes it off the OBJECT schema, which reaches the picker through
* `useRefObjectSchema(reference_to)` (`plugin-dashboard/src/recordFields.tsx`
* records the same measurement). Retiring it is a separate adjudication.
* ### `reference_to_field` — objectui#6711
*
* Swept across `packages/` and `apps/` (and again across the producer repo), the
* only occurrences of the identifier anywhere were this array literal — the
* write — and prose recording that nothing reads it. No member access, no
* destructuring, no bracket read. `@objectstack/spec`'s FieldSchema does not
* declare it either, so nothing authorable produces it.
*
* ### `titleFormat` — objectui#6874
*
* A zero of a different kind, and a stronger one. `titleFormat` is a real, live
* key with plenty of readers — it simply has no FIELD-meta reader. The sweep did
* not fail to find readers; it found every member read of the identifier across
* `packages/` and `apps/` (tests included) and classified each one by receiver:
*
* - `objectDef` / `objectSchema` / `objSchema` — `core/utils/record-title.ts`,
* `components/.../containers.tsx`, `plugin-detail/DetailView.tsx`,
* `ObjectKanban.tsx`, `ObjectCalendar.tsx`, `react/hooks/useRecordSearch.ts`.
* OBJECT schema, every one.
* - `refObjectSchema?.titleFormat` — `fields/widgets/LookupField.tsx`: the
* REFERENCED object's schema, fetched by `getSchema(referenceTo)`. Also an
* OBJECT schema, and the one that matters here — it is what this grid's own
* inline picker reads.
* - `param.titleFormat` — `app-shell/utils/paramToField.ts`, off a resolved
* `ActionParamDef`; the field-def read next to it is `field.title_format`,
* a different spelling on a different surface.
*
* `RecordPickerDialog` and `lookupColumnDisplay` receive it as a PROP, and the
* repo's single `titleFormat=` pass is `titleFormat={refTitleFormat}` —
* object-schema sourced. ⇒ copying `reference_to` is what makes `titleFormat`
* work on this path; copying `titleFormat` onto the meta reached nothing.
* `plugin-dashboard/src/recordFields.tsx` recorded this same measurement first
* and declined to copy the key, so it was a measured no-op in two seams and had
* been retired from only one.
*
* ### The control that makes both zeros a reading
*
* Not an artefact of how the sweep was written: the same sweep over the
* surviving list-mates finds a real FIELD-meta reader for every one of them —
* `reference_to` / `reference` / `display_field` off the cell's `field` prop in
* `LookupCellRenderer` (`fields/src/index.tsx`), and `id_field` /
* `description_field` / `lookup_filters` / `lookupFilters` off `fieldMeta?.…`
* in `LookupField` / `UserField`. There is no third reader-less key: all seven
* survivors are read off a field meta.
*
* ⚠️ The sweep bounds these two repos. A host application outside them could
* still be reading the key off `fieldMeta`; the repo's own contract is what this
* retirement is about.
* still be reading either key off `fieldMeta`; the repo's own contract is what
* these retirements are about.
*
* ⛔ Do not re-add a key for symmetry with the object-schema field def. A
* member written from the def on every column build and read by nothing is
* exactly what objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`)
* retired from the sibling producer. Add a key when a reader on THIS path is
* measured, not before. The absence is pinned in
* `__tests__/relationalMetaCopySet-6711.test.tsx`.
* measured, not before. Both absences are pinned, at all three call sites —
* `__tests__/relationalMetaCopySet-6711.test.tsx` and
* `__tests__/relationalMetaCopySet-6874.test.tsx`.
*/
const RELATIONAL_META_KEYS = [
'reference_to', 'reference',
'display_field', 'id_field', 'description_field',
'lookup_filters', 'lookupFilters', 'titleFormat',
'lookup_filters', 'lookupFilters',
] as const;

/**
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,7 +15,7 @@
*
* ## The control against vacuity lives in the same assertions
*
* Each case also asserts that the eight SURVIVING keys do arrive on the same
* Each case also asserts that the seven SURVIVING keys do arrive on the same
* meta. An absence assertion on its own passes for the wrong reason as soon as
* the fixture stops reaching the copy path at all (a renamed helper, a column
* path that no longer resolves this renderer, a def the grid never reads); the
Expand DownExpand Up@@ -60,16 +60,19 @@ const MANAGER_DEF = {
description_field: 'title',
lookup_filters: [['active', '=', true]],
lookupFilters: [['active', '=', true]],
// Also retired, in objectui#6874, and pinned in its own file
// (`relationalMetaCopySet-6874.test.tsx`). Kept on the fixture so this file's
// survivor control stays a list of keys the grid really does still copy.
titleFormat: '{name}',
// The retired key (objectui#6711). Kept on the fixture on purpose.
reference_to_field: 'MUST_NOT_BE_COPIED',
};

/** The eight keys that survive the retirement — the control. */
/** The seven keys that survive both retirements — the control. */
const SURVIVING_KEYS = [
'reference_to', 'reference',
'display_field', 'id_field', 'description_field',
'lookup_filters', 'lookupFilters', 'titleFormat',
'lookup_filters', 'lookupFilters',
] as const;

const ROWS = [{ id: 'r1', name: 'Tower T1', manager: 'u1' }];
Expand DownExpand Up@@ -158,7 +161,7 @@ describe('objectui#6711 — ObjectGrid no longer copies `reference_to_field` ont
expect(meta).not.toHaveProperty('reference_to_field');
});

it(`still copies the eight surviving relational keys (${name})`, async () => {
it(`still copies the seven surviving relational keys (${name})`, async () => {
const meta = await renderAndCaptureMeta(schemaExtra);
for (const key of SURVIVING_KEYS) {
expect(meta).toHaveProperty(key);
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
fix(plugin-grid): retire the FIELD-meta-dead `titleFormat` relational key by os-sam · Pull Request #7020 · objectstack-ai/objectui · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .changeset/6874-retire-titleformat.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
---
'@object-ui/plugin-grid': patch
---

`ObjectGrid` no longer copies `titleFormat` onto a relational column's `fieldMeta`
(objectui#6874).

`RELATIONAL_META_KEYS` listed eight keys that `applyRelationalMeta` copies off the
object-schema field def onto the built `fieldMeta`, at all three of `generateColumns`'s
column-building call sites. `titleFormat` was one of them and had **zero FIELD-meta
readers**.

This is a zero of a different kind from objectui#6711's, and a stronger one: `titleFormat`
is a real, live key with plenty of readers — it just has none on a field meta. The sweep
did not fail to find readers. It found every member read of the identifier across
`packages/` and `apps/` (tests included) and classified each by its receiver:

- `objectDef` / `objectSchema` / `objSchema` — `core/utils/record-title.ts`,
`components/renderers/layout/containers.tsx`, `plugin-detail/DetailView.tsx`,
`plugin-kanban/ObjectKanban.tsx`, `plugin-calendar/ObjectCalendar.tsx`,
`react/hooks/useRecordSearch.ts`. An OBJECT schema, every one.
- `refObjectSchema?.titleFormat` — `fields/widgets/LookupField.tsx`: the REFERENCED
object's schema, fetched by `getSchema(referenceTo)`. Also an OBJECT schema, and the one
that decides this case — it is what the grid's own inline picker reads.
- `param.titleFormat` — `app-shell/utils/paramToField.ts`, off a resolved `ActionParamDef`.
The field-def read beside it is `field.title_format`, a different spelling on a different
surface.

`RecordPickerDialog` and `lookupColumnDisplay` receive it as a PROP, and the repo's single
`titleFormat=` pass is `titleFormat={refTitleFormat}` — object-schema sourced. So copying
`reference_to` is what makes `titleFormat` work on this path, and copying `titleFormat`
onto the meta reached nothing.

Nothing renders differently, and the argument does not rest on the member sweep alone: the
only computed access to the meta bag anywhere in `@object-ui/fields` or `plugin-grid` is
`applyRelationalMeta`'s own write, so no consumer can pick the key up dynamically. The key
is also not a member of any declared type on this path — `applyRelationalMeta` writes into
a `Record<string, any>` and the bag reaches cell renderers through an `as any` cast.

`plugin-dashboard/src/recordFields.tsx` had already recorded this exact measurement as its
reason for not copying the key into that seam, so it was a measured no-op in two seams and
retired from only one. Same defect class as objectui#6625 (`FieldMeta.decimals`),
objectui#6597 (`FieldMeta.referenceTo`) and objectui#6711 (`reference_to_field`), and the
same disposition as objectui#6711 on this very list.

⚠️ **What the measurement bounds.** The sweep covers this repo and the producer repo. A
host application outside them could still be reading `titleFormat` off the `fieldMeta` a
cell renderer receives; that was never a declared promise this renderer made, and this
repo's own contract is what the retirement is about — but the world was not measured, and a
host reading the key off a field meta gets `undefined` after this change. The supported
source is unchanged and unaffected: the referenced object's schema.

Because the key had no readers on this path, the suite stays green whether or not the
removal is correct, so the absence is pinned directly instead
(`__tests__/relationalMetaCopySet-6874.test.tsx`): all three call sites, each with a
presence assertion on the seven surviving keys as the control against a fixture that passes
by never reaching the copy path.
Original file line numberDiff line numberDiff line change
Expand Up@@ -237,15 +237,18 @@ describe('objectui#6694 — RecordDetailDrawer lookup rows carry their reference
/**
* The copy-set boundary.
*
* `ObjectGrid`'s `applyRelationalMeta` copies NINE keys; this seam copies THREE,
* `ObjectGrid`'s `applyRelationalMeta` copies SEVEN keys — it copied NINE until
* objectui#6711 and objectui#6874 retired `reference_to_field` and `titleFormat`
* from its list, both on the reader measurement this seam had already recorded.
* This seam copies THREE,
* and the difference is measured rather than preferred: the grid's cells are
* EDITABLE, so its extra keys feed the inline picker (`LookupField` / `UserField`
* read `id_field`, `description_field`, `lookup_filters`, `lookupFilters`).
* These two widgets are read-only — their only render path ends at a CELL
* renderer — and `packages/fields/src/index.tsx` reads exactly three relational
* keys off a cell's `field` prop.
*
* ⛔ This is what stops the omitted six from being added back "for parity": a
* ⛔ This is what stops the omitted keys from being added back "for parity": a
* `FieldMeta` member written on every call and read by nothing is precisely what
* objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`) retired from this
* same file. If these widgets ever gain inline editing, that is the event that
Expand All@@ -257,7 +260,11 @@ describe('objectui#6694 — buildFieldMeta copies the cell-read relational keys
reference_to: 'project',
reference: 'project',
display_field: 'project_code',
// The six the grid also copies, which have no reader on this path:
// Six keys with no reader on this path. FOUR of them the grid still copies
// (its picker-only keys); the other two it has since retired as well —
// `reference_to_field` (objectui#6711) and `titleFormat` (objectui#6874).
// All six stay on the fixture on purpose: the assertion below pins THIS
// seam's boundary, which does not move when the grid's list does.
reference_to_field: 'x',
id_field: 'x',
description_field: 'x',
Expand Down
20 changes: 13 additions & 7 deletions packages/plugin-dashboard/src/recordFields.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -92,11 +92,15 @@ export const NUMERIC_FIELD_TYPES = new Set([
* widgets funnel through, which is what this module exists for (see the file
* header: the two surfaces must never drift).
*
* ## ⚠️ The copy set is DELIBERATELY 3 of the grid's 9 — measured, per key
* ## ⚠️ The copy set is DELIBERATELY 3 of the grid's 7 — measured, per key
*
* `RELATIONAL_META_KEYS` is `reference_to`, `reference`, `reference_to_field`,
* `display_field`, `id_field`, `description_field`, `lookup_filters`,
* `lookupFilters`, `titleFormat`. The grid needs all nine because its cells are
* `RELATIONAL_META_KEYS` is `reference_to`, `reference`, `display_field`,
* `id_field`, `description_field`, `lookup_filters`, `lookupFilters`. It listed
* NINE until the two keys this file had already measured as reader-less were
* retired from it as well — `reference_to_field` (objectui#6711) and
* `titleFormat` (objectui#6874).
*
* The grid needs the remaining seven because its cells are
* EDITABLE — its own docblock says the extra keys "drive the inline picker's
* query (LookupField reads reference_to/reference, display_field, id_field,
* description_field, lookup_filters)", and the defect that earned them was an
Expand All@@ -114,15 +118,17 @@ export const NUMERIC_FIELD_TYPES = new Set([
* mentions in that module; read only by `fields/src/widgets/LookupField.tsx`
* and `UserField.tsx`, both EDITORS. ⛔ NOT copied.
* - `reference_to_field` — ZERO member reads anywhere in the repo. ⛔ NOT
* copied.
* copied. ⭐ The grid has since retired it from its own list too
* (objectui#6711); this measurement is what that retirement acted on.
* - `titleFormat` — never read off a FIELD meta at all; every reader takes it
* off the OBJECT schema (`getRecordDisplayName` in `@object-ui/core`,
* `containers.tsx`). On this path that object schema arrives through
* `useRefObjectSchema(reference_to)` — so copying `reference_to` is what
* makes `titleFormat` work, and copying `titleFormat` here would reach
* nothing. ⛔ NOT copied.
* nothing. ⛔ NOT copied. ⭐ The grid has since retired it too
* (objectui#6874), on exactly this reading.
*
* ⛔ Do not "restore parity" by widening this to the grid's nine. A member
* ⛔ Do not "restore parity" by widening this to the grid's seven. A member
* written from the schema def on every call and read by nothing is exactly what
* objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`) retired from this
* very file. Add a key when a reader on THIS path is measured, not before; if
Expand Down
74 changes: 53 additions & 21 deletions packages/plugin-grid/src/ObjectGrid.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -430,42 +430,74 @@ function getDataConfig(schema: ObjectGridSchema): ViewData | null {
* raw id after moving to another row. Copy them from the object-schema field
* definition onto the built `fieldMeta` for every column-building path.
*
* ## ⛔ `reference_to_field` was in this list and is RETIRED (objectui#6711)
* ## ⛔ Two keys were in this list and are RETIRED
*
* Every key here has to have a measured reader on this grid's own render path —
* the cell renderers and inline editors in `@object-ui/fields` that
* `getCellRenderer` dispatches into. `reference_to_field` had none: swept across
* `packages/` and `apps/` (and again across the producer repo), the only
* occurrences of the identifier anywhere were this array literal — the write —
* and prose recording that nothing reads it. No member access, no destructuring,
* no bracket read. `@objectstack/spec`'s FieldSchema does not declare it either,
* so nothing authorable produces it.
* `getCellRenderer` dispatches into. Two keys had none, for two different
* reasons, and each retirement was its own adjudication.
*
* The control that makes that zero a reading, not an artefact of how the sweep
* was written: the same sweep over its list-mates finds real readers for each of
* them — `reference_to` / `reference` / `display_field` in `LookupCellRenderer`,
* `id_field` / `description_field` / `lookup_filters` / `lookupFilters` in
* `LookupField` / `UserField`. ⚠️ One exception, measured and deliberately NOT
* acted on here: `titleFormat` has no FIELD-meta reader either — every reader
* takes it off the OBJECT schema, which reaches the picker through
* `useRefObjectSchema(reference_to)` (`plugin-dashboard/src/recordFields.tsx`
* records the same measurement). Retiring it is a separate adjudication.
* ### `reference_to_field` — objectui#6711
*
* Swept across `packages/` and `apps/` (and again across the producer repo), the
* only occurrences of the identifier anywhere were this array literal — the
* write — and prose recording that nothing reads it. No member access, no
* destructuring, no bracket read. `@objectstack/spec`'s FieldSchema does not
* declare it either, so nothing authorable produces it.
*
* ### `titleFormat` — objectui#6874
*
* A zero of a different kind, and a stronger one. `titleFormat` is a real, live
* key with plenty of readers — it simply has no FIELD-meta reader. The sweep did
* not fail to find readers; it found every member read of the identifier across
* `packages/` and `apps/` (tests included) and classified each one by receiver:
*
* - `objectDef` / `objectSchema` / `objSchema` — `core/utils/record-title.ts`,
* `components/.../containers.tsx`, `plugin-detail/DetailView.tsx`,
* `ObjectKanban.tsx`, `ObjectCalendar.tsx`, `react/hooks/useRecordSearch.ts`.
* OBJECT schema, every one.
* - `refObjectSchema?.titleFormat` — `fields/widgets/LookupField.tsx`: the
* REFERENCED object's schema, fetched by `getSchema(referenceTo)`. Also an
* OBJECT schema, and the one that matters here — it is what this grid's own
* inline picker reads.
* - `param.titleFormat` — `app-shell/utils/paramToField.ts`, off a resolved
* `ActionParamDef`; the field-def read next to it is `field.title_format`,
* a different spelling on a different surface.
*
* `RecordPickerDialog` and `lookupColumnDisplay` receive it as a PROP, and the
* repo's single `titleFormat=` pass is `titleFormat={refTitleFormat}` —
* object-schema sourced. ⇒ copying `reference_to` is what makes `titleFormat`
* work on this path; copying `titleFormat` onto the meta reached nothing.
* `plugin-dashboard/src/recordFields.tsx` recorded this same measurement first
* and declined to copy the key, so it was a measured no-op in two seams and had
* been retired from only one.
*
* ### The control that makes both zeros a reading
*
* Not an artefact of how the sweep was written: the same sweep over the
* surviving list-mates finds a real FIELD-meta reader for every one of them —
* `reference_to` / `reference` / `display_field` off the cell's `field` prop in
* `LookupCellRenderer` (`fields/src/index.tsx`), and `id_field` /
* `description_field` / `lookup_filters` / `lookupFilters` off `fieldMeta?.…`
* in `LookupField` / `UserField`. There is no third reader-less key: all seven
* survivors are read off a field meta.
*
* ⚠️ The sweep bounds these two repos. A host application outside them could
* still be reading the key off `fieldMeta`; the repo's own contract is what this
* retirement is about.
* still be reading either key off `fieldMeta`; the repo's own contract is what
* these retirements are about.
*
* ⛔ Do not re-add a key for symmetry with the object-schema field def. A
* member written from the def on every column build and read by nothing is
* exactly what objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`)
* retired from the sibling producer. Add a key when a reader on THIS path is
* measured, not before. The absence is pinned in
* `__tests__/relationalMetaCopySet-6711.test.tsx`.
* measured, not before. Both absences are pinned, at all three call sites —
* `__tests__/relationalMetaCopySet-6711.test.tsx` and
* `__tests__/relationalMetaCopySet-6874.test.tsx`.
*/
const RELATIONAL_META_KEYS = [
'reference_to', 'reference',
'display_field', 'id_field', 'description_field',
'lookup_filters', 'lookupFilters', 'titleFormat',
'lookup_filters', 'lookupFilters',
] as const;

/**
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,7 +15,7 @@
*
* ## The control against vacuity lives in the same assertions
*
* Each case also asserts that the eight SURVIVING keys do arrive on the same
* Each case also asserts that the seven SURVIVING keys do arrive on the same
* meta. An absence assertion on its own passes for the wrong reason as soon as
* the fixture stops reaching the copy path at all (a renamed helper, a column
* path that no longer resolves this renderer, a def the grid never reads); the
Expand DownExpand Up@@ -60,16 +60,19 @@ const MANAGER_DEF = {
description_field: 'title',
lookup_filters: [['active', '=', true]],
lookupFilters: [['active', '=', true]],
// Also retired, in objectui#6874, and pinned in its own file
// (`relationalMetaCopySet-6874.test.tsx`). Kept on the fixture so this file's
// survivor control stays a list of keys the grid really does still copy.
titleFormat: '{name}',
// The retired key (objectui#6711). Kept on the fixture on purpose.
reference_to_field: 'MUST_NOT_BE_COPIED',
};

/** The eight keys that survive the retirement — the control. */
/** The seven keys that survive both retirements — the control. */
const SURVIVING_KEYS = [
'reference_to', 'reference',
'display_field', 'id_field', 'description_field',
'lookup_filters', 'lookupFilters', 'titleFormat',
'lookup_filters', 'lookupFilters',
] as const;

const ROWS = [{ id: 'r1', name: 'Tower T1', manager: 'u1' }];
Expand DownExpand Up@@ -158,7 +161,7 @@ describe('objectui#6711 — ObjectGrid no longer copies `reference_to_field` ont
expect(meta).not.toHaveProperty('reference_to_field');
});

it(`still copies the eight surviving relational keys (${name})`, async () => {
it(`still copies the seven surviving relational keys (${name})`, async () => {
const meta = await renderAndCaptureMeta(schemaExtra);
for (const key of SURVIVING_KEYS) {
expect(meta).toHaveProperty(key);
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(plugin-grid): retire the FIELD-meta-dead `titleFormat` relational key by os-sam · Pull Request #7020 · objectstack-ai/objectui · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .changeset/6874-retire-titleformat.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
---
'@object-ui/plugin-grid': patch
---

`ObjectGrid` no longer copies `titleFormat` onto a relational column's `fieldMeta`
(objectui#6874).

`RELATIONAL_META_KEYS` listed eight keys that `applyRelationalMeta` copies off the
object-schema field def onto the built `fieldMeta`, at all three of `generateColumns`'s
column-building call sites. `titleFormat` was one of them and had **zero FIELD-meta
readers**.

This is a zero of a different kind from objectui#6711's, and a stronger one: `titleFormat`
is a real, live key with plenty of readers — it just has none on a field meta. The sweep
did not fail to find readers. It found every member read of the identifier across
`packages/` and `apps/` (tests included) and classified each by its receiver:

- `objectDef` / `objectSchema` / `objSchema` — `core/utils/record-title.ts`,
`components/renderers/layout/containers.tsx`, `plugin-detail/DetailView.tsx`,
`plugin-kanban/ObjectKanban.tsx`, `plugin-calendar/ObjectCalendar.tsx`,
`react/hooks/useRecordSearch.ts`. An OBJECT schema, every one.
- `refObjectSchema?.titleFormat` — `fields/widgets/LookupField.tsx`: the REFERENCED
object's schema, fetched by `getSchema(referenceTo)`. Also an OBJECT schema, and the one
that decides this case — it is what the grid's own inline picker reads.
- `param.titleFormat` — `app-shell/utils/paramToField.ts`, off a resolved `ActionParamDef`.
The field-def read beside it is `field.title_format`, a different spelling on a different
surface.

`RecordPickerDialog` and `lookupColumnDisplay` receive it as a PROP, and the repo's single
`titleFormat=` pass is `titleFormat={refTitleFormat}` — object-schema sourced. So copying
`reference_to` is what makes `titleFormat` work on this path, and copying `titleFormat`
onto the meta reached nothing.

Nothing renders differently, and the argument does not rest on the member sweep alone: the
only computed access to the meta bag anywhere in `@object-ui/fields` or `plugin-grid` is
`applyRelationalMeta`'s own write, so no consumer can pick the key up dynamically. The key
is also not a member of any declared type on this path — `applyRelationalMeta` writes into
a `Record<string, any>` and the bag reaches cell renderers through an `as any` cast.

`plugin-dashboard/src/recordFields.tsx` had already recorded this exact measurement as its
reason for not copying the key into that seam, so it was a measured no-op in two seams and
retired from only one. Same defect class as objectui#6625 (`FieldMeta.decimals`),
objectui#6597 (`FieldMeta.referenceTo`) and objectui#6711 (`reference_to_field`), and the
same disposition as objectui#6711 on this very list.

⚠️ **What the measurement bounds.** The sweep covers this repo and the producer repo. A
host application outside them could still be reading `titleFormat` off the `fieldMeta` a
cell renderer receives; that was never a declared promise this renderer made, and this
repo's own contract is what the retirement is about — but the world was not measured, and a
host reading the key off a field meta gets `undefined` after this change. The supported
source is unchanged and unaffected: the referenced object's schema.

Because the key had no readers on this path, the suite stays green whether or not the
removal is correct, so the absence is pinned directly instead
(`__tests__/relationalMetaCopySet-6874.test.tsx`): all three call sites, each with a
presence assertion on the seven surviving keys as the control against a fixture that passes
by never reaching the copy path.
Original file line numberDiff line numberDiff line change
Expand Up@@ -237,15 +237,18 @@ describe('objectui#6694 — RecordDetailDrawer lookup rows carry their reference
/**
* The copy-set boundary.
*
* `ObjectGrid`'s `applyRelationalMeta` copies NINE keys; this seam copies THREE,
* `ObjectGrid`'s `applyRelationalMeta` copies SEVEN keys — it copied NINE until
* objectui#6711 and objectui#6874 retired `reference_to_field` and `titleFormat`
* from its list, both on the reader measurement this seam had already recorded.
* This seam copies THREE,
* and the difference is measured rather than preferred: the grid's cells are
* EDITABLE, so its extra keys feed the inline picker (`LookupField` / `UserField`
* read `id_field`, `description_field`, `lookup_filters`, `lookupFilters`).
* These two widgets are read-only — their only render path ends at a CELL
* renderer — and `packages/fields/src/index.tsx` reads exactly three relational
* keys off a cell's `field` prop.
*
* ⛔ This is what stops the omitted six from being added back "for parity": a
* ⛔ This is what stops the omitted keys from being added back "for parity": a
* `FieldMeta` member written on every call and read by nothing is precisely what
* objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`) retired from this
* same file. If these widgets ever gain inline editing, that is the event that
Expand All@@ -257,7 +260,11 @@ describe('objectui#6694 — buildFieldMeta copies the cell-read relational keys
reference_to: 'project',
reference: 'project',
display_field: 'project_code',
// The six the grid also copies, which have no reader on this path:
// Six keys with no reader on this path. FOUR of them the grid still copies
// (its picker-only keys); the other two it has since retired as well —
// `reference_to_field` (objectui#6711) and `titleFormat` (objectui#6874).
// All six stay on the fixture on purpose: the assertion below pins THIS
// seam's boundary, which does not move when the grid's list does.
reference_to_field: 'x',
id_field: 'x',
description_field: 'x',
Expand Down
20 changes: 13 additions & 7 deletions packages/plugin-dashboard/src/recordFields.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -92,11 +92,15 @@ export const NUMERIC_FIELD_TYPES = new Set([
* widgets funnel through, which is what this module exists for (see the file
* header: the two surfaces must never drift).
*
* ## ⚠️ The copy set is DELIBERATELY 3 of the grid's 9 — measured, per key
* ## ⚠️ The copy set is DELIBERATELY 3 of the grid's 7 — measured, per key
*
* `RELATIONAL_META_KEYS` is `reference_to`, `reference`, `reference_to_field`,
* `display_field`, `id_field`, `description_field`, `lookup_filters`,
* `lookupFilters`, `titleFormat`. The grid needs all nine because its cells are
* `RELATIONAL_META_KEYS` is `reference_to`, `reference`, `display_field`,
* `id_field`, `description_field`, `lookup_filters`, `lookupFilters`. It listed
* NINE until the two keys this file had already measured as reader-less were
* retired from it as well — `reference_to_field` (objectui#6711) and
* `titleFormat` (objectui#6874).
*
* The grid needs the remaining seven because its cells are
* EDITABLE — its own docblock says the extra keys "drive the inline picker's
* query (LookupField reads reference_to/reference, display_field, id_field,
* description_field, lookup_filters)", and the defect that earned them was an
Expand All@@ -114,15 +118,17 @@ export const NUMERIC_FIELD_TYPES = new Set([
* mentions in that module; read only by `fields/src/widgets/LookupField.tsx`
* and `UserField.tsx`, both EDITORS. ⛔ NOT copied.
* - `reference_to_field` — ZERO member reads anywhere in the repo. ⛔ NOT
* copied.
* copied. ⭐ The grid has since retired it from its own list too
* (objectui#6711); this measurement is what that retirement acted on.
* - `titleFormat` — never read off a FIELD meta at all; every reader takes it
* off the OBJECT schema (`getRecordDisplayName` in `@object-ui/core`,
* `containers.tsx`). On this path that object schema arrives through
* `useRefObjectSchema(reference_to)` — so copying `reference_to` is what
* makes `titleFormat` work, and copying `titleFormat` here would reach
* nothing. ⛔ NOT copied.
* nothing. ⛔ NOT copied. ⭐ The grid has since retired it too
* (objectui#6874), on exactly this reading.
*
* ⛔ Do not "restore parity" by widening this to the grid's nine. A member
* ⛔ Do not "restore parity" by widening this to the grid's seven. A member
* written from the schema def on every call and read by nothing is exactly what
* objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`) retired from this
* very file. Add a key when a reader on THIS path is measured, not before; if
Expand Down
74 changes: 53 additions & 21 deletions packages/plugin-grid/src/ObjectGrid.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -430,42 +430,74 @@ function getDataConfig(schema: ObjectGridSchema): ViewData | null {
* raw id after moving to another row. Copy them from the object-schema field
* definition onto the built `fieldMeta` for every column-building path.
*
* ## ⛔ `reference_to_field` was in this list and is RETIRED (objectui#6711)
* ## ⛔ Two keys were in this list and are RETIRED
*
* Every key here has to have a measured reader on this grid's own render path —
* the cell renderers and inline editors in `@object-ui/fields` that
* `getCellRenderer` dispatches into. `reference_to_field` had none: swept across
* `packages/` and `apps/` (and again across the producer repo), the only
* occurrences of the identifier anywhere were this array literal — the write —
* and prose recording that nothing reads it. No member access, no destructuring,
* no bracket read. `@objectstack/spec`'s FieldSchema does not declare it either,
* so nothing authorable produces it.
* `getCellRenderer` dispatches into. Two keys had none, for two different
* reasons, and each retirement was its own adjudication.
*
* The control that makes that zero a reading, not an artefact of how the sweep
* was written: the same sweep over its list-mates finds real readers for each of
* them — `reference_to` / `reference` / `display_field` in `LookupCellRenderer`,
* `id_field` / `description_field` / `lookup_filters` / `lookupFilters` in
* `LookupField` / `UserField`. ⚠️ One exception, measured and deliberately NOT
* acted on here: `titleFormat` has no FIELD-meta reader either — every reader
* takes it off the OBJECT schema, which reaches the picker through
* `useRefObjectSchema(reference_to)` (`plugin-dashboard/src/recordFields.tsx`
* records the same measurement). Retiring it is a separate adjudication.
* ### `reference_to_field` — objectui#6711
*
* Swept across `packages/` and `apps/` (and again across the producer repo), the
* only occurrences of the identifier anywhere were this array literal — the
* write — and prose recording that nothing reads it. No member access, no
* destructuring, no bracket read. `@objectstack/spec`'s FieldSchema does not
* declare it either, so nothing authorable produces it.
*
* ### `titleFormat` — objectui#6874
*
* A zero of a different kind, and a stronger one. `titleFormat` is a real, live
* key with plenty of readers — it simply has no FIELD-meta reader. The sweep did
* not fail to find readers; it found every member read of the identifier across
* `packages/` and `apps/` (tests included) and classified each one by receiver:
*
* - `objectDef` / `objectSchema` / `objSchema` — `core/utils/record-title.ts`,
* `components/.../containers.tsx`, `plugin-detail/DetailView.tsx`,
* `ObjectKanban.tsx`, `ObjectCalendar.tsx`, `react/hooks/useRecordSearch.ts`.
* OBJECT schema, every one.
* - `refObjectSchema?.titleFormat` — `fields/widgets/LookupField.tsx`: the
* REFERENCED object's schema, fetched by `getSchema(referenceTo)`. Also an
* OBJECT schema, and the one that matters here — it is what this grid's own
* inline picker reads.
* - `param.titleFormat` — `app-shell/utils/paramToField.ts`, off a resolved
* `ActionParamDef`; the field-def read next to it is `field.title_format`,
* a different spelling on a different surface.
*
* `RecordPickerDialog` and `lookupColumnDisplay` receive it as a PROP, and the
* repo's single `titleFormat=` pass is `titleFormat={refTitleFormat}` —
* object-schema sourced. ⇒ copying `reference_to` is what makes `titleFormat`
* work on this path; copying `titleFormat` onto the meta reached nothing.
* `plugin-dashboard/src/recordFields.tsx` recorded this same measurement first
* and declined to copy the key, so it was a measured no-op in two seams and had
* been retired from only one.
*
* ### The control that makes both zeros a reading
*
* Not an artefact of how the sweep was written: the same sweep over the
* surviving list-mates finds a real FIELD-meta reader for every one of them —
* `reference_to` / `reference` / `display_field` off the cell's `field` prop in
* `LookupCellRenderer` (`fields/src/index.tsx`), and `id_field` /
* `description_field` / `lookup_filters` / `lookupFilters` off `fieldMeta?.…`
* in `LookupField` / `UserField`. There is no third reader-less key: all seven
* survivors are read off a field meta.
*
* ⚠️ The sweep bounds these two repos. A host application outside them could
* still be reading the key off `fieldMeta`; the repo's own contract is what this
* retirement is about.
* still be reading either key off `fieldMeta`; the repo's own contract is what
* these retirements are about.
*
* ⛔ Do not re-add a key for symmetry with the object-schema field def. A
* member written from the def on every column build and read by nothing is
* exactly what objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`)
* retired from the sibling producer. Add a key when a reader on THIS path is
* measured, not before. The absence is pinned in
* `__tests__/relationalMetaCopySet-6711.test.tsx`.
* measured, not before. Both absences are pinned, at all three call sites —
* `__tests__/relationalMetaCopySet-6711.test.tsx` and
* `__tests__/relationalMetaCopySet-6874.test.tsx`.
*/
const RELATIONAL_META_KEYS = [
'reference_to', 'reference',
'display_field', 'id_field', 'description_field',
'lookup_filters', 'lookupFilters', 'titleFormat',
'lookup_filters', 'lookupFilters',
] as const;

/**
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,7 +15,7 @@
*
* ## The control against vacuity lives in the same assertions
*
* Each case also asserts that the eight SURVIVING keys do arrive on the same
* Each case also asserts that the seven SURVIVING keys do arrive on the same
* meta. An absence assertion on its own passes for the wrong reason as soon as
* the fixture stops reaching the copy path at all (a renamed helper, a column
* path that no longer resolves this renderer, a def the grid never reads); the
Expand DownExpand Up@@ -60,16 +60,19 @@ const MANAGER_DEF = {
description_field: 'title',
lookup_filters: [['active', '=', true]],
lookupFilters: [['active', '=', true]],
// Also retired, in objectui#6874, and pinned in its own file
// (`relationalMetaCopySet-6874.test.tsx`). Kept on the fixture so this file's
// survivor control stays a list of keys the grid really does still copy.
titleFormat: '{name}',
// The retired key (objectui#6711). Kept on the fixture on purpose.
reference_to_field: 'MUST_NOT_BE_COPIED',
};

/** The eight keys that survive the retirement — the control. */
/** The seven keys that survive both retirements — the control. */
const SURVIVING_KEYS = [
'reference_to', 'reference',
'display_field', 'id_field', 'description_field',
'lookup_filters', 'lookupFilters', 'titleFormat',
'lookup_filters', 'lookupFilters',
] as const;

const ROWS = [{ id: 'r1', name: 'Tower T1', manager: 'u1' }];
Expand DownExpand Up@@ -158,7 +161,7 @@ describe('objectui#6711 — ObjectGrid no longer copies `reference_to_field` ont
expect(meta).not.toHaveProperty('reference_to_field');
});

it(`still copies the eight surviving relational keys (${name})`, async () => {
it(`still copies the seven surviving relational keys (${name})`, async () => {
const meta = await renderAndCaptureMeta(schemaExtra);
for (const key of SURVIVING_KEYS) {
expect(meta).toHaveProperty(key);
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(plugin-grid): retire the FIELD-meta-dead `titleFormat` relational key by os-sam · Pull Request #7020 · objectstack-ai/objectui · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .changeset/6874-retire-titleformat.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
---
'@object-ui/plugin-grid': patch
---

`ObjectGrid` no longer copies `titleFormat` onto a relational column's `fieldMeta`
(objectui#6874).

`RELATIONAL_META_KEYS` listed eight keys that `applyRelationalMeta` copies off the
object-schema field def onto the built `fieldMeta`, at all three of `generateColumns`'s
column-building call sites. `titleFormat` was one of them and had **zero FIELD-meta
readers**.

This is a zero of a different kind from objectui#6711's, and a stronger one: `titleFormat`
is a real, live key with plenty of readers — it just has none on a field meta. The sweep
did not fail to find readers. It found every member read of the identifier across
`packages/` and `apps/` (tests included) and classified each by its receiver:

- `objectDef` / `objectSchema` / `objSchema` — `core/utils/record-title.ts`,
`components/renderers/layout/containers.tsx`, `plugin-detail/DetailView.tsx`,
`plugin-kanban/ObjectKanban.tsx`, `plugin-calendar/ObjectCalendar.tsx`,
`react/hooks/useRecordSearch.ts`. An OBJECT schema, every one.
- `refObjectSchema?.titleFormat` — `fields/widgets/LookupField.tsx`: the REFERENCED
object's schema, fetched by `getSchema(referenceTo)`. Also an OBJECT schema, and the one
that decides this case — it is what the grid's own inline picker reads.
- `param.titleFormat` — `app-shell/utils/paramToField.ts`, off a resolved `ActionParamDef`.
The field-def read beside it is `field.title_format`, a different spelling on a different
surface.

`RecordPickerDialog` and `lookupColumnDisplay` receive it as a PROP, and the repo's single
`titleFormat=` pass is `titleFormat={refTitleFormat}` — object-schema sourced. So copying
`reference_to` is what makes `titleFormat` work on this path, and copying `titleFormat`
onto the meta reached nothing.

Nothing renders differently, and the argument does not rest on the member sweep alone: the
only computed access to the meta bag anywhere in `@object-ui/fields` or `plugin-grid` is
`applyRelationalMeta`'s own write, so no consumer can pick the key up dynamically. The key
is also not a member of any declared type on this path — `applyRelationalMeta` writes into
a `Record<string, any>` and the bag reaches cell renderers through an `as any` cast.

`plugin-dashboard/src/recordFields.tsx` had already recorded this exact measurement as its
reason for not copying the key into that seam, so it was a measured no-op in two seams and
retired from only one. Same defect class as objectui#6625 (`FieldMeta.decimals`),
objectui#6597 (`FieldMeta.referenceTo`) and objectui#6711 (`reference_to_field`), and the
same disposition as objectui#6711 on this very list.

⚠️ **What the measurement bounds.** The sweep covers this repo and the producer repo. A
host application outside them could still be reading `titleFormat` off the `fieldMeta` a
cell renderer receives; that was never a declared promise this renderer made, and this
repo's own contract is what the retirement is about — but the world was not measured, and a
host reading the key off a field meta gets `undefined` after this change. The supported
source is unchanged and unaffected: the referenced object's schema.

Because the key had no readers on this path, the suite stays green whether or not the
removal is correct, so the absence is pinned directly instead
(`__tests__/relationalMetaCopySet-6874.test.tsx`): all three call sites, each with a
presence assertion on the seven surviving keys as the control against a fixture that passes
by never reaching the copy path.
Original file line numberDiff line numberDiff line change
Expand Up@@ -237,15 +237,18 @@ describe('objectui#6694 — RecordDetailDrawer lookup rows carry their reference
/**
* The copy-set boundary.
*
* `ObjectGrid`'s `applyRelationalMeta` copies NINE keys; this seam copies THREE,
* `ObjectGrid`'s `applyRelationalMeta` copies SEVEN keys — it copied NINE until
* objectui#6711 and objectui#6874 retired `reference_to_field` and `titleFormat`
* from its list, both on the reader measurement this seam had already recorded.
* This seam copies THREE,
* and the difference is measured rather than preferred: the grid's cells are
* EDITABLE, so its extra keys feed the inline picker (`LookupField` / `UserField`
* read `id_field`, `description_field`, `lookup_filters`, `lookupFilters`).
* These two widgets are read-only — their only render path ends at a CELL
* renderer — and `packages/fields/src/index.tsx` reads exactly three relational
* keys off a cell's `field` prop.
*
* ⛔ This is what stops the omitted six from being added back "for parity": a
* ⛔ This is what stops the omitted keys from being added back "for parity": a
* `FieldMeta` member written on every call and read by nothing is precisely what
* objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`) retired from this
* same file. If these widgets ever gain inline editing, that is the event that
Expand All@@ -257,7 +260,11 @@ describe('objectui#6694 — buildFieldMeta copies the cell-read relational keys
reference_to: 'project',
reference: 'project',
display_field: 'project_code',
// The six the grid also copies, which have no reader on this path:
// Six keys with no reader on this path. FOUR of them the grid still copies
// (its picker-only keys); the other two it has since retired as well —
// `reference_to_field` (objectui#6711) and `titleFormat` (objectui#6874).
// All six stay on the fixture on purpose: the assertion below pins THIS
// seam's boundary, which does not move when the grid's list does.
reference_to_field: 'x',
id_field: 'x',
description_field: 'x',
Expand Down
20 changes: 13 additions & 7 deletions packages/plugin-dashboard/src/recordFields.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -92,11 +92,15 @@ export const NUMERIC_FIELD_TYPES = new Set([
* widgets funnel through, which is what this module exists for (see the file
* header: the two surfaces must never drift).
*
* ## ⚠️ The copy set is DELIBERATELY 3 of the grid's 9 — measured, per key
* ## ⚠️ The copy set is DELIBERATELY 3 of the grid's 7 — measured, per key
*
* `RELATIONAL_META_KEYS` is `reference_to`, `reference`, `reference_to_field`,
* `display_field`, `id_field`, `description_field`, `lookup_filters`,
* `lookupFilters`, `titleFormat`. The grid needs all nine because its cells are
* `RELATIONAL_META_KEYS` is `reference_to`, `reference`, `display_field`,
* `id_field`, `description_field`, `lookup_filters`, `lookupFilters`. It listed
* NINE until the two keys this file had already measured as reader-less were
* retired from it as well — `reference_to_field` (objectui#6711) and
* `titleFormat` (objectui#6874).
*
* The grid needs the remaining seven because its cells are
* EDITABLE — its own docblock says the extra keys "drive the inline picker's
* query (LookupField reads reference_to/reference, display_field, id_field,
* description_field, lookup_filters)", and the defect that earned them was an
Expand All@@ -114,15 +118,17 @@ export const NUMERIC_FIELD_TYPES = new Set([
* mentions in that module; read only by `fields/src/widgets/LookupField.tsx`
* and `UserField.tsx`, both EDITORS. ⛔ NOT copied.
* - `reference_to_field` — ZERO member reads anywhere in the repo. ⛔ NOT
* copied.
* copied. ⭐ The grid has since retired it from its own list too
* (objectui#6711); this measurement is what that retirement acted on.
* - `titleFormat` — never read off a FIELD meta at all; every reader takes it
* off the OBJECT schema (`getRecordDisplayName` in `@object-ui/core`,
* `containers.tsx`). On this path that object schema arrives through
* `useRefObjectSchema(reference_to)` — so copying `reference_to` is what
* makes `titleFormat` work, and copying `titleFormat` here would reach
* nothing. ⛔ NOT copied.
* nothing. ⛔ NOT copied. ⭐ The grid has since retired it too
* (objectui#6874), on exactly this reading.
*
* ⛔ Do not "restore parity" by widening this to the grid's nine. A member
* ⛔ Do not "restore parity" by widening this to the grid's seven. A member
* written from the schema def on every call and read by nothing is exactly what
* objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`) retired from this
* very file. Add a key when a reader on THIS path is measured, not before; if
Expand Down
74 changes: 53 additions & 21 deletions packages/plugin-grid/src/ObjectGrid.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -430,42 +430,74 @@ function getDataConfig(schema: ObjectGridSchema): ViewData | null {
* raw id after moving to another row. Copy them from the object-schema field
* definition onto the built `fieldMeta` for every column-building path.
*
* ## ⛔ `reference_to_field` was in this list and is RETIRED (objectui#6711)
* ## ⛔ Two keys were in this list and are RETIRED
*
* Every key here has to have a measured reader on this grid's own render path —
* the cell renderers and inline editors in `@object-ui/fields` that
* `getCellRenderer` dispatches into. `reference_to_field` had none: swept across
* `packages/` and `apps/` (and again across the producer repo), the only
* occurrences of the identifier anywhere were this array literal — the write —
* and prose recording that nothing reads it. No member access, no destructuring,
* no bracket read. `@objectstack/spec`'s FieldSchema does not declare it either,
* so nothing authorable produces it.
* `getCellRenderer` dispatches into. Two keys had none, for two different
* reasons, and each retirement was its own adjudication.
*
* The control that makes that zero a reading, not an artefact of how the sweep
* was written: the same sweep over its list-mates finds real readers for each of
* them — `reference_to` / `reference` / `display_field` in `LookupCellRenderer`,
* `id_field` / `description_field` / `lookup_filters` / `lookupFilters` in
* `LookupField` / `UserField`. ⚠️ One exception, measured and deliberately NOT
* acted on here: `titleFormat` has no FIELD-meta reader either — every reader
* takes it off the OBJECT schema, which reaches the picker through
* `useRefObjectSchema(reference_to)` (`plugin-dashboard/src/recordFields.tsx`
* records the same measurement). Retiring it is a separate adjudication.
* ### `reference_to_field` — objectui#6711
*
* Swept across `packages/` and `apps/` (and again across the producer repo), the
* only occurrences of the identifier anywhere were this array literal — the
* write — and prose recording that nothing reads it. No member access, no
* destructuring, no bracket read. `@objectstack/spec`'s FieldSchema does not
* declare it either, so nothing authorable produces it.
*
* ### `titleFormat` — objectui#6874
*
* A zero of a different kind, and a stronger one. `titleFormat` is a real, live
* key with plenty of readers — it simply has no FIELD-meta reader. The sweep did
* not fail to find readers; it found every member read of the identifier across
* `packages/` and `apps/` (tests included) and classified each one by receiver:
*
* - `objectDef` / `objectSchema` / `objSchema` — `core/utils/record-title.ts`,
* `components/.../containers.tsx`, `plugin-detail/DetailView.tsx`,
* `ObjectKanban.tsx`, `ObjectCalendar.tsx`, `react/hooks/useRecordSearch.ts`.
* OBJECT schema, every one.
* - `refObjectSchema?.titleFormat` — `fields/widgets/LookupField.tsx`: the
* REFERENCED object's schema, fetched by `getSchema(referenceTo)`. Also an
* OBJECT schema, and the one that matters here — it is what this grid's own
* inline picker reads.
* - `param.titleFormat` — `app-shell/utils/paramToField.ts`, off a resolved
* `ActionParamDef`; the field-def read next to it is `field.title_format`,
* a different spelling on a different surface.
*
* `RecordPickerDialog` and `lookupColumnDisplay` receive it as a PROP, and the
* repo's single `titleFormat=` pass is `titleFormat={refTitleFormat}` —
* object-schema sourced. ⇒ copying `reference_to` is what makes `titleFormat`
* work on this path; copying `titleFormat` onto the meta reached nothing.
* `plugin-dashboard/src/recordFields.tsx` recorded this same measurement first
* and declined to copy the key, so it was a measured no-op in two seams and had
* been retired from only one.
*
* ### The control that makes both zeros a reading
*
* Not an artefact of how the sweep was written: the same sweep over the
* surviving list-mates finds a real FIELD-meta reader for every one of them —
* `reference_to` / `reference` / `display_field` off the cell's `field` prop in
* `LookupCellRenderer` (`fields/src/index.tsx`), and `id_field` /
* `description_field` / `lookup_filters` / `lookupFilters` off `fieldMeta?.…`
* in `LookupField` / `UserField`. There is no third reader-less key: all seven
* survivors are read off a field meta.
*
* ⚠️ The sweep bounds these two repos. A host application outside them could
* still be reading the key off `fieldMeta`; the repo's own contract is what this
* retirement is about.
* still be reading either key off `fieldMeta`; the repo's own contract is what
* these retirements are about.
*
* ⛔ Do not re-add a key for symmetry with the object-schema field def. A
* member written from the def on every column build and read by nothing is
* exactly what objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`)
* retired from the sibling producer. Add a key when a reader on THIS path is
* measured, not before. The absence is pinned in
* `__tests__/relationalMetaCopySet-6711.test.tsx`.
* measured, not before. Both absences are pinned, at all three call sites —
* `__tests__/relationalMetaCopySet-6711.test.tsx` and
* `__tests__/relationalMetaCopySet-6874.test.tsx`.
*/
const RELATIONAL_META_KEYS = [
'reference_to', 'reference',
'display_field', 'id_field', 'description_field',
'lookup_filters', 'lookupFilters', 'titleFormat',
'lookup_filters', 'lookupFilters',
] as const;

/**
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,7 +15,7 @@
*
* ## The control against vacuity lives in the same assertions
*
* Each case also asserts that the eight SURVIVING keys do arrive on the same
* Each case also asserts that the seven SURVIVING keys do arrive on the same
* meta. An absence assertion on its own passes for the wrong reason as soon as
* the fixture stops reaching the copy path at all (a renamed helper, a column
* path that no longer resolves this renderer, a def the grid never reads); the
Expand DownExpand Up@@ -60,16 +60,19 @@ const MANAGER_DEF = {
description_field: 'title',
lookup_filters: [['active', '=', true]],
lookupFilters: [['active', '=', true]],
// Also retired, in objectui#6874, and pinned in its own file
// (`relationalMetaCopySet-6874.test.tsx`). Kept on the fixture so this file's
// survivor control stays a list of keys the grid really does still copy.
titleFormat: '{name}',
// The retired key (objectui#6711). Kept on the fixture on purpose.
reference_to_field: 'MUST_NOT_BE_COPIED',
};

/** The eight keys that survive the retirement — the control. */
/** The seven keys that survive both retirements — the control. */
const SURVIVING_KEYS = [
'reference_to', 'reference',
'display_field', 'id_field', 'description_field',
'lookup_filters', 'lookupFilters', 'titleFormat',
'lookup_filters', 'lookupFilters',
] as const;

const ROWS = [{ id: 'r1', name: 'Tower T1', manager: 'u1' }];
Expand DownExpand Up@@ -158,7 +161,7 @@ describe('objectui#6711 — ObjectGrid no longer copies `reference_to_field` ont
expect(meta).not.toHaveProperty('reference_to_field');
});

it(`still copies the eight surviving relational keys (${name})`, async () => {
it(`still copies the seven surviving relational keys (${name})`, async () => {
const meta = await renderAndCaptureMeta(schemaExtra);
for (const key of SURVIVING_KEYS) {
expect(meta).toHaveProperty(key);
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' fix(plugin-grid): retire the FIELD-meta-dead `titleFormat` relational key by os-sam · Pull Request #7020 · objectstack-ai/objectui · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .changeset/6874-retire-titleformat.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
---
'@object-ui/plugin-grid': patch
---

`ObjectGrid` no longer copies `titleFormat` onto a relational column's `fieldMeta`
(objectui#6874).

`RELATIONAL_META_KEYS` listed eight keys that `applyRelationalMeta` copies off the
object-schema field def onto the built `fieldMeta`, at all three of `generateColumns`'s
column-building call sites. `titleFormat` was one of them and had **zero FIELD-meta
readers**.

This is a zero of a different kind from objectui#6711's, and a stronger one: `titleFormat`
is a real, live key with plenty of readers — it just has none on a field meta. The sweep
did not fail to find readers. It found every member read of the identifier across
`packages/` and `apps/` (tests included) and classified each by its receiver:

- `objectDef` / `objectSchema` / `objSchema` — `core/utils/record-title.ts`,
`components/renderers/layout/containers.tsx`, `plugin-detail/DetailView.tsx`,
`plugin-kanban/ObjectKanban.tsx`, `plugin-calendar/ObjectCalendar.tsx`,
`react/hooks/useRecordSearch.ts`. An OBJECT schema, every one.
- `refObjectSchema?.titleFormat` — `fields/widgets/LookupField.tsx`: the REFERENCED
object's schema, fetched by `getSchema(referenceTo)`. Also an OBJECT schema, and the one
that decides this case — it is what the grid's own inline picker reads.
- `param.titleFormat` — `app-shell/utils/paramToField.ts`, off a resolved `ActionParamDef`.
The field-def read beside it is `field.title_format`, a different spelling on a different
surface.

`RecordPickerDialog` and `lookupColumnDisplay` receive it as a PROP, and the repo's single
`titleFormat=` pass is `titleFormat={refTitleFormat}` — object-schema sourced. So copying
`reference_to` is what makes `titleFormat` work on this path, and copying `titleFormat`
onto the meta reached nothing.

Nothing renders differently, and the argument does not rest on the member sweep alone: the
only computed access to the meta bag anywhere in `@object-ui/fields` or `plugin-grid` is
`applyRelationalMeta`'s own write, so no consumer can pick the key up dynamically. The key
is also not a member of any declared type on this path — `applyRelationalMeta` writes into
a `Record<string, any>` and the bag reaches cell renderers through an `as any` cast.

`plugin-dashboard/src/recordFields.tsx` had already recorded this exact measurement as its
reason for not copying the key into that seam, so it was a measured no-op in two seams and
retired from only one. Same defect class as objectui#6625 (`FieldMeta.decimals`),
objectui#6597 (`FieldMeta.referenceTo`) and objectui#6711 (`reference_to_field`), and the
same disposition as objectui#6711 on this very list.

⚠️ **What the measurement bounds.** The sweep covers this repo and the producer repo. A
host application outside them could still be reading `titleFormat` off the `fieldMeta` a
cell renderer receives; that was never a declared promise this renderer made, and this
repo's own contract is what the retirement is about — but the world was not measured, and a
host reading the key off a field meta gets `undefined` after this change. The supported
source is unchanged and unaffected: the referenced object's schema.

Because the key had no readers on this path, the suite stays green whether or not the
removal is correct, so the absence is pinned directly instead
(`__tests__/relationalMetaCopySet-6874.test.tsx`): all three call sites, each with a
presence assertion on the seven surviving keys as the control against a fixture that passes
by never reaching the copy path.
Original file line numberDiff line numberDiff line change
Expand Up@@ -237,15 +237,18 @@ describe('objectui#6694 — RecordDetailDrawer lookup rows carry their reference
/**
* The copy-set boundary.
*
* `ObjectGrid`'s `applyRelationalMeta` copies NINE keys; this seam copies THREE,
* `ObjectGrid`'s `applyRelationalMeta` copies SEVEN keys — it copied NINE until
* objectui#6711 and objectui#6874 retired `reference_to_field` and `titleFormat`
* from its list, both on the reader measurement this seam had already recorded.
* This seam copies THREE,
* and the difference is measured rather than preferred: the grid's cells are
* EDITABLE, so its extra keys feed the inline picker (`LookupField` / `UserField`
* read `id_field`, `description_field`, `lookup_filters`, `lookupFilters`).
* These two widgets are read-only — their only render path ends at a CELL
* renderer — and `packages/fields/src/index.tsx` reads exactly three relational
* keys off a cell's `field` prop.
*
* ⛔ This is what stops the omitted six from being added back "for parity": a
* ⛔ This is what stops the omitted keys from being added back "for parity": a
* `FieldMeta` member written on every call and read by nothing is precisely what
* objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`) retired from this
* same file. If these widgets ever gain inline editing, that is the event that
Expand All@@ -257,7 +260,11 @@ describe('objectui#6694 — buildFieldMeta copies the cell-read relational keys
reference_to: 'project',
reference: 'project',
display_field: 'project_code',
// The six the grid also copies, which have no reader on this path:
// Six keys with no reader on this path. FOUR of them the grid still copies
// (its picker-only keys); the other two it has since retired as well —
// `reference_to_field` (objectui#6711) and `titleFormat` (objectui#6874).
// All six stay on the fixture on purpose: the assertion below pins THIS
// seam's boundary, which does not move when the grid's list does.
reference_to_field: 'x',
id_field: 'x',
description_field: 'x',
Expand Down
20 changes: 13 additions & 7 deletions packages/plugin-dashboard/src/recordFields.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -92,11 +92,15 @@ export const NUMERIC_FIELD_TYPES = new Set([
* widgets funnel through, which is what this module exists for (see the file
* header: the two surfaces must never drift).
*
* ## ⚠️ The copy set is DELIBERATELY 3 of the grid's 9 — measured, per key
* ## ⚠️ The copy set is DELIBERATELY 3 of the grid's 7 — measured, per key
*
* `RELATIONAL_META_KEYS` is `reference_to`, `reference`, `reference_to_field`,
* `display_field`, `id_field`, `description_field`, `lookup_filters`,
* `lookupFilters`, `titleFormat`. The grid needs all nine because its cells are
* `RELATIONAL_META_KEYS` is `reference_to`, `reference`, `display_field`,
* `id_field`, `description_field`, `lookup_filters`, `lookupFilters`. It listed
* NINE until the two keys this file had already measured as reader-less were
* retired from it as well — `reference_to_field` (objectui#6711) and
* `titleFormat` (objectui#6874).
*
* The grid needs the remaining seven because its cells are
* EDITABLE — its own docblock says the extra keys "drive the inline picker's
* query (LookupField reads reference_to/reference, display_field, id_field,
* description_field, lookup_filters)", and the defect that earned them was an
Expand All@@ -114,15 +118,17 @@ export const NUMERIC_FIELD_TYPES = new Set([
* mentions in that module; read only by `fields/src/widgets/LookupField.tsx`
* and `UserField.tsx`, both EDITORS. ⛔ NOT copied.
* - `reference_to_field` — ZERO member reads anywhere in the repo. ⛔ NOT
* copied.
* copied. ⭐ The grid has since retired it from its own list too
* (objectui#6711); this measurement is what that retirement acted on.
* - `titleFormat` — never read off a FIELD meta at all; every reader takes it
* off the OBJECT schema (`getRecordDisplayName` in `@object-ui/core`,
* `containers.tsx`). On this path that object schema arrives through
* `useRefObjectSchema(reference_to)` — so copying `reference_to` is what
* makes `titleFormat` work, and copying `titleFormat` here would reach
* nothing. ⛔ NOT copied.
* nothing. ⛔ NOT copied. ⭐ The grid has since retired it too
* (objectui#6874), on exactly this reading.
*
* ⛔ Do not "restore parity" by widening this to the grid's nine. A member
* ⛔ Do not "restore parity" by widening this to the grid's seven. A member
* written from the schema def on every call and read by nothing is exactly what
* objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`) retired from this
* very file. Add a key when a reader on THIS path is measured, not before; if
Expand Down
74 changes: 53 additions & 21 deletions packages/plugin-grid/src/ObjectGrid.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -430,42 +430,74 @@ function getDataConfig(schema: ObjectGridSchema): ViewData | null {
* raw id after moving to another row. Copy them from the object-schema field
* definition onto the built `fieldMeta` for every column-building path.
*
* ## ⛔ `reference_to_field` was in this list and is RETIRED (objectui#6711)
* ## ⛔ Two keys were in this list and are RETIRED
*
* Every key here has to have a measured reader on this grid's own render path —
* the cell renderers and inline editors in `@object-ui/fields` that
* `getCellRenderer` dispatches into. `reference_to_field` had none: swept across
* `packages/` and `apps/` (and again across the producer repo), the only
* occurrences of the identifier anywhere were this array literal — the write —
* and prose recording that nothing reads it. No member access, no destructuring,
* no bracket read. `@objectstack/spec`'s FieldSchema does not declare it either,
* so nothing authorable produces it.
* `getCellRenderer` dispatches into. Two keys had none, for two different
* reasons, and each retirement was its own adjudication.
*
* The control that makes that zero a reading, not an artefact of how the sweep
* was written: the same sweep over its list-mates finds real readers for each of
* them — `reference_to` / `reference` / `display_field` in `LookupCellRenderer`,
* `id_field` / `description_field` / `lookup_filters` / `lookupFilters` in
* `LookupField` / `UserField`. ⚠️ One exception, measured and deliberately NOT
* acted on here: `titleFormat` has no FIELD-meta reader either — every reader
* takes it off the OBJECT schema, which reaches the picker through
* `useRefObjectSchema(reference_to)` (`plugin-dashboard/src/recordFields.tsx`
* records the same measurement). Retiring it is a separate adjudication.
* ### `reference_to_field` — objectui#6711
*
* Swept across `packages/` and `apps/` (and again across the producer repo), the
* only occurrences of the identifier anywhere were this array literal — the
* write — and prose recording that nothing reads it. No member access, no
* destructuring, no bracket read. `@objectstack/spec`'s FieldSchema does not
* declare it either, so nothing authorable produces it.
*
* ### `titleFormat` — objectui#6874
*
* A zero of a different kind, and a stronger one. `titleFormat` is a real, live
* key with plenty of readers — it simply has no FIELD-meta reader. The sweep did
* not fail to find readers; it found every member read of the identifier across
* `packages/` and `apps/` (tests included) and classified each one by receiver:
*
* - `objectDef` / `objectSchema` / `objSchema` — `core/utils/record-title.ts`,
* `components/.../containers.tsx`, `plugin-detail/DetailView.tsx`,
* `ObjectKanban.tsx`, `ObjectCalendar.tsx`, `react/hooks/useRecordSearch.ts`.
* OBJECT schema, every one.
* - `refObjectSchema?.titleFormat` — `fields/widgets/LookupField.tsx`: the
* REFERENCED object's schema, fetched by `getSchema(referenceTo)`. Also an
* OBJECT schema, and the one that matters here — it is what this grid's own
* inline picker reads.
* - `param.titleFormat` — `app-shell/utils/paramToField.ts`, off a resolved
* `ActionParamDef`; the field-def read next to it is `field.title_format`,
* a different spelling on a different surface.
*
* `RecordPickerDialog` and `lookupColumnDisplay` receive it as a PROP, and the
* repo's single `titleFormat=` pass is `titleFormat={refTitleFormat}` —
* object-schema sourced. ⇒ copying `reference_to` is what makes `titleFormat`
* work on this path; copying `titleFormat` onto the meta reached nothing.
* `plugin-dashboard/src/recordFields.tsx` recorded this same measurement first
* and declined to copy the key, so it was a measured no-op in two seams and had
* been retired from only one.
*
* ### The control that makes both zeros a reading
*
* Not an artefact of how the sweep was written: the same sweep over the
* surviving list-mates finds a real FIELD-meta reader for every one of them —
* `reference_to` / `reference` / `display_field` off the cell's `field` prop in
* `LookupCellRenderer` (`fields/src/index.tsx`), and `id_field` /
* `description_field` / `lookup_filters` / `lookupFilters` off `fieldMeta?.…`
* in `LookupField` / `UserField`. There is no third reader-less key: all seven
* survivors are read off a field meta.
*
* ⚠️ The sweep bounds these two repos. A host application outside them could
* still be reading the key off `fieldMeta`; the repo's own contract is what this
* retirement is about.
* still be reading either key off `fieldMeta`; the repo's own contract is what
* these retirements are about.
*
* ⛔ Do not re-add a key for symmetry with the object-schema field def. A
* member written from the def on every column build and read by nothing is
* exactly what objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`)
* retired from the sibling producer. Add a key when a reader on THIS path is
* measured, not before. The absence is pinned in
* `__tests__/relationalMetaCopySet-6711.test.tsx`.
* measured, not before. Both absences are pinned, at all three call sites —
* `__tests__/relationalMetaCopySet-6711.test.tsx` and
* `__tests__/relationalMetaCopySet-6874.test.tsx`.
*/
const RELATIONAL_META_KEYS = [
'reference_to', 'reference',
'display_field', 'id_field', 'description_field',
'lookup_filters', 'lookupFilters', 'titleFormat',
'lookup_filters', 'lookupFilters',
] as const;

/**
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,7 +15,7 @@
*
* ## The control against vacuity lives in the same assertions
*
* Each case also asserts that the eight SURVIVING keys do arrive on the same
* Each case also asserts that the seven SURVIVING keys do arrive on the same
* meta. An absence assertion on its own passes for the wrong reason as soon as
* the fixture stops reaching the copy path at all (a renamed helper, a column
* path that no longer resolves this renderer, a def the grid never reads); the
Expand DownExpand Up@@ -60,16 +60,19 @@ const MANAGER_DEF = {
description_field: 'title',
lookup_filters: [['active', '=', true]],
lookupFilters: [['active', '=', true]],
// Also retired, in objectui#6874, and pinned in its own file
// (`relationalMetaCopySet-6874.test.tsx`). Kept on the fixture so this file's
// survivor control stays a list of keys the grid really does still copy.
titleFormat: '{name}',
// The retired key (objectui#6711). Kept on the fixture on purpose.
reference_to_field: 'MUST_NOT_BE_COPIED',
};

/** The eight keys that survive the retirement — the control. */
/** The seven keys that survive both retirements — the control. */
const SURVIVING_KEYS = [
'reference_to', 'reference',
'display_field', 'id_field', 'description_field',
'lookup_filters', 'lookupFilters', 'titleFormat',
'lookup_filters', 'lookupFilters',
] as const;

const ROWS = [{ id: 'r1', name: 'Tower T1', manager: 'u1' }];
Expand DownExpand Up@@ -158,7 +161,7 @@ describe('objectui#6711 — ObjectGrid no longer copies `reference_to_field` ont
expect(meta).not.toHaveProperty('reference_to_field');
});

it(`still copies the eight surviving relational keys (${name})`, async () => {
it(`still copies the seven surviving relational keys (${name})`, async () => {
const meta = await renderAndCaptureMeta(schemaExtra);
for (const key of SURVIVING_KEYS) {
expect(meta).toHaveProperty(key);
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(plugin-grid): retire the FIELD-meta-dead `titleFormat` relational key by os-sam · Pull Request #7020 · objectstack-ai/objectui · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .changeset/6874-retire-titleformat.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
---
'@object-ui/plugin-grid': patch
---

`ObjectGrid` no longer copies `titleFormat` onto a relational column's `fieldMeta`
(objectui#6874).

`RELATIONAL_META_KEYS` listed eight keys that `applyRelationalMeta` copies off the
object-schema field def onto the built `fieldMeta`, at all three of `generateColumns`'s
column-building call sites. `titleFormat` was one of them and had **zero FIELD-meta
readers**.

This is a zero of a different kind from objectui#6711's, and a stronger one: `titleFormat`
is a real, live key with plenty of readers — it just has none on a field meta. The sweep
did not fail to find readers. It found every member read of the identifier across
`packages/` and `apps/` (tests included) and classified each by its receiver:

- `objectDef` / `objectSchema` / `objSchema` — `core/utils/record-title.ts`,
`components/renderers/layout/containers.tsx`, `plugin-detail/DetailView.tsx`,
`plugin-kanban/ObjectKanban.tsx`, `plugin-calendar/ObjectCalendar.tsx`,
`react/hooks/useRecordSearch.ts`. An OBJECT schema, every one.
- `refObjectSchema?.titleFormat` — `fields/widgets/LookupField.tsx`: the REFERENCED
object's schema, fetched by `getSchema(referenceTo)`. Also an OBJECT schema, and the one
that decides this case — it is what the grid's own inline picker reads.
- `param.titleFormat` — `app-shell/utils/paramToField.ts`, off a resolved `ActionParamDef`.
The field-def read beside it is `field.title_format`, a different spelling on a different
surface.

`RecordPickerDialog` and `lookupColumnDisplay` receive it as a PROP, and the repo's single
`titleFormat=` pass is `titleFormat={refTitleFormat}` — object-schema sourced. So copying
`reference_to` is what makes `titleFormat` work on this path, and copying `titleFormat`
onto the meta reached nothing.

Nothing renders differently, and the argument does not rest on the member sweep alone: the
only computed access to the meta bag anywhere in `@object-ui/fields` or `plugin-grid` is
`applyRelationalMeta`'s own write, so no consumer can pick the key up dynamically. The key
is also not a member of any declared type on this path — `applyRelationalMeta` writes into
a `Record<string, any>` and the bag reaches cell renderers through an `as any` cast.

`plugin-dashboard/src/recordFields.tsx` had already recorded this exact measurement as its
reason for not copying the key into that seam, so it was a measured no-op in two seams and
retired from only one. Same defect class as objectui#6625 (`FieldMeta.decimals`),
objectui#6597 (`FieldMeta.referenceTo`) and objectui#6711 (`reference_to_field`), and the
same disposition as objectui#6711 on this very list.

⚠️ **What the measurement bounds.** The sweep covers this repo and the producer repo. A
host application outside them could still be reading `titleFormat` off the `fieldMeta` a
cell renderer receives; that was never a declared promise this renderer made, and this
repo's own contract is what the retirement is about — but the world was not measured, and a
host reading the key off a field meta gets `undefined` after this change. The supported
source is unchanged and unaffected: the referenced object's schema.

Because the key had no readers on this path, the suite stays green whether or not the
removal is correct, so the absence is pinned directly instead
(`__tests__/relationalMetaCopySet-6874.test.tsx`): all three call sites, each with a
presence assertion on the seven surviving keys as the control against a fixture that passes
by never reaching the copy path.
Original file line numberDiff line numberDiff line change
Expand Up@@ -237,15 +237,18 @@ describe('objectui#6694 — RecordDetailDrawer lookup rows carry their reference
/**
* The copy-set boundary.
*
* `ObjectGrid`'s `applyRelationalMeta` copies NINE keys; this seam copies THREE,
* `ObjectGrid`'s `applyRelationalMeta` copies SEVEN keys — it copied NINE until
* objectui#6711 and objectui#6874 retired `reference_to_field` and `titleFormat`
* from its list, both on the reader measurement this seam had already recorded.
* This seam copies THREE,
* and the difference is measured rather than preferred: the grid's cells are
* EDITABLE, so its extra keys feed the inline picker (`LookupField` / `UserField`
* read `id_field`, `description_field`, `lookup_filters`, `lookupFilters`).
* These two widgets are read-only — their only render path ends at a CELL
* renderer — and `packages/fields/src/index.tsx` reads exactly three relational
* keys off a cell's `field` prop.
*
* ⛔ This is what stops the omitted six from being added back "for parity": a
* ⛔ This is what stops the omitted keys from being added back "for parity": a
* `FieldMeta` member written on every call and read by nothing is precisely what
* objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`) retired from this
* same file. If these widgets ever gain inline editing, that is the event that
Expand All@@ -257,7 +260,11 @@ describe('objectui#6694 — buildFieldMeta copies the cell-read relational keys
reference_to: 'project',
reference: 'project',
display_field: 'project_code',
// The six the grid also copies, which have no reader on this path:
// Six keys with no reader on this path. FOUR of them the grid still copies
// (its picker-only keys); the other two it has since retired as well —
// `reference_to_field` (objectui#6711) and `titleFormat` (objectui#6874).
// All six stay on the fixture on purpose: the assertion below pins THIS
// seam's boundary, which does not move when the grid's list does.
reference_to_field: 'x',
id_field: 'x',
description_field: 'x',
Expand Down
20 changes: 13 additions & 7 deletions packages/plugin-dashboard/src/recordFields.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -92,11 +92,15 @@ export const NUMERIC_FIELD_TYPES = new Set([
* widgets funnel through, which is what this module exists for (see the file
* header: the two surfaces must never drift).
*
* ## ⚠️ The copy set is DELIBERATELY 3 of the grid's 9 — measured, per key
* ## ⚠️ The copy set is DELIBERATELY 3 of the grid's 7 — measured, per key
*
* `RELATIONAL_META_KEYS` is `reference_to`, `reference`, `reference_to_field`,
* `display_field`, `id_field`, `description_field`, `lookup_filters`,
* `lookupFilters`, `titleFormat`. The grid needs all nine because its cells are
* `RELATIONAL_META_KEYS` is `reference_to`, `reference`, `display_field`,
* `id_field`, `description_field`, `lookup_filters`, `lookupFilters`. It listed
* NINE until the two keys this file had already measured as reader-less were
* retired from it as well — `reference_to_field` (objectui#6711) and
* `titleFormat` (objectui#6874).
*
* The grid needs the remaining seven because its cells are
* EDITABLE — its own docblock says the extra keys "drive the inline picker's
* query (LookupField reads reference_to/reference, display_field, id_field,
* description_field, lookup_filters)", and the defect that earned them was an
Expand All@@ -114,15 +118,17 @@ export const NUMERIC_FIELD_TYPES = new Set([
* mentions in that module; read only by `fields/src/widgets/LookupField.tsx`
* and `UserField.tsx`, both EDITORS. ⛔ NOT copied.
* - `reference_to_field` — ZERO member reads anywhere in the repo. ⛔ NOT
* copied.
* copied. ⭐ The grid has since retired it from its own list too
* (objectui#6711); this measurement is what that retirement acted on.
* - `titleFormat` — never read off a FIELD meta at all; every reader takes it
* off the OBJECT schema (`getRecordDisplayName` in `@object-ui/core`,
* `containers.tsx`). On this path that object schema arrives through
* `useRefObjectSchema(reference_to)` — so copying `reference_to` is what
* makes `titleFormat` work, and copying `titleFormat` here would reach
* nothing. ⛔ NOT copied.
* nothing. ⛔ NOT copied. ⭐ The grid has since retired it too
* (objectui#6874), on exactly this reading.
*
* ⛔ Do not "restore parity" by widening this to the grid's nine. A member
* ⛔ Do not "restore parity" by widening this to the grid's seven. A member
* written from the schema def on every call and read by nothing is exactly what
* objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`) retired from this
* very file. Add a key when a reader on THIS path is measured, not before; if
Expand Down
74 changes: 53 additions & 21 deletions packages/plugin-grid/src/ObjectGrid.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -430,42 +430,74 @@ function getDataConfig(schema: ObjectGridSchema): ViewData | null {
* raw id after moving to another row. Copy them from the object-schema field
* definition onto the built `fieldMeta` for every column-building path.
*
* ## ⛔ `reference_to_field` was in this list and is RETIRED (objectui#6711)
* ## ⛔ Two keys were in this list and are RETIRED
*
* Every key here has to have a measured reader on this grid's own render path —
* the cell renderers and inline editors in `@object-ui/fields` that
* `getCellRenderer` dispatches into. `reference_to_field` had none: swept across
* `packages/` and `apps/` (and again across the producer repo), the only
* occurrences of the identifier anywhere were this array literal — the write —
* and prose recording that nothing reads it. No member access, no destructuring,
* no bracket read. `@objectstack/spec`'s FieldSchema does not declare it either,
* so nothing authorable produces it.
* `getCellRenderer` dispatches into. Two keys had none, for two different
* reasons, and each retirement was its own adjudication.
*
* The control that makes that zero a reading, not an artefact of how the sweep
* was written: the same sweep over its list-mates finds real readers for each of
* them — `reference_to` / `reference` / `display_field` in `LookupCellRenderer`,
* `id_field` / `description_field` / `lookup_filters` / `lookupFilters` in
* `LookupField` / `UserField`. ⚠️ One exception, measured and deliberately NOT
* acted on here: `titleFormat` has no FIELD-meta reader either — every reader
* takes it off the OBJECT schema, which reaches the picker through
* `useRefObjectSchema(reference_to)` (`plugin-dashboard/src/recordFields.tsx`
* records the same measurement). Retiring it is a separate adjudication.
* ### `reference_to_field` — objectui#6711
*
* Swept across `packages/` and `apps/` (and again across the producer repo), the
* only occurrences of the identifier anywhere were this array literal — the
* write — and prose recording that nothing reads it. No member access, no
* destructuring, no bracket read. `@objectstack/spec`'s FieldSchema does not
* declare it either, so nothing authorable produces it.
*
* ### `titleFormat` — objectui#6874
*
* A zero of a different kind, and a stronger one. `titleFormat` is a real, live
* key with plenty of readers — it simply has no FIELD-meta reader. The sweep did
* not fail to find readers; it found every member read of the identifier across
* `packages/` and `apps/` (tests included) and classified each one by receiver:
*
* - `objectDef` / `objectSchema` / `objSchema` — `core/utils/record-title.ts`,
* `components/.../containers.tsx`, `plugin-detail/DetailView.tsx`,
* `ObjectKanban.tsx`, `ObjectCalendar.tsx`, `react/hooks/useRecordSearch.ts`.
* OBJECT schema, every one.
* - `refObjectSchema?.titleFormat` — `fields/widgets/LookupField.tsx`: the
* REFERENCED object's schema, fetched by `getSchema(referenceTo)`. Also an
* OBJECT schema, and the one that matters here — it is what this grid's own
* inline picker reads.
* - `param.titleFormat` — `app-shell/utils/paramToField.ts`, off a resolved
* `ActionParamDef`; the field-def read next to it is `field.title_format`,
* a different spelling on a different surface.
*
* `RecordPickerDialog` and `lookupColumnDisplay` receive it as a PROP, and the
* repo's single `titleFormat=` pass is `titleFormat={refTitleFormat}` —
* object-schema sourced. ⇒ copying `reference_to` is what makes `titleFormat`
* work on this path; copying `titleFormat` onto the meta reached nothing.
* `plugin-dashboard/src/recordFields.tsx` recorded this same measurement first
* and declined to copy the key, so it was a measured no-op in two seams and had
* been retired from only one.
*
* ### The control that makes both zeros a reading
*
* Not an artefact of how the sweep was written: the same sweep over the
* surviving list-mates finds a real FIELD-meta reader for every one of them —
* `reference_to` / `reference` / `display_field` off the cell's `field` prop in
* `LookupCellRenderer` (`fields/src/index.tsx`), and `id_field` /
* `description_field` / `lookup_filters` / `lookupFilters` off `fieldMeta?.…`
* in `LookupField` / `UserField`. There is no third reader-less key: all seven
* survivors are read off a field meta.
*
* ⚠️ The sweep bounds these two repos. A host application outside them could
* still be reading the key off `fieldMeta`; the repo's own contract is what this
* retirement is about.
* still be reading either key off `fieldMeta`; the repo's own contract is what
* these retirements are about.
*
* ⛔ Do not re-add a key for symmetry with the object-schema field def. A
* member written from the def on every column build and read by nothing is
* exactly what objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`)
* retired from the sibling producer. Add a key when a reader on THIS path is
* measured, not before. The absence is pinned in
* `__tests__/relationalMetaCopySet-6711.test.tsx`.
* measured, not before. Both absences are pinned, at all three call sites —
* `__tests__/relationalMetaCopySet-6711.test.tsx` and
* `__tests__/relationalMetaCopySet-6874.test.tsx`.
*/
const RELATIONAL_META_KEYS = [
'reference_to', 'reference',
'display_field', 'id_field', 'description_field',
'lookup_filters', 'lookupFilters', 'titleFormat',
'lookup_filters', 'lookupFilters',
] as const;

/**
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,7 +15,7 @@
*
* ## The control against vacuity lives in the same assertions
*
* Each case also asserts that the eight SURVIVING keys do arrive on the same
* Each case also asserts that the seven SURVIVING keys do arrive on the same
* meta. An absence assertion on its own passes for the wrong reason as soon as
* the fixture stops reaching the copy path at all (a renamed helper, a column
* path that no longer resolves this renderer, a def the grid never reads); the
Expand DownExpand Up@@ -60,16 +60,19 @@ const MANAGER_DEF = {
description_field: 'title',
lookup_filters: [['active', '=', true]],
lookupFilters: [['active', '=', true]],
// Also retired, in objectui#6874, and pinned in its own file
// (`relationalMetaCopySet-6874.test.tsx`). Kept on the fixture so this file's
// survivor control stays a list of keys the grid really does still copy.
titleFormat: '{name}',
// The retired key (objectui#6711). Kept on the fixture on purpose.
reference_to_field: 'MUST_NOT_BE_COPIED',
};

/** The eight keys that survive the retirement — the control. */
/** The seven keys that survive both retirements — the control. */
const SURVIVING_KEYS = [
'reference_to', 'reference',
'display_field', 'id_field', 'description_field',
'lookup_filters', 'lookupFilters', 'titleFormat',
'lookup_filters', 'lookupFilters',
] as const;

const ROWS = [{ id: 'r1', name: 'Tower T1', manager: 'u1' }];
Expand DownExpand Up@@ -158,7 +161,7 @@ describe('objectui#6711 — ObjectGrid no longer copies `reference_to_field` ont
expect(meta).not.toHaveProperty('reference_to_field');
});

it(`still copies the eight surviving relational keys (${name})`, async () => {
it(`still copies the seven surviving relational keys (${name})`, async () => {
const meta = await renderAndCaptureMeta(schemaExtra);
for (const key of SURVIVING_KEYS) {
expect(meta).toHaveProperty(key);
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(plugin-grid): retire the FIELD-meta-dead `titleFormat` relational key by os-sam · Pull Request #7020 · objectstack-ai/objectui · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .changeset/6874-retire-titleformat.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
---
'@object-ui/plugin-grid': patch
---

`ObjectGrid` no longer copies `titleFormat` onto a relational column's `fieldMeta`
(objectui#6874).

`RELATIONAL_META_KEYS` listed eight keys that `applyRelationalMeta` copies off the
object-schema field def onto the built `fieldMeta`, at all three of `generateColumns`'s
column-building call sites. `titleFormat` was one of them and had **zero FIELD-meta
readers**.

This is a zero of a different kind from objectui#6711's, and a stronger one: `titleFormat`
is a real, live key with plenty of readers — it just has none on a field meta. The sweep
did not fail to find readers. It found every member read of the identifier across
`packages/` and `apps/` (tests included) and classified each by its receiver:

- `objectDef` / `objectSchema` / `objSchema` — `core/utils/record-title.ts`,
`components/renderers/layout/containers.tsx`, `plugin-detail/DetailView.tsx`,
`plugin-kanban/ObjectKanban.tsx`, `plugin-calendar/ObjectCalendar.tsx`,
`react/hooks/useRecordSearch.ts`. An OBJECT schema, every one.
- `refObjectSchema?.titleFormat` — `fields/widgets/LookupField.tsx`: the REFERENCED
object's schema, fetched by `getSchema(referenceTo)`. Also an OBJECT schema, and the one
that decides this case — it is what the grid's own inline picker reads.
- `param.titleFormat` — `app-shell/utils/paramToField.ts`, off a resolved `ActionParamDef`.
The field-def read beside it is `field.title_format`, a different spelling on a different
surface.

`RecordPickerDialog` and `lookupColumnDisplay` receive it as a PROP, and the repo's single
`titleFormat=` pass is `titleFormat={refTitleFormat}` — object-schema sourced. So copying
`reference_to` is what makes `titleFormat` work on this path, and copying `titleFormat`
onto the meta reached nothing.

Nothing renders differently, and the argument does not rest on the member sweep alone: the
only computed access to the meta bag anywhere in `@object-ui/fields` or `plugin-grid` is
`applyRelationalMeta`'s own write, so no consumer can pick the key up dynamically. The key
is also not a member of any declared type on this path — `applyRelationalMeta` writes into
a `Record<string, any>` and the bag reaches cell renderers through an `as any` cast.

`plugin-dashboard/src/recordFields.tsx` had already recorded this exact measurement as its
reason for not copying the key into that seam, so it was a measured no-op in two seams and
retired from only one. Same defect class as objectui#6625 (`FieldMeta.decimals`),
objectui#6597 (`FieldMeta.referenceTo`) and objectui#6711 (`reference_to_field`), and the
same disposition as objectui#6711 on this very list.

⚠️ **What the measurement bounds.** The sweep covers this repo and the producer repo. A
host application outside them could still be reading `titleFormat` off the `fieldMeta` a
cell renderer receives; that was never a declared promise this renderer made, and this
repo's own contract is what the retirement is about — but the world was not measured, and a
host reading the key off a field meta gets `undefined` after this change. The supported
source is unchanged and unaffected: the referenced object's schema.

Because the key had no readers on this path, the suite stays green whether or not the
removal is correct, so the absence is pinned directly instead
(`__tests__/relationalMetaCopySet-6874.test.tsx`): all three call sites, each with a
presence assertion on the seven surviving keys as the control against a fixture that passes
by never reaching the copy path.
Original file line numberDiff line numberDiff line change
Expand Up@@ -237,15 +237,18 @@ describe('objectui#6694 — RecordDetailDrawer lookup rows carry their reference
/**
* The copy-set boundary.
*
* `ObjectGrid`'s `applyRelationalMeta` copies NINE keys; this seam copies THREE,
* `ObjectGrid`'s `applyRelationalMeta` copies SEVEN keys — it copied NINE until
* objectui#6711 and objectui#6874 retired `reference_to_field` and `titleFormat`
* from its list, both on the reader measurement this seam had already recorded.
* This seam copies THREE,
* and the difference is measured rather than preferred: the grid's cells are
* EDITABLE, so its extra keys feed the inline picker (`LookupField` / `UserField`
* read `id_field`, `description_field`, `lookup_filters`, `lookupFilters`).
* These two widgets are read-only — their only render path ends at a CELL
* renderer — and `packages/fields/src/index.tsx` reads exactly three relational
* keys off a cell's `field` prop.
*
* ⛔ This is what stops the omitted six from being added back "for parity": a
* ⛔ This is what stops the omitted keys from being added back "for parity": a
* `FieldMeta` member written on every call and read by nothing is precisely what
* objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`) retired from this
* same file. If these widgets ever gain inline editing, that is the event that
Expand All@@ -257,7 +260,11 @@ describe('objectui#6694 — buildFieldMeta copies the cell-read relational keys
reference_to: 'project',
reference: 'project',
display_field: 'project_code',
// The six the grid also copies, which have no reader on this path:
// Six keys with no reader on this path. FOUR of them the grid still copies
// (its picker-only keys); the other two it has since retired as well —
// `reference_to_field` (objectui#6711) and `titleFormat` (objectui#6874).
// All six stay on the fixture on purpose: the assertion below pins THIS
// seam's boundary, which does not move when the grid's list does.
reference_to_field: 'x',
id_field: 'x',
description_field: 'x',
Expand Down
20 changes: 13 additions & 7 deletions packages/plugin-dashboard/src/recordFields.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -92,11 +92,15 @@ export const NUMERIC_FIELD_TYPES = new Set([
* widgets funnel through, which is what this module exists for (see the file
* header: the two surfaces must never drift).
*
* ## ⚠️ The copy set is DELIBERATELY 3 of the grid's 9 — measured, per key
* ## ⚠️ The copy set is DELIBERATELY 3 of the grid's 7 — measured, per key
*
* `RELATIONAL_META_KEYS` is `reference_to`, `reference`, `reference_to_field`,
* `display_field`, `id_field`, `description_field`, `lookup_filters`,
* `lookupFilters`, `titleFormat`. The grid needs all nine because its cells are
* `RELATIONAL_META_KEYS` is `reference_to`, `reference`, `display_field`,
* `id_field`, `description_field`, `lookup_filters`, `lookupFilters`. It listed
* NINE until the two keys this file had already measured as reader-less were
* retired from it as well — `reference_to_field` (objectui#6711) and
* `titleFormat` (objectui#6874).
*
* The grid needs the remaining seven because its cells are
* EDITABLE — its own docblock says the extra keys "drive the inline picker's
* query (LookupField reads reference_to/reference, display_field, id_field,
* description_field, lookup_filters)", and the defect that earned them was an
Expand All@@ -114,15 +118,17 @@ export const NUMERIC_FIELD_TYPES = new Set([
* mentions in that module; read only by `fields/src/widgets/LookupField.tsx`
* and `UserField.tsx`, both EDITORS. ⛔ NOT copied.
* - `reference_to_field` — ZERO member reads anywhere in the repo. ⛔ NOT
* copied.
* copied. ⭐ The grid has since retired it from its own list too
* (objectui#6711); this measurement is what that retirement acted on.
* - `titleFormat` — never read off a FIELD meta at all; every reader takes it
* off the OBJECT schema (`getRecordDisplayName` in `@object-ui/core`,
* `containers.tsx`). On this path that object schema arrives through
* `useRefObjectSchema(reference_to)` — so copying `reference_to` is what
* makes `titleFormat` work, and copying `titleFormat` here would reach
* nothing. ⛔ NOT copied.
* nothing. ⛔ NOT copied. ⭐ The grid has since retired it too
* (objectui#6874), on exactly this reading.
*
* ⛔ Do not "restore parity" by widening this to the grid's nine. A member
* ⛔ Do not "restore parity" by widening this to the grid's seven. A member
* written from the schema def on every call and read by nothing is exactly what
* objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`) retired from this
* very file. Add a key when a reader on THIS path is measured, not before; if
Expand Down
74 changes: 53 additions & 21 deletions packages/plugin-grid/src/ObjectGrid.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -430,42 +430,74 @@ function getDataConfig(schema: ObjectGridSchema): ViewData | null {
* raw id after moving to another row. Copy them from the object-schema field
* definition onto the built `fieldMeta` for every column-building path.
*
* ## ⛔ `reference_to_field` was in this list and is RETIRED (objectui#6711)
* ## ⛔ Two keys were in this list and are RETIRED
*
* Every key here has to have a measured reader on this grid's own render path —
* the cell renderers and inline editors in `@object-ui/fields` that
* `getCellRenderer` dispatches into. `reference_to_field` had none: swept across
* `packages/` and `apps/` (and again across the producer repo), the only
* occurrences of the identifier anywhere were this array literal — the write —
* and prose recording that nothing reads it. No member access, no destructuring,
* no bracket read. `@objectstack/spec`'s FieldSchema does not declare it either,
* so nothing authorable produces it.
* `getCellRenderer` dispatches into. Two keys had none, for two different
* reasons, and each retirement was its own adjudication.
*
* The control that makes that zero a reading, not an artefact of how the sweep
* was written: the same sweep over its list-mates finds real readers for each of
* them — `reference_to` / `reference` / `display_field` in `LookupCellRenderer`,
* `id_field` / `description_field` / `lookup_filters` / `lookupFilters` in
* `LookupField` / `UserField`. ⚠️ One exception, measured and deliberately NOT
* acted on here: `titleFormat` has no FIELD-meta reader either — every reader
* takes it off the OBJECT schema, which reaches the picker through
* `useRefObjectSchema(reference_to)` (`plugin-dashboard/src/recordFields.tsx`
* records the same measurement). Retiring it is a separate adjudication.
* ### `reference_to_field` — objectui#6711
*
* Swept across `packages/` and `apps/` (and again across the producer repo), the
* only occurrences of the identifier anywhere were this array literal — the
* write — and prose recording that nothing reads it. No member access, no
* destructuring, no bracket read. `@objectstack/spec`'s FieldSchema does not
* declare it either, so nothing authorable produces it.
*
* ### `titleFormat` — objectui#6874
*
* A zero of a different kind, and a stronger one. `titleFormat` is a real, live
* key with plenty of readers — it simply has no FIELD-meta reader. The sweep did
* not fail to find readers; it found every member read of the identifier across
* `packages/` and `apps/` (tests included) and classified each one by receiver:
*
* - `objectDef` / `objectSchema` / `objSchema` — `core/utils/record-title.ts`,
* `components/.../containers.tsx`, `plugin-detail/DetailView.tsx`,
* `ObjectKanban.tsx`, `ObjectCalendar.tsx`, `react/hooks/useRecordSearch.ts`.
* OBJECT schema, every one.
* - `refObjectSchema?.titleFormat` — `fields/widgets/LookupField.tsx`: the
* REFERENCED object's schema, fetched by `getSchema(referenceTo)`. Also an
* OBJECT schema, and the one that matters here — it is what this grid's own
* inline picker reads.
* - `param.titleFormat` — `app-shell/utils/paramToField.ts`, off a resolved
* `ActionParamDef`; the field-def read next to it is `field.title_format`,
* a different spelling on a different surface.
*
* `RecordPickerDialog` and `lookupColumnDisplay` receive it as a PROP, and the
* repo's single `titleFormat=` pass is `titleFormat={refTitleFormat}` —
* object-schema sourced. ⇒ copying `reference_to` is what makes `titleFormat`
* work on this path; copying `titleFormat` onto the meta reached nothing.
* `plugin-dashboard/src/recordFields.tsx` recorded this same measurement first
* and declined to copy the key, so it was a measured no-op in two seams and had
* been retired from only one.
*
* ### The control that makes both zeros a reading
*
* Not an artefact of how the sweep was written: the same sweep over the
* surviving list-mates finds a real FIELD-meta reader for every one of them —
* `reference_to` / `reference` / `display_field` off the cell's `field` prop in
* `LookupCellRenderer` (`fields/src/index.tsx`), and `id_field` /
* `description_field` / `lookup_filters` / `lookupFilters` off `fieldMeta?.…`
* in `LookupField` / `UserField`. There is no third reader-less key: all seven
* survivors are read off a field meta.
*
* ⚠️ The sweep bounds these two repos. A host application outside them could
* still be reading the key off `fieldMeta`; the repo's own contract is what this
* retirement is about.
* still be reading either key off `fieldMeta`; the repo's own contract is what
* these retirements are about.
*
* ⛔ Do not re-add a key for symmetry with the object-schema field def. A
* member written from the def on every column build and read by nothing is
* exactly what objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`)
* retired from the sibling producer. Add a key when a reader on THIS path is
* measured, not before. The absence is pinned in
* `__tests__/relationalMetaCopySet-6711.test.tsx`.
* measured, not before. Both absences are pinned, at all three call sites —
* `__tests__/relationalMetaCopySet-6711.test.tsx` and
* `__tests__/relationalMetaCopySet-6874.test.tsx`.
*/
const RELATIONAL_META_KEYS = [
'reference_to', 'reference',
'display_field', 'id_field', 'description_field',
'lookup_filters', 'lookupFilters', 'titleFormat',
'lookup_filters', 'lookupFilters',
] as const;

/**
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,7 +15,7 @@
*
* ## The control against vacuity lives in the same assertions
*
* Each case also asserts that the eight SURVIVING keys do arrive on the same
* Each case also asserts that the seven SURVIVING keys do arrive on the same
* meta. An absence assertion on its own passes for the wrong reason as soon as
* the fixture stops reaching the copy path at all (a renamed helper, a column
* path that no longer resolves this renderer, a def the grid never reads); the
Expand DownExpand Up@@ -60,16 +60,19 @@ const MANAGER_DEF = {
description_field: 'title',
lookup_filters: [['active', '=', true]],
lookupFilters: [['active', '=', true]],
// Also retired, in objectui#6874, and pinned in its own file
// (`relationalMetaCopySet-6874.test.tsx`). Kept on the fixture so this file's
// survivor control stays a list of keys the grid really does still copy.
titleFormat: '{name}',
// The retired key (objectui#6711). Kept on the fixture on purpose.
reference_to_field: 'MUST_NOT_BE_COPIED',
};

/** The eight keys that survive the retirement — the control. */
/** The seven keys that survive both retirements — the control. */
const SURVIVING_KEYS = [
'reference_to', 'reference',
'display_field', 'id_field', 'description_field',
'lookup_filters', 'lookupFilters', 'titleFormat',
'lookup_filters', 'lookupFilters',
] as const;

const ROWS = [{ id: 'r1', name: 'Tower T1', manager: 'u1' }];
Expand DownExpand Up@@ -158,7 +161,7 @@ describe('objectui#6711 — ObjectGrid no longer copies `reference_to_field` ont
expect(meta).not.toHaveProperty('reference_to_field');
});

it(`still copies the eight surviving relational keys (${name})`, async () => {
it(`still copies the seven surviving relational keys (${name})`, async () => {
const meta = await renderAndCaptureMeta(schemaExtra);
for (const key of SURVIVING_KEYS) {
expect(meta).toHaveProperty(key);
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); fix(plugin-grid): retire the FIELD-meta-dead `titleFormat` relational key by os-sam · Pull Request #7020 · objectstack-ai/objectui · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .changeset/6874-retire-titleformat.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
---
'@object-ui/plugin-grid': patch
---

`ObjectGrid` no longer copies `titleFormat` onto a relational column's `fieldMeta`
(objectui#6874).

`RELATIONAL_META_KEYS` listed eight keys that `applyRelationalMeta` copies off the
object-schema field def onto the built `fieldMeta`, at all three of `generateColumns`'s
column-building call sites. `titleFormat` was one of them and had **zero FIELD-meta
readers**.

This is a zero of a different kind from objectui#6711's, and a stronger one: `titleFormat`
is a real, live key with plenty of readers — it just has none on a field meta. The sweep
did not fail to find readers. It found every member read of the identifier across
`packages/` and `apps/` (tests included) and classified each by its receiver:

- `objectDef` / `objectSchema` / `objSchema` — `core/utils/record-title.ts`,
`components/renderers/layout/containers.tsx`, `plugin-detail/DetailView.tsx`,
`plugin-kanban/ObjectKanban.tsx`, `plugin-calendar/ObjectCalendar.tsx`,
`react/hooks/useRecordSearch.ts`. An OBJECT schema, every one.
- `refObjectSchema?.titleFormat` — `fields/widgets/LookupField.tsx`: the REFERENCED
object's schema, fetched by `getSchema(referenceTo)`. Also an OBJECT schema, and the one
that decides this case — it is what the grid's own inline picker reads.
- `param.titleFormat` — `app-shell/utils/paramToField.ts`, off a resolved `ActionParamDef`.
The field-def read beside it is `field.title_format`, a different spelling on a different
surface.

`RecordPickerDialog` and `lookupColumnDisplay` receive it as a PROP, and the repo's single
`titleFormat=` pass is `titleFormat={refTitleFormat}` — object-schema sourced. So copying
`reference_to` is what makes `titleFormat` work on this path, and copying `titleFormat`
onto the meta reached nothing.

Nothing renders differently, and the argument does not rest on the member sweep alone: the
only computed access to the meta bag anywhere in `@object-ui/fields` or `plugin-grid` is
`applyRelationalMeta`'s own write, so no consumer can pick the key up dynamically. The key
is also not a member of any declared type on this path — `applyRelationalMeta` writes into
a `Record<string, any>` and the bag reaches cell renderers through an `as any` cast.

`plugin-dashboard/src/recordFields.tsx` had already recorded this exact measurement as its
reason for not copying the key into that seam, so it was a measured no-op in two seams and
retired from only one. Same defect class as objectui#6625 (`FieldMeta.decimals`),
objectui#6597 (`FieldMeta.referenceTo`) and objectui#6711 (`reference_to_field`), and the
same disposition as objectui#6711 on this very list.

⚠️ **What the measurement bounds.** The sweep covers this repo and the producer repo. A
host application outside them could still be reading `titleFormat` off the `fieldMeta` a
cell renderer receives; that was never a declared promise this renderer made, and this
repo's own contract is what the retirement is about — but the world was not measured, and a
host reading the key off a field meta gets `undefined` after this change. The supported
source is unchanged and unaffected: the referenced object's schema.

Because the key had no readers on this path, the suite stays green whether or not the
removal is correct, so the absence is pinned directly instead
(`__tests__/relationalMetaCopySet-6874.test.tsx`): all three call sites, each with a
presence assertion on the seven surviving keys as the control against a fixture that passes
by never reaching the copy path.
Original file line numberDiff line numberDiff line change
Expand Up@@ -237,15 +237,18 @@ describe('objectui#6694 — RecordDetailDrawer lookup rows carry their reference
/**
* The copy-set boundary.
*
* `ObjectGrid`'s `applyRelationalMeta` copies NINE keys; this seam copies THREE,
* `ObjectGrid`'s `applyRelationalMeta` copies SEVEN keys — it copied NINE until
* objectui#6711 and objectui#6874 retired `reference_to_field` and `titleFormat`
* from its list, both on the reader measurement this seam had already recorded.
* This seam copies THREE,
* and the difference is measured rather than preferred: the grid's cells are
* EDITABLE, so its extra keys feed the inline picker (`LookupField` / `UserField`
* read `id_field`, `description_field`, `lookup_filters`, `lookupFilters`).
* These two widgets are read-only — their only render path ends at a CELL
* renderer — and `packages/fields/src/index.tsx` reads exactly three relational
* keys off a cell's `field` prop.
*
* ⛔ This is what stops the omitted six from being added back "for parity": a
* ⛔ This is what stops the omitted keys from being added back "for parity": a
* `FieldMeta` member written on every call and read by nothing is precisely what
* objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`) retired from this
* same file. If these widgets ever gain inline editing, that is the event that
Expand All@@ -257,7 +260,11 @@ describe('objectui#6694 — buildFieldMeta copies the cell-read relational keys
reference_to: 'project',
reference: 'project',
display_field: 'project_code',
// The six the grid also copies, which have no reader on this path:
// Six keys with no reader on this path. FOUR of them the grid still copies
// (its picker-only keys); the other two it has since retired as well —
// `reference_to_field` (objectui#6711) and `titleFormat` (objectui#6874).
// All six stay on the fixture on purpose: the assertion below pins THIS
// seam's boundary, which does not move when the grid's list does.
reference_to_field: 'x',
id_field: 'x',
description_field: 'x',
Expand Down
20 changes: 13 additions & 7 deletions packages/plugin-dashboard/src/recordFields.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -92,11 +92,15 @@ export const NUMERIC_FIELD_TYPES = new Set([
* widgets funnel through, which is what this module exists for (see the file
* header: the two surfaces must never drift).
*
* ## ⚠️ The copy set is DELIBERATELY 3 of the grid's 9 — measured, per key
* ## ⚠️ The copy set is DELIBERATELY 3 of the grid's 7 — measured, per key
*
* `RELATIONAL_META_KEYS` is `reference_to`, `reference`, `reference_to_field`,
* `display_field`, `id_field`, `description_field`, `lookup_filters`,
* `lookupFilters`, `titleFormat`. The grid needs all nine because its cells are
* `RELATIONAL_META_KEYS` is `reference_to`, `reference`, `display_field`,
* `id_field`, `description_field`, `lookup_filters`, `lookupFilters`. It listed
* NINE until the two keys this file had already measured as reader-less were
* retired from it as well — `reference_to_field` (objectui#6711) and
* `titleFormat` (objectui#6874).
*
* The grid needs the remaining seven because its cells are
* EDITABLE — its own docblock says the extra keys "drive the inline picker's
* query (LookupField reads reference_to/reference, display_field, id_field,
* description_field, lookup_filters)", and the defect that earned them was an
Expand All@@ -114,15 +118,17 @@ export const NUMERIC_FIELD_TYPES = new Set([
* mentions in that module; read only by `fields/src/widgets/LookupField.tsx`
* and `UserField.tsx`, both EDITORS. ⛔ NOT copied.
* - `reference_to_field` — ZERO member reads anywhere in the repo. ⛔ NOT
* copied.
* copied. ⭐ The grid has since retired it from its own list too
* (objectui#6711); this measurement is what that retirement acted on.
* - `titleFormat` — never read off a FIELD meta at all; every reader takes it
* off the OBJECT schema (`getRecordDisplayName` in `@object-ui/core`,
* `containers.tsx`). On this path that object schema arrives through
* `useRefObjectSchema(reference_to)` — so copying `reference_to` is what
* makes `titleFormat` work, and copying `titleFormat` here would reach
* nothing. ⛔ NOT copied.
* nothing. ⛔ NOT copied. ⭐ The grid has since retired it too
* (objectui#6874), on exactly this reading.
*
* ⛔ Do not "restore parity" by widening this to the grid's nine. A member
* ⛔ Do not "restore parity" by widening this to the grid's seven. A member
* written from the schema def on every call and read by nothing is exactly what
* objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`) retired from this
* very file. Add a key when a reader on THIS path is measured, not before; if
Expand Down
74 changes: 53 additions & 21 deletions packages/plugin-grid/src/ObjectGrid.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -430,42 +430,74 @@ function getDataConfig(schema: ObjectGridSchema): ViewData | null {
* raw id after moving to another row. Copy them from the object-schema field
* definition onto the built `fieldMeta` for every column-building path.
*
* ## ⛔ `reference_to_field` was in this list and is RETIRED (objectui#6711)
* ## ⛔ Two keys were in this list and are RETIRED
*
* Every key here has to have a measured reader on this grid's own render path —
* the cell renderers and inline editors in `@object-ui/fields` that
* `getCellRenderer` dispatches into. `reference_to_field` had none: swept across
* `packages/` and `apps/` (and again across the producer repo), the only
* occurrences of the identifier anywhere were this array literal — the write —
* and prose recording that nothing reads it. No member access, no destructuring,
* no bracket read. `@objectstack/spec`'s FieldSchema does not declare it either,
* so nothing authorable produces it.
* `getCellRenderer` dispatches into. Two keys had none, for two different
* reasons, and each retirement was its own adjudication.
*
* The control that makes that zero a reading, not an artefact of how the sweep
* was written: the same sweep over its list-mates finds real readers for each of
* them — `reference_to` / `reference` / `display_field` in `LookupCellRenderer`,
* `id_field` / `description_field` / `lookup_filters` / `lookupFilters` in
* `LookupField` / `UserField`. ⚠️ One exception, measured and deliberately NOT
* acted on here: `titleFormat` has no FIELD-meta reader either — every reader
* takes it off the OBJECT schema, which reaches the picker through
* `useRefObjectSchema(reference_to)` (`plugin-dashboard/src/recordFields.tsx`
* records the same measurement). Retiring it is a separate adjudication.
* ### `reference_to_field` — objectui#6711
*
* Swept across `packages/` and `apps/` (and again across the producer repo), the
* only occurrences of the identifier anywhere were this array literal — the
* write — and prose recording that nothing reads it. No member access, no
* destructuring, no bracket read. `@objectstack/spec`'s FieldSchema does not
* declare it either, so nothing authorable produces it.
*
* ### `titleFormat` — objectui#6874
*
* A zero of a different kind, and a stronger one. `titleFormat` is a real, live
* key with plenty of readers — it simply has no FIELD-meta reader. The sweep did
* not fail to find readers; it found every member read of the identifier across
* `packages/` and `apps/` (tests included) and classified each one by receiver:
*
* - `objectDef` / `objectSchema` / `objSchema` — `core/utils/record-title.ts`,
* `components/.../containers.tsx`, `plugin-detail/DetailView.tsx`,
* `ObjectKanban.tsx`, `ObjectCalendar.tsx`, `react/hooks/useRecordSearch.ts`.
* OBJECT schema, every one.
* - `refObjectSchema?.titleFormat` — `fields/widgets/LookupField.tsx`: the
* REFERENCED object's schema, fetched by `getSchema(referenceTo)`. Also an
* OBJECT schema, and the one that matters here — it is what this grid's own
* inline picker reads.
* - `param.titleFormat` — `app-shell/utils/paramToField.ts`, off a resolved
* `ActionParamDef`; the field-def read next to it is `field.title_format`,
* a different spelling on a different surface.
*
* `RecordPickerDialog` and `lookupColumnDisplay` receive it as a PROP, and the
* repo's single `titleFormat=` pass is `titleFormat={refTitleFormat}` —
* object-schema sourced. ⇒ copying `reference_to` is what makes `titleFormat`
* work on this path; copying `titleFormat` onto the meta reached nothing.
* `plugin-dashboard/src/recordFields.tsx` recorded this same measurement first
* and declined to copy the key, so it was a measured no-op in two seams and had
* been retired from only one.
*
* ### The control that makes both zeros a reading
*
* Not an artefact of how the sweep was written: the same sweep over the
* surviving list-mates finds a real FIELD-meta reader for every one of them —
* `reference_to` / `reference` / `display_field` off the cell's `field` prop in
* `LookupCellRenderer` (`fields/src/index.tsx`), and `id_field` /
* `description_field` / `lookup_filters` / `lookupFilters` off `fieldMeta?.…`
* in `LookupField` / `UserField`. There is no third reader-less key: all seven
* survivors are read off a field meta.
*
* ⚠️ The sweep bounds these two repos. A host application outside them could
* still be reading the key off `fieldMeta`; the repo's own contract is what this
* retirement is about.
* still be reading either key off `fieldMeta`; the repo's own contract is what
* these retirements are about.
*
* ⛔ Do not re-add a key for symmetry with the object-schema field def. A
* member written from the def on every column build and read by nothing is
* exactly what objectui#6625 (`decimals`) and objectui#6597 (`referenceTo`)
* retired from the sibling producer. Add a key when a reader on THIS path is
* measured, not before. The absence is pinned in
* `__tests__/relationalMetaCopySet-6711.test.tsx`.
* measured, not before. Both absences are pinned, at all three call sites —
* `__tests__/relationalMetaCopySet-6711.test.tsx` and
* `__tests__/relationalMetaCopySet-6874.test.tsx`.
*/
const RELATIONAL_META_KEYS = [
'reference_to', 'reference',
'display_field', 'id_field', 'description_field',
'lookup_filters', 'lookupFilters', 'titleFormat',
'lookup_filters', 'lookupFilters',
] as const;

/**
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,7 +15,7 @@
*
* ## The control against vacuity lives in the same assertions
*
* Each case also asserts that the eight SURVIVING keys do arrive on the same
* Each case also asserts that the seven SURVIVING keys do arrive on the same
* meta. An absence assertion on its own passes for the wrong reason as soon as
* the fixture stops reaching the copy path at all (a renamed helper, a column
* path that no longer resolves this renderer, a def the grid never reads); the
Expand DownExpand Up@@ -60,16 +60,19 @@ const MANAGER_DEF = {
description_field: 'title',
lookup_filters: [['active', '=', true]],
lookupFilters: [['active', '=', true]],
// Also retired, in objectui#6874, and pinned in its own file
// (`relationalMetaCopySet-6874.test.tsx`). Kept on the fixture so this file's
// survivor control stays a list of keys the grid really does still copy.
titleFormat: '{name}',
// The retired key (objectui#6711). Kept on the fixture on purpose.
reference_to_field: 'MUST_NOT_BE_COPIED',
};

/** The eight keys that survive the retirement — the control. */
/** The seven keys that survive both retirements — the control. */
const SURVIVING_KEYS = [
'reference_to', 'reference',
'display_field', 'id_field', 'description_field',
'lookup_filters', 'lookupFilters', 'titleFormat',
'lookup_filters', 'lookupFilters',
] as const;

const ROWS = [{ id: 'r1', name: 'Tower T1', manager: 'u1' }];
Expand DownExpand Up@@ -158,7 +161,7 @@ describe('objectui#6711 — ObjectGrid no longer copies `reference_to_field` ont
expect(meta).not.toHaveProperty('reference_to_field');
});

it(`still copies the eight surviving relational keys (${name})`, async () => {
it(`still copies the seven surviving relational keys (${name})`, async () => {
const meta = await renderAndCaptureMeta(schemaExtra);
for (const key of SURVIVING_KEYS) {
expect(meta).toHaveProperty(key);
Expand Down
Loading
Loading