From b545f6257473261e99cedb5c4c8dab93a6230692 Mon Sep 17 00:00:00 2001 From: Alessio Gravili Date: Fri, 6 Oct 2023 12:39:22 +0200 Subject: [PATCH] chore: richtext-lexical: various styling and validation improvements --- packages/richtext-lexical/src/field/Field.tsx | 19 +++++- .../richtext-lexical/src/field/index.scss | 38 ++++++++++++ .../src/field/lexical/LexicalEditor.scss | 58 ++++++++++--------- .../src/populate/defaultValue.ts | 20 +++++++ .../richtext-lexical/src/validate/index.ts | 14 +++-- test/fields/collections/RichText/index.ts | 2 + 6 files changed, 116 insertions(+), 35 deletions(-) diff --git a/packages/richtext-lexical/src/field/Field.tsx b/packages/richtext-lexical/src/field/Field.tsx index 1f4817dcbb..647dcd5b9e 100644 --- a/packages/richtext-lexical/src/field/Field.tsx +++ b/packages/richtext-lexical/src/field/Field.tsx @@ -6,7 +6,8 @@ import { ErrorBoundary } from 'react-error-boundary' import type { FieldProps } from '../types' -import { richTextValidate } from '../validate' +import { defaultRichTextValueV2 } from '../populate/defaultValue' +import { richTextValidateHOC } from '../validate' import './index.scss' import { LexicalProvider } from './lexical/LexicalProvider' @@ -23,13 +24,12 @@ const RichText: React.FC = (props) => { style, width, }, - admin, defaultValue: defaultValueFromProps, editorConfig, label, path: pathFromProps, required, - validate = richTextValidate, + validate = richTextValidateHOC({ editorConfig }), } = props const path = pathFromProps || name @@ -49,6 +49,19 @@ const RichText: React.FC = (props) => { const { errorMessage, initialValue, setValue, showError, value } = fieldType + let valueToUse = value + + if (typeof valueToUse === 'string') { + try { + const parsedJSON = JSON.parse(valueToUse) + valueToUse = parsedJSON + } catch (err) { + valueToUse = null + } + } + + if (!valueToUse) valueToUse = defaultValueFromProps || defaultRichTextValueV2 + const classes = [ baseClass, 'field-type', diff --git a/packages/richtext-lexical/src/field/index.scss b/packages/richtext-lexical/src/field/index.scss index 5c7191332f..c650307d66 100644 --- a/packages/richtext-lexical/src/field/index.scss +++ b/packages/richtext-lexical/src/field/index.scss @@ -1 +1,39 @@ @import 'payload/scss'; + +.rich-text-lexical { + display: flex; + isolation: isolate; + + &__wrap { + width: 100%; + position: relative; + } + + &--read-only { + .editor-shell { + background: var(--theme-elevation-200); + color: var(--theme-elevation-450); + padding: base(0.5); + } + } +} + +html[data-theme='light'] { + .rich-text-lexical { + &.error { + .editor-shell { + @include lightInputError; + } + } + } +} + +html[data-theme='dark'] { + .rich-text-lexical { + &.error { + .editor-shell { + @include darkInputError; + } + } + } +} diff --git a/packages/richtext-lexical/src/field/lexical/LexicalEditor.scss b/packages/richtext-lexical/src/field/lexical/LexicalEditor.scss index 5cf164adb5..817f0fb9c5 100644 --- a/packages/richtext-lexical/src/field/lexical/LexicalEditor.scss +++ b/packages/richtext-lexical/src/field/lexical/LexicalEditor.scss @@ -1,34 +1,36 @@ @import 'payload/scss'; -.editor-shell { - position: relative; +.rich-text-lexical { + .editor-shell { + position: relative; - font-family: var(--font-serif); - font-size: base(0.625); + font-family: var(--font-serif); + font-size: base(0.625); - h1, - h2, - h3, - h4, - h5, - h6 { - font-family: var(--font-body); - line-height: 1.125; + h1, + h2, + h3, + h4, + h5, + h6 { + font-family: var(--font-body); + line-height: 1.125; + } + } + + .editor-placeholder { + position: absolute; + top: 8px; + left: 0px; + font-size: 15px; + color: var(--theme-elevation-500); + /* Prevent text selection */ + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + + /* Make it behave more like a background element (no interaction) */ + pointer-events: none; } } - -.editor-placeholder { - position: absolute; - top: 8px; - left: 0px; - font-size: 15px; - color: var(--theme-elevation-500); - /* Prevent text selection */ - user-select: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - - /* Make it behave more like a background element (no interaction) */ - pointer-events: none; -} diff --git a/packages/richtext-lexical/src/populate/defaultValue.ts b/packages/richtext-lexical/src/populate/defaultValue.ts index 9c5e04f496..6b7f9b1f13 100644 --- a/packages/richtext-lexical/src/populate/defaultValue.ts +++ b/packages/richtext-lexical/src/populate/defaultValue.ts @@ -27,3 +27,23 @@ export const defaultRichTextValue = { version: 1, }, } + +export const defaultRichTextValueV2 = { + root: { + children: [ + { + children: [], + direction: null, + format: '', + indent: 0, + type: 'paragraph', + version: 1, + }, + ], + direction: null, + format: '', + indent: 0, + type: 'root', + version: 1, + }, +} diff --git a/packages/richtext-lexical/src/validate/index.ts b/packages/richtext-lexical/src/validate/index.ts index 8968268d35..0df1b0a378 100644 --- a/packages/richtext-lexical/src/validate/index.ts +++ b/packages/richtext-lexical/src/validate/index.ts @@ -3,7 +3,7 @@ import type { RichTextField, Validate } from 'payload/types' import type { SanitizedEditorConfig } from '../field/lexical/config/types' -import { defaultRichTextValue } from '../populate/defaultValue' +import { defaultRichTextValue, defaultRichTextValueV2 } from '../populate/defaultValue' import { validateNodes } from './validateNodes' export const richTextValidateHOC = ({ editorConfig }: { editorConfig: SanitizedEditorConfig }) => { @@ -16,9 +16,15 @@ export const richTextValidateHOC = ({ editorConfig }: { editorConfig: SanitizedE const { required, t } = options if (required) { - const stringifiedDefaultValue = JSON.stringify(defaultRichTextValue) - if (value && JSON.stringify(value) !== stringifiedDefaultValue) return true - return t('validation:required') + if ( + !value || + !value?.root?.children || + !value?.root?.children?.length || + JSON.stringify(value) === JSON.stringify(defaultRichTextValue) || + JSON.stringify(value) === JSON.stringify(defaultRichTextValueV2) + ) { + return t('validation:required') + } } // Traverse through nodes and validate them. Just like a node can hook into the population process (e.g. link or relationship nodes), diff --git a/test/fields/collections/RichText/index.ts b/test/fields/collections/RichText/index.ts index a10ae8cc49..dc3a09ba87 100644 --- a/test/fields/collections/RichText/index.ts +++ b/test/fields/collections/RichText/index.ts @@ -29,6 +29,7 @@ const RichTextFields: CollectionConfig = { { name: 'richTextLexicalCustomFields', type: 'richText', + required: true, editor: lexicalEditor({ userConfig(defaultEditorConfig) { defaultEditorConfig.features.push(TreeviewFeature()) @@ -298,6 +299,7 @@ const RichTextFields: CollectionConfig = { export const richTextBulletsDoc = { title: 'Bullets and Indentation', + richTextLexicalCustomFields: generateLexicalRichText(), richText: [ { type: 'ul',