feat(admin): allow trusted plugins to add content list columns - #2195
feat(admin): allow trusted plugins to add content list columns#2195logelog wants to merge 6 commits into
Conversation
🦋 Changeset detectedLatest commit: a64e590 The changes in this PR will be included in the next version bump. This PR includes changesets to release 17 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Scope checkThis PR changes 586 lines across 10 files. Large PRs are harder to review and more likely to be closed without review. If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs. See CONTRIBUTING.md for contribution guidelines. |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/auth-atproto
@emdash-cms/blocks
@emdash-cms/cloudflare
@emdash-cms/contentful-to-portable-text
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/plugin-cli
@emdash-cms/plugin-types
@emdash-cms/registry-client
@emdash-cms/registry-lexicons
@emdash-cms/registry-verification
@emdash-cms/sandbox-workerd
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-field-kit
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
There was a problem hiding this comment.
This is a focused, well-scoped addition to the trusted-plugin admin surface. It solves the right problem (display-only content-list columns contributed by trusted React plugins) in the right way: EmDash keeps ownership of the table while plugins provide header/cell components;filtering, sorting, and search are deliberately left out because they need server-backed semantics. The change follows the same trusted-plugin model already used for pages, widgets, and field widgets, and it is backed by an approved Discussion (#2048).
I checked the new resolver, the boundary component, the ContentList integration, the router wiring, the test coverage, and the changeset. The logic is sound: disabled/stale plugins are omitted, role and collection filters work, malformed/duplicate definitions are isolated, and error boundaries keep a broken column from crashing the list. No logic bugs, security issues, regressions, or logged-out query-count impacts were found.
There are three AGENTS.md import-extension violations on changed/new lines: two new internal imports omit the required .js extension. Fixing those is the only blocker.
| import { Trans } from "@lingui/react/macro"; | ||
| import * as React from "react"; | ||
|
|
||
| import type { AdminManifest, ContentItem } from "./api"; |
There was a problem hiding this comment.
[needs fixing] Internal ESM imports must use a .js extension per AGENTS.md. This new file imports from ./api without it.
| import type { AdminManifest, ContentItem } from "./api"; | |
| import type { AdminManifest, ContentItem } from "./api.js"; |
| ContentDateField, | ||
| ContentItem, | ||
| TrashedContentItem, | ||
| } from "../lib/api"; |
There was a problem hiding this comment.
[needs fixing] This changed import from ../lib/api is missing the required .js extension for internal ESM imports.
| } from "../lib/api"; | |
| } from "../lib/api.js"; |
| ContentListColumnHeaderContext, | ||
| ContentListColumnCellContext, | ||
| ContentListColumnExtension, | ||
| } from "./lib/content-list-columns"; |
There was a problem hiding this comment.
[needs fixing] The new re-export from ./lib/content-list-columns is an internal ESM import and needs the .js extension.
| } from "./lib/content-list-columns"; | |
| } from "./lib/content-list-columns.js"; |
Overlapping PRsThis PR modifies files that are also changed by other open PRs:
This may cause merge conflicts or duplicated work. A maintainer will coordinate. |
ee1f044 to
0a83f6d
Compare
0a83f6d to
8825b20
Compare
8825b20 to
ffe1365
Compare
ffe1365 to
139939b
Compare
139939b to
8e2d040
Compare
There was a problem hiding this comment.
This is the right change, implemented in the right way. EmDash keeps ownership of the content-list table, pagination, loading states, and row actions while trusted React plugins contribute display-only header/cell columns. That matches the existing trusted-plugin pattern for pages, widgets, and field widgets. Sorting, filtering, and search are deliberately left out (they require server-backed contracts and are tracked separately).
This re-read checked the resolver (resolveContentListColumns and validation), the error boundary (ContentListColumnBoundary), the ContentList integration and colSpan math, the router wiring, the manifest/schema/seed/migration changes, the test coverage, and the two changesets.
The previous blocking import-extension violations are resolved: new internal imports now use the required .js extension. No new AGENTS.md convention violations, logic bugs, security issues, regressions, missing locale filters, or logged-out query-count impacts were found in the changed code. Test coverage is solid: it exercises resolver filtering/ordering/duplication, error isolation, pagination context passed to cells, Trash omission, and the new custom-field formatting paths.
Note that this diff still contains the stacked commits from #2194 (collection-configured scalar list columns). Those changes also look straightforward and consistent with the rest of the PR.
8e2d040 to
9669537
Compare
There was a problem hiding this comment.
This is still the right change for the stated goal: EmDash keeps control of the content-list table, pagination, and row actions while trusted React plugins contribute display-only header/cell columns, mirroring the existing trusted-plugin pattern. I re-read the resolver, error boundary, ContentList integration and colSpan math, router wiring, manifest/seed/schema/migration changes, test coverage, and the two changesets. The previous blocking import-extension issues are almost fully resolved; only one touched barrel-file export still uses an extensionless internal path. No new logic bugs, security issues, locale-filter misses, logged-out query-count impacts, or material regressions were found.
Headline: the PR is in good shape and close to a clean merge; fixing the missing .js extension on the changed components/index.ts export is the only remaining AGENTS.md convention item.
| // Page components | ||
| export { Dashboard, type DashboardProps } from "./Dashboard"; | ||
| export { ContentList, type ContentListProps } from "./ContentList"; | ||
| export { ContentList, type ContentListColumn, type ContentListProps } from "./ContentList"; |
There was a problem hiding this comment.
[needs fixing] The changed export still imports from ./ContentList without the required .js extension. AGENTS.md's import convention requires internal imports to use .js for ESM (import { X } from "../foo.js"). Other exports in this barrel file share the same pre-existing omission, but this is the line touched by the PR, so update it now and consider fixing the rest of the file in a small follow-up.
| export { ContentList, type ContentListColumn, type ContentListProps } from "./ContentList"; | |
| export { ContentList, type ContentListColumn, type ContentListProps } from "./ContentList.js"; |
9669537 to
a64e590
Compare
What does this PR do?
Adds a focused, display-only content-list column extension for trusted React plugins.
Plugins can export typed
contentListColumnsdefinitions for computed metadata such as SEO scores, moderation states, or workflow statuses. EmDash keeps ownership of the table, pagination, row actions, loading states, and empty states while passing each cell the current content item, collection, and locale.The resolver:
Sorting, filtering, and search are intentionally outside this display-only contract. They need server-backed contracts; a browser comparator would only reorder the currently loaded page. Search is covered by #2191, indexed sorting by #2212, and indexed filtering by #2213.
This PR is stacked on #2194 so the shared content-list column layout has a single owner. Both can be reviewed at the same time, but #2194 should merge first; GitHub will then narrow this PR to its unique trusted-plugin extension commits automatically.
This complements #2194, which handles collection-configured scalar fields, and addresses the plugin-computed column portion of #2179.
Discussion: #2048
Type of change
Checklist
pnpm typecheckpassespnpm lintpassespnpm testpasses (or targeted tests for my change)pnpm formathas been runAI-generated code disclosure
Screenshots / test output
Validated on EmDash 0.32.0 (base
776d65f7) with Node 24.16.0 and pnpm 11.9.0 on macOS. The workspace has all six stacked contributions applied, so these are the integrated totals rather than a per-PR subset:CI runs each PR on its own branch.