From 5a7e8a980be4e93f2503d8d007019948199a4867 Mon Sep 17 00:00:00 2001 From: Dan Ribbens Date: Mon, 28 Mar 2022 11:23:22 -0400 Subject: [PATCH] feat: builds a way to inject custom React providers into admin UI * fix: rich text textarea height * feat: custom providers for admin panel * docs: custom provider component --- .../components/CustomProvider/index.tsx | 29 +++++++++++ demo/payload.config.ts | 2 + docs/admin/components.mdx | 12 ++++- .../forms/field-types/RichText/RichText.tsx | 1 + .../forms/field-types/RichText/index.scss | 5 +- .../utilities/CustomProvider/index.tsx | 48 +++++++++++++++++++ src/admin/index.tsx | 5 +- src/config/schema.ts | 1 + src/config/types.ts | 1 + 9 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 demo/client/components/CustomProvider/index.tsx create mode 100644 src/admin/components/utilities/CustomProvider/index.tsx diff --git a/demo/client/components/CustomProvider/index.tsx b/demo/client/components/CustomProvider/index.tsx new file mode 100644 index 0000000000..ea7f3e58d8 --- /dev/null +++ b/demo/client/components/CustomProvider/index.tsx @@ -0,0 +1,29 @@ +import React, { createContext, useState, useContext } from 'react'; + +type CustomContext = { + getCustom + setCustom +} + +const Context = createContext({} as CustomContext); + +const CustomProvider: React.FC = ({ children }) => { + const [getCustom, setCustom] = useState({}); + + const value = { + getCustom, + setCustom, + }; + + console.log('custom provider called'); + + return ( + + {children} + + ); +}; + +export default CustomProvider; + +export const useCustom = () => useContext(Context); diff --git a/demo/payload.config.ts b/demo/payload.config.ts index 7e97f687aa..e2e906b23b 100644 --- a/demo/payload.config.ts +++ b/demo/payload.config.ts @@ -40,6 +40,7 @@ import CustomRouteWithDefaultTemplate from './client/components/views/CustomDefa import AfterDashboard from './client/components/AfterDashboard'; import AfterNavLinks from './client/components/AfterNavLinks'; import BeforeLogin from './client/components/BeforeLogin'; +// import CustomProvider from './client/components/CustomProvider'; export default buildConfig({ cookiePrefix: 'payload', @@ -58,6 +59,7 @@ export default buildConfig({ // disable: true, scss: path.resolve(__dirname, './client/scss/overrides.scss'), components: { + // providers: [CustomProvider, CustomProvider], routes: [ { path: '/custom-minimal-route', diff --git a/docs/admin/components.mdx b/docs/admin/components.mdx index c3b9af480d..cbb1d935ab 100644 --- a/docs/admin/components.mdx +++ b/docs/admin/components.mdx @@ -33,13 +33,14 @@ You can override a set of admin panel-wide components by providing a component t | **`graphics.Icon`** | Used as a graphic within the `Nav` component. Often represents a condensed version of a full logo. | | **`graphics.Logo`** | The full logo to be used in contexts like the `Login` view. | | **`routes`** | Define your own routes to add to the Payload Admin UI. [More](#custom-routes) | +| **`providers`** | Define your own provider components that will wrap the Payload Admin UI. [More](#custom-providers) | #### Full example: `payload.config.js` ```js import { buildConfig } from 'payload/config'; -import { MyCustomNav, MyCustomLogo, MyCustomIcon, MyCustomAccount, MyCustomDashboard } from './customComponents.js'; +import { MyCustomNav, MyCustomLogo, MyCustomIcon, MyCustomAccount, MyCustomDashboard, MyProvider } from './customComponents.js'; export default buildConfig({ admin: { @@ -52,7 +53,8 @@ export default buildConfig({ views: { Account: MyCustomAccount, Dashboard: MyCustomDashboard, - } + }, + providers: [MyProvider], } } }) @@ -152,6 +154,12 @@ You can find examples of custom route views in the [Payload source code `/demo/c To see how to pass in your custom views to create custom routes of your own, take a look at the `admin.components.routes` property of the [Payload demo config](https://github.com/payloadcms/payload/blob/master/demo/payload.config.ts). +## Custom providers + +As your admin customizations gets more complex you may want to share state between fields or other components. You can add custom providers to do add your own context to any Payload app for use in other custom components within the admin panel. Within your config add `admin.components.providers`, these can be used to share context or provide other custom functionality. Read the [React context](https://reactjs.org/docs/context.html) docs to learn more. + +Remember to pass the `children` prop in to your provider component and return it within to render the admin UI + ### Styling Custom Components Payload exports its SCSS variables and mixins for reuse in your own custom components. This is helpful in cases where you might want to style a custom input similarly to Payload's built-ini styling so it blends more thoroughly into the existing admin UI. diff --git a/src/admin/components/forms/field-types/RichText/RichText.tsx b/src/admin/components/forms/field-types/RichText/RichText.tsx index 95f6aaff01..0cacaf77e1 100644 --- a/src/admin/components/forms/field-types/RichText/RichText.tsx +++ b/src/admin/components/forms/field-types/RichText/RichText.tsx @@ -272,6 +272,7 @@ const RichText: React.FC = (props) => { ref={editorRef} > { + const Component = providers[0]; + if (providers.length > 1) { + return ( + + + {children} + + + ); + } + return ( + + {children} + + ); +}; + +export const CustomProvider: React.FC<{ children }> = ({ children }) => { + const config = useConfig(); + + const { + admin: { + components: { + providers, + }, + }, + } = config; + + if (Array.isArray(providers) && providers.length > 0) { + return ( + + {children} + + ); + } + + return ( + + {children} + + ); +}; diff --git a/src/admin/index.tsx b/src/admin/index.tsx index dd983e89fd..76ce6ad2a7 100644 --- a/src/admin/index.tsx +++ b/src/admin/index.tsx @@ -10,6 +10,7 @@ import { ModalProvider, ModalContainer } from '@faceless-ui/modal'; import { ToastContainer, Slide } from 'react-toastify'; import { ConfigProvider, AuthProvider } from '@payloadcms/config-provider'; import { PreferencesProvider } from './components/utilities/Preferences'; +import { CustomProvider } from './components/utilities/CustomProvider'; import { SearchParamsProvider } from './components/utilities/SearchParams'; import { LocaleProvider } from './components/utilities/Locale'; import Routes from './components/Routes'; @@ -38,7 +39,9 @@ const Index = () => ( - + + + diff --git a/src/config/schema.ts b/src/config/schema.ts index aa6a996244..8e4406c185 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -59,6 +59,7 @@ export default joi.object({ sensitive: joi.bool(), }), ), + providers: joi.array().items(component), beforeDashboard: joi.array().items(component), afterDashboard: joi.array().items(component), beforeLogin: joi.array().items(component), diff --git a/src/config/types.ts b/src/config/types.ts index b952f71fdc..6ad3fb4d71 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -102,6 +102,7 @@ export type Config = { dateFormat?: string components?: { routes?: AdminRoute[] + providers?: React.ComponentType[] beforeDashboard?: React.ComponentType[] afterDashboard?: React.ComponentType[] beforeLogin?: React.ComponentType[]