Skip to content

feat(admin): allow trusted plugins to add content list columns - #2195

Open
logelog wants to merge 6 commits into
emdash-cms:mainfrom
logelog:feat/plugin-content-list-columns-v2
Open

feat(admin): allow trusted plugins to add content list columns#2195
logelog wants to merge 6 commits into
emdash-cms:mainfrom
logelog:feat/plugin-content-list-columns-v2

Conversation

@logelog

@logelog logelog commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds a focused, display-only content-list column extension for trusted React plugins.

Plugins can export typed contentListColumns definitions 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:

  • filters disabled or stale plugins
  • supports collection and minimum-role visibility
  • orders contributions deterministically
  • validates malformed and duplicate definitions
  • isolates failing predicates, headers, and cells so the content list remains usable
  • omits plugin columns from Trash

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

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable)
  • I have added a changeset (if this PR changes a published package)
  • New features link to an approved Discussion: Trusted native-plugin slots for content-list columns and editor sidebar panels #2048 (opened, awaiting maintainer review)

AI-generated code disclosure

  • This PR includes AI generated code: Claude Opus 5, GPT-5.6 (via private dev orchestra)

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:

  • core: 403 files, 5211 tests passed, 3 skipped
  • admin: 117 files, 1421 tests passed
  • Cloudflare: 21 files, 300 tests passed
  • Workerd: 12 files, 84 tests passed
  • real local D1: 2 files, 7 tests passed
  • typechecks and builds for admin, core, cloudflare, workerd, and the demo lab

CI runs each PR on its own branch.

@changeset-bot

changeset-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a64e590

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 17 packages
Name Type
emdash Minor
@emdash-cms/admin Minor
@emdash-cms/cloudflare Minor
@emdash-cms/sandbox-workerd Patch
@emdash-cms/plugin-mcp-smoke Major
@emdash-cms/fixture-perf-site Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/do-demo-site Patch
@emdash-cms/do-solo-demo-site Patch
@emdash-cms/auth Minor
@emdash-cms/blocks Minor
@emdash-cms/gutenberg-to-portable-text Minor
@emdash-cms/x402 Minor
create-emdash Minor
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds Patch

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

@github-actions

Copy link
Copy Markdown
Contributor

Scope check

This 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.

@github-actions github-actions Bot added the review/needs-review No maintainer or bot review yet label Jul 22, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jul 22, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@2195

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@2195

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@2195

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@2195

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@2195

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@2195

emdash

npm i https://pkg.pr.new/emdash@2195

create-emdash

npm i https://pkg.pr.new/create-emdash@2195

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@2195

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@2195

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@2195

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@2195

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@2195

@emdash-cms/registry-verification

npm i https://pkg.pr.new/@emdash-cms/registry-verification@2195

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@2195

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@2195

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@2195

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@2195

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@2195

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@2195

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@2195

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@2195

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@2195

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@2195

commit: a64e590

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] Internal ESM imports must use a .js extension per AGENTS.md. This new file imports from ./api without it.

Suggested change
import type { AdminManifest, ContentItem } from "./api";
import type { AdminManifest, ContentItem } from "./api.js";

ContentDateField,
ContentItem,
TrashedContentItem,
} from "../lib/api";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] This changed import from ../lib/api is missing the required .js extension for internal ESM imports.

Suggested change
} from "../lib/api";
} from "../lib/api.js";

Comment thread packages/admin/src/index.ts Outdated
ContentListColumnHeaderContext,
ContentListColumnCellContext,
ContentListColumnExtension,
} from "./lib/content-list-columns";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] The new re-export from ./lib/content-list-columns is an internal ESM import and needs the .js extension.

Suggested change
} from "./lib/content-list-columns";
} from "./lib/content-list-columns.js";

@github-actions

Copy link
Copy Markdown
Contributor

Overlapping PRs

This PR modifies files that are also changed by other open PRs:

This may cause merge conflicts or duplicated work. A maintainer will coordinate.

@github-actions github-actions Bot added review/awaiting-author Reviewed; waiting on the author to respond review/needs-rereview Author pushed changes since the last review and removed review/needs-review No maintainer or bot review yet review/awaiting-author Reviewed; waiting on the author to respond labels Jul 22, 2026
@logelog
logelog force-pushed the feat/plugin-content-list-columns-v2 branch from ee1f044 to 0a83f6d Compare July 22, 2026 22:57
@logelog
logelog force-pushed the feat/plugin-content-list-columns-v2 branch from 0a83f6d to 8825b20 Compare July 24, 2026 19:26
@logelog
logelog force-pushed the feat/plugin-content-list-columns-v2 branch from 8825b20 to ffe1365 Compare August 2, 2026 23:36
@logelog
logelog force-pushed the feat/plugin-content-list-columns-v2 branch from ffe1365 to 139939b Compare August 3, 2026 12:20
@logelog
logelog force-pushed the feat/plugin-content-list-columns-v2 branch from 139939b to 8e2d040 Compare August 3, 2026 12:22
@MA2153 MA2153 added the bot:review Trigger an emdashbot code review on this PR label Aug 3, 2026

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@logelog
logelog force-pushed the feat/plugin-content-list-columns-v2 branch from 8e2d040 to 9669537 Compare August 7, 2026 21:53
@github-actions github-actions Bot added review/needs-rereview Author pushed changes since the last review and removed review/approved Approved; no new commits since labels Aug 7, 2026
@logelog logelog closed this Aug 7, 2026
@logelog logelog reopened this Aug 7, 2026

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread packages/admin/src/components/index.ts Outdated
// Page components
export { Dashboard, type DashboardProps } from "./Dashboard";
export { ContentList, type ContentListProps } from "./ContentList";
export { ContentList, type ContentListColumn, type ContentListProps } from "./ContentList";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

Suggested change
export { ContentList, type ContentListColumn, type ContentListProps } from "./ContentList";
export { ContentList, type ContentListColumn, type ContentListProps } from "./ContentList.js";

@logelog
logelog force-pushed the feat/plugin-content-list-columns-v2 branch from 9669537 to a64e590 Compare August 7, 2026 23:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants