Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* [DataRowOptions]: Added `reserveSpace` property to checkbox configuration
- When set and `checkbox.isVisible` is `false`, reserves space for the checkbox in the row
- Automatically calculated for tree-like data structures to maintain consistent alignment
* [RTE]: default block and mark components render through `PlateElement` / `PlateLeaf` (with `asChild` where a semantic tag is used) so custom Plate plugins that wrap nodes (e.g. `inject.aboveComponent`) compose correctly ([#3062](https://github.com/epam/UUI/issues/3062)).


**What's Fixed**
Expand All @@ -29,6 +30,7 @@
* Fixed Property Explorer by providing values for properties that are not automatically resolved ([#2832](https://github.com/epam/UUI/issues/2832))
* Fixed tree table indentation when child rows have no checkbox: child rows now reserve consistent checkbox space for alignment ([#2844](https://github.com/epam/UUI/issues/2844))
* [Tooltip]: fixed tooltip not showing on keyboard focus for complex elements with focusable children (e.g. Switch) ([#2959](https://github.com/epam/UUI/issues/2959))
* [RTE]: Todo list items migrate legacy `element.data.checked` to `element.checked`. Iframe nodes normalize `url` from `url` or legacy `data.src`.


# 6.4.3 - 04.02.2026
Expand Down
4 changes: 1 addition & 3 deletions uui-docs/src/demoData/slateInitialValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,7 @@ export const slateInitialValue = [
},
{
type: 'toDoItem',
data: {
checked: false,
},
checked: true,
children: [
{
text: ' An item',
Expand Down
16 changes: 13 additions & 3 deletions uui-editor/src/__tests__/__snapshots__/normalizers.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,14 @@ Array [
"type": "paragraph",
},
Object {
"checked": false,
"children": Array [
Object {
"text": " An item",
"uui-richTextEditor-span-mark": true,
},
],
"data": Object {
"checked": false,
},
"data": Object {},
"type": "toDoItem",
},
Object {
Expand Down Expand Up @@ -417,6 +416,17 @@ Array [
"type": "iframe",
"url": "https://www.youtube.com/embed/5qap5aO4i9A",
},
Object {
"children": Array [
Object {
"text": "",
"uui-richTextEditor-span-mark": true,
},
],
"data": Object {},
"type": "iframe",
"url": "https://www.youtube.com/embed/5qap5aO4i9A",
},
Object {
"children": Array [
Object {
Expand Down
12 changes: 12 additions & 0 deletions uui-editor/src/__tests__/data/plate-migration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,18 @@ export const initialValue = [
],
url: 'https://www.youtube.com/embed/5qap5aO4i9A',
},
{
data: {
src: 'https://www.youtube.com/embed/5qap5aO4i9A',
},
type: 'iframe',
children: [
{
text: '',
'uui-richTextEditor-span-mark': true,
},
],
},
{
data: {
checked: false,
Expand Down
50 changes: 37 additions & 13 deletions uui-editor/src/components.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { MARK_BOLD, MARK_CODE, MARK_ITALIC, MARK_SUPERSCRIPT, MARK_UNDERLINE } from '@udecode/plate-basic-marks';
import { PlatePluginComponent } from '@udecode/plate-common';
import { PlateElement, PlateLeaf, PlatePluginComponent } from '@udecode/plate-common';
import { ELEMENT_H1, ELEMENT_H2, ELEMENT_H3, ELEMENT_H4, ELEMENT_H5, ELEMENT_H6 } from '@udecode/plate-heading';
import { ELEMENT_PARAGRAPH } from '@udecode/plate-paragraph';

Expand All @@ -24,18 +24,42 @@ export const createPlateUI = <T extends string = string>(
>,
) => {
const components: { [key: string]: PlatePluginComponent } = {
[ELEMENT_H1]: (props) => <h1 { ...props.attributes }>{ props.children }</h1>,
[ELEMENT_H2]: (props) => <h2 { ...props.attributes }>{ props.children }</h2>,
[ELEMENT_H3]: (props) => <h3 { ...props.attributes }>{ props.children }</h3>,
[ELEMENT_H4]: (props) => <h4 { ...props.attributes }>{ props.children }</h4>,
[ELEMENT_H5]: (props) => <h5 { ...props.attributes }>{ props.children }</h5>,
[ELEMENT_H6]: (props) => <h6 { ...props.attributes }>{ props.children }</h6>,
[ELEMENT_PARAGRAPH]: (props) => <p { ...props.attributes }>{ props.children }</p>,
[MARK_BOLD]: (props) => <strong { ...props.attributes }>{ props.children }</strong>,
[MARK_CODE]: (props) => <code { ...props.attributes }>{ props.children }</code>,
[MARK_ITALIC]: (props) => <em { ...props.attributes }>{ props.children }</em>,
[MARK_SUPERSCRIPT]: (props) => <sup { ...props.attributes }>{ props.children }</sup>,
[MARK_UNDERLINE]: (props) => <u { ...props.attributes }>{ props.children }</u>,
[ELEMENT_H1]: (props) => (
<PlateElement as="h1" { ...props } />
),
[ELEMENT_H2]: (props) => (
<PlateElement as="h2" { ...props } />
),
[ELEMENT_H3]: (props) => (
<PlateElement as="h3" { ...props } />
),
[ELEMENT_H4]: (props) => (
<PlateElement as="h4" { ...props } />
),
[ELEMENT_H5]: (props) => (
<PlateElement as="h5" { ...props } />
),
[ELEMENT_H6]: (props) => (
<PlateElement as="h6" { ...props } />
),
[ELEMENT_PARAGRAPH]: (props) => (
<PlateElement as="p" { ...props } />
),
[MARK_BOLD]: (props) => (
<PlateLeaf as="strong" { ...props } />
),
[MARK_CODE]: (props) => (
<PlateLeaf as="code" { ...props } />
),
[MARK_ITALIC]: (props) => (
<PlateLeaf as="em" { ...props } />
),
[MARK_SUPERSCRIPT]: (props) => (
<PlateLeaf as="sup" { ...props } />
),
[MARK_UNDERLINE]: (props) => (
<PlateLeaf as="u" { ...props } />
),
};

if (overrideByKey) {
Expand Down
37 changes: 35 additions & 2 deletions uui-editor/src/migrations/normalizers.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Value, setNodes, PlateEditor, TNodeEntry } from '@udecode/plate-common';
import { TTodoListItemElement } from '@udecode/plate-list';
import { TLinkElement } from '@udecode/plate-link';
import { TTableCellElement, TTableElement } from '@udecode/plate-table';
import { TAttachmentElement } from '../plugins/attachmentPlugin/types';
import { TIframeElement } from '../plugins/iframePlugin/types';
import { TImageElement } from '../plugins/imagePlugin/types';
import { toNewAlign } from './legacy_migrations';
import { DepreactedTTableElement, DeprecatedImageElement, DeprecatedTAttachmentElement, DeprecatedTIframeElement, DeprecatedTLinkElement, DeprecatedTTableCellElement } from './types';
import { isLegacyTodoListItemElement } from './utils';

/**
* Migration property functions
Expand Down Expand Up @@ -116,14 +118,20 @@ export const normalizeIframeElement = (editor: PlateEditor<Value>, entry: TNodeE
const iframeNode = node as DeprecatedTIframeElement;

if (iframeNode.data) {
const { src, ...otherData } = iframeNode.data;
const { data: { src, ...otherData }, url } = iframeNode;

// removing props
if (!src) {
return;
}

const iframe: TIframeElement = { ...iframeNode, data: { ...otherData } };
const newUrl = url || src;

const iframe: TIframeElement = {
...iframeNode,
url: newUrl,
data: { ...otherData },
};

setNodes<TTableCellElement>(
editor,
Expand Down Expand Up @@ -175,3 +183,28 @@ export const normaizeColoredText = (editor: PlateEditor<Value>, entry: TNodeEntr
);
}
};

/** migrate checked property if needed */
export const migrateCheckedPropertyIfNeeded = <V extends Value>(
editor: PlateEditor<V>,
entry: TNodeEntry<TTodoListItemElement>,
) => {
const [node, path] = entry;

if (isLegacyTodoListItemElement(node)) {
const { data: { checked, ...otherData }, ...otherNodeData } = node;
const updatedNode: TTodoListItemElement = {
...otherNodeData,
data: {
...otherData,
},
checked: checked,
};

setNodes<TTodoListItemElement>(
editor,
updatedNode,
{ at: path },
);
}
};
7 changes: 7 additions & 0 deletions uui-editor/src/migrations/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { TLinkElement } from '@udecode/plate-link';
import { TTableCellElement, TTableElement } from '@udecode/plate-table';
import { TTodoListItemElement } from '@udecode/plate-list';
import { TAttachmentElement } from '../plugins/attachmentPlugin/types';
import { TIframeElement } from '../plugins/iframePlugin/types';
import { TImageElement } from '../plugins/imagePlugin/types';
Expand Down Expand Up @@ -51,6 +52,12 @@ export type DeprecatedTAttachmentElement = TAttachmentElement & {
}
};

export type DeprecatedTTodoListItemElement = TTodoListItemElement & {
data?: TTodoListItemElement['data'] & {
checked?: boolean; // removed
}
};

/**
* Legacy slate schema types
*/
Expand Down
8 changes: 6 additions & 2 deletions uui-editor/src/migrations/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Value } from '@udecode/plate-common';
import { TElement, Value } from '@udecode/plate-common';
import { EditorValue } from '../types';
import { migrateLegacySchema } from './legacy_migrations';
import { SlateSchema } from './types';
import { DeprecatedTTodoListItemElement, SlateSchema } from './types';

/** type guard to distinct slate format */
export const isSlateSchema = (value: EditorValue): value is SlateSchema => {
Expand All @@ -21,3 +21,7 @@ export const getMigratedPlateValue = (value: EditorValue): Value | undefined =>
export const isPlateValue = (value: EditorValue): value is Value => {
return Array.isArray(value);
};

export const isLegacyTodoListItemElement = (element: TElement): element is DeprecatedTTodoListItemElement =>
element.data !== undefined
&& 'checked' in (element.data as Record<string, unknown>);
17 changes: 6 additions & 11 deletions uui-editor/src/plugins/attachmentPlugin/AttachmentBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,20 @@ import { ReactComponent as TextIcon } from '../../icons/file-file_text-24.svg';
import { ReactComponent as MailIcon } from '../../icons/file-file_eml-24.svg';

import css from './AttachmentBlock.module.scss';
import { AnyObject, PlateEditor, PlatePluginComponent, setElements } from '@udecode/plate-common';
import { PlateElement, PlateElementProps, setElements, useElement } from '@udecode/plate-common';
import { useFocused, useReadOnly, useSelected } from 'slate-react';
import { TAttachmentElement } from './types';

export const AttachmentBlock: PlatePluginComponent<{
editor: PlateEditor,
attributes: AnyObject,
children: React.ReactNode,
element: TAttachmentElement
}> = function AttachmentComp(props) {
export const AttachmentBlock = function AttachmentComp({ children, ...props }: PlateElementProps) {
const element = useElement<TAttachmentElement>();
const isFocused = useFocused();
const isSelected = useSelected() && isFocused;
const isReadonly = useReadOnly();

const { element, editor, children } = props;
const [fileName, setFileName] = useState(element.data.fileName || '');

const changeName = (name: string) => {
setElements(editor, {
setElements(props.editor, {
...element,
data: {
...element.data,
Expand Down Expand Up @@ -100,7 +95,7 @@ export const AttachmentBlock: PlatePluginComponent<{
};

return (
<div { ...props.attributes }>
<PlateElement as="div" { ...props }>
<FlexRow
rawProps={ {
contentEditable: false,
Expand Down Expand Up @@ -146,6 +141,6 @@ export const AttachmentBlock: PlatePluginComponent<{
</FlexCell>
</FlexRow>
{ children }
</div>
</PlateElement>
);
};
31 changes: 4 additions & 27 deletions uui-editor/src/plugins/baseMarksPlugin/baseMarksPlugin.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
PlateEditor, PlatePluginComponent, isMarkActive, PlatePlugin,
PlateEditor, PlateLeaf, PlateLeafProps, isMarkActive, PlatePlugin,
} from '@udecode/plate-common';
import React from 'react';

Expand All @@ -17,32 +17,9 @@ import { ReactComponent as UnderlineIcon } from '../../icons/underline.svg';
import { handleMarkButtonClick } from '../../utils/handleMarkButtonClick';
import { BOLD_KEY, ITALIC_KEY, UNDERLINE_KEY } from './constants';

// eslint-disable-next-line react/function-component-definition
const Bold: PlatePluginComponent = (props) => {
const { attributes, children } = props;

return (
<span { ...attributes }><strong>{ children }</strong></span>
);
};

// eslint-disable-next-line react/function-component-definition
const Italic: PlatePluginComponent = (props) => {
const { attributes, children } = props;

return (
<span { ...attributes }><i>{ children }</i></span>
);
};

// eslint-disable-next-line react/function-component-definition
const Underline: PlatePluginComponent = (props) => {
const { attributes, children } = props;

return (
<span { ...attributes }><u>{ children }</u></span>
);
};
const Bold = (props: PlateLeafProps) => <PlateLeaf as="strong" { ...props } />;
const Italic = (props: PlateLeafProps) => <PlateLeaf as="i" { ...props } />;
const Underline = (props: PlateLeafProps) => <PlateLeaf as="u" { ...props } />;

export const boldPlugin = (): PlatePlugin => createBoldPlugin<WithToolbarButton>({
type: BOLD_KEY,
Expand Down
17 changes: 6 additions & 11 deletions uui-editor/src/plugins/iframePlugin/IframeBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,23 @@ import { uuiMod } from '@epam/uui-core';
import cx from 'classnames';
import { sanitizeUrl } from '@braintree/sanitize-url';
import { useSelected } from 'slate-react';
import { AnyObject, PlatePluginComponent } from '@udecode/plate-common';
import { PlateElement, PlateElementProps, useElement } from '@udecode/plate-common';
import { TIframeElement } from './types';

const IFRAME_GLOBAL_CLASS = 'uui-rte-iframe';
const PDF_GLOBAL_CLASS = 'uui-rte-iframe-pdf';

export const IframeBlock: PlatePluginComponent<{
attributes: AnyObject,
children: React.ReactNode,
element: TIframeElement
}> = function IframeComp(props) {
const { attributes, children, element } = props;
export const IframeBlock = function IframeComp({ children, ...props }: PlateElementProps) {
const element = useElement<TIframeElement>();
const isSelected = useSelected();

const isPdf = element.data?.extension === 'pdf';
const style = element.data?.style;

const url: string = element.url || element.src as string; // element.src it's previous editor format structure
const url: string = element.url;

return (
// style attr needed for serialization
<div { ...attributes }>
<PlateElement as="div" { ...props }>
<iframe
title={ url }
allowFullScreen={ true }
Expand All @@ -34,6 +29,6 @@ export const IframeBlock: PlatePluginComponent<{
className={ cx(css.content, isSelected && uuiMod.focus, IFRAME_GLOBAL_CLASS, isPdf && PDF_GLOBAL_CLASS) }
/>
{ children }
</div>
</PlateElement>
);
};
Loading
Loading