From deafe20cc364474580d993801e32fee227cc8832 Mon Sep 17 00:00:00 2001 From: Sergey Makhnatkin Date: Mon, 23 Dec 2024 16:14:52 +0300 Subject: [PATCH] feat(toolbars): updated flattenPreset (#531) --- src/bundle/MarkdownEditorView.tsx | 2 +- .../toolbar/utils/flattenPreset.test.tsx | 311 ++++++++++++++++++ src/bundle/toolbar/utils/flattenPreset.ts | 15 + .../{utils.ts => utils/toolbarsConfigs.ts} | 49 +-- 4 files changed, 353 insertions(+), 24 deletions(-) create mode 100644 src/bundle/toolbar/utils/flattenPreset.test.tsx create mode 100644 src/bundle/toolbar/utils/flattenPreset.ts rename src/bundle/toolbar/{utils.ts => utils/toolbarsConfigs.ts} (69%) diff --git a/src/bundle/MarkdownEditorView.tsx b/src/bundle/MarkdownEditorView.tsx index d0fe9d6f..98cbb49f 100644 --- a/src/bundle/MarkdownEditorView.tsx +++ b/src/bundle/MarkdownEditorView.tsx @@ -20,7 +20,7 @@ import {MToolbarData, MToolbarItemData, WToolbarData, WToolbarItemData} from './ import {useMarkdownEditorContext} from './context'; import {EditorSettings, EditorSettingsProps} from './settings'; import {stickyCn} from './sticky'; -import {getToolbarsConfigs} from './toolbar/utils'; +import {getToolbarsConfigs} from './toolbar/utils/toolbarsConfigs'; import type {MarkdownEditorMode} from './types'; import '../styles/styles.scss'; diff --git a/src/bundle/toolbar/utils/flattenPreset.test.tsx b/src/bundle/toolbar/utils/flattenPreset.test.tsx new file mode 100644 index 00000000..341160ac --- /dev/null +++ b/src/bundle/toolbar/utils/flattenPreset.test.tsx @@ -0,0 +1,311 @@ +import React from 'react'; + +import {ToolbarDataType} from '../../../toolbar/types'; +import type {WToolbarData} from '../../config/wysiwyg'; + +import {flattenPreset} from './flattenPreset'; + +interface IconProps {} +const Icon: React.FC = () => { + return
; +}; + +interface WToolbarColorsProps {} +const WToolbarColors: React.FC = () => { + return
; +}; + +const input: WToolbarData = [ + [ + { + type: ToolbarDataType.SingleButton, + id: 'undo', + icon: {data: Icon}, + hotkey: 'mod+z', + hintWhenDisabled: false, + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + { + type: ToolbarDataType.SingleButton, + id: 'redo', + icon: {data: Icon}, + hotkey: 'mod+shift+z', + hintWhenDisabled: false, + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + ], + [ + { + type: ToolbarDataType.SingleButton, + id: 'bold', + icon: {data: Icon}, + hotkey: 'mod+b', + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + { + type: ToolbarDataType.SingleButton, + id: 'italic', + icon: {data: Icon}, + hotkey: 'mod+i', + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + { + type: ToolbarDataType.SingleButton, + id: 'underline', + icon: {data: Icon}, + hotkey: 'mod+u', + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + { + type: ToolbarDataType.SingleButton, + id: 'strike', + icon: {data: Icon}, + hotkey: 'mod+shift+s', + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + { + type: ToolbarDataType.SingleButton, + id: 'mono', + icon: {data: Icon}, + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + { + type: ToolbarDataType.SingleButton, + id: 'mark', + icon: {data: Icon}, + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + ], + [ + { + type: ToolbarDataType.ListButton, + id: 'heading', + icon: {data: Icon}, + withArrow: true, + title: '', + data: [ + { + id: 'paragraph', + icon: {data: Icon}, + hotkey: 'cmd+alt+0', + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + { + id: 'heading1', + icon: {data: Icon}, + hotkey: 'cmd+alt+1', + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + { + id: 'heading2', + icon: {data: Icon}, + hotkey: 'cmd+alt+2', + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + { + id: 'heading3', + icon: {data: Icon}, + hotkey: 'cmd+alt+3', + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + { + id: 'heading4', + icon: {data: Icon}, + hotkey: 'cmd+alt+4', + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + { + id: 'heading5', + icon: {data: Icon}, + hotkey: 'cmd+alt+5', + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + { + id: 'heading6', + icon: {data: Icon}, + hotkey: 'cmd+alt+6', + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + ], + }, + { + type: ToolbarDataType.ListButton, + id: 'lists', + icon: {data: Icon}, + withArrow: true, + title: '', + data: [ + { + id: 'bulletList', + icon: {data: Icon}, + hotkey: 'mod+shift+l', + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + { + id: 'orderedList', + icon: {data: Icon}, + hotkey: 'mod+shift+m', + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + { + id: 'sinkListItem', + icon: {data: Icon}, + hotkey: 'tab', + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + { + id: 'liftListItem', + icon: {data: Icon}, + hotkey: 'shift+tab', + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + ], + }, + { + type: ToolbarDataType.ReactComponent, + id: 'colorify', + width: 42, + component: WToolbarColors, + }, + { + type: ToolbarDataType.SingleButton, + id: 'link', + icon: {data: Icon}, + hotkey: 'mod+k', + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + { + type: ToolbarDataType.SingleButton, + id: 'note', + icon: {data: Icon}, + hotkey: 'cmd+alt+8', + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + { + type: ToolbarDataType.SingleButton, + id: 'cut', + icon: {data: Icon}, + hotkey: 'cmd+alt+7', + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + { + type: ToolbarDataType.SingleButton, + id: 'quote', + icon: {data: Icon}, + hotkey: 'mod+shift+.', + exec: () => {}, + isActive: () => false, + isEnable: () => false, + title: '', + }, + ], +]; + +const expectedIds = [ + 'undo', + 'redo', + 'bold', + 'italic', + 'underline', + 'strike', + 'mono', + 'mark', + 'paragraph', + 'heading1', + 'heading2', + 'heading3', + 'heading4', + 'heading5', + 'heading6', + 'bulletList', + 'orderedList', + 'sinkListItem', + 'liftListItem', + 'colorify', + 'link', + 'note', + 'cut', + 'quote', +]; + +describe('flattenPreset', () => { + it('should flatten nested toolbar data and return a correctly flattened structure', () => { + const result = flattenPreset(input); + const expectedOutput = expectedIds.map((id) => expect.objectContaining({id})); + + expect(result).toEqual(expect.arrayContaining(expectedOutput)); + }); + + it('should return a list of flattened IDs', () => { + const result = flattenPreset(input); + const resultIds = result.map((item) => item.id); + + expect(resultIds).toEqual(expectedIds); + }); +}); diff --git a/src/bundle/toolbar/utils/flattenPreset.ts b/src/bundle/toolbar/utils/flattenPreset.ts new file mode 100644 index 00000000..40412fa2 --- /dev/null +++ b/src/bundle/toolbar/utils/flattenPreset.ts @@ -0,0 +1,15 @@ +import {ToolbarDataType} from '../../../toolbar/types'; +import type {MToolbarData, MToolbarItemData, WToolbarData, WToolbarItemData} from '../../config'; + +export const flattenPreset = ( + config: T, +): T extends WToolbarData ? WToolbarItemData[] : MToolbarItemData[] => { + return config.flat().reduce<(WToolbarItemData | MToolbarItemData)[]>((acc, item) => { + if (item.type === ToolbarDataType.ListButton && Array.isArray(item.data)) { + return acc.concat(item.data); + } + + acc.push(item as WToolbarItemData | MToolbarItemData); + return acc; + }, []) as unknown as T extends WToolbarData ? WToolbarItemData[] : MToolbarItemData[]; +}; diff --git a/src/bundle/toolbar/utils.ts b/src/bundle/toolbar/utils/toolbarsConfigs.ts similarity index 69% rename from src/bundle/toolbar/utils.ts rename to src/bundle/toolbar/utils/toolbarsConfigs.ts index 94447bc1..a3dfcbf5 100644 --- a/src/bundle/toolbar/utils.ts +++ b/src/bundle/toolbar/utils/toolbarsConfigs.ts @@ -1,15 +1,17 @@ -import {ToolbarName} from '../../modules/toolbars/constants'; -import {commonmark, defaultPreset, full, yfm, zero} from '../../modules/toolbars/presets'; +import {ToolbarName} from '../../../modules/toolbars/constants'; +import {commonmark, defaultPreset, full, yfm, zero} from '../../../modules/toolbars/presets'; import type { ToolbarItem, ToolbarItemMarkup, ToolbarItemWysiwyg, ToolbarsPreset, -} from '../../modules/toolbars/types'; -import type {MToolbarData, MToolbarItemData, WToolbarData, WToolbarItemData} from '../../toolbar'; -import {ToolbarDataType, ToolbarIconData} from '../../toolbar'; -import type {MarkdownEditorViewProps} from '../MarkdownEditorView'; -import {MarkdownEditorPreset} from '../types'; +} from '../../../modules/toolbars/types'; +import type {MToolbarData, WToolbarData} from '../../../toolbar'; +import {ToolbarDataType, ToolbarIconData} from '../../../toolbar'; +import type {MarkdownEditorViewProps} from '../../MarkdownEditorView'; +import {MarkdownEditorPreset} from '../../types'; + +import {flattenPreset} from './flattenPreset'; const defaultPresets: Record = { zero, @@ -58,7 +60,7 @@ const transformItem = ( }; }; -export const createConfig = ( +export const createToolbarConfig = ( editorType: 'wysiwyg' | 'markup', toolbarPreset: ToolbarsPreset | MarkdownEditorPreset, toolbarName: string, @@ -84,13 +86,6 @@ export const createConfig = ( return toolbarData as T; }; -const flattenPreset = (config: T) => { - // TODO: @makhnatkin add logic for flatten - return (config[0] ?? []) as unknown as T extends WToolbarData - ? WToolbarItemData[] - : MToolbarItemData[]; -}; - interface GetToolbarsConfigsArgs { toolbarsPreset?: ToolbarsPreset; props: Pick< @@ -104,28 +99,36 @@ interface GetToolbarsConfigsArgs { } export const getToolbarsConfigs = ({toolbarsPreset, props, preset}: GetToolbarsConfigsArgs) => { const wysiwygToolbarConfig = toolbarsPreset - ? createConfig('wysiwyg', toolbarsPreset, ToolbarName.wysiwygMain) + ? createToolbarConfig('wysiwyg', toolbarsPreset, ToolbarName.wysiwygMain) : props.wysiwygToolbarConfig ?? - createConfig('wysiwyg', preset, ToolbarName.wysiwygMain); + createToolbarConfig('wysiwyg', preset, ToolbarName.wysiwygMain); const markupToolbarConfig = toolbarsPreset - ? createConfig('markup', toolbarsPreset, ToolbarName.markupMain) + ? createToolbarConfig('markup', toolbarsPreset, ToolbarName.markupMain) : props.markupToolbarConfig ?? - createConfig('markup', preset, ToolbarName.markupMain); + createToolbarConfig('markup', preset, ToolbarName.markupMain); const wysiwygHiddenActionsConfig = toolbarsPreset ? flattenPreset( - createConfig('wysiwyg', toolbarsPreset, ToolbarName.wysiwygHidden), + createToolbarConfig( + 'wysiwyg', + toolbarsPreset, + ToolbarName.wysiwygHidden, + ), ) : props.wysiwygHiddenActionsConfig ?? - flattenPreset(createConfig('wysiwyg', preset, ToolbarName.wysiwygHidden)); + flattenPreset( + createToolbarConfig('wysiwyg', preset, ToolbarName.wysiwygHidden), + ); const markupHiddenActionsConfig = toolbarsPreset ? flattenPreset( - createConfig('markup', toolbarsPreset, ToolbarName.markupHidden), + createToolbarConfig('markup', toolbarsPreset, ToolbarName.markupHidden), ) : props.markupHiddenActionsConfig ?? - flattenPreset(createConfig('markup', preset, ToolbarName.markupHidden)); + flattenPreset( + createToolbarConfig('markup', preset, ToolbarName.markupHidden), + ); return { wysiwygToolbarConfig,