From a222ac62dd33dee1c8a472dadb439d5a01178141 Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 11:33:11 +0800 Subject: [PATCH] [Doc] Documentation of content management plugin (#7575) (#7659) * Add documentation for content management plugin * update documentation for content management plugin * Changeset file for PR #7575 created/updated * update documentation to mention that the contents are sorted by `order` --------- (cherry picked from commit d0520a60b8910d2aa6d53d3f0d37b477aff205c2) Signed-off-by: Yulong Ruan Signed-off-by: github-actions[bot] Co-authored-by: github-actions[bot] Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com> --- changelogs/fragments/7575.yml | 2 + src/plugins/content_management/README.md | 237 +++++++++++++++++++++++ 2 files changed, 239 insertions(+) create mode 100644 changelogs/fragments/7575.yml create mode 100644 src/plugins/content_management/README.md diff --git a/changelogs/fragments/7575.yml b/changelogs/fragments/7575.yml new file mode 100644 index 000000000000..3f1335de1ecc --- /dev/null +++ b/changelogs/fragments/7575.yml @@ -0,0 +1,2 @@ +doc: +- Add documentation for dynamic page creation ([#7575](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7575)) \ No newline at end of file diff --git a/src/plugins/content_management/README.md b/src/plugins/content_management/README.md new file mode 100644 index 000000000000..8d3d96a8062d --- /dev/null +++ b/src/plugins/content_management/README.md @@ -0,0 +1,237 @@ +# Plugin for managing dynamic page creation in OSD +Use this plugin to create pages that multiple plugins can contribute to. A typical use case is the OSD homepage, +which can have contents contributed by different plugins, see the screenshot: + +![image](https://github.com/user-attachments/assets/501c2433-38c5-4b53-9974-de6f63eab94d) + +## Getting started +### Step 1: Add `contentManagement` to `requiredPlugins` +Ensure `contentManagement` is listed in the `requiredPlugins` array of your plugin's manifest file. +```json +{ + "requiredPlugins": ["contentManagement"] +} +``` + +### Step 2: Create a page with defined sections +A section is typically a container on the page, and a page could have multiple sections. Using the homepage as an example: +```typescript +export const HOME_PAGE_ID = 'osd_homepage'; +export enum SECTIONS { + GET_STARTED = `get_started`, + SERVICE_CARDS = `service_cards`, + RECENTLY_VIEWED = `recently_viewed`, +} + +export class MyPlugin implements Plugin { + public setup(core, { contentManagement }) { + contentManagement.registerPage({ + id: HOME_PAGE_ID, + title: 'Home', + sections: [ + { + id: SECTIONS.SERVICE_CARDS, + order: 3000, + kind: 'dashboard', + }, + { + id: SECTIONS.RECENTLY_VIEWED, + order: 2000, + title: 'Recently viewed', + kind: 'custom', + render: (contents) => ( + <> + {contents.map((content) => content.kind === 'custom' ? content.render() : null)} + + ), + }, + { + id: SECTIONS.GET_STARTED, + order: 1000, + title: 'Define your path forward with OpenSearch', + kind: 'card', + }, + ], + }); + } +} +``` +Here we defined a page with three different kinds of sections: `dashboard`, `custom` and `card`, the sections will be sorted by `order` in ascending order. +Each type of section serves a different purpose: + +#### `card` section +A `card` section is one of the pre-defined section type that renders a horizontal list of OuiCard components, to add +contents to a `card` section, call `contentManagement.registerContentProvider` with a `title` and `description`, and the content +will be sorted by its `order` in ascending order. +```typescript +export class MyPlugin implements Plugin { + public start(core, { contentManagement }) { + contentManagement.registerContentProvider({ + id: `home_get_start`, // id for the content provider, could be any unique string + getTargetArea: () => `${HOME_PAGE_ID}/${SECTIONS.GET_STARTED}`, // target area follow the convention: {page_id}/{section_id} + getContent: () => ({ + id: 'card_content_id', // id for the content, could be any unique string + kind: 'card', + order: 1000, + description: 'Card description', + title: 'Card title' + }), + }); + } +} +``` + +#### `dashboard` section +A `dashboard` section is typically a dashboard embeddable container, it can render visualization or dashboard by their id, +or it can render arbitrary React components. + +Add a saved visualization to a `dashboard` section **statically** +```typescript +export class MyPlugin implements Plugin { + public start(core, { contentManagement }) { + contentManagement.registerContentProvider({ + id: `visualization_content_provider`, + getTargetArea: () => `${HOME_PAGE_ID}/${SECTIONS.SERVICE_CARDS}`, + getContent: () => ({ + id: 'visualization_content_id', // id for the content, could be any unique string + kind: 'visualization', + order: 1000, + input: { + kind: 'static', + id: 'c0ba29f0-eb8f-11ed-8e00-17d7d50cd7b2' // the visualization id + } + }), + }); + } +} +``` + +Add a saved visualization to a `dashboard` section **dynamically** +```diff +export class MyPlugin implements Plugin { + public start(core, { contentManagement }) { + contentManagement.registerContentProvider({ + id: `visualization_content_provider`, + getTargetArea: () => `${HOME_PAGE_ID}/${SECTIONS.SERVICE_CARDS}`, + getContent: () => ({ + id: 'visualization_content_id', + kind: 'visualization', // with `visualization` kind + order: 1000, + input: { +- kind: 'static', +- id: 'c0ba29f0-eb8f-11ed-8e00-17d7d50cd7b2' // the visualization id ++ kind: 'dynamic', ++ get: () => Promise.resolve('c0ba29f0-eb8f-11ed-8e00-17d7d50cd7b2') // the visualization id + } + }), + }); + } +} +``` + +Add a saved dashboard to a `dashboard` section **statically** +```typescript +export class MyPlugin implements Plugin { + public start(core, { contentManagement }) { + contentManagement.registerContentProvider({ + id: `dashboard_content_provider`, + getTargetArea: () => `${HOME_PAGE_ID}/${SECTIONS.SERVICE_CARDS}`, + getContent: () => ({ + id: 'dashboard_content_id', + kind: 'dashboard', // with `dashboard` kind + order: 1000, + input: { + kind: 'static', + id: 'c39012d0-eb7a-11ed-8e00-17d7d50cd7b2' // the saved dashboard id + } + }), + }); + } +} +``` + +Similarly, you can add a saved dashboard to a `dashboard` section **dynamically** +```diff +export class MyPlugin implements Plugin { + public start(core, { contentManagement }) { + contentManagement.registerContentProvider({ + id: `dashboard_content_provider`, + getTargetArea: () => `${HOME_PAGE_ID}/${SECTIONS.SERVICE_CARDS}`, + getContent: () => ({ + id: 'dashboard_content_id', + kind: 'dashboard', // with `dashboard` kind + order: 1000, + input: { +- kind: 'static', +- id: 'c39012d0-eb7a-11ed-8e00-17d7d50cd7b2' // the saved dashboard id ++ kind: 'dynamic', ++ get: () => Promise.resolve('c39012d0-eb7a-11ed-8e00-17d7d50cd7b2') // the saved dashboard id + } + }), + }); + } +} +``` + +You can also add custom components to a `dashboard` section +```typescript +export class MyPlugin implements Plugin { + public start(core, { contentManagement }) { + contentManagement.registerContentProvider({ + id: `custom_content_provider`, + getTargetArea: () => `${HOME_PAGE_ID}/${SECTIONS.SERVICE_CARDS}`, + getContent: () => ({ + id: 'custom_content_id', + kind: 'custom', // with `dashboard` kind + order: 1000, + render: () => + }), + }); + } +} +``` + +#### `custom` section +If the existing pre-defined sections do not meet your needs, you could use `custom` section to customize the rendering of the contents, +a custom section is typically defined with `kind: 'custom'` and a `render` function: +```typescript +{ + id: SECTIONS.RECENTLY_VIEWED, + order: 2000, + title: 'Recently viewed', + kind: 'custom', + render: (contents) => ( + <> + {contents.map((content) => content.kind === 'custom' ? content.render() : null)} + + ), +} +``` + +Now adds content to a `custom` section +```typescript +export class MyPlugin implements Plugin { + public start(core, { contentManagement }) { + contentManagement.registerContentProvider({ + id: 'recent_provider_id', + getContent: () => { + return { + order: 1, + id: 'recent_content_id', + kind: 'custom', + render: () => , + }; + }, + getTargetArea: () => `${HOME_PAGE_ID}/${SECTIONS.RECENTLY_VIEWED}`, + }); + } +} +``` + +### Step 3: Render the page +Finally, render the page in your application: +```typescript + + {contentManagement.renderPage(HOME_PAGE_ID)} + +```