Skip to content

feat(integrations): CnDetailPage sidebar Object form accepts useRegistry / excludeIntegrations (manifest-driven registry sidebar)#218

Open
rubenvdlinde wants to merge 3 commits into
feature/integration-xwikifrom
feature/integration-sidebar-manifest
Open

feat(integrations): CnDetailPage sidebar Object form accepts useRegistry / excludeIntegrations (manifest-driven registry sidebar)#218
rubenvdlinde wants to merge 3 commits into
feature/integration-xwikifrom
feature/integration-sidebar-manifest

Conversation

@rubenvdlinde
Copy link
Copy Markdown
Contributor

Closes the gap found while live-testing the xWiki leaf: a consuming app whose detail sidebar is manifest-driven (CnDetailPage via CnAppRoot's #sidebar slot) couldn't flip that sidebar to registry mode — useRegistry was a CnObjectSidebar prop only, with no path from pages[].config.sidebar. Stacked on #213#210#209#204#202beta.

What's in this PR

  • CnDetailPage — the sidebar Object form (and sidebarProps) now accept useRegistry: boolean + excludeIntegrations: string[]; mergeSidebarSources() merges them and syncSidebarState() pushes them into the injected objectSidebarState (reset when the page is suppressed). The sidebar prop JSDoc documents the new keys, the mutual exclusion with tabs, and the host-app binding requirement.
  • app-manifest.schema.json + validateManifest.jsconfig.sidebar on detail pages may now carry useRegistry / excludeIntegrations; validation rejects a non-boolean useRegistry, non-array / non-string excludeIntegrations, and useRegistry: true together with tabs (mutually exclusive). Schema descriptions updated.
  • docscn-object-sidebar.md gains a "Registry-driven tabs (useRegistry)" section with the #sidebar-slot binding snippet + a manifest example; cn-detail-page.md's sidebar-config section mentions the new keys; CLAUDE.md grows a CnDetailPage bullet; _generated/CnDetailPage.md regenerated.
  • testsCnDetailPageSidebarConfig.spec.js +4 (pushes into objectSidebarState; defaults; cleared on suppression; sidebarProps fallback); app-manifest.schema.spec.js +4 (accepts both keys; rejects non-boolean useRegistry; rejects useRegistry+tabs; rejects bad excludeIntegrations).

The consuming app still has to bind :use-registry / :exclude-integrations from objectSidebarState onto its #sidebar-slot <CnObjectSidebar> (documented) — same shape as the :tabs / :hidden-tabs binding it already does. That's why my live-test deskdesk rewrite (which only changed App.vue partially) didn't flip the sidebar — this PR makes the manifest path actually work end-to-end.

Test plan

  • npx jest → 72 suites, 1040 passing (4 pre-existing failures: CnMapWidget + CnAiChatPanel, unrelated, also red on origin/beta)
  • npm run lint — no new errors (7 remaining are pre-existing in useObjectLock.js)
  • npm run check:jsdoc → "All 79 components meet their JSDoc baseline"
  • node scripts/check-docs.js — only the pre-existing CnAi*/useAi*/default gaps

…try / excludeIntegrations (manifest-driven registry sidebar)

Closes the gap found while live-testing the xWiki leaf: a consuming
app whose detail sidebar is manifest-driven (CnDetailPage via
CnAppRoot's #sidebar slot) couldn't flip that sidebar to registry
mode — `useRegistry` was a `CnObjectSidebar` prop only, with no path
from `pages[].config.sidebar`.

  - CnDetailPage: the `sidebar` Object form (and `sidebarProps`) now
    accept `useRegistry: boolean` and `excludeIntegrations: string[]`;
    `mergeSidebarSources()` merges them and `syncSidebarState()`
    pushes them into the injected `objectSidebarState`
    (`objectSidebarState.useRegistry` / `.excludeIntegrations`), reset
    when the page is suppressed. The `sidebar` prop JSDoc documents
    the new keys + the mutual exclusion with `tabs` + the host-app
    binding requirement.
  - app-manifest.schema.json + validateManifest.js: `config.sidebar`
    on detail pages may now carry `useRegistry` (boolean) and
    `excludeIntegrations` (string[]); validation rejects a non-boolean
    `useRegistry`, non-array / non-string `excludeIntegrations`, and
    `useRegistry: true` together with `tabs` (mutually exclusive). The
    schema descriptions are updated to mention both.
  - docs: cn-object-sidebar.md gets a "Registry-driven tabs
    (useRegistry)" section including the `#sidebar`-slot binding
    snippet and the manifest example; cn-detail-page.md's sidebar
    config object section mentions `useRegistry` / `excludeIntegrations`;
    CLAUDE.md's "Surface components consume the registry" list grows a
    CnDetailPage bullet; CnDetailPage's `_generated` partial regenerated.
  - tests: CnDetailPageSidebarConfig.spec.js — 4 new tests (Object
    form pushes useRegistry/excludeIntegrations into objectSidebarState;
    defaults; cleared on suppression; sidebarProps fallback);
    app-manifest.schema.spec.js — 4 new tests (accepts both keys;
    rejects non-boolean useRegistry; rejects useRegistry+tabs; rejects
    bad excludeIntegrations).

The consuming app still has to bind `:use-registry` /
`:exclude-integrations` from `objectSidebarState` onto its
`#sidebar`-slot `<CnObjectSidebar>` (documented) — that's the one
non-automatic step, the same shape as the existing `:tabs` /
`:hidden-tabs` binding it already does.

Verification: `npx jest` → 72 suites, 1040 passing (4 pre-existing
failures: CnMapWidget + CnAiChatPanel, unrelated); `npm run lint` —
no new errors; `npm run check:jsdoc` → "All 79 components meet their
JSDoc baseline".
…tionsController shape)

The AD-23 failure-path contract on OpenRegister's ObjectIntegrationsController
surfaces the ProviderUnavailableException cause under `details.cause`, but
CnXwikiTab.degradedMessage() only checked `details.reason` / a bare `reason` —
so a real "openconnector-source-missing" 503 fell through to the generic
"unavailable" banner instead of the more actionable "reconnect connector" one.
Accept `details.cause` (preferred) plus the legacy shapes, and treat
`openconnector-down` as a reconnect cause too. Tests + the SFC header updated.
…IntegrationsController shape)

