diff --git a/docs/admin/components.mdx b/docs/admin/components.mdx index 5a5144330..5a17d6d1d 100644 --- a/docs/admin/components.mdx +++ b/docs/admin/components.mdx @@ -11,35 +11,39 @@ While designing the Payload Admin panel, we determined it should be as minimal a To swap in your own React component, first, consult the list of available component overrides below. Determine the scope that corresponds to what you are trying to accomplish, and then author your React component accordingly. - Tip:
- Custom components will automatically be provided with all props that the default component would accept. + Tip: +
+ Custom components will automatically be provided with all props that the + default component would accept.
### Base Component Overrides You can override a set of admin panel-wide components by providing a component to your base Payload config's `admin.components` property. The following options are available: -| Path | Description | -| --------------------- | -------------| -| **`Nav`** | Contains the sidebar and mobile Nav in its entirety. | -| **`BeforeDashboard`** | Array of components to inject into the built-in Dashboard, _before_ the default dashboard contents. | -| **`AfterDashboard`** | Array of components to inject into the built-in Dashboard, _after_ the default dashboard contents. [Demo](https://github.com/payloadcms/payload/tree/master/test/admin/components/AfterDashboard/index.tsx)| -| **`BeforeLogin`** | Array of components to inject into the built-in Login, _before_ the default login form. | -| **`AfterLogin`** | Array of components to inject into the built-in Login, _after_ the default login form. | -| **`BeforeNavLinks`** | Array of components to inject into the built-in Nav, _before_ the links themselves. | -| **`AfterNavLinks`** | Array of components to inject into the built-in Nav, _after_ the links. | -| **`views.Account`** | The Account view is used to show the currently logged in user's Account page. | -| **`views.Dashboard`** | The main landing page of the Admin panel. | -| **`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) | +| Path | Description | +| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **`Nav`** | Contains the sidebar and mobile Nav in its entirety. | +| **`logout.Button`** | A custom React component. +| **`BeforeDashboard`** | Array of components to inject into the built-in Dashboard, _before_ the default dashboard contents. | +| **`AfterDashboard`** | Array of components to inject into the built-in Dashboard, _after_ the default dashboard contents. [Demo](https://github.com/payloadcms/payload/tree/master/test/admin/components/AfterDashboard/index.tsx) | +| **`BeforeLogin`** | Array of components to inject into the built-in Login, _before_ the default login form. | +| **`AfterLogin`** | Array of components to inject into the built-in Login, _after_ the default login form. | +| **`BeforeNavLinks`** | Array of components to inject into the built-in Nav, _before_ the links themselves. | +| **`AfterNavLinks`** | Array of components to inject into the built-in Nav, _after_ the links. | +| **`views.Account`** | The Account view is used to show the currently logged in user's Account page. | +| **`views.Dashboard`** | The main landing page of the Admin panel. | +| **`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` + ```ts -import { buildConfig } from 'payload/config' +import { buildConfig } from "payload/config"; import { MyCustomNav, MyCustomLogo, @@ -47,7 +51,7 @@ import { MyCustomAccount, MyCustomDashboard, MyProvider, -} from './customComponents'; +} from "./customComponents"; export default buildConfig({ admin: { @@ -67,75 +71,69 @@ export default buildConfig({ }); ``` -*For more examples regarding how to customize components, look at the following [examples](https://github.com/payloadcms/payload/tree/master/test/admin/components).* +_For more examples regarding how to customize components, look at the following [examples](https://github.com/payloadcms/payload/tree/master/test/admin/components)._ ### Collections You can override components on a Collection-by-Collection basis via each Collection's `admin` property. -| Path | Description | -| ---------------- | -------------| -| **`views.Edit`** | Used while a document within this Collection is being edited. | -| **`views.List`** | The `List` view is used to render a paginated, filterable table of Documents in this Collection. | +| Path | Description | +| ---------------- | ------------------------------------------------------------------------------------------------ | +| **`views.Edit`** | Used while a document within this Collection is being edited. | +| **`views.List`** | The `List` view is used to render a paginated, filterable table of Documents in this Collection. | ### Globals As with Collections, You can override components on a global-by-global basis via their `admin` property. -| Path | Description | -| ---------------- | -------------| -| **`views.Edit`** | Used while this Global is being edited. | +| Path | Description | +| ---------------- | --------------------------------------- | +| **`views.Edit`** | Used while this Global is being edited. | ### Fields All Payload fields support the ability to swap in your own React components. So, for example, instead of rendering a default Text input, you might need to render a color picker that provides the editor with a custom color picker interface to restrict the data entered to colors only. - Tip:
- Don't see a built-in field type that you need? Build it! Using a combination of custom validation and custom components, you can override the entirety of how a component functions within the admin panel and effectively create your own field type. + Tip: +
+ Don't see a built-in field type that you need? Build it! Using a combination + of custom validation and custom components, you can override the entirety of + how a component functions within the admin panel and effectively create your + own field type.
**Fields support the following custom components:** -| Component | Description | -| --------------- |------------------------------------------------------------------------------------------------------------------------------| -| **`Filter`** | Override the text input that is presented in the `List` view when a user is filtering documents by the customized field. | -| **`Cell`** | Used in the `List` view's table to represent a table-based preview of the data stored in the field. [More](#cell-component) | -| **`Field`** | Swap out the field itself within all `Edit` views. [More](#field-component) | +| Component | Description | +| ------------ | --------------------------------------------------------------------------------------------------------------------------- | +| **`Filter`** | Override the text input that is presented in the `List` view when a user is filtering documents by the customized field. | +| **`Cell`** | Used in the `List` view's table to represent a table-based preview of the data stored in the field. [More](#cell-component) | +| **`Field`** | Swap out the field itself within all `Edit` views. [More](#field-component) | ## Cell Component These are the props that will be passed to your custom Cell to use in your own components. -| Property | Description | -|--------------|-------------------------------------------------------------------| -| **`field`** | An object that includes the field configuration. | -| **`colIndex`** | A unique number for the column in the list. | +| Property | Description | +| ---------------- | ----------------------------------------------------------------- | +| **`field`** | An object that includes the field configuration. | +| **`colIndex`** | A unique number for the column in the list. | | **`collection`** | An object with the config of the collection that the field is in. | | **`cellData`** | The data for the field that the cell represents. | -| **`rowData`** | An object with all the field values for the row. | +| **`rowData`** | An object with all the field values for the row. | #### Example ```tsx -import React from 'react'; -import './index.scss'; -const baseClass = 'custom-cell'; +import React from "react"; +import "./index.scss"; +const baseClass = "custom-cell"; const CustomCell: React.FC = (props) => { - const { - field, - colIndex, - collection, - cellData, - rowData, - } = props; + const { field, colIndex, collection, cellData, rowData } = props; - return ( - - { cellData } - - ); + return {cellData}; }; ``` @@ -148,21 +146,28 @@ When writing your own custom components you can make use of a number of hooks to When swapping out the `Field` component, you'll be responsible for sending and receiving the field's `value` from the form itself. To do so, import the `useField` hook as follows: ```tsx -import { useField } from 'payload/components/forms' +import { useField } from "payload/components/forms"; -type Props = { path: string } +type Props = { path: string }; const CustomTextField: React.FC = ({ path }) => { // highlight-start - const { value, setValue } = useField({ path }) + const { value, setValue } = useField({ path }); // highlight-end - return setValue(e.target.value)} value={value.path} /> -} + return ( + setValue(e.target.value)} value={value.path} /> + ); +}; ``` - For more information regarding the hooks that are available to you while you build custom components, including the useField hook, click here. + For more information regarding the hooks that are available to you while you + build custom components, including the useField hook,{" "} + + click here + + . ## Custom routes @@ -171,28 +176,31 @@ You can easily add your own custom routes to the Payload Admin panel using the ` **Custom routes support the following properties:** -| Property | Description | -| ----------------- | -------------| -| **`Component`** * | Pass in the component that should be rendered when a user navigates to this route. | -| **`path`** * | React Router `path`. [See the React Router docs](https://v5.reactrouter.com/web/api/Route/path-string-string) for more info. | -| **`exact`** | React Router `exact` property. [More](https://v5.reactrouter.com/web/api/Route/exact-bool) | -| **`strict`** | React Router `strict` property. [More](https://v5.reactrouter.com/web/api/Route/strict-bool) | -| **`sensitive`** | React Router `sensitive` property. [More](https://v5.reactrouter.com/web/api/Route/sensitive-bool) | +| Property | Description | +| ------------------ | ---------------------------------------------------------------------------------------------------------------------------- | +| **`Component`** \* | Pass in the component that should be rendered when a user navigates to this route. | +| **`path`** \* | React Router `path`. [See the React Router docs](https://v5.reactrouter.com/web/api/Route/path-string-string) for more info. | +| **`exact`** | React Router `exact` property. [More](https://v5.reactrouter.com/web/api/Route/exact-bool) | +| **`strict`** | React Router `strict` property. [More](https://v5.reactrouter.com/web/api/Route/strict-bool) | +| **`sensitive`** | React Router `sensitive` property. [More](https://v5.reactrouter.com/web/api/Route/sensitive-bool) | -*\* An asterisk denotes that a property is required.* +_\* An asterisk denotes that a property is required._ #### Custom route components Your custom route components will be given all the props that a React Router `` typically would receive, as well as two props from Payload: -| Prop | Description | -| ---------------------- | -------------| -| **`user`** | The currently logged in user. Will be `null` if no user is logged in. | -| **`canAccessAdmin`** * | If the currently logged in user is allowed to access the admin panel or not. | +| Prop | Description | +| ----------------------- | ---------------------------------------------------------------------------- | +| **`user`** | The currently logged in user. Will be `null` if no user is logged in. | +| **`canAccessAdmin`** \* | If the currently logged in user is allowed to access the admin panel or not. | - Note:
- It's up to you to secure your custom routes. If your route requires a user to be logged in or to have certain access rights, you should handle that within your route component yourself. + Note: +
+ It's up to you to secure your custom routes. If your route requires a user to + be logged in or to have certain access rights, you should handle that within + your route component yourself.
#### Example @@ -208,7 +216,10 @@ To see how to pass in your custom views to create custom routes of your own, tak 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. -Reminder: Don't forget to pass the **children** prop through the provider component for the admin UI to show + + Reminder: Don't forget to pass the **children** prop through + the provider component for the admin UI to show + ### Styling Custom Components @@ -225,7 +236,7 @@ To make use of Payload SCSS variables / mixins to use directly in your own compo In any custom component you can get the selected locale with the `useLocale` hook. Here is a simple example: ```tsx -import { useLocale } from 'payload/components/utilities'; +import { useLocale } from "payload/components/utilities"; const Greeting: React.FC = () => { // highlight-start @@ -233,12 +244,10 @@ const Greeting: React.FC = () => { // highlight-end const trans = { - en: 'Hello', - es: 'Hola', + en: "Hello", + es: "Hola", }; - return ( - { trans[locale] } - ); + return {trans[locale]} ; }; ``` diff --git a/docs/admin/overview.mdx b/docs/admin/overview.mdx index 3674de354..25dc05a92 100644 --- a/docs/admin/overview.mdx +++ b/docs/admin/overview.mdx @@ -22,18 +22,20 @@ The Payload Admin panel is built with Webpack, code-split, highly performant (ev All options for the Admin panel are defined in your base Payload config file. -| Option | Description | -| -------------------- | -------------| -| `user` | The `slug` of a Collection that you want be used to log in to the Admin dashboard. [More](/docs/admin/overview#the-admin-user-collection) | -| `meta` | Base meta data to use for the Admin panel. Included properties are `titleSuffix`, `ogImage`, and `favicon`. | -| `disable` | If set to `true`, the entire Admin panel will be disabled. | -| `indexHTML` | Optionally replace the entirety of the `index.html` file used by the Admin panel. Reference the [base index.html file](https://github.com/payloadcms/payload/blob/master/src/admin/index.html) to ensure your replacement has the appropriate HTML elements. | -| `css` | Absolute path to a stylesheet that you can use to override / customize the Admin panel styling. [More](/docs/admin/customizing-css). | -| `scss` | Absolute path to a Sass variables / mixins stylesheet meant to override Payload styles to make for an easy re-skinning of the Admin panel. [More](/docs/admin/customizing-css#overriding-scss-variables). | -| `dateFormat` | Global date format that will be used for all dates in the Admin panel. Any valid [date-fns](https://date-fns.org/) format pattern can be used. | -| `avatar` | Set account profile picture. Options: `gravatar`, `default` or a custom React component. | -| `components` | Component overrides that affect the entirety of the Admin panel. [More](/docs/admin/components) | -| `webpack` | Customize the Webpack config that's used to generate the Admin panel. [More](/docs/admin/webpack) | +| Option | Description | +| --------------------- | -------------| +| `user` | The `slug` of a Collection that you want be used to log in to the Admin dashboard. [More](/docs/admin/overview#the-admin-user-collection) | +| `meta` | Base meta data to use for the Admin panel. Included properties are `titleSuffix`, `ogImage`, and `favicon`. | +| `disable` | If set to `true`, the entire Admin panel will be disabled. | +| `indexHTML` | Optionally replace the entirety of the `index.html` file used by the Admin panel. Reference the [base index.html file](https://github.com/payloadcms/payload/blob/master/src/admin/index.html) to ensure your replacement has the appropriate HTML elements. | +| `css` | Absolute path to a stylesheet that you can use to override / customize the Admin panel styling. [More](/docs/admin/customizing-css). | +| `scss` | Absolute path to a Sass variables / mixins stylesheet meant to override Payload styles to make for an easy re-skinning of the Admin panel. [More](/docs/admin/customizing-css#overriding-scss-variables). | +| `dateFormat` | Global date format that will be used for all dates in the Admin panel. Any valid [date-fns](https://date-fns.org/) format pattern can be used. | +| `avatar` | Set account profile picture. Options: `gravatar`, `default` or a custom React component. | +| `components` | Component overrides that affect the entirety of the Admin panel. [More](/docs/admin/components) | +| `webpack` | Customize the Webpack config that's used to generate the Admin panel. [More](/docs/admin/webpack) | | +| **`logoutRoute`** | The route for the `logout` page. | +| **`inactivityRoute`** | The route for the `logout` inactivity page. | ### The Admin User Collection diff --git a/src/admin/components/Routes.tsx b/src/admin/components/Routes.tsx index 918813da6..e291cd9a8 100644 --- a/src/admin/components/Routes.tsx +++ b/src/admin/components/Routes.tsx @@ -32,9 +32,12 @@ const Routes = () => { const canAccessAdmin = permissions?.canAccessAdmin; + const config = useConfig(); const { admin: { user: userSlug, + logoutRoute, + inactivityRoute: logoutInactivityRoute, components: { routes: customRoutes, } = {}, @@ -42,7 +45,8 @@ const Routes = () => { routes, collections, globals, - } = useConfig(); + } = config; + const userCollection = collections.find(({ slug }) => slug === userSlug); @@ -103,10 +107,10 @@ const Routes = () => { - + - + diff --git a/src/admin/components/elements/Logout/index.tsx b/src/admin/components/elements/Logout/index.tsx new file mode 100644 index 000000000..4a92fbae9 --- /dev/null +++ b/src/admin/components/elements/Logout/index.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { useConfig } from '../../utilities/Config'; +import RenderCustomComponent from '../../utilities/RenderCustomComponent'; +import LogOut from '../../icons/LogOut'; + +const baseClass = 'nav'; + +const DefaultLogout = () => { + const config = useConfig(); + const { + routes: { admin }, + admin: { + logoutRoute, + components: { logout } + } + } = config; + return ( + + + + ); +}; + +const Logout: React.FC = () => { + const { + admin: { + components: { + logout: { Button: CustomLogout } = { + Button: undefined, + }, + } = {}, + } = {}, + } = useConfig(); + + return ( + + ); +}; + +export default Logout; diff --git a/src/admin/components/elements/Nav/index.tsx b/src/admin/components/elements/Nav/index.tsx index 7b43b9c7f..cc6ac0be9 100644 --- a/src/admin/components/elements/Nav/index.tsx +++ b/src/admin/components/elements/Nav/index.tsx @@ -4,7 +4,6 @@ import { useConfig } from '../../utilities/Config'; import { useAuth } from '../../utilities/Auth'; import RenderCustomComponent from '../../utilities/RenderCustomComponent'; import Chevron from '../../icons/Chevron'; -import LogOut from '../../icons/LogOut'; import Menu from '../../icons/Menu'; import CloseMenu from '../../icons/CloseMenu'; import Icon from '../../graphics/Icon'; @@ -14,6 +13,7 @@ import NavGroup from '../NavGroup'; import { groupNavItems, Group, EntityToGroup, EntityType } from '../../../utilities/groupNavItems'; import './index.scss'; +import Logout from '../Logout'; const baseClass = 'nav'; @@ -31,7 +31,7 @@ const DefaultNav = () => { admin: { components: { beforeNavLinks, - afterNavLinks, + afterNavLinks }, }, } = useConfig(); @@ -137,12 +137,7 @@ const DefaultNav = () => { > - - - + diff --git a/src/admin/components/modals/StayLoggedIn/index.tsx b/src/admin/components/modals/StayLoggedIn/index.tsx index 3cb06a08f..48856a220 100644 --- a/src/admin/components/modals/StayLoggedIn/index.tsx +++ b/src/admin/components/modals/StayLoggedIn/index.tsx @@ -15,7 +15,13 @@ const modalSlug = 'stay-logged-in'; const StayLoggedInModal: React.FC = (props) => { const { refreshCookie } = props; const history = useHistory(); - const { routes: { admin } } = useConfig(); + const config = useConfig(); + const { + routes: { admin }, + admin: { + logoutRoute + } + } = config; const { toggleModal } = useModal(); return ( @@ -31,7 +37,7 @@ const StayLoggedInModal: React.FC = (props) => { buttonStyle="secondary" onClick={() => { toggleModal(modalSlug); - history.push(`${admin}/logout`); + history.push(`${admin}${logoutRoute}`); }} > Log out diff --git a/src/admin/components/utilities/Auth/index.tsx b/src/admin/components/utilities/Auth/index.tsx index b9846181c..59de3ea37 100644 --- a/src/admin/components/utilities/Auth/index.tsx +++ b/src/admin/components/utilities/Auth/index.tsx @@ -25,6 +25,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children const { admin: { user: userSlug, + inactivityRoute: logoutInactivityRoute, }, serverURL, routes: { @@ -57,7 +58,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children setUser(json.user); } else { setUser(null); - push(`${admin}/logout-inactivity`); + push(`${admin}${logoutInactivityRoute}`); } }, 1000); } @@ -145,7 +146,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children if (remainingTime > 0) { forceLogOut = setTimeout(() => { setUser(null); - push(`${admin}/logout-inactivity`); + push(`${admin}${logoutInactivityRoute}`); closeAllModals(); }, Math.min(remainingTime * 1000, maxTimeoutTime)); } diff --git a/src/admin/components/views/Login/index.tsx b/src/admin/components/views/Login/index.tsx index b0551611a..56299ea8f 100644 --- a/src/admin/components/views/Login/index.tsx +++ b/src/admin/components/views/Login/index.tsx @@ -18,12 +18,15 @@ const baseClass = 'login'; const Login: React.FC = () => { const history = useHistory(); const { user, setToken } = useAuth(); + const config = useConfig(); const { admin: { user: userSlug, + logoutRoute, components: { beforeLogin, afterLogin, + logout } = {}, }, serverURL, @@ -32,7 +35,7 @@ const Login: React.FC = () => { api, }, collections, - } = useConfig(); + } = config; const collection = collections.find(({ slug }) => slug === userSlug); @@ -56,7 +59,7 @@ const Login: React.FC = () => {

To log in with another user, you should {' '} - log out + log out {' '} first.

diff --git a/src/admin/components/views/ResetPassword/index.tsx b/src/admin/components/views/ResetPassword/index.tsx index 3990c1fa3..f600bb25d 100644 --- a/src/admin/components/views/ResetPassword/index.tsx +++ b/src/admin/components/views/ResetPassword/index.tsx @@ -17,8 +17,9 @@ import HiddenInput from '../../forms/field-types/HiddenInput'; const baseClass = 'reset-password'; const ResetPassword: React.FC = () => { - const { admin: { user: userSlug }, serverURL, routes: { admin, api } } = useConfig(); - const { token } = useParams<{token?: string}>(); + const config = useConfig(); + const { admin: { user: userSlug, logoutRoute }, serverURL, routes: { admin, api } } = config; + const { token } = useParams<{ token?: string }>(); const history = useHistory(); const { user, setToken } = useAuth(); @@ -43,7 +44,7 @@ const ResetPassword: React.FC = () => {

To log in with another user, you should {' '} - log out + log out {' '} first.

diff --git a/src/admin/components/views/Unauthorized/index.tsx b/src/admin/components/views/Unauthorized/index.tsx index 124a4b6e0..cc984ba15 100644 --- a/src/admin/components/views/Unauthorized/index.tsx +++ b/src/admin/components/views/Unauthorized/index.tsx @@ -5,8 +5,13 @@ import Meta from '../../utilities/Meta'; import MinimalTemplate from '../../templates/Minimal'; const Unauthorized: React.FC = () => { - const { routes: { admin } } = useConfig(); - + const config = useConfig(); + const { + routes: { admin }, + admin: { + logoutRoute + }, + } = config; return ( {
diff --git a/src/config/defaults.ts b/src/config/defaults.ts index 5727df3a4..de0d46a11 100644 --- a/src/config/defaults.ts +++ b/src/config/defaults.ts @@ -19,7 +19,8 @@ export const defaults: Config = { disable: false, indexHTML: path.resolve(__dirname, '../admin/index.html'), avatar: 'default', - components: {}, + logoutRoute: '/logout', + inactivityRoute: '/logout-inactivity', css: path.resolve(__dirname, '../admin/scss/custom.css'), dateFormat: 'MMMM do yyyy, h:mm a', }, diff --git a/src/config/schema.ts b/src/config/schema.ts index 8e9c2c338..7a43edc51 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -1,3 +1,4 @@ +import { JSONDefinition } from 'graphql-scalars'; import joi from 'joi'; const component = joi.alternatives().try( @@ -62,6 +63,8 @@ export default joi.object({ joi.string(), component, ), + logoutRoute: joi.string(), + inactivityRoute: joi.string(), components: joi.object() .keys({ routes: joi.array() @@ -82,6 +85,9 @@ export default joi.object({ beforeNavLinks: joi.array().items(component), afterNavLinks: joi.array().items(component), Nav: component, + logout: joi.object({ + Button: component, + }), views: joi.object({ Dashboard: component, Account: component, diff --git a/src/config/types.ts b/src/config/types.ts index 1b99a4060..cbe6ed545 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -154,6 +154,8 @@ export type Config = { css?: string dateFormat?: string avatar?: 'default' | 'gravatar' | React.ComponentType, + logoutRoute?: string, + inactivityRoute?: string, components?: { routes?: AdminRoute[] providers?: React.ComponentType<{ children: React.ReactNode }>[] @@ -164,6 +166,9 @@ export type Config = { beforeNavLinks?: React.ComponentType[] afterNavLinks?: React.ComponentType[] Nav?: React.ComponentType + logout?: { + Button?: React.ComponentType, + } graphics?: { Icon?: React.ComponentType Logo?: React.ComponentType diff --git a/test/admin/components/Logout/index.tsx b/test/admin/components/Logout/index.tsx new file mode 100644 index 000000000..79f416aeb --- /dev/null +++ b/test/admin/components/Logout/index.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { useConfig } from '../../../../src/admin/components/utilities/Config'; +import LogOut from '../../../../src/admin/components/icons/LogOut'; + + +const Logout: React.FC = () => { + const config = useConfig(); + const { + routes: { + admin, + }, + admin: { + logoutRoute + }, + } = config; + return ( + + + + ); +}; + +export default Logout; diff --git a/test/admin/config.ts b/test/admin/config.ts index b0823537a..e2c86b53f 100644 --- a/test/admin/config.ts +++ b/test/admin/config.ts @@ -8,6 +8,7 @@ import CustomDefaultRoute from './components/views/CustomDefault'; import BeforeLogin from './components/BeforeLogin'; import AfterNavLinks from './components/AfterNavLinks'; import { slug, globalSlug } from './shared'; +import Logout from './components/Logout'; export interface Post { id: string; @@ -38,6 +39,9 @@ export default buildConfig({ beforeLogin: [ BeforeLogin, ], + logout: { + Button: Logout, + }, afterNavLinks: [ AfterNavLinks, ], diff --git a/test/dev.js b/test/dev.js index d08f3d9eb..64e112f8e 100644 --- a/test/dev.js +++ b/test/dev.js @@ -15,6 +15,11 @@ require('@babel/register')({ const [testSuiteDir] = process.argv.slice(2); +if (!testSuiteDir) { + console.error('ERROR: You must provide an argument for "testSuiteDir"'); + process.exit(1); +} + const configPath = path.resolve(__dirname, testSuiteDir, 'config.ts'); if (!fs.existsSync(configPath)) { diff --git a/test/devServer.ts b/test/devServer.ts index 933ff6c91..27f2c2611 100644 --- a/test/devServer.ts +++ b/test/devServer.ts @@ -2,8 +2,8 @@ import express from 'express'; import { v4 as uuid } from 'uuid'; import payload from '../src'; +require("dotenv").config(); const expressApp = express(); - const init = async () => { await payload.initAsync({ secret: uuid(), diff --git a/test/runE2E.ts b/test/runE2E.ts index 4c1c045e8..b657436df 100644 --- a/test/runE2E.ts +++ b/test/runE2E.ts @@ -15,7 +15,7 @@ const suiteName = args[0]; // Run all if (!suiteName || args[0].startsWith('-')) { const bail = args.includes('--bail'); - const files = glob.sync(`${path.resolve(__dirname)}/**/*e2e.spec.ts`); + const files = glob.sync(`${path.resolve(__dirname).replace(/\\/g, '/')}/**/*e2e.spec.ts`); console.log(`\n\nExecuting all ${files.length} E2E tests...`); files.forEach((file) => { clearWebpackCache();