We can build custom widgets for CourseLit which can be displayed in the top
, bottom
, aside
or footer
sections of the application.
The valid section values are top
, bottom
, aside
, footerLeft
and footerRight
.
To install a widget follow these steps.
- Install the package
yarn workspace @courselit/web add my-widget
- Now open
ui-config/widget.tsx
file located inapps/web
and add the widget to thewidgets
section as shown below.
import mywidget from "my-widget";
export default {
widgets: {
// other widgets
[mywidget.metadata.name]: mywidget,
},
};
A CourseLit compatible widget exports an object called metadata
which contains meta information about the widget. The metadata
object has a property called name
. Read more about the structure of a widget below.
A widget needs to export the following objects in order for it to be detected by CourseLit. The names of the objects should be the same for every widget.
- metadata: This is a plain JSON object which specifies the configuration of the widget.
- widget: The actual
React
component which will be visible on the public facing site. - adminWidget: This is an optional
React
component which will be visible in the admin area. This component is used to provide access to the settings and data of the widget to administrators of the app.
The widget
and adminWidget
components receive the following props from the system.
- name: The name of the widget. This can be used while interacting with the database via GraphQL endpoints (described in the following sections).
- settings: The widget's settings object.
- config: An object containing various configuration settings. Check this file to see what all configurations are available.
- section: A name of the section where the widget is being displayed. As a widget can be displayed in multiple sections (if it supports), you can use this value to adapt the styling of the widget.
- state: The app's state powered by Redux. Equivalent to Redux's
store.getState()
. - dispatch: The Redux dispatcher.
- id: An identifier to identify widget's data in the app state's
widgetsData
property.
The metadata object specifies how the widget is integrated into the system. The following settings are available.
- name: String. (Required). Any one word string which is used to identify the widget in the system. You have to make sure that this does not conflict with any other widget in the system otherwise the database will be messed up.
- displayName: String. (Required). Any string. This is the name of widget an admin user will see while interacting with it from the dashboard.
- compatibleWith: Array of strings. (Required). An array of strings which specifies the section(s) of the application the widget is compatible with. The available sections are
top
,bottom
,aside
,footerLeft
andfooterRight
. - icon: String. (Optional). A URL string which points to an image. This will be used as the icon of the widget in the dashboard. If this setting is not provided, the default logo will be used.
- excludeFromPaths: Array of strings. (Optional). By default, once integrated the widget will be visible on every page. If there is a case where we do not want to display the widget on certain pages, the page URLs should be listed here. One can include
Next.js
based dynamic URLs like/posts/[id]/[slug]
as CourseLit's front-end is based on Next.js.
export default {
name: "buttondown",
displayName: "Buttondown",
compatibleWith: ["bottom", "aside"],
icon: "https://buttondown.email/static/images/icons/[email protected]",
excludeFromPaths: ["/post/[id]/[slug]", "/login"],
};
A widget can save its settings inside the Domain
model under the layout
property. In your adminWiget
component you can access a prop called onChange
. You can call this method with the complete settings object for your widget.
The settings object can be any arbitrary JavaScript object.
const AdminWidget = (props) => {
const { onChange } = props;
const settings = {
propA: "value",
propB: 1,
};
return (
<div>
<button onClick={() => onChange(settings)}>Save settings</button>
</div>
);
};
This will reflect your changes in the Edit Widget
component.
It is recommended to fetch the data required for showing the widget, on the server side. You can request data from CourseLit's GraphQL API by attaching a method called getData
to your widget
component.
While loading the app, all such methods from all the used widgets across the app will be combined and executed as a single query to reduce the round trips to the server.
The data fetched from the server will be stored in the widgetsData
property of the app state. Every query will get a unique id in the combined query so that while displaying your widget you can pull out the right data from widgetsData
.
The getData
method has the following signature.
YourComponent.getData(widgetId: string, widgetSettings: Record<string, unknown>) => string;
You will get a unique widgetId
from the framework. You have to use this as a key to your query. In your React components (widget and adminWidget) you will get this id in a prop called id
.
// Fetches courses with a certain tag
Widget.getData = (id: string, settings: Record<string, unknown>) => `
${id}: getCourses(offset: 1, tag: "${settings && settings.tag}") {
id,
title,
cost,
featuredImage {
thumbnail
},
slug,
courseId,
isBlog,
description
}
`;
CourseLit uses Material-UI's Theming system hence you can introduce additional custom variables to the app's theme which you can later consume in your widget.
Make sure there is a default styling as other themes may or may not provide the custom variables required by your Widget.
To learn how to design themes for CourseLit, see this link.
Shared widgets are those whose settings are stored on the domain level, instead of page level. Hence, a user is not required to configure a shared widget individually for every single page it is used on.
Any page can use a shared widget but the widget's settings are going to be saved and retrieved from the domain.
Come chat with us in our official Discord channel.