OpenRegister's `ObjectIntegrationsController::index` returns `{ items: [...] }`,
but the xwiki tab/card only un-wrapped `{ results: [...] }` (or a bare array) —
so against the real OR endpoint `this.pages` ended up being the envelope object,
`v-for` iterated its values, and the linked-page row rendered blank. Accept
`results` ?? `items` ?? bare-array for both components (list fetch and the
single-entity `loadEntity` lookup). +1 test for the `{ items }` envelope.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[CONCERN] Vue 2 reactivity: new keys on provided objectSidebarState may not be reactive

CnDetailPage writes objectSidebarState.useRegistry and objectSidebarState.excludeIntegrations by direct property assignment (lines 211-212, 216-217). In Vue 2, adding new properties to an already-reactive plain object bypasses the observation system — downstream template bindings such as :use-registry="objectSidebarState.useRegistry" on the host's <CnObjectSidebar> will NOT update reactively unless the host pre-declared those keys in its data() before providing the object. The test helper makeState() correctly initialises both fields, confirming the authors know about this, but the component itself provides no guard. Fix: document this requirement prominently in the migration note, OR use Vue.set(this.objectSidebarState, 'useRegistry', value) on first write so the component self-heals.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[CONCERN] sidebarProps fallback for useRegistry / excludeIntegrations is undocumented

The mergeSidebarConfig helper falls back to props.useRegistry (i.e. this.sidebarProps.useRegistry) when the sidebar Object form omits it. The test exercises this code path, but the JSDoc for the sidebarProps prop only lists register, schema, hiddenTabs, title, subtitleuseRegistry and excludeIntegrations are not mentioned. Either update the sidebarProps JSDoc to list the full accepted key set, or explicitly deprecate the sidebarProps path in favour of the Object sidebar form only.

Copy link
Copy Markdown

@WilcoLouwerse WilcoLouwerse left a comment

Choose a reason for hiding this comment

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

Review

🟡 Concerns (2)

🟢 Minor (3)

  • No validation warning when excludeIntegrations is set without useRegistry: true (src/utils/validateManifest.js:349)
    excludeIntegrations is validated for type but there is no warning when it is set with useRegistry absent or false. In that case the array is forwarded to CnObjectSidebar where it has no effect. A console.warn in syncSidebarState for this combination would surface the misconfiguration earlier and mirror the style of the existing useRegistry + tabs mutual-exclusion check.
  • Nullish coalescing on data.results changes behaviour when results is explicit null (src/components/CnXwikiCard/CnXwikiCard.vue:258)
    The new entity-resolve uses data.results ?? data.items ?? .... The ?? operator treats null as nullish, so { results: null, items: [...] } would pick up items. The previous code (Array.isArray(data.results) === true) returned false for null and fell through to data directly. If any API variant returns results: null as a deliberate 'no results' sentinel, the new code silently changes the resolved entity.
  • Docs example assumes CnObjectSidebar already accepts useRegistry / excludeIntegrations props (docs/components/cn-object-sidebar.md:157)
    The new docs example shows :use-registry and :exclude-integrations bound to <CnObjectSidebar>. This PR is stacked on feature/integration-xwiki. If those props were added to CnObjectSidebar in a prior PR in that chain, the example is correct — but the generated docs (docs/components/_generated/CnObjectSidebar.md) were not updated in this PR, making it impossible to verify from the diff alone. Confirm merge order so host-wiring docs never land on master before the CnObjectSidebar prop additions do.

Reviewed by WilcoLouwerse via automated batch review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants