diff --git a/changelogs/upcoming/7676.md b/changelogs/upcoming/7676.md new file mode 100644 index 000000000000..74353df485e9 --- /dev/null +++ b/changelogs/upcoming/7676.md @@ -0,0 +1,5 @@ +- Updated `getDefaultEuiMarkdownPlugins()` to allow excluding the following plugins in addition to `tooltip`: + - `checkbox` + - `linkValidator` + - `lineBreaks` + - `emoji` diff --git a/src-docs/src/views/markdown_editor/markdown_editor.js b/src-docs/src/views/markdown_editor/markdown_editor.js index 3e6b98a12c0e..aa88476100c4 100644 --- a/src-docs/src/views/markdown_editor/markdown_editor.js +++ b/src-docs/src/views/markdown_editor/markdown_editor.js @@ -21,7 +21,6 @@ The editor also ships with some built in plugins. For example it can handle chec - [ ] Or empty It can also handle emojis! :smile: - And it can render !{tooltip[tooltips like this](Look! I'm a very helpful tooltip content!)} `; diff --git a/src-docs/src/views/markdown_editor/markdown_editor_example.js b/src-docs/src/views/markdown_editor/markdown_editor_example.js index baa91ddf5116..81f795fb07d9 100644 --- a/src-docs/src/views/markdown_editor/markdown_editor_example.js +++ b/src-docs/src/views/markdown_editor/markdown_editor_example.js @@ -60,7 +60,15 @@ const markdownEditorNoPluginsSnippet = `const { parsingPlugins, processingPlugins, uiPlugins, -} = getDefaultEuiMarkdownPlugins({ exclude: ['tooltip'] }); +} = getDefaultEuiMarkdownPlugins({ + exclude: [ + 'tooltip', + 'checkbox', + 'linkValidator', + 'lineBreaks', + 'emoji', + ], +}); - The EuiMarkdownEditor comes with a default plugin for{' '} - tooltip support. However, this may be unfamiliar or - unnecessary in some contexts, and you can unregister this plugin by - excluding it from the - parsingPlugins,{' '} + The EuiMarkdownEditor comes with several default + plugins, demo'd below. If these defaults are unnecessary for your + use-case or context, you can unregister these plugins by excluding + them from the parsingPlugins,{' '} processingPlugins and uiPlugins{' '} options with a single exclude parameter passed to{' '} getDefaultEuiMarkdownPlugins(). This will ensure - the syntax won't be identified or rendered and no additional UI, - like the button and help syntax, will be displayed. + the syntax won't be identified or rendered, and no additional UI (like + toolbar buttons or help syntax) will be displayed by the unregistered + plugins.

), props: { diff --git a/src-docs/src/views/markdown_editor/markdown_editor_no_plugins.js b/src-docs/src/views/markdown_editor/markdown_editor_no_plugins.js index 5b4dc9b2e644..f77e9207cc04 100644 --- a/src-docs/src/views/markdown_editor/markdown_editor_no_plugins.js +++ b/src-docs/src/views/markdown_editor/markdown_editor_no_plugins.js @@ -1,35 +1,112 @@ -import React, { useState } from 'react'; +import React, { useState, useMemo } from 'react'; import { EuiMarkdownEditor, getDefaultEuiMarkdownPlugins, + EuiFlexGroup, + EuiFlexItem, + EuiSwitch, } from '../../../../src/components'; -const initialContent = `## This is how we do it :smile: +const initialContent = ` +### tooltip -In this example, we unregistered the built in **tooltip** plugin. So the button in the toolbar and the help syntax won't be displayed. - -And the following syntax no longer works. +When disabled, the button in the toolbar and the help syntax won't be displayed, and the following syntax will no longer works. !{tooltip[anchor text](Tooltip content)} -`; -const { parsingPlugins, processingPlugins, uiPlugins } = - getDefaultEuiMarkdownPlugins({ exclude: ['tooltip'] }); +### checkbox + +When disabled, a EuiCheckbox will no longer render. + +- [ ] TODO: Disable some default plugins + +### emoji + +When disabled, text will render instead of an emoji. + +:smile: + +### linkValidator + +When disabled, all links will render as links instead of as text. + +[This is a sus link](file://) + +### lineBreaks + +When disabled, these sentence will be in the same line. +When enabled, these sentences will be separated by a line break. + +Two blank lines in a row will create a new paragraph as usual. +`; export default () => { const [value, setValue] = useState(initialContent); + const [tooltip, excludeTooltips] = useState(false); + const [checkbox, excludeCheckboxes] = useState(true); + const [emoji, excludeEmojis] = useState(true); + const [linkValidator, excludeLinkValidator] = useState(true); + const [lineBreaks, excludeLineBreaks] = useState(false); + + const { parsingPlugins, processingPlugins, uiPlugins } = useMemo(() => { + const excludedPlugins = []; + if (!tooltip) excludedPlugins.push('tooltip'); + if (!checkbox) excludedPlugins.push('checkbox'); + if (!emoji) excludedPlugins.push('emoji'); + if (!linkValidator) excludedPlugins.push('linkValidator'); + if (!lineBreaks) excludedPlugins.push('lineBreaks'); + + return getDefaultEuiMarkdownPlugins({ + exclude: excludedPlugins, + }); + }, [tooltip, checkbox, emoji, linkValidator, lineBreaks]); + return ( <> - + + + excludeTooltips(!tooltip)} + /> + excludeCheckboxes(!checkbox)} + /> + excludeEmojis(!emoji)} + /> + excludeLinkValidator(!linkValidator)} + /> + excludeLineBreaks(!lineBreaks)} + /> + + + + + ); }; diff --git a/src/components/markdown_editor/plugins/markdown_default_plugins/parsing_plugins.ts b/src/components/markdown_editor/plugins/markdown_default_plugins/parsing_plugins.ts index b2a9e708b79f..d0ebcb12d829 100644 --- a/src/components/markdown_editor/plugins/markdown_default_plugins/parsing_plugins.ts +++ b/src/components/markdown_editor/plugins/markdown_default_plugins/parsing_plugins.ts @@ -32,30 +32,40 @@ import { euiMarkdownLinkValidator, EuiMarkdownLinkValidatorOptions, } from '../markdown_link_validator'; +import type { ExcludableDefaultPlugins, DefaultPluginsConfig } from './plugins'; export type DefaultEuiMarkdownParsingPlugins = PluggableList; +const DEFAULT_PARSING_PLUGINS: Record< + ExcludableDefaultPlugins, + DefaultEuiMarkdownParsingPlugins[0] +> = { + emoji: [emoji, { emoticon: false }], + lineBreaks: [breaks, {}], + linkValidator: [ + euiMarkdownLinkValidator, + { + allowRelative: true, + allowProtocols: ['https:', 'http:', 'mailto:'], + } as EuiMarkdownLinkValidatorOptions, + ], + checkbox: [MarkdownCheckbox.parser, {}], + tooltip: [MarkdownTooltip.parser, {}], +}; + export const getDefaultEuiMarkdownParsingPlugins = ({ exclude, -}: { exclude?: Array<'tooltip'> } = {}): DefaultEuiMarkdownParsingPlugins => { - const excludeSet = new Set(exclude); +}: DefaultPluginsConfig = {}): DefaultEuiMarkdownParsingPlugins => { const parsingPlugins: PluggableList = [ [markdown, {}], [highlight, {}], - [emoji, { emoticon: false }], - [breaks, {}], - [ - euiMarkdownLinkValidator, - { - allowRelative: true, - allowProtocols: ['https:', 'http:', 'mailto:'], - } as EuiMarkdownLinkValidatorOptions, - ], - [MarkdownCheckbox.parser, {}], ]; - if (!excludeSet.has('tooltip')) - parsingPlugins.push([MarkdownTooltip.parser, {}]); + Object.entries(DEFAULT_PARSING_PLUGINS).forEach(([pluginName, plugin]) => { + if (!exclude?.includes(pluginName as ExcludableDefaultPlugins)) { + parsingPlugins.push(plugin); + } + }); return parsingPlugins; }; diff --git a/src/components/markdown_editor/plugins/markdown_default_plugins/plugins.test.ts b/src/components/markdown_editor/plugins/markdown_default_plugins/plugins.test.ts new file mode 100644 index 000000000000..211b95a6f769 --- /dev/null +++ b/src/components/markdown_editor/plugins/markdown_default_plugins/plugins.test.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getDefaultEuiMarkdownPlugins } from './plugins'; + +describe('default plugins', () => { + test('no exclusions', () => { + const { parsingPlugins, processingPlugins, uiPlugins } = + getDefaultEuiMarkdownPlugins(); + + expect(parsingPlugins).toHaveLength(7); + expect(Object.keys(processingPlugins[1][1].components)).toHaveLength(8); + expect(uiPlugins).toHaveLength(1); + + expect(processingPlugins[1][1].components.tooltipPlugin).toBeDefined(); + expect(processingPlugins[1][1].components.checkboxPlugin).toBeDefined(); + }); + + test('exclude tooltips', () => { + const { parsingPlugins, processingPlugins, uiPlugins } = + getDefaultEuiMarkdownPlugins({ + exclude: ['tooltip'], + }); + + expect(parsingPlugins).toHaveLength(6); + expect(processingPlugins[1][1].components.tooltipPlugin).toBeUndefined(); + expect(uiPlugins).toHaveLength(0); + }); + + test('exclude checkboxes', () => { + const { parsingPlugins, processingPlugins, uiPlugins } = + getDefaultEuiMarkdownPlugins({ + exclude: ['checkbox'], + }); + + expect(parsingPlugins).toHaveLength(6); + expect(processingPlugins[1][1].components.checkboxPlugin).toBeUndefined(); + expect(uiPlugins).toHaveLength(1); + }); + + test('all exclusions', () => { + const { parsingPlugins, processingPlugins, uiPlugins } = + getDefaultEuiMarkdownPlugins({ + exclude: [ + 'tooltip', + 'checkbox', + 'lineBreaks', + 'linkValidator', + 'emoji', + ], + }); + + expect(parsingPlugins).toHaveLength(2); + expect(Object.keys(processingPlugins[1][1].components)).toHaveLength(6); + expect(uiPlugins).toHaveLength(0); + }); +}); diff --git a/src/components/markdown_editor/plugins/markdown_default_plugins/plugins.ts b/src/components/markdown_editor/plugins/markdown_default_plugins/plugins.ts index 77c658f14c21..9a8c3001d616 100644 --- a/src/components/markdown_editor/plugins/markdown_default_plugins/plugins.ts +++ b/src/components/markdown_editor/plugins/markdown_default_plugins/plugins.ts @@ -19,8 +19,19 @@ import { DefaultEuiMarkdownProcessingPlugins, } from './processing_plugins'; +export type ExcludableDefaultPlugins = + | 'emoji' + | 'lineBreaks' + | 'linkValidator' + | 'checkbox' + | 'tooltip'; + +export type DefaultPluginsConfig = + | undefined + | { exclude?: ExcludableDefaultPlugins[] }; + export const getDefaultEuiMarkdownPlugins = ( - config: undefined | { exclude?: Array<'tooltip'> } + config?: DefaultPluginsConfig ): { parsingPlugins: DefaultEuiMarkdownParsingPlugins; processingPlugins: DefaultEuiMarkdownProcessingPlugins; diff --git a/src/components/markdown_editor/plugins/markdown_default_plugins/processing_plugins.tsx b/src/components/markdown_editor/plugins/markdown_default_plugins/processing_plugins.tsx index af79901d2686..04c575d826ae 100644 --- a/src/components/markdown_editor/plugins/markdown_default_plugins/processing_plugins.tsx +++ b/src/components/markdown_editor/plugins/markdown_default_plugins/processing_plugins.tsx @@ -28,13 +28,16 @@ import { Options as Remark2RehypeOptions, Handler } from 'mdast-util-to-hast'; import all from 'mdast-util-to-hast/lib/all'; import rehype2react from 'rehype-react'; import remark2rehype from 'remark-rehype'; -import * as MarkdownTooltip from '../markdown_tooltip'; -import * as MarkdownCheckbox from '../markdown_checkbox'; -import { FENCED_CLASS } from '../remark/remark_prismjs'; + import { EuiLink } from '../../../link'; import { EuiCodeBlock, EuiCode } from '../../../code'; import { EuiHorizontalRule } from '../../../horizontal_rule'; +import { FENCED_CLASS } from '../remark/remark_prismjs'; +import * as MarkdownTooltip from '../markdown_tooltip'; +import * as MarkdownCheckbox from '../markdown_checkbox'; +import type { ExcludableDefaultPlugins, DefaultPluginsConfig } from './plugins'; + const unknownHandler: Handler = (h, node) => { return h(node, node.type, node, all(h, node)); }; @@ -50,12 +53,26 @@ export type DefaultEuiMarkdownProcessingPlugins = [ ...PluggableList // any additional are generic ]; +const DEFAULT_COMPONENT_RENDERERS: Partial< + Record> +> = { + checkbox: MarkdownCheckbox.renderer, + tooltip: MarkdownTooltip.renderer, +}; + export const getDefaultEuiMarkdownProcessingPlugins = ({ exclude, -}: { - exclude?: Array<'tooltip'>; -} = {}): DefaultEuiMarkdownProcessingPlugins => { - const excludeSet = new Set(exclude); +}: DefaultPluginsConfig = {}): DefaultEuiMarkdownProcessingPlugins => { + const componentPluginsWithExclusions: Rehype2ReactOptions['components'] = {}; + + Object.entries(DEFAULT_COMPONENT_RENDERERS).forEach( + ([excludeName, renderer]) => { + if (!exclude?.includes(excludeName as ExcludableDefaultPlugins)) { + const pluginName = `${excludeName}Plugin`; + componentPluginsWithExclusions[pluginName] = renderer; + } + } + ); const plugins: DefaultEuiMarkdownProcessingPlugins = [ [ @@ -99,15 +116,12 @@ export const getDefaultEuiMarkdownProcessingPlugins = ({ ), hr: (props) => , - checkboxPlugin: MarkdownCheckbox.renderer, + ...componentPluginsWithExclusions, }, }, ], ]; - if (!excludeSet.has('tooltip')) - plugins[1][1].components.tooltipPlugin = MarkdownTooltip.renderer; - return plugins; }; diff --git a/src/components/markdown_editor/plugins/markdown_default_plugins/ui_plugins.ts b/src/components/markdown_editor/plugins/markdown_default_plugins/ui_plugins.ts index f5dc92cb9c8a..131f3daed9aa 100644 --- a/src/components/markdown_editor/plugins/markdown_default_plugins/ui_plugins.ts +++ b/src/components/markdown_editor/plugins/markdown_default_plugins/ui_plugins.ts @@ -6,18 +6,28 @@ * Side Public License, v 1. */ -import * as MarkdownTooltip from '../markdown_tooltip'; import { EuiMarkdownEditorUiPlugin } from './../../markdown_types'; +import * as MarkdownTooltip from '../markdown_tooltip'; +import type { ExcludableDefaultPlugins, DefaultPluginsConfig } from './plugins'; export type DefaultEuiMarkdownUiPlugins = EuiMarkdownEditorUiPlugin[]; +const DEFAULT_UI_PLUGINS: Partial< + Record +> = { + tooltip: MarkdownTooltip.plugin, +}; + export const getDefaultEuiMarkdownUiPlugins = ({ exclude, -}: { exclude?: Array<'tooltip'> } = {}): DefaultEuiMarkdownUiPlugins => { - const excludeSet = new Set(exclude); - const uiPlugins = []; +}: DefaultPluginsConfig = {}): DefaultEuiMarkdownUiPlugins => { + const uiPlugins: EuiMarkdownEditorUiPlugin[] = []; - if (!excludeSet.has('tooltip')) uiPlugins.push(MarkdownTooltip.plugin); + Object.entries(DEFAULT_UI_PLUGINS).forEach(([pluginName, plugin]) => { + if (!exclude?.includes(pluginName as ExcludableDefaultPlugins)) { + uiPlugins.push(plugin); + } + }); return uiPlugins; };