Expose configuration

This section describes how to expose configuration of a plugin.

Background

The heart of Nocto is the architecture which lets you dynamically expose interface of plugins to everyone else.

One of such functionalities is the exposing the configuration. Plugins can define how they can be configured by the others.

Slots vs configuration:

You can think about the configuration as just passing the arguments to your plugin. For instance, you can expose some column names, endpoints, table title etc., so something which can be fast and easy changed. Slots on the other hand requires changing the whole UI. If someone wants to hide or change the column name in the table, the better option is just use the configuration.

Config schema

Your plugin definition is in index.tsx, but the plugin's implementation can contain many different files. Let's assume that you have a file called plugin.tsx which will export the very simple component (as entry point to your plugin).

import { Text, Container } from "@medusajs/ui"

export const Slot1 = () => {
  return (
    <Container>
      <Text>Your custom plugin in a slot 1</Text>
    </Container>
  )
}

export default Slot1

And here is your definition of the plugin in index.tsx:

export const myPlugin = {
  id: "@my-plugin",
  routes: () => [
    {
      path: "/my-plugin",
      layout: "main",
      lazy: () =>
        import("./plugin").then((mod) => ({
          Component: () => mod.default(),
        })),
    },
  ],
}

You can see that your plugin exposes a route under /my-plugin and renders default components from ./plugin which is the Slot1.

Now, if you want to expose configuration, you need firstly define schema. We are using zod for this task.

Let's say you want to give possibility to change the text via configuration. Your schema can look like this:

import * as z from "zod"

export const ConfigSchema = z.object({
  text: z.string().default("Your custom plugin in a slot 1"),
})

In that schema, we are defining text which is a string and by default it is Your custom plugin in a slot 1.

Default values:

We encourage to always set default value if possible. It is easier for maintainance as the configuration might not be used, even if it is exposed.

Use above schema to put it in index.tsx under configSchema parameter.

import { ConfigSchema } from "./plugin";

export const myPlugin = {
  id: "@my-plugin",
  configSchema: ConfigSchema,
  routes: () => [
    {
      path: "/my-plugin",
      layout: "main",
      lazy: () =>
        import("./plugin").then((mod) => ({
          Component: () => mod.default(),
        })),
    },
  ],
}

Finally, let's start using this configuration in your plugin so people can dynamically change it. For that task, we are going to use nocto context and created schema. Here is the example in plugin.tsx:

import { Text, Container } from "@medusajs/ui"
import { useNoctoPluginContext } from "@rsc-labs/noctojs-plugin-system"
import * as z from "zod"

export const ConfigSchema = z.object({
  text: z.string().default("Your custom plugin in a slot 1"),
})

type ConfigData = z.infer<typeof ConfigSchema>;

export const Slot1 = () => {

  const { pluginConfigRegistry } = useNoctoPluginContext()

  const config = pluginConfigRegistry.get<ConfigData>("@my-plugin");

  return (
    <Container>
      <Text>{config && config.text}</Text>
    </Container>
  )
}

export default Slot1

You can see that firstly we are using nocto context to get the pluginConfigRegistry and then we are using it to get our config which is a type coming from zod.

At the end the config.text is used to render the text.

Future:

We are going to simplify this mechanism in the future, including schema definition and usage of config.

Now, users can use your plugin and define their configuration, for instance:

import { NoctoConfig } from "@rsc-labs/noctojs-plugin-system"
import {
  Sparkles,
} from "@medusajs/icons"

export const noctoConfig: NoctoConfig = {
  plugins: {
    "@login": {
      config: {
        title: "Welcome to Nocto",
        hint: "Sign in to access",
        mainIcon: Sparkles
      }
    },
    "@order-detail": {},
    "@campaigns-routes": {},
    "@categories-routes": {},
    "@collections-routes": {},
    "@core-routes": {},
    "@customer-groups-routes": {},
    "@public-routes": {},
    "@reservations-routes": {},
    "@settings-routes": {},

    "@my-plugin": {
      config: {
        text: "My configuration text"
      }
    }
  },
  sidebar: {
    "@orders": { order: 1 },
    "@products": { order: 2 },
    "@inventory": { order: 3 },
    "@customers": { order: 4 },
    "@promotions": { order: 5 },
    "@price-lists": { order: 6 },
  }
}

Based on the above, it will render My configuration text intead of Your custom plugin in a slot 1,