Skip to content

Add Widgets API documentation and enable unstableWidgetsApi feature flag #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 17 additions & 19 deletions docusaurus/docs/cms/configurations/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ sidebar_label: Features
description: Enable experimental Strapi features
displayed_sidebar: cmsSidebar
tags:
- additional configuration
- configuration
- features configuration
- future flag
- additional configuration
- configuration
- features configuration
- future flag
---

# Features configuration
Expand All @@ -31,17 +31,17 @@ To enable a future flag:

1. (_optional_) If the server is running, stop it with `Ctrl-C`.
2. Open the `config/features.js|ts` file or create it if the file does not exist yet. The file will export a `future` object with all the future flags to enable.
3. To enable a future flag, add its property name (see [full list](#available-future-flags)) to the `future` object and ensure the property's value is set to `true`. The following example shows how to enable the `contentReleasesScheduling` future flag:
3. To enable a future flag, add its property name (see [full list](#available-future-flags)) to the `future` object and ensure the property's value is set to `true`. The following example shows how to enable the `unstableWidgetsApi` future flag:

<Tabs groupId='js-ts'>

<TabItem value="js" label="JavaScript">

```ts title="/config/features.ts"
module.export = ({ env }) => ({
```js title="/config/features.js"
module.exports = ({ env }) => ({
future: {
// You could also simply write: contentReleases: true
contentReleasesScheduling: env.bool('STRAPI_FUTURE_CONTENT_RELEASES_SCHEDULING', false),
// You could also simply write: unstableWidgetsApi: true
unstableWidgetsApi: env.bool('STRAPI_FUTURE_UNSTABLE_WIDGETS_API', false),
},
})

Expand All @@ -50,10 +50,10 @@ To enable a future flag:
This example assumes that you have an `.env` environment file at the root of your application and that the file includes the following line:

```json title=".env"
STRAPI_FUTURE_CONTENT_RELEASES_SCHEDULING=true
STRAPI_FUTURE_UNSTABLE_WIDGETS_API=true
```

If your environment file does not include this value, the `contentReleasesScheduling` future flag property value will default to `false` and the experimental feature will not be enabled.
If your environment file does not include this value, the `unstableWidgetsApi` future flag property value will default to `false` and the experimental feature will not be enabled.

</TabItem>

Expand All @@ -62,19 +62,19 @@ To enable a future flag:
```ts title="/config/features.ts"
export default {
future: {
// You could also simply write: contentReleases: true
contentReleasesScheduling: env.bool('STRAPI_FUTURE_CONTENT_RELEASES_SCHEDULING', false),
// You could also simply write: unstableWidgetsApi: true
unstableWidgetsApi: env.bool('STRAPI_FUTURE_UNSTABLE_WIDGETS_API', false),
},
};
```

This example assumes that you have an `.env` environment file at the root of your application and that the file includes the following line:

```json title=".env"
STRAPI_FUTURE_CONTENT_RELEASES_SCHEDULING=true
STRAPI_FUTURE_UNSTABLE_WIDGETS_API=true
```

If your environment file does not include this value, the `contentReleases` future flag property value will default to `false` and the experimental feature will not be enabled.
If your environment file does not include this value, the `unstableWidgetsApi` future flag property value will default to `false` and the experimental feature will not be enabled.

</TabItem>
</Tabs>
Expand Down Expand Up @@ -107,10 +107,8 @@ Developers can use the following APIs to interact with future flags:

## Available future flags

There are currently no available future flags. This section will be updated once new experimental features are available for testing.

<!-- The following future flags are currently available and can be used in the `future` object of the `config/features` configuration file:
The following future flags are currently available and can be used in the `future` object of the `config/features` configuration file:

| Property name | Related feature | Suggested environment variable name |
| ----------------- | -------------------------------------------- | ----------------------------------------- |
| `contentReleasesScheduling` | [Releases Scheduling](/cms/features/releases#usage) | `STRAPI_FUTURE_CONTENT_RELEASES_SCHEDULING` | -->
| `unstableWidgetsApi` | Experimental Widgets API | `STRAPI_FUTURE_UNSTABLE_WIDGETS_API` |
124 changes: 124 additions & 0 deletions docusaurus/docs/cms/plugins-development/widgets-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
---
title: Widgets API
description: Learn how to use the Widgets API to create and manage widgets in the Strapi admin panel.
displayed_sidebar: cmsSidebar
tags:
- widgets
- admin panel
- plugins development
---

# Widgets API

The Widgets API allows you to create and manage widgets in the Strapi admin panel. Widgets are customizable components that can be added to various parts of the admin interface to display information or provide additional functionality.

## Creating a Widget

To create a widget, you need to use the `Widgets` class from the `@strapi/admin` package. Here's an example of how to create a widget:

Comment on lines +16 to +18
Copy link

Choose a reason for hiding this comment

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

This is wrong, what we should explain instead is:

  • they should register the widgets during Strapi's register phase
  • instead of importing anything, they should access the widgets register api via the app object provided as a param of the Strapi's register lifecycle. Exactly like custom fields

```javascript
import { Widgets } from '@strapi/admin';

const widgets = new Widgets();

widgets.register({
id: 'my-widget',
pluginId: 'my-plugin',
icon: MyWidgetIcon,
title: {
id: 'my-widget.title',
defaultMessage: 'My Widget',
},
component: () => import('./components/MyWidget'),
Copy link

Choose a reason for hiding this comment

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

the component function should return a component directly, not a file that has a component as a default export. so:

Suggested change
component: () => import('./components/MyWidget'),
component: async () => {
const { Component } = await import('./components/MyWidget');
return Component

});
```

The `register` method accepts an object with the following properties:

- `id` (string): A unique identifier for the widget.
- `pluginId` (string, optional): The ID of the plugin if the widget belongs to a specific plugin.
- `icon` (React.ComponentType): The icon component to display for the widget.
- `title` (MessageDescriptor): The title of the widget, using React Intl format.
- `component` (function): A function that returns a Promise resolving to the widget's React component.
- `permissions` (array, optional): An array of permission objects to control access to the widget.

## Managing Widgets

The `Widgets` class provides methods to manage widgets:

### getAll()

Retrieves all registered widgets:

```javascript
const allWidgets = widgets.getAll();
```
Comment on lines +49 to +55
Copy link

Choose a reason for hiding this comment

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

this isn't useful to users, it just allows us to render the homepage. Registering is all that matters for them so I recommend to omit the rest


### register()

Registers one or multiple widgets:

```javascript
// Register a single widget
widgets.register({
id: 'widget1',
// ... other properties
});

// Register multiple widgets
widgets.register([
{
id: 'widget1',
// ... other properties
},
{
id: 'widget2',
// ... other properties
},
]);
```

## Widget UID

Widgets are identified by a unique UID, which is automatically generated based on the `pluginId` and `id`:

- For plugin widgets: `plugin::{pluginId}.{id}`
- For global widgets: `global::{id}`
Copy link

Choose a reason for hiding this comment

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

I don't think "global" is clear to users. Basically it's for widgets created directly within an app, not via a plugin

Copy link

Choose a reason for hiding this comment

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

But also, users shouldn't care at all about the UID, it's just useful to us to render the homepage


## Best Practices
Copy link

Choose a reason for hiding this comment

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

this part is quite visibly AI generated imo, most of it is common sense


1. Use meaningful and unique IDs for your widgets to avoid conflicts.
2. Implement lazy loading for widget components to improve performance.
3. Use the `permissions` property to control access to widgets based on user roles.
4. Provide clear and concise titles for widgets to improve user experience.
5. Use appropriate icons that represent the widget's functionality.

## Example: Creating a Dashboard Widget

Here's an example of how to create a dashboard widget that displays recent content:

```javascript
import { Widgets } from '@strapi/admin';
import RecentContentIcon from './icons/RecentContent';

const widgets = new Widgets();

widgets.register({
Comment on lines +101 to +106
Copy link

Choose a reason for hiding this comment

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

same comment as before, this isn't how registration works

id: 'recent-content',
icon: RecentContentIcon,
title: {
id: 'dashboard.widgets.recent-content.title',
defaultMessage: 'Recent Content',
},
component: () => import('./components/RecentContentWidget'),
Copy link

Choose a reason for hiding this comment

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

I don't see the point of this example, it's the same thing as the one above.

What would be interesting instead is to show how to build the actual component

permissions: [
{ action: 'plugin::content-manager.explorer.read', subject: 'api::article.article' },
],
});
```

In this example, we create a widget that displays recent content. The widget is only accessible to users who have permission to read articles in the Content Manager.

Remember to implement the `RecentContentWidget` component and define the necessary translations for the widget title.

By using the Widgets API, you can create powerful and customizable widgets to enhance the Strapi admin panel experience for your users.