From 1f64b5167c726c904d8b9b50e88fb3734ad2cda9 Mon Sep 17 00:00:00 2001 From: "dev-docs-github-app[bot]" <178952281+dev-docs-github-app[bot]@users.noreply.github.com> Date: Thu, 17 Apr 2025 03:55:42 +0000 Subject: [PATCH 1/7] Create file --- dev-docs.json | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 dev-docs.json diff --git a/dev-docs.json b/dev-docs.json new file mode 100644 index 0000000000..ad795ee991 --- /dev/null +++ b/dev-docs.json @@ -0,0 +1,9 @@ +{ + "gitHubApp": { + "approvalWorkflow": true, + "userDocsWorkflows": [ + "generateUserDocs" + ], + "issues": true + } +} \ No newline at end of file From 4e377779011d92e6f9b0d21449935fafcb366c85 Mon Sep 17 00:00:00 2001 From: avb-dev-docs <123575048+avb-dev-docs@users.noreply.github.com> Date: Wed, 16 Apr 2025 20:57:34 -0700 Subject: [PATCH 2/7] Update dev-docs.json --- dev-docs.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dev-docs.json b/dev-docs.json index ad795ee991..41cc331aa8 100644 --- a/dev-docs.json +++ b/dev-docs.json @@ -5,5 +5,6 @@ "generateUserDocs" ], "issues": true - } -} \ No newline at end of file + }, + "aiProvider": "google" +} From 2d5e3350fbc20bd1beed8a494ceaabf20e7965c3 Mon Sep 17 00:00:00 2001 From: avb-dev-docs <123575048+avb-dev-docs@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:18:09 -0700 Subject: [PATCH 3/7] Update dev-docs.json --- dev-docs.json | 7 ------- 1 file changed, 7 deletions(-) diff --git a/dev-docs.json b/dev-docs.json index 41cc331aa8..e1820254da 100644 --- a/dev-docs.json +++ b/dev-docs.json @@ -1,10 +1,3 @@ { - "gitHubApp": { - "approvalWorkflow": true, - "userDocsWorkflows": [ - "generateUserDocs" - ], - "issues": true - }, "aiProvider": "google" } From bc3f683baa7e5d64878f2d1c5301f7c16a3bf6dc Mon Sep 17 00:00:00 2001 From: avb-dev-docs <123575048+avb-dev-docs@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:52:22 -0700 Subject: [PATCH 4/7] Update dev-docs.json --- dev-docs.json | 1 - 1 file changed, 1 deletion(-) diff --git a/dev-docs.json b/dev-docs.json index e1820254da..2c63c08510 100644 --- a/dev-docs.json +++ b/dev-docs.json @@ -1,3 +1,2 @@ { - "aiProvider": "google" } From fa1bfe0fdd0fff819903ae472dbe6af8382e865a Mon Sep 17 00:00:00 2001 From: avb-dev-docs <123575048+avb-dev-docs@users.noreply.github.com> Date: Wed, 16 Apr 2025 22:52:20 -0700 Subject: [PATCH 5/7] Update dev-docs.json --- dev-docs.json | 1 + 1 file changed, 1 insertion(+) diff --git a/dev-docs.json b/dev-docs.json index 2c63c08510..379223a96b 100644 --- a/dev-docs.json +++ b/dev-docs.json @@ -1,2 +1,3 @@ { + "aiProvider": "aws" } From 954fa94216ecff472ab2395d01acf60895a10347 Mon Sep 17 00:00:00 2001 From: "dev-docs-github-app-dev[bot]" <178211755+dev-docs-github-app-dev[bot]@users.noreply.github.com> Date: Fri, 18 Apr 2025 06:19:16 +0000 Subject: [PATCH 6/7] Create file --- .../cms/admin-panel-customization/widgets.md | 184 ++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 docusaurus/docs/cms/admin-panel-customization/widgets.md diff --git a/docusaurus/docs/cms/admin-panel-customization/widgets.md b/docusaurus/docs/cms/admin-panel-customization/widgets.md new file mode 100644 index 0000000000..f59ffed429 --- /dev/null +++ b/docusaurus/docs/cms/admin-panel-customization/widgets.md @@ -0,0 +1,184 @@ +--- +title: Widgets API +description: Learn how to use the Widgets API to extend Strapi's admin panel with custom widgets. +displayed_sidebar: cmsSidebar +sidebar_label: Widgets +toc_max_heading_level: 4 +tags: +- admin panel +- admin panel customization +- widgets +--- + +# Widgets API + +Strapi's Widgets API allows you to extend the admin panel with custom widgets. Widgets are modular components that can be registered and displayed in specific areas of the admin interface. + +## Introduction + +Widgets are reusable UI components that can be added to the admin panel. They provide a way to extend the functionality of the admin panel without modifying its core code. Widgets can be used to display various types of information, provide shortcuts to frequently used functions, or integrate with external services. + +You can register widgets in two ways: +- As a plugin developer, you can register widgets that will be available whenever your plugin is installed +- As a Strapi developer, you can register widgets for a specific instance of a Strapi application + +## API Reference + +### Registering Widgets + +The Widgets API provides methods to register one or more widgets: + +```typescript +// Register a single widget +strapi.admin.widgets.register({ + id: 'my-widget', + component: () => import('./components/MyWidget'), + title: { id: 'myWidget.title', defaultMessage: 'My Widget' }, + icon: IconComponent, +}); + +// Register multiple widgets +strapi.admin.widgets.register([ + { + id: 'first-widget', + component: () => import('./components/FirstWidget'), + title: { id: 'firstWidget.title', defaultMessage: 'First Widget' }, + icon: FirstIconComponent, + }, + { + id: 'second-widget', + component: () => import('./components/SecondWidget'), + title: { id: 'secondWidget.title', defaultMessage: 'Second Widget' }, + icon: SecondIconComponent, + } +]); +``` + +### Getting All Registered Widgets + +You can retrieve all registered widgets using the `getAll` method: + +```typescript +const widgets = strapi.admin.widgets.getAll(); +``` + +## Widget Properties + +When registering a widget, you need to provide several properties: + +### Required Properties + +| Property | Type | Description | +|----------|------|-------------| +| `id` | `string` | A unique identifier for the widget | +| `component` | `() => Promise<{ default: React.ComponentType }>` | A function that returns a promise resolving to the widget component | +| `title` | `MessageDescriptor` | The title of the widget (supports internationalization) | +| `icon` | `React.ComponentType` | The icon component to display with the widget | + +### Optional Properties + +| Property | Type | Description | +|----------|------|-------------| +| `link` | `{ label: MessageDescriptor; href: To }` | A link to include in the widget | +| `pluginId` | `string` | The ID of the plugin registering the widget | +| `permissions` | `Permission[]` | Permissions required to view the widget | + +## Usage Examples + +### Basic Widget Registration + +```typescript +import { CheckCircle } from '@strapi/icons'; + +// In your plugin's admin/src/index.js or in src/admin/app.js +export default { + register(app) { + app.widgets.register({ + id: 'dashboard-welcome', + component: () => import('./components/DashboardWelcome'), + title: { + id: 'dashboard.welcome.title', + defaultMessage: 'Welcome to your dashboard', + }, + icon: CheckCircle, + }); + }, +}; +``` + +### Widget with a Link and Permissions + +```typescript +import { Heart } from '@strapi/icons'; + +export default { + register(app) { + app.widgets.register({ + id: 'content-metrics', + component: () => import('./components/ContentMetrics'), + title: { + id: 'metrics.content.title', + defaultMessage: 'Content Metrics', + }, + icon: Heart, + link: { + label: { + id: 'metrics.content.link', + defaultMessage: 'View all metrics', + }, + href: '/metrics', + }, + permissions: [ + { action: 'plugin::content-manager.read', subject: 'metrics' }, + ], + }); + }, +}; +``` + +### Plugin-specific Widget + +When registering a widget from a plugin, include the `pluginId` to properly namespace the widget: + +```typescript +export default { + register(app) { + app.widgets.register({ + id: 'analytics-summary', + pluginId: 'my-analytics-plugin', + component: () => import('./components/AnalyticsSummary'), + title: { + id: 'analytics.summary.title', + defaultMessage: 'Analytics Summary', + }, + icon: () => null, + }); + }, +}; +``` + +## Widget UID + +When widgets are registered, they are assigned a unique identifier (UID) based on their `id` and optional `pluginId`: + +- For global widgets: `global::{id}` +- For plugin widgets: `plugin::{pluginId}.{id}` + +This UID is used internally to track and manage registered widgets. + +```doc.dev +Need more information about where/how widgets are displayed in the admin panel UI. +The code reveals the registration mechanism but not how these widgets are rendered or where they appear in the interface. +Additional details about any widget-specific styling or layout considerations would also be helpful. +``` + +## Error Handling + +The Widgets API will throw errors if any required properties are missing when registering a widget: + +- If `id` is missing: "An id must be provided" +- If `component` is missing: "A component must be provided" +- If `title` is missing: "A title must be provided" +- If `icon` is missing: "An icon must be provided" + +Ensure all required properties are provided to avoid runtime errors. \ No newline at end of file From 211751325d073ab34d1df87891c4d4585fd3e919 Mon Sep 17 00:00:00 2001 From: "dev-docs-github-app-dev[bot]" <178211755+dev-docs-github-app-dev[bot]@users.noreply.github.com> Date: Fri, 18 Apr 2025 06:21:22 +0000 Subject: [PATCH 7/7] Update file --- .../admin-panel-customization/extension.md | 80 ++++++++++++++++++- 1 file changed, 77 insertions(+), 3 deletions(-) diff --git a/docusaurus/docs/cms/admin-panel-customization/extension.md b/docusaurus/docs/cms/admin-panel-customization/extension.md index 60ae7d42db..46b69125e7 100644 --- a/docusaurus/docs/cms/admin-panel-customization/extension.md +++ b/docusaurus/docs/cms/admin-panel-customization/extension.md @@ -5,9 +5,8 @@ displayed_sidebar: cmsSidebar sidebar_label: Extension toc_max_heading_level: 4 tags: -- admin panel -- admin panel customization - + - admin panel + - admin panel customization --- import HotReloading from '/docs/snippets/hot-reloading-admin-panel.md' @@ -28,6 +27,81 @@ There are 2 use cases where you might want to extend the admin panel: 👉 This can be done by directly updating the `/src/admin/app.[tsx|js]` file, which can import any file located in `/src/admin/extensions`. +## Widgets API + +The Widgets API allows you to create custom widgets that can be displayed in the admin panel dashboard. Widgets are React components that appear on the dashboard and can provide valuable information or functionality specific to your application. + +### Registering a widget + +To register a widget, use the `register` method from the Widgets class. Each widget requires specific properties: + +```typescript +// Example widget registration +strapi.admin.widgets.register({ + // Unique identifier for this widget + id: 'my-custom-widget', + // The icon displayed with the widget + icon: MyCustomIcon, + // The title displayed for the widget + title: { id: 'my-custom-widget.title', defaultMessage: 'My Custom Widget' }, + // Optional link to provide additional actions + link: { + label: { id: 'my-custom-widget.link', defaultMessage: 'Learn more' }, + href: '/some-path', + }, + // The React component to render (lazy loaded) + component: () => import('./components/MyCustomWidget'), + // Optional permissions to restrict who can see this widget + permissions: [ + { action: 'plugin::content-manager.explorer.read', subject: 'api::article.article' } + ], +}); +``` + +You can also register multiple widgets at once by passing an array: + +```typescript +strapi.admin.widgets.register([ + { + id: 'widget-1', + // ...other properties + }, + { + id: 'widget-2', + // ...other properties + } +]); +``` + +### Widget properties + +Each widget must have the following properties: + +| Property | Type | Description | +|----------|------|-------------| +| `id` | `string` | A unique identifier for the widget | +| `icon` | `React.ComponentType` | The icon component to display with the widget | +| `title` | `MessageDescriptor` | The title of the widget (internationalized) | +| `component` | `() => Promise<{ default: React.ComponentType }>` | The React component to render (lazy loaded) | +| `link` | `{ label: MessageDescriptor, href: To }` | Optional link providing additional context or actions | +| `permissions` | `Permission[]` | Optional array of permissions to restrict widget visibility | +| `pluginId` | `string` | Optional plugin identifier when the widget is provided by a plugin | + +### Widget UIDs + +When registering a widget, it's assigned a unique identifier (UID) based on its source: + +- For widgets from plugins: `plugin::{pluginId}.{id}` +- For global widgets: `global::{id}` + +### Retrieving registered widgets + +To get all registered widgets, use the `getAll` method: + +```typescript +const allWidgets = strapi.admin.widgets.getAll(); +``` + :::strapi Additional resources * If you're searching for ways of replacing the default WYSIWYG editor, please refer to the [corresponding page](/cms/admin-panel-customization/wysiwyg-editor). * The will also provide additional information on developing for Strapi's admin panel.