From e5cccd2bee3b224bf188a144c4262e81ce9f0f6c Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Thu, 11 Mar 2021 11:37:32 -0600 Subject: [PATCH 01/18] replace highlight.js with prism.js via refractor --- package.json | 5 +- .../components/guide_section/guide_section.js | 39 +++-- src/components/code/_code_block.scss | 128 ++++++---------- src/components/code/_code_block.tsx | 143 ++++++++---------- .../plugins/markdown_default_plugins.tsx | 16 +- .../plugins/remark/remark_prismjs.ts | 48 ++++++ .../markdown_editor/unified-plugins.d.ts | 6 - yarn.lock | 125 +++++++++------ 8 files changed, 264 insertions(+), 246 deletions(-) create mode 100644 src/components/markdown_editor/plugins/remark/remark_prismjs.ts diff --git a/package.json b/package.json index 5e30ebf5cda..ad2c7c468d2 100644 --- a/package.json +++ b/package.json @@ -53,10 +53,10 @@ "@types/react-input-autosize": "^2.2.0", "@types/react-virtualized-auto-sizer": "^1.0.0", "@types/react-window": "^1.8.2", + "@types/refractor": "^3.0.0", "@types/vfile-message": "^2.0.0", "chroma-js": "^2.1.0", "classnames": "^2.2.6", - "highlight.js": "^9.18.5", "lodash": "^4.17.20", "numeral": "^2.0.6", "prop-types": "^15.6.0", @@ -68,11 +68,11 @@ "react-is": "~16.3.0", "react-virtualized-auto-sizer": "^1.0.2", "react-window": "^1.8.5", + "refractor": "^3.3.1", "rehype-raw": "^5.0.0", "rehype-react": "^6.0.0", "rehype-stringify": "^8.0.0", "remark-emoji": "^2.1.0", - "remark-highlight.js": "^6.0.0", "remark-parse": "^8.0.3", "remark-rehype": "^8.0.0", "tabbable": "^3.0.0", @@ -101,7 +101,6 @@ "@svgr/plugin-svgo": "^4.0.3", "@types/classnames": "^2.2.10", "@types/enzyme": "^3.10.5", - "@types/highlight.js": "^9.12.4", "@types/jest": "^24.0.6", "@types/node": "^10.17.5", "@types/react": "^16.9.34", diff --git a/src-docs/src/components/guide_section/guide_section.js b/src-docs/src/components/guide_section/guide_section.js index 373e1351a67..95c44d7d938 100644 --- a/src-docs/src/components/guide_section/guide_section.js +++ b/src-docs/src/components/guide_section/guide_section.js @@ -1,5 +1,6 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; +import classNames from 'classnames'; import { EuiCode, @@ -40,7 +41,7 @@ const slugify = (str) => { }; export const markup = (text) => { - const regex = /(#[a-zA-Z]+)|(`[^`]+`)/g; + const regex = /(^#[a-zA-Z]+)|(?<=\s)(#[a-zA-Z]+)|(`[^`]+`)/g; return text.split('\n').map((token) => { const values = token.split(regex).map((token, index) => { if (!token) { @@ -387,9 +388,6 @@ export class GuideSection extends Component { ]; const types = humanizedType.split(/\([^=]*\) =>\s\w*\)*/); - const typeMarkup = ( - {markup(humanizedType)} - ); const descriptionMarkup = markup(propDescription); let defaultValueMarkup = ''; if (defaultValue) { @@ -405,31 +403,30 @@ export class GuideSection extends Component { let defaultTypeCell = ( - {typeMarkup} + + {humanizedType} + ); if (functionMatches.length > 0) { - const elements = []; + let elements = ''; let j = 0; for (let i = 0; i < types.length; i++) { if (functionMatches[j]) { - elements.push( - - {types[i]}
-
- ); - elements.push( - - {functionMatches[j][0]}
-
- ); + elements = + `${elements}` + + `${types[i]}` + + '\n' + + `${functionMatches[j][0]}` + + '\n'; j++; } else { - elements.push( - - {types[i]}
-
- ); + elements = `${elements}` + `${types[i]}` + '\n'; } } defaultTypeCell = ( diff --git a/src/components/code/_code_block.scss b/src/components/code/_code_block.scss index 10022f9dd20..19e9edf7f74 100644 --- a/src/components/code/_code_block.scss +++ b/src/components/code/_code_block.scss @@ -1,3 +1,5 @@ +$tokenRegex: #E90; + .euiCodeBlock { max-width: 100%; display: block; @@ -164,7 +166,7 @@ background: transparent; } - .hljs > *::selection { + .prismjs > *::selection { // Only change the color if the variable IS a color // or else no highlight color shows up at all @if type-of($euiCodeBlockSelectedBackgroundColor) == color { @@ -172,112 +174,74 @@ } } - .hljs-comment, - .hljs-quote { + .token.comment, + .token.prolog, + .token.doctype, + .token.cdata { color: $euiCodeBlockCommentColor; font-style: italic; } - .hljs-selector-tag { - color: $euiCodeBlockSelectorTagColor; - font-weight: $euiCodeFontWeightBold; - } - - .hljs-string, - .hljs-subst, - .hljs-doctag { - color: $euiCodeBlockStringColor; - } - - .hljs-number, - .hljs-literal, - .hljs-regexp, - .hljs-variable, - .hljs-template-variable, - .hljs-tag .hljs-attr { - color: $euiCodeBlockNumberColor; - } - - .hljs-keyword { - color: $euiCodeBlockKeywordColor; + .token.punctuation { + opacity: .7; } - .hljs-function > .hljs-title { - color: $euiCodeBlockFunctionTitleColor; + .token.namespace { + opacity: .7; } - .hljs-tag { + .token.property, + .token.tag, + .token.boolean, + .token.number, + .token.constant, + .token.symbol { color: $euiCodeBlockTagColor; } - .hljs-name { - color: $euiCodeBlockNameColor; - } - - .hljs-type, - .hljs-class .hljs-title { - color: $euiCodeBlockTypeColor; - } - - .hljs-attribute { - color: $euiCodeBlockAttributeColor; - } - - .hljs-symbol, - .hljs-bullet, - .hljs-built_in, - .hljs-builtin-name, - .hljs-link { - color: $euiCodeBlockSymbolColor; - } - - .hljs-params { - color: $euiCodeBlockParamsColor; - } - - .hljs-meta { - color: $euiCodeBlockMetaColor; - } - - .hljs-title { - color: $euiCodeBlockTitleColor; - } - - .hljs-section { - color: $euiCodeBlockSectionColor; - } - - .hljs-addition, - .hljs-deletion { - padding-left: $euiSizeXS; - margin-left: -$euiSizeXS; + .token.selector, + .token.attr-name, + .token.string, + .token.char, + .token.builtin, + .token.inserted { + color: $euiCodeBlockStringColor; } - .hljs-addition { - box-shadow: -$euiSizeXS 0 $euiCodeBlockAdditionColor; + .token.operator, + .token.entity, + .token.url, + .language-css .token.string, + .style .token.string, + .token.variable { + color: inherit; } - .hljs-deletion { - box-shadow: -$euiSizeXS 0 $euiCodeBlockDeletionColor; + .token.atrule, + .token.attr-value, + .token.keyword { + color: $euiCodeBlockKeywordColor; } - .hljs-selector-class { - color: $euiCodeBlockSelectorClassColor; + .token.regex, + .token.important { + color: $tokenRegex; } - .hljs-selector-id { - color: $euiCodeBlockSelectorIdColor; + .token.important, + .token.bold { + font-weight: $euiFontWeightBold; } - .hljs-emphasis { + .token.italic { font-style: italic; } - .hljs-strong { - font-weight: $euiCodeFontWeightBold; + .token.entity { + cursor: help; } - .hljs-link { - text-decoration: underline; + .token.deleted { + color: $euiCodeBlockDeletionColor; } } diff --git a/src/components/code/_code_block.tsx b/src/components/code/_code_block.tsx index 131b8d8588e..3596f5b2d94 100644 --- a/src/components/code/_code_block.tsx +++ b/src/components/code/_code_block.tsx @@ -17,17 +17,17 @@ * under the License. */ -import classNames from 'classnames'; -import hljs from 'highlight.js'; import React, { CSSProperties, FunctionComponent, KeyboardEvent, + ReactNode, useEffect, - useRef, + useMemo, useState, } from 'react'; -import { createPortal } from 'react-dom'; +import classNames from 'classnames'; +import { highlight, AST, RefractorNode } from 'refractor'; import { keys, useCombinedRefs } from '../../services'; import { EuiButtonIcon } from '../button'; import { keysOf } from '../common'; @@ -39,6 +39,32 @@ import { useMutationObserver } from '../observer/mutation_observer'; import { useResizeObserver } from '../observer/resize_observer'; import { EuiOverlayMask } from '../overlay_mask'; +const isAstElement = (node: RefractorNode): node is AST.Element => + node.hasOwnProperty('type') && node.type === 'element'; + +const nodeToHtml = ( + node: RefractorNode, + idx: number, + nodes: RefractorNode[], + depth: number = 0 +): ReactNode => { + if (isAstElement(node)) { + const { properties, tagName, children } = node; + + return React.createElement( + tagName, + { + ...properties, + key: `node-${depth}-${idx}`, + className: classNames(properties.className), + }, + children && children.map((el, i) => nodeToHtml(el, i, nodes, depth + 1)) + ); + } + + return node.value; +}; + const fontSizeToClassNameMap = { s: 'euiCodeBlock--fontSmall', m: 'euiCodeBlock--fontMedium', @@ -75,7 +101,7 @@ export interface EuiCodeBlockImplProps { /** * Sets the syntax highlighting for a specific language - * @see http://highlightjs.readthedocs.io/en/latest/css-classes-reference.html#language-names-and-aliases + * @see https://github.com/wooorm/refractor#syntaxes * for options */ language?: string; @@ -108,9 +134,6 @@ export const EuiCodeBlockImpl: FunctionComponent = ({ ...rest }) => { const [isFullScreen, setIsFullScreen] = useState(false); - const [isPortalTargetReady, setIsPortalTargetReady] = useState(false); - const codeTarget = useRef(null); - const code = useRef(null); const [wrapperRef, setWrapperRef] = useState(null); const [innerTextRef, innerText] = useInnerText(''); const [tabIndex, setTabIndex] = useState<-1 | 0>(-1); @@ -119,9 +142,14 @@ export const EuiCodeBlockImpl: FunctionComponent = ({ setWrapperRef, ]); const { width, height } = useResizeObserver(wrapperRef); - const [codeFullScreen, setCodeFullScreen] = useState( - null - ); + + const content = useMemo(() => { + if (!language || typeof children !== 'string') { + return children; + } + const nodes = highlight(children, language); + return nodes.length === 0 ? children : nodes.map(nodeToHtml); + }, [children, language]); const doesOverflow = () => { if (!wrapperRef) return; @@ -140,42 +168,6 @@ export const EuiCodeBlockImpl: FunctionComponent = ({ useEffect(doesOverflow, [width, height, wrapperRef]); - useEffect(() => { - codeTarget.current = document.createElement('div'); - setIsPortalTargetReady(true); - }, []); - - useEffect(() => { - /** - * because React maintains a mapping between its Virtual DOM representation and the actual - * DOM elements (including text nodes), and hljs modifies the DOM structure which leads - * to React updating detached nodes, we render to a document fragment and - * copy from that fragment into the target elements - * (https://github.com/elastic/eui/issues/2322) - */ - const html = isPortalTargetReady ? codeTarget.current!.innerHTML : ''; - - if (code.current) { - code.current.innerHTML = html; - } - - if (language) { - if (code.current) { - hljs.highlightBlock(code.current); - } - } - }); - - useEffect(() => { - if (codeFullScreen) { - const html = isPortalTargetReady ? codeTarget.current!.innerHTML : ''; - codeFullScreen.innerHTML = html; - if (language) { - hljs.highlightBlock(codeFullScreen); - } - } - }, [isPortalTargetReady, codeFullScreen, language]); - const onKeyDown = (event: KeyboardEvent) => { if (event.key === keys.ESCAPE) { event.preventDefault(); @@ -201,6 +193,10 @@ export const EuiCodeBlockImpl: FunctionComponent = ({ 'euiCodeBlock--inline': inline, 'euiCodeBlock--hasControls': isCopyable || overflowHeight, }, + { + prismjs: !className?.includes('prismjs'), + [`language-${language || 'none'}`]: !className?.includes('language'), + }, className ); @@ -217,7 +213,11 @@ export const EuiCodeBlockImpl: FunctionComponent = ({ optionalStyles.maxHeight = overflowHeight; } - const codeSnippet = ; + const codeSnippet = ( + + {content} + + ); const wrapperProps = { className: classes, @@ -225,12 +225,7 @@ export const EuiCodeBlockImpl: FunctionComponent = ({ }; if (inline) { - return isPortalTargetReady ? ( - <> - {createPortal(children, codeTarget.current!)} - {codeSnippet} - - ) : null; + return {codeSnippet}; } const getCopyButton = (textToCopy?: string) => { @@ -317,11 +312,9 @@ export const EuiCodeBlockImpl: FunctionComponent = ({
-                
+                
+                  {content}
+                
               
{codeBlockControls} @@ -335,25 +328,21 @@ export const EuiCodeBlockImpl: FunctionComponent = ({ }; const codeBlockControls = getCodeBlockControls(innerText); - return isPortalTargetReady ? ( - <> - {createPortal(children, codeTarget.current!)} -
-
-          {codeSnippet}
-        
- - {/* + return ( +
+
+        {codeSnippet}
+      
+ {/* If the below fullScreen code renders, it actually attaches to the body because of EuiOverlayMask's React portal usage. */} - {codeBlockControls} - {getFullScreenDisplay(codeBlockControls)} -
- - ) : null; + {codeBlockControls} + {getFullScreenDisplay(codeBlockControls)} +
+ ); }; diff --git a/src/components/markdown_editor/plugins/markdown_default_plugins.tsx b/src/components/markdown_editor/plugins/markdown_default_plugins.tsx index a62d1e6d101..e4183a1c1db 100644 --- a/src/components/markdown_editor/plugins/markdown_default_plugins.tsx +++ b/src/components/markdown_editor/plugins/markdown_default_plugins.tsx @@ -17,6 +17,7 @@ * under the License. */ +import React, { createElement } from 'react'; // Importing seemingly unused types from `unified` because the definitions // are exported for two versions of TypeScript (3.4, 4.0) and implicit // imports during eui.d.ts generation default to the incorrect version (3.4). @@ -34,20 +35,19 @@ import { // eslint-disable-next-line @typescript-eslint/no-unused-vars Settings, } from 'unified'; -import remark2rehype from 'remark-rehype'; +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 markdown from 'remark-parse'; +import emoji from 'remark-emoji'; +import remark2rehype from 'remark-rehype'; +import highlight from './remark/remark_prismjs'; import * as MarkdownTooltip from './markdown_tooltip'; import * as MarkdownCheckbox from './markdown_checkbox'; import { markdownLinkValidator } from './markdown_link_validator'; -import React, { createElement } from 'react'; +import { EuiMarkdownEditorUiPlugin } from './../markdown_types'; import { EuiLink } from '../../link'; import { EuiCodeBlock, EuiCode } from '../../code'; -import markdown from 'remark-parse'; -import highlight from 'remark-highlight.js'; -import emoji from 'remark-emoji'; -import all from 'mdast-util-to-hast/lib/all'; -import { Options as Remark2RehypeOptions, Handler } from 'mdast-util-to-hast'; -import { EuiMarkdownEditorUiPlugin } from './../markdown_types'; export const getDefaultEuiMarkdownParsingPlugins = (): PluggableList => [ [markdown, {}], diff --git a/src/components/markdown_editor/plugins/remark/remark_prismjs.ts b/src/components/markdown_editor/plugins/remark/remark_prismjs.ts new file mode 100644 index 00000000000..72b400fb0bb --- /dev/null +++ b/src/components/markdown_editor/plugins/remark/remark_prismjs.ts @@ -0,0 +1,48 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import refractor from 'refractor'; +import visit from 'unist-util-visit'; +import { Plugin } from 'unified'; + +const attacher: Plugin = () => { + return (ast) => visit(ast, 'code', visitor); + + function visitor(node: any) { + const { data = {}, lang: language } = node; + + if (!language) { + return; + } + + node.data = data; + data.hChildren = refractor.highlight(node.value, language); + data.hProperties = { + ...data.hProperties, + language, + className: [ + 'prismjs', + ...(data.hProperties?.className || []), + `language-${language}`, + ], + }; + } +}; + +export default attacher; diff --git a/src/components/markdown_editor/unified-plugins.d.ts b/src/components/markdown_editor/unified-plugins.d.ts index 53b661079a4..a1596876d10 100644 --- a/src/components/markdown_editor/unified-plugins.d.ts +++ b/src/components/markdown_editor/unified-plugins.d.ts @@ -24,12 +24,6 @@ declare module 'remark-emoji' { export = RemarkEmoji; } -declare module 'remark-highlight.js' { - import { Plugin } from 'unified'; - const RemarkHighlight: Plugin; - export = RemarkHighlight; -} - declare module 'mdast-util-to-hast/lib/all' { // eslint-disable-next-line import/no-unresolved import { Node } from 'unist'; diff --git a/yarn.lock b/yarn.lock index 1affa187aa6..a556048a8d7 100755 --- a/yarn.lock +++ b/yarn.lock @@ -1630,11 +1630,6 @@ dependencies: "@types/unist" "*" -"@types/highlight.js@^9.12.4": - version "9.12.4" - resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-9.12.4.tgz#8c3496bd1b50cc04aeefd691140aa571d4dbfa34" - integrity sha512-t2szdkwmg2JJyuCM20e8kR2X59WCE5Zkl4bzm1u1Oukjm79zpbiAv+QjnwLnuuV0WHEcX2NgUItu0pAMKuOPww== - "@types/history@*": version "4.7.8" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" @@ -1736,6 +1731,11 @@ resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== +"@types/prismjs@*": + version "1.16.3" + resolved "https://registry.yarnpkg.com/@types/prismjs/-/prismjs-1.16.3.tgz#73ae78b3e339777a1a1b7a8df89dcd6b8fe750c5" + integrity sha512-7lbX0Odbg9rnzXRdYdgPQZFkjd38QHpD6tvWxbLi6VXGQbXr054doixIS+TwftHP6afffA1zxCZrIcJRS/MkYQ== + "@types/prop-types@*": version "15.7.3" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" @@ -1813,6 +1813,13 @@ "@types/prop-types" "*" csstype "^2.2.0" +"@types/refractor@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/refractor/-/refractor-3.0.0.tgz#c535cfad1c54cf377ae2984f6cf6e9627a36ea66" + integrity sha512-jkCqkTpxMXXfN03Xpzj+mBMxo9IxG616SV2U42iwHkBGq/f8RrX3DCzLayIqUV+MAIBCUvl5xPnjqpUtZRnMqA== + dependencies: + "@types/prismjs" "*" + "@types/responselike@*": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" @@ -3887,6 +3894,15 @@ cli-width@^3.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== +clipboard@^2.0.0: + version "2.0.7" + resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.7.tgz#da927f817b1859426df39212ac81feb07dbaeadc" + integrity sha512-8M8WEZcIvs0hgOma+wAPkrUxpv0PMY1L6VsAJh/2DOKARIMpyWe6ZLcEoe1qktl6/ced5ceYHs+oGedSbgZ3sg== + dependencies: + good-listener "^1.2.2" + select "^1.1.2" + tiny-emitter "^2.0.0" + cliui@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" @@ -5027,6 +5043,11 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +delegate@^3.1.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" + integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw== + delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" @@ -6420,13 +6441,6 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" -fault@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/fault/-/fault-1.0.4.tgz#eafcfc0a6d214fc94601e170df29954a4f842f13" - integrity sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA== - dependencies: - format "^0.2.0" - faye-websocket@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" @@ -6820,11 +6834,6 @@ form-data@~2.3.2: combined-stream "1.0.6" mime-types "^2.1.12" -format@^0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" - integrity sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs= - forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -7436,6 +7445,13 @@ gonzales-pe@^4.0.3, gonzales-pe@^4.2.2: dependencies: minimist "^1.2.5" +good-listener@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" + integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA= + dependencies: + delegate "^3.1.2" + got@^10.7.0: version "10.7.0" resolved "https://registry.yarnpkg.com/got/-/got-10.7.0.tgz#62889dbcd6cca32cd6a154cc2d0c6895121d091f" @@ -7755,6 +7771,17 @@ hastscript@^5.0.0: property-information "^5.0.0" space-separated-tokens "^1.0.0" +hastscript@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-6.0.0.tgz#e8768d7eac56c3fdeac8a92830d58e811e5bf640" + integrity sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w== + dependencies: + "@types/hast" "^2.0.0" + comma-separated-tokens "^1.0.0" + hast-util-parse-selector "^2.0.0" + property-information "^5.0.0" + space-separated-tokens "^1.0.0" + he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -7765,16 +7792,6 @@ hex-color-regex@^1.1.0: resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== -highlight.js@^9.18.5: - version "9.18.5" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.5.tgz#d18a359867f378c138d6819edfc2a8acd5f29825" - integrity sha512-a5bFyofd/BHCX52/8i8uJkjr9DYwXIPnM/plwI6W7ezItLGqzt7X2G2nXuYSfsIJdkwwj/g9DG1LkcGJI/dDoA== - -highlight.js@~10.1.0: - version "10.1.2" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.1.2.tgz#c20db951ba1c22c055010648dfffd7b2a968e00c" - integrity sha512-Q39v/Mn5mfBlMff9r+zzA+gWxRsCRKwEMvYTiisLr/XUiFI/4puWt0Ojdko3R3JCNWGdOWaA5g/Yxqa23kC5AA== - history@^4.9.0: version "4.10.1" resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" @@ -10031,14 +10048,6 @@ lowercase-keys@^2.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== -lowlight@^1.2.0: - version "1.14.0" - resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-1.14.0.tgz#83ebc143fec0f9e6c0d3deffe01be129ce56b108" - integrity sha512-N2E7zTM7r1CwbzwspPxJvmjAbxljCPThTFawEX2Z7+P3NGrrvY54u8kyU16IY4qWfoVIxY8SYCS8jTkuG7TqYA== - dependencies: - fault "^1.0.0" - highlight.js "~10.1.0" - lru-cache@^4.0.0, lru-cache@^4.0.1, lru-cache@^4.1.1: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -12682,9 +12691,16 @@ pretty-hrtime@^1.0.3: integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= prism-react-renderer@^1.0.2: - version "1.1.1" - resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.1.1.tgz#1c1be61b1eb9446a146ca7a50b7bcf36f2a70a44" - integrity sha512-MgMhSdHuHymNRqD6KM3eGS0PNqgK9q4QF5P0yoQQvpB6jNjeSAi3jcSAz0Sua/t9fa4xDOMar9HJbLa08gl9ug== + version "1.2.0" + resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.2.0.tgz#5ad4f90c3e447069426c8a53a0eafde60909cdf4" + integrity sha512-GHqzxLYImx1iKN1jJURcuRoA/0ygCcNhfGw1IT8nPIMzarmKQ3Nc+JcG0gi8JXQzuh0C5ShE4npMIoqNin40hg== + +prismjs@~1.23.0: + version "1.23.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.23.0.tgz#d3b3967f7d72440690497652a9d40ff046067f33" + integrity sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA== + optionalDependencies: + clipboard "^2.0.0" process-nextick-args@^1.0.6: version "1.0.7" @@ -13482,6 +13498,15 @@ reflect.ownkeys@^0.2.0: resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460" integrity sha1-dJrO7H8/34tj+SegSAnpDFwLNGA= +refractor@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/refractor/-/refractor-3.3.1.tgz#ebbc04b427ea81dc25ad333f7f67a0b5f4f0be3a" + integrity sha512-vaN6R56kLMuBszHSWlwTpcZ8KTMG6aUCok4GrxYDT20UIOXxOc5o6oDc8tNTzSlH3m2sI+Eu9Jo2kVdDcUTWYw== + dependencies: + hastscript "^6.0.0" + parse-entities "^2.0.0" + prismjs "~1.23.0" + regenerate-unicode-properties@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" @@ -13621,14 +13646,6 @@ remark-emoji@^2.1.0: node-emoji "^1.10.0" unist-util-visit "^2.0.2" -remark-highlight.js@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/remark-highlight.js/-/remark-highlight.js-6.0.0.tgz#cb05387ddddeb078f0b61ce6299f6323f05098fc" - integrity sha512-eNHP/ezuDKoeh3KV+rWLeBVzU3SYVt0uu1tHdsCB6TUJYHwTNMJWZ5+nU+2fJHQXb+7PtpfGXVtFS9zk/W6qdw== - dependencies: - lowlight "^1.2.0" - unist-util-visit "^2.0.0" - remark-math@1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/remark-math/-/remark-math-1.0.4.tgz#ced46473075ff99e4678a154a1a3d0dde403a9f2" @@ -14285,6 +14302,11 @@ select-hose@^2.0.0: resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= +select@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" + integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0= + selfsigned@^1.10.7: version "1.10.8" resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.8.tgz#0d17208b7d12c33f8eac85c41835f27fc3d81a30" @@ -15656,6 +15678,11 @@ timsort@^0.3.0: resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= +tiny-emitter@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" + integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== + tiny-invariant@^1.0.2, tiny-invariant@^1.0.6: version "1.1.0" resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" @@ -16456,9 +16483,9 @@ vfile-location@^2.0.0: integrity sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA== vfile-location@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-3.1.0.tgz#81cd8a04b0ac935185f4fce16f270503fc2f692f" - integrity sha512-FCZ4AN9xMcjFIG1oGmZKo61PjwJHRVA+0/tPUP2ul4uIwjGGndIxavEMRpWn5p4xwm/ZsdXp9YNygf1ZyE4x8g== + version "3.2.0" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-3.2.0.tgz#d8e41fbcbd406063669ebf6c33d56ae8721d0f3c" + integrity sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA== vfile-message@*, vfile-message@^2.0.0: version "2.0.4" From ecf4f65a7d4947fe4af20b6ce14a0828abc37a3a Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Wed, 17 Mar 2021 16:21:40 -0500 Subject: [PATCH 02/18] merge fix: file renamed --- src-docs/src/services/playground/knobs.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src-docs/src/services/playground/knobs.js b/src-docs/src/services/playground/knobs.js index a5676ec7f83..f18e5c73478 100644 --- a/src-docs/src/services/playground/knobs.js +++ b/src-docs/src/services/playground/knobs.js @@ -27,7 +27,7 @@ import { } from '../../../../src/components/'; export const markup = (text) => { - const regex = /(#[a-zA-Z]+)|(`[^`]+`)/g; + const regex = /(^#[a-zA-Z]+)|(?<=\s)(#[a-zA-Z]+)|(`[^`]+`)/g; return text.split('\n').map((token) => { const values = token.split(regex).map((token, index) => { if (!token) { @@ -379,7 +379,7 @@ const KnobColumn = ({ state, knobNames, error, set, isPlayground }) => { if (humanizedType) { typeMarkup = humanizedType && ( - {markup(humanizedType)} + {humanizedType} ); const functionMatches = [ @@ -389,17 +389,19 @@ const KnobColumn = ({ state, knobNames, error, set, isPlayground }) => { const types = humanizedType.split(/\([^=]*\) =>\s\w*\)*/); if (functionMatches.length > 0) { - const elements = []; + let elements = ''; let j = 0; for (let i = 0; i < types.length; i++) { if (functionMatches[j]) { - elements.push(
{types[i]}
); - elements.push( -
{functionMatches[j][0]}
- ); + elements = + `${elements}` + + `${types[i]}` + + '\n' + + `${functionMatches[j][0]}` + + '\n'; j++; } else { - elements.push(
{types[i]}
); + elements = `${elements}` + `${types[i]}` + '\n'; } } typeMarkup = ( From 4a688eeb924def8f80a14326ec91489e54fa56b1 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Thu, 18 Mar 2021 11:00:09 -0500 Subject: [PATCH 03/18] group lines --- src/components/code/_code_block.scss | 4 ++++ src/components/code/_code_block.tsx | 22 ++++++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/components/code/_code_block.scss b/src/components/code/_code_block.scss index 19e9edf7f74..0711caba15f 100644 --- a/src/components/code/_code_block.scss +++ b/src/components/code/_code_block.scss @@ -40,6 +40,10 @@ $tokenRegex: #E90; margin-top: $euiSizeXS; } + .euiCodeBlock__line { + display: block; + } + &.euiCodeBlock-isFullScreen { position: fixed; top: 0; diff --git a/src/components/code/_code_block.tsx b/src/components/code/_code_block.tsx index 3596f5b2d94..9eb1550fa2b 100644 --- a/src/components/code/_code_block.tsx +++ b/src/components/code/_code_block.tsx @@ -65,6 +65,22 @@ const nodeToHtml = ( return node.value; }; +const highlightByLine = (code: string, language: string): RefractorNode[] => { + const lines = code.split('\n'); + // Trim lines from end + while (lines[lines.length - 1] === '') { + lines.splice(-1, 1); + } + return lines.map((line) => ({ + type: 'element', + tagName: 'span', + properties: { + className: ['euiCodeBlock__line'], + }, + children: highlight(line ? line : '\n', language), + })); +}; + const fontSizeToClassNameMap = { s: 'euiCodeBlock--fontSmall', m: 'euiCodeBlock--fontMedium', @@ -147,9 +163,11 @@ export const EuiCodeBlockImpl: FunctionComponent = ({ if (!language || typeof children !== 'string') { return children; } - const nodes = highlight(children, language); + const nodes = inline + ? highlight(children, language) + : highlightByLine(children, language); return nodes.length === 0 ? children : nodes.map(nodeToHtml); - }, [children, language]); + }, [children, language, inline]); const doesOverflow = () => { if (!wrapperRef) return; From 7cd76176590ea69a1cd4f38337ef39241d1d5d49 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Thu, 18 Mar 2021 13:49:48 -0500 Subject: [PATCH 04/18] update tests --- .../__snapshots__/_code_block.test.tsx.snap | 18 +- .../code/__snapshots__/code.test.tsx.snap | 2 +- .../__snapshots__/code_block.test.tsx.snap | 168 ++++++++++++------ src/components/code/_code_block.test.tsx | 40 ++--- src/components/code/code.test.tsx | 14 +- src/components/code/code_block.test.tsx | 38 ++-- 6 files changed, 156 insertions(+), 124 deletions(-) diff --git a/src/components/code/__snapshots__/_code_block.test.tsx.snap b/src/components/code/__snapshots__/_code_block.test.tsx.snap index d2369076574..47443fe20a0 100644 --- a/src/components/code/__snapshots__/_code_block.test.tsx.snap +++ b/src/components/code/__snapshots__/_code_block.test.tsx.snap @@ -2,14 +2,14 @@ exports[`EuiCodeBlockImpl block highlights javascript code, adding "js" class 1`] = `
     
   
@@ -17,7 +17,7 @@ exports[`EuiCodeBlockImpl block highlights javascript code, adding "js" class 1` exports[`EuiCodeBlockImpl block renders a pre block tag 1`] = `
   
   
   
 
 `;
 
 exports[`EuiCodeBlockImpl inline renders an inline code tag 1`] = `
 
   
   
   
-  
+
-      
-        constvalue =
-        'State 1'
-      
+      const value = 'State 1'
     
" @@ -15,12 +12,9 @@ exports[`EuiCodeBlock dynamic content updates DOM when input changes 1`] = ` exports[`EuiCodeBlock dynamic content updates DOM when input changes 2`] = ` "
-
+
-      
-        constvalue =
-        'State 2'
-      
+      const value = 'State 2'
     
" @@ -28,7 +22,7 @@ exports[`EuiCodeBlock dynamic content updates DOM when input changes 2`] = ` exports[`EuiCodeBlock props fontSize l is rendered 1`] = `
   
   
-  
-    
-      var some = 'code';
-console.log(some);
-    
-  
-
- - - + var some = 'code'; +console.log(some); + +
+
+
+ + + + + + + + + + + +
+
-
-
+ + `; exports[`EuiCodeBlock props language is rendered 1`] = `
     
-      var some = 'code';
-console.log(some);
+      
+        var some = 'code';
+      
+      
+        console.log(some);
+      
     
   
@@ -141,12 +195,12 @@ console.log(some); exports[`EuiCodeBlock props overflowHeight is rendered 1`] = `
     
   
   
   
   
   
   
 {
   describe('inline', () => {
     test('renders an inline code tag', () => {
-      const component = mount(
+      const component = render(
         
           {code}
         
       );
 
-      expect(snapshotCodeBlock(component)).toMatchSnapshot();
+      expect(component).toMatchSnapshot();
     });
 
     test('highlights javascript code, adding "js" class', () => {
-      const component = mount();
+      const component = render(
+        
+      );
 
-      expect(snapshotCodeBlock(component)).toMatchSnapshot();
+      expect(component).toMatchSnapshot();
     });
 
     test('renders with transparent background', () => {
-      const component = mount(
+      const component = render(
         
       );
 
-      expect(snapshotCodeBlock(component)).toMatchSnapshot();
+      expect(component).toMatchSnapshot();
     });
   });
 
   describe('block', () => {
     test('renders a pre block tag', () => {
-      const component = mount(
+      const component = render(
         
           {code}
         
       );
 
-      expect(snapshotCodeBlock(component)).toMatchSnapshot();
+      expect(component).toMatchSnapshot();
     });
 
     test('highlights javascript code, adding "js" class', () => {
-      const component = mount(
+      const component = render(
         
       );
 
-      expect(snapshotCodeBlock(component)).toMatchSnapshot();
+      expect(component).toMatchSnapshot();
     });
 
     test('renders with transparent background', () => {
-      const component = mount(
+      const component = render(
         
       );
 
-      expect(snapshotCodeBlock(component)).toMatchSnapshot();
+      expect(component).toMatchSnapshot();
     });
 
     test('renders a pre block tag with a css class modifier', () => {
-      const component = mount(
+      const component = render(
         
           {code}
         
       );
-      expect(snapshotCodeBlock(component)).toMatchSnapshot();
+      expect(component).toMatchSnapshot();
     });
   });
 });
diff --git a/src/components/code/code.test.tsx b/src/components/code/code.test.tsx
index 67a9fbd9a0e..6b25e2bafec 100644
--- a/src/components/code/code.test.tsx
+++ b/src/components/code/code.test.tsx
@@ -18,26 +18,18 @@
  */
 
 import React from 'react';
-import { mount, ReactWrapper } from 'enzyme';
+import { render } from 'enzyme';
 import { requiredProps } from '../../test/required_props';
 
 import { EuiCode } from './code';
 
-function snapshotCodeBlock(component: ReactWrapper) {
-  // Get the Portal's sibling and return its html
-  const renderedHtml = component.find('Portal + *').html();
-  const container = document.createElement('div');
-  container.innerHTML = renderedHtml;
-  return container.firstChild;
-}
-
 const code = `var some = 'code';
 console.log(some);`;
 
 describe('EuiCode', () => {
   test('renders a code snippet', () => {
-    const component = mount({code});
+    const component = render({code});
 
-    expect(snapshotCodeBlock(component)).toMatchSnapshot();
+    expect(component).toMatchSnapshot();
   });
 });
diff --git a/src/components/code/code_block.test.tsx b/src/components/code/code_block.test.tsx
index 2843b61ff16..6015800ddaa 100644
--- a/src/components/code/code_block.test.tsx
+++ b/src/components/code/code_block.test.tsx
@@ -19,7 +19,7 @@
 
 import React, { useState, useEffect } from 'react';
 import ReactDOM from 'react-dom';
-import { mount, ReactWrapper } from 'enzyme';
+import { mount, render } from 'enzyme';
 import html from 'html';
 import { act } from 'react-dom/test-utils';
 import { requiredProps } from '../../test/required_props';
@@ -27,34 +27,26 @@ import { requiredProps } from '../../test/required_props';
 import { EuiCodeBlock } from './code_block';
 import { FONT_SIZES, PADDING_SIZES } from './_code_block';
 
-function snapshotCodeBlock(component: ReactWrapper) {
-  // Get the Portal's sibling and return its html
-  const renderedHtml = component.find('Portal + *').html();
-  const container = document.createElement('div');
-  container.innerHTML = renderedHtml;
-  return container.firstChild;
-}
-
 const code = `var some = 'code';
 console.log(some);`;
 
 describe('EuiCodeBlock', () => {
   test('renders a code block', () => {
-    const component = mount(
+    const component = render(
       {code}
     );
 
-    expect(snapshotCodeBlock(component)).toMatchSnapshot();
+    expect(component).toMatchSnapshot();
   });
 
   describe('props', () => {
     describe('transparentBackground', () => {
       it('is rendered', () => {
-        const component = mount(
+        const component = render(
           {code}
         );
 
-        expect(snapshotCodeBlock(component)).toMatchSnapshot();
+        expect(component).toMatchSnapshot();
       });
     });
 
@@ -62,38 +54,38 @@ describe('EuiCodeBlock', () => {
       it('is rendered', () => {
         const component = mount({code});
 
-        expect(snapshotCodeBlock(component)).toMatchSnapshot();
+        expect(component).toMatchSnapshot();
       });
     });
 
     describe('overflowHeight', () => {
       it('is rendered', () => {
-        const component = mount(
+        const component = render(
           {code}
         );
 
-        expect(snapshotCodeBlock(component)).toMatchSnapshot();
+        expect(component).toMatchSnapshot();
       });
     });
 
     describe('language', () => {
       it('is rendered', () => {
-        const component = mount(
+        const component = render(
           {code}
         );
 
-        expect(snapshotCodeBlock(component)).toMatchSnapshot();
+        expect(component).toMatchSnapshot();
       });
     });
 
     describe('fontSize', () => {
       FONT_SIZES.forEach((fontSize) => {
         test(`${fontSize} is rendered`, () => {
-          const component = mount(
+          const component = render(
             {code}
           );
 
-          expect(snapshotCodeBlock(component)).toMatchSnapshot();
+          expect(component).toMatchSnapshot();
         });
       });
     });
@@ -101,11 +93,11 @@ describe('EuiCodeBlock', () => {
     describe('paddingSize', () => {
       PADDING_SIZES.forEach((paddingSize) => {
         test(`${paddingSize} is rendered`, () => {
-          const component = mount(
+          const component = render(
             {code}
           );
 
-          expect(snapshotCodeBlock(component)).toMatchSnapshot();
+          expect(component).toMatchSnapshot();
         });
       });
     });
@@ -131,7 +123,7 @@ describe('EuiCodeBlock', () => {
         const [value, setValue] = useState('State 1');
 
         useEffect(() => {
-          // Wait a tick for EuiCodeBlock internal state to update on mount
+          // Wait a tick for EuiCodeBlock internal state to update on render
           setTimeout(() => {
             takeSnapshot();
             act(() => {

From 71e13398feb0e2dcb52d88ad495b30917fa282df Mon Sep 17 00:00:00 2001
From: Greg Thompson 
Date: Thu, 18 Mar 2021 15:32:03 -0500
Subject: [PATCH 05/18] i18n block

---
 src-docs/src/views/package/i18n_tokens.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src-docs/src/views/package/i18n_tokens.js b/src-docs/src/views/package/i18n_tokens.js
index fe1c8528672..c7766972f74 100644
--- a/src-docs/src/views/package/i18n_tokens.js
+++ b/src-docs/src/views/package/i18n_tokens.js
@@ -37,7 +37,7 @@ const columns = [
     render({ defString, highlighting }) {
       return (
         

From f7b852b392b4962d15952372d5f0df247c68cd97 Mon Sep 17 00:00:00 2001
From: Greg Thompson 
Date: Thu, 18 Mar 2021 16:43:08 -0500
Subject: [PATCH 06/18] remove regex lookbehind not supported in safari

---
 src-docs/src/services/playground/knobs.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src-docs/src/services/playground/knobs.js b/src-docs/src/services/playground/knobs.js
index f18e5c73478..979ed06278d 100644
--- a/src-docs/src/services/playground/knobs.js
+++ b/src-docs/src/services/playground/knobs.js
@@ -27,7 +27,7 @@ import {
 } from '../../../../src/components/';
 
 export const markup = (text) => {
-  const regex = /(^#[a-zA-Z]+)|(?<=\s)(#[a-zA-Z]+)|(`[^`]+`)/g;
+  const regex = /(\B#[a-zA-Z]+)|(`[^`]+`)/g;
   return text.split('\n').map((token) => {
     const values = token.split(regex).map((token, index) => {
       if (!token) {

From 205aa218fbba7d4c9f24716e8112d5e16989ad29 Mon Sep 17 00:00:00 2001
From: Greg Thompson 
Date: Thu, 18 Mar 2021 16:58:16 -0500
Subject: [PATCH 07/18] CL

---
 CHANGELOG.md                          | 1 +
 src-docs/src/views/code/code_block.js | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6699de33a6a..a91f18674ef 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
 ## [`master`](https://github.com/elastic/eui/tree/master)
 
 - Added `indexRuntime` glyph in `EuiIcon` ([#4650](https://github.com/elastic/eui/pull/4650))
+- Replaced `highlight.js` with `prism.js`/`refractor` for code syntax highlighting ([#4638](https://github.com/elastic/eui/pull/4638))
 
 ## [`31.11.0`](https://github.com/elastic/eui/tree/v31.11.0)
 
diff --git a/src-docs/src/views/code/code_block.js b/src-docs/src/views/code/code_block.js
index 6e4c57df391..0314991a617 100644
--- a/src-docs/src/views/code/code_block.js
+++ b/src-docs/src/views/code/code_block.js
@@ -15,7 +15,7 @@ export default () => (
     
 
     
Date: Mon, 29 Mar 2021 13:55:03 -0500
Subject: [PATCH 08/18] js -> jsx

---
 src-docs/src/views/code/code_block_pre.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src-docs/src/views/code/code_block_pre.js b/src-docs/src/views/code/code_block_pre.js
index b27f43d6eea..4df2aad65b0 100644
--- a/src-docs/src/views/code/code_block_pre.js
+++ b/src-docs/src/views/code/code_block_pre.js
@@ -5,7 +5,7 @@ import { EuiCodeBlock } from '../../../../src/components';
 export default () => (
   
Date: Tue, 30 Mar 2021 12:44:27 -0500 Subject: [PATCH 09/18] token color parity --- src/components/code/_code_block.scss | 132 +++++++++++++++++++-------- 1 file changed, 93 insertions(+), 39 deletions(-) diff --git a/src/components/code/_code_block.scss b/src/components/code/_code_block.scss index 0711caba15f..ea1fddb34a4 100644 --- a/src/components/code/_code_block.scss +++ b/src/components/code/_code_block.scss @@ -1,5 +1,3 @@ -$tokenRegex: #E90; - .euiCodeBlock { max-width: 100%; display: block; @@ -178,74 +176,130 @@ $tokenRegex: #E90; } } + .token.punctuation:not(.interpolation-punctuation):not([class='attr-*']), + .token.namespace { + opacity: .7; + } + .token.comment, .token.prolog, .token.doctype, - .token.cdata { + .token.cdata, + .token.coord, + .token.blockquote { color: $euiCodeBlockCommentColor; font-style: italic; } - .token.punctuation { - opacity: .7; + .token.selector { + color: $euiCodeBlockSelectorTagColor; } - .token.namespace { - opacity: .7; + .token.string, + .token.interpolation, + .token.interpolation-punctuation, + .token.doc-comment .token.keyword, + .token.tag .token.attr-value, + .token.url .token.content { + color: $euiCodeBlockStringColor; } - .token.property, - .token.tag, - .token.boolean, .token.number, - .token.constant, - .token.symbol { + .token.boolean, + .token.keyword.nil, + .token.regex, + .token.variable, + .token.unit, + .token.hexcode, + .token.tag .token.attr-name, + .token.tag .token.attr-equals { + color: $euiCodeBlockNumberColor; + } + + .token.atrule .token.rule, + .token.keyword { + color: $euiCodeBlockKeywordColor; + } + + .token.function { + color: $euiCodeBlockFunctionTitleColor; + } + + .token.tag { color: $euiCodeBlockTagColor; } - .token.selector, - .token.attr-name, - .token.string, - .token.char, - .token.builtin, - .token.inserted { - color: $euiCodeBlockStringColor; + .token.class-name { + color: $euiCodeBlockTypeColor; } - .token.operator, - .token.entity, - .token.url, - .language-css .token.string, - .style .token.string, - .token.variable { - color: inherit; + .token.property { + color: $euiCodeBlockAttributeColor; } - .token.atrule, - .token.attr-value, - .token.keyword { - color: $euiCodeBlockKeywordColor; + .token.console, + .token.list-punctuation, + .token.url-reference, + .token.url .token.url { + color: $euiCodeBlockSymbolColor; } - .token.regex, + .token.paramater { + color: $euiCodeBlockParamsColor; + } + + .token.meta, .token.important { - color: $tokenRegex; + color: $euiCodeBlockMetaColor; } - .token.important, - .token.bold { - font-weight: $euiFontWeightBold; + .token.title { + color: $euiCodeBlockTitleColor; + } + + .token.section { + color: $euiCodeBlockSectionColor; + } + + .token.prefix.inserted, + .token.prefix.deleted { + padding-left: $euiSizeXS; + margin-left: -$euiSizeXS; + } + + .token.prefix.inserted { + box-shadow: -$euiSizeXS 0 $euiCodeBlockAdditionColor; + color: $euiCodeBlockAdditionColor; + } + + .token.prefix.deleted { + box-shadow: -$euiSizeXS 0 $euiCodeBlockDeletionColor; + color: $euiCodeBlockDeletionColor; + } + + .token.selector .token.class { + color: $euiCodeBlockSelectorClassColor; + } + + .token.selector .token.id { + color: $euiCodeBlockSelectorIdColor; } .token.italic { font-style: italic; } - .token.entity { - cursor: help; + .token.important, + .token.bold { + font-weight: $euiCodeFontWeightBold; } - .token.deleted { - color: $euiCodeBlockDeletionColor; + .token.url-reference, + .token.url .token.url { + text-decoration: underline; + } + + .token.entity { + cursor: help; } } From 335a8959e30679d507e72df4edce4d18a6153c96 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Mon, 5 Apr 2021 15:10:12 -0500 Subject: [PATCH 10/18] update line grouping method --- .../__snapshots__/code_block.test.tsx.snap | 1 + src/components/code/_code_block.scss | 8 +- src/components/code/_code_block.tsx | 95 ++++++++++++++++--- 3 files changed, 86 insertions(+), 18 deletions(-) diff --git a/src/components/code/__snapshots__/code_block.test.tsx.snap b/src/components/code/__snapshots__/code_block.test.tsx.snap index 1f9131d2df4..7ebc5412e0f 100644 --- a/src/components/code/__snapshots__/code_block.test.tsx.snap +++ b/src/components/code/__snapshots__/code_block.test.tsx.snap @@ -182,6 +182,7 @@ exports[`EuiCodeBlock props language is rendered 1`] = ` class="euiCodeBlock__line" > var some = 'code'; + node.hasOwnProperty('type') && node.type === 'element'; @@ -65,20 +70,82 @@ const nodeToHtml = ( return node.value; }; -const highlightByLine = (code: string, language: string): RefractorNode[] => { - const lines = code.split('\n'); - // Trim lines from end - while (lines[lines.length - 1] === '') { - lines.splice(-1, 1); - } - return lines.map((line) => ({ - type: 'element', - tagName: 'span', - properties: { - className: ['euiCodeBlock__line'], - }, - children: highlight(line ? line : '\n', language), - })); +const addLineData = ( + nodes: ExtendedRefractorNode[], + data = { lineNumber: 1 } +): ExtendedRefractorNode[] => { + return nodes.reduce((result, node) => { + const lineStart = data.lineNumber; + if (node.type === 'text') { + if (node.value.indexOf('\n') === -1) { + node.lineStart = lineStart; + node.lineEnd = lineStart; + result.push(node); + } else { + const lines = node.value.split('\n'); + lines.forEach((line, i) => { + const num = i === 0 ? data.lineNumber : ++data.lineNumber; + result.push({ + type: 'text', + value: i === lines.length - 1 ? line : `${line}\n`, + lineStart: num, + lineEnd: num, + }); + }); + } + return result; + } + + if (node.children) { + const children = addLineData(node.children, data); + const first = children[0]; + const last = children[children.length - 1]; + const start = first.lineStart ?? lineStart; + const end = last.lineEnd ?? lineStart; + if (start !== end) { + children.forEach((node) => { + result.push(node); + }); + } else { + node.lineStart = start; + node.lineEnd = end; + node.children = children; + result.push(node); + } + return result; + } + + result.push(node); + return result; + }, []); +}; + +function wrapLines(nodes: ExtendedRefractorNode[]) { + const grouped: ExtendedRefractorNode[][] = []; + nodes.forEach((node) => { + const lineStart = node.lineStart! - 1; + if (grouped[lineStart]) { + grouped[lineStart].push(node); + } else { + grouped[lineStart] = [node]; + } + }); + const wrapped: RefractorNode[] = []; + grouped.forEach((node) => { + wrapped.push({ + type: 'element', + tagName: 'span', + properties: { + className: ['euiCodeBlock__line'], + }, + children: node, + }); + }); + return wrapped; +} + +const highlightByLine = (children: string, language: string) => { + return wrapLines(addLineData(highlight(children, language))); }; const fontSizeToClassNameMap = { From 95567a0ab08a4dab26cecad87393dcb432bfefb4 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Mon, 5 Apr 2021 15:13:16 -0500 Subject: [PATCH 11/18] use jsx language in docs --- .../guide_section/guide_section_parts/guide_section_code.tsx | 2 +- src-docs/src/services/playground/playground.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src-docs/src/components/guide_section/guide_section_parts/guide_section_code.tsx b/src-docs/src/components/guide_section/guide_section_parts/guide_section_code.tsx index b2fc48fa16d..db330c7dcba 100644 --- a/src-docs/src/components/guide_section/guide_section_parts/guide_section_code.tsx +++ b/src-docs/src/components/guide_section/guide_section_parts/guide_section_code.tsx @@ -34,7 +34,7 @@ export const GuideSectionExampleCode: FunctionComponent return ( <> - + {codeToRender} {codeSandboxLink} diff --git a/src-docs/src/services/playground/playground.js b/src-docs/src/services/playground/playground.js index dfd90dfe97f..545791b1ca0 100644 --- a/src-docs/src/services/playground/playground.js +++ b/src-docs/src/services/playground/playground.js @@ -81,7 +81,7 @@ export default ({
From bc3dfe80e1e8c936f0b5e5a15220ac13f928e039 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Wed, 7 Apr 2021 13:52:16 -0500 Subject: [PATCH 12/18] reduce newlines --- src/components/code/_code_block.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/code/_code_block.tsx b/src/components/code/_code_block.tsx index f7eee71054c..9fe6134501c 100644 --- a/src/components/code/_code_block.tsx +++ b/src/components/code/_code_block.tsx @@ -218,7 +218,10 @@ export const EuiCodeBlockImpl: FunctionComponent = ({ }) => { const [isFullScreen, setIsFullScreen] = useState(false); const [wrapperRef, setWrapperRef] = useState(null); - const [innerTextRef, innerText] = useInnerText(''); + const [innerTextRef, _innerText] = useInnerText(''); + const innerText = useMemo(() => _innerText?.replace(/\n\n/g, '\n'), [ + _innerText, + ]); const [tabIndex, setTabIndex] = useState<-1 | 0>(-1); const combinedRef = useCombinedRefs([ innerTextRef, From 64b8418fe47eca2314ecdcc4de6d11ef5aab8dc0 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Wed, 7 Apr 2021 14:16:26 -0500 Subject: [PATCH 13/18] docs --- src-docs/src/views/code/code_example.js | 44 ++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src-docs/src/views/code/code_example.js b/src-docs/src/views/code/code_example.js index 1875b076cc5..af4a86cb793 100644 --- a/src-docs/src/views/code/code_example.js +++ b/src-docs/src/views/code/code_example.js @@ -4,7 +4,14 @@ import { renderToHtml } from '../../services'; import { GuideSectionTypes } from '../../components'; -import { EuiCode, EuiCodeBlock } from '../../../../src/components'; +import { + EuiCode, + EuiCodeBlock, + EuiLink, + EuiSpacer, + EuiText, + EuiTitle, +} from '../../../../src/components'; import { codeBlockConfig, codeConfig } from './playground'; import Code from './code'; @@ -26,6 +33,41 @@ const codeBlockPreHtml = renderToHtml(CodeBlockPre); export const CodeExample = { title: 'Code', + intro: ( + <> + + +

Language syntax options

+
+

+ The EuiCode and EuiCodeBlock{' '} + components support{' '} + + all language syntaxes + {' '} + supported by the + prism{' '} + + library + + . +

+
+ + + +

Language syntax distinctions

+
+

+ JSX code (often React) has distinct language syntaxes from the base + JavaScript and TypeScript languages. For instance, use{' '} + language="jsx" or{' '} + language="tsx" when the code in question + is JSX. +

+
+ + ), sections: [ { title: 'Inline', From 8fda593bae32d7318c9cd9e8ca36fb7b6933b2cb Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Wed, 7 Apr 2021 14:26:25 -0500 Subject: [PATCH 14/18] docs --- src-docs/src/views/code/code_example.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src-docs/src/views/code/code_example.js b/src-docs/src/views/code/code_example.js index af4a86cb793..13c5b448293 100644 --- a/src-docs/src/views/code/code_example.js +++ b/src-docs/src/views/code/code_example.js @@ -51,6 +51,9 @@ export const CodeExample = { library . +
+ The language prop can also be omitted to simply + render formatted but unhighlighted code.

From a111eeb3f71e18837346335590f167f89b4596f5 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Wed, 7 Apr 2021 15:16:18 -0500 Subject: [PATCH 15/18] headings --- src-docs/src/views/code/code_example.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src-docs/src/views/code/code_example.js b/src-docs/src/views/code/code_example.js index 13c5b448293..73274123fac 100644 --- a/src-docs/src/views/code/code_example.js +++ b/src-docs/src/views/code/code_example.js @@ -8,9 +8,7 @@ import { EuiCode, EuiCodeBlock, EuiLink, - EuiSpacer, EuiText, - EuiTitle, } from '../../../../src/components'; import { codeBlockConfig, codeConfig } from './playground'; @@ -36,9 +34,6 @@ export const CodeExample = { intro: ( <> - -

Language syntax options

-

The EuiCode and EuiCodeBlock{' '} components support{' '} @@ -55,12 +50,6 @@ export const CodeExample = { The language prop can also be omitted to simply render formatted but unhighlighted code.

-
- - - -

Language syntax distinctions

-

JSX code (often React) has distinct language syntaxes from the base JavaScript and TypeScript languages. For instance, use{' '} From 45dfed07d1c9cd1ad21bbb7355f1ce3648251d4c Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Tue, 13 Apr 2021 15:35:14 -0500 Subject: [PATCH 16/18] Update src-docs/src/views/code/code_example.js Co-authored-by: Caroline Horn <549577+cchaos@users.noreply.github.com> --- src-docs/src/views/code/code_example.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src-docs/src/views/code/code_example.js b/src-docs/src/views/code/code_example.js index 73274123fac..a9321aa55b1 100644 --- a/src-docs/src/views/code/code_example.js +++ b/src-docs/src/views/code/code_example.js @@ -52,10 +52,9 @@ export const CodeExample = {

JSX code (often React) has distinct language syntaxes from the base - JavaScript and TypeScript languages. For instance, use{' '} + JavaScript and TypeScript languages. For these instances, use{' '} language="jsx" or{' '} - language="tsx" when the code in question - is JSX. + language="tsx".

From 8fc69830e78718d3515d12da81a582784b1da55a Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Tue, 13 Apr 2021 15:52:10 -0500 Subject: [PATCH 17/18] playground updates --- src-docs/src/views/code/code_example.js | 3 ++- src-docs/src/views/code/playground.js | 12 ------------ 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src-docs/src/views/code/code_example.js b/src-docs/src/views/code/code_example.js index a9321aa55b1..a41b0e056ad 100644 --- a/src-docs/src/views/code/code_example.js +++ b/src-docs/src/views/code/code_example.js @@ -81,6 +81,7 @@ export const CodeExample = { snippet: codeSnippet, props: { EuiCode }, demo: , + playground: codeConfig, }, { title: 'Code block', @@ -105,6 +106,7 @@ export const CodeExample = { snippet: codeBlockSnippet, props: { EuiCodeBlock }, demo: , + playground: codeBlockConfig, }, { title: 'Code block and white-space', @@ -131,5 +133,4 @@ export const CodeExample = { demo: , }, ], - playground: [codeBlockConfig, codeConfig], }; diff --git a/src-docs/src/views/code/playground.js b/src-docs/src/views/code/playground.js index 1d9b7c56fd3..8067db896f0 100644 --- a/src-docs/src/views/code/playground.js +++ b/src-docs/src/views/code/playground.js @@ -20,12 +20,6 @@ export const codeBlockConfig = () => { hidden: false, }; - propsToUse.inline = { - ...propsToUse.inline, - type: PropTypes.Boolean, - value: false, - }; - return { config: { componentName: 'EuiCodeBlock', @@ -56,12 +50,6 @@ export const codeConfig = () => { hidden: false, }; - propsToUse.inline = { - ...propsToUse.inline, - type: PropTypes.Boolean, - value: false, - }; - return { config: { componentName: 'EuiCode', From ee4e68d1eff86a88a2fe0526485f5ce98cd72c68 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Thu, 29 Apr 2021 16:10:36 -0500 Subject: [PATCH 18/18] os newlines --- src/components/code/_code_block.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/code/_code_block.tsx b/src/components/code/_code_block.tsx index 9fe6134501c..f5b4493084c 100644 --- a/src/components/code/_code_block.tsx +++ b/src/components/code/_code_block.tsx @@ -77,12 +77,12 @@ const addLineData = ( return nodes.reduce((result, node) => { const lineStart = data.lineNumber; if (node.type === 'text') { - if (node.value.indexOf('\n') === -1) { + if (!node.value.match(/\r\n?|\n/)) { node.lineStart = lineStart; node.lineEnd = lineStart; result.push(node); } else { - const lines = node.value.split('\n'); + const lines = node.value.split(/\r\n?|\n/); lines.forEach((line, i) => { const num = i === 0 ? data.lineNumber : ++data.lineNumber; result.push({ @@ -219,9 +219,10 @@ export const EuiCodeBlockImpl: FunctionComponent = ({ const [isFullScreen, setIsFullScreen] = useState(false); const [wrapperRef, setWrapperRef] = useState(null); const [innerTextRef, _innerText] = useInnerText(''); - const innerText = useMemo(() => _innerText?.replace(/\n\n/g, '\n'), [ - _innerText, - ]); + const innerText = useMemo( + () => _innerText?.replace(/[\r\n?]{2}|\n\n/g, '\n'), + [_innerText] + ); const [tabIndex, setTabIndex] = useState<-1 | 0>(-1); const combinedRef = useCombinedRefs([ innerTextRef,