diff --git a/CHANGELOG.md b/CHANGELOG.md index c61f5471f14..e2d52e1bc6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## [`master`](https://github.com/elastic/eui/tree/master) +- Converted `EuiCode` and `EuiCodeBlock` and to Typescript ([#2835](https://github.com/elastic/eui/pull/2835)) - Converted `EuiFilePicker` to TypeScript ([#2832](https://github.com/elastic/eui/issues/2832)) - Exported `EuiSelectOptionProps` type ([#2830](https://github.com/elastic/eui/pull/2830)) - Added `paperClip` glyph to `EuiIcon` ([#2845](https://github.com/elastic/eui/pull/2845)) diff --git a/package.json b/package.json index c9affa3ddd2..6d77bfa6d9c 100644 --- a/package.json +++ b/package.json @@ -48,8 +48,8 @@ "@types/enzyme": "^3.1.13", "@types/lodash": "^4.14.116", "@types/numeral": "^0.0.25", - "@types/react-virtualized": "^9.18.7", "@types/react-beautiful-dnd": "^10.1.0", + "@types/react-virtualized": "^9.18.7", "chroma-js": "^2.0.4", "classnames": "^2.2.5", "highlight.js": "^9.12.0", @@ -85,6 +85,7 @@ "@svgr/core": "5.0.1", "@svgr/plugin-svgo": "^4.0.3", "@types/classnames": "^2.2.6", + "@types/highlight.js": "^9.12.3", "@types/jest": "^24.0.6", "@types/react": "^16.9.11", "@types/react-dom": "^16.9.4", diff --git a/scripts/jest/polyfills/mutation_observer.js b/scripts/jest/polyfills/mutation_observer.js index 7bf55f00a90..efd725e7d8d 100644 --- a/scripts/jest/polyfills/mutation_observer.js +++ b/scripts/jest/polyfills/mutation_observer.js @@ -524,7 +524,7 @@ var MutationNotifier = /** @class */ (function (_super) { __extends(MutationNotifier, _super); function MutationNotifier() { var _this = _super.call(this) || this; - _this.setMaxListeners(150); // bump this as needed - some tests do not perform the unmounting lifecycle + _this.setMaxListeners(294); // bump this as needed - some tests do not perform the unmounting lifecycle return _this; } MutationNotifier.getInstance = function () { diff --git a/src/components/code/__snapshots__/_code_block.test.js.snap b/src/components/code/__snapshots__/_code_block.test.tsx.snap similarity index 100% rename from src/components/code/__snapshots__/_code_block.test.js.snap rename to src/components/code/__snapshots__/_code_block.test.tsx.snap diff --git a/src/components/code/__snapshots__/code.test.js.snap b/src/components/code/__snapshots__/code.test.tsx.snap similarity index 100% rename from src/components/code/__snapshots__/code.test.js.snap rename to src/components/code/__snapshots__/code.test.tsx.snap diff --git a/src/components/code/__snapshots__/code_block.test.js.snap b/src/components/code/__snapshots__/code_block.test.tsx.snap similarity index 100% rename from src/components/code/__snapshots__/code_block.test.js.snap rename to src/components/code/__snapshots__/code_block.test.tsx.snap diff --git a/src/components/code/_code_block.test.js b/src/components/code/_code_block.test.tsx similarity index 95% rename from src/components/code/_code_block.test.js rename to src/components/code/_code_block.test.tsx index 236ab41b493..83bdae243c4 100644 --- a/src/components/code/_code_block.test.js +++ b/src/components/code/_code_block.test.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import { mount } from 'enzyme'; +import { mount, ReactWrapper } from 'enzyme'; import { requiredProps } from '../../test/required_props'; import { EuiCodeBlockImpl } from './_code_block'; -function snapshotCodeBlock(component) { +function snapshotCodeBlock(component: ReactWrapper) { // Get the Portal's sibling and return its html const renderedHtml = component.find('Portal + *').html(); const container = document.createElement('div'); diff --git a/src/components/code/_code_block.js b/src/components/code/_code_block.tsx similarity index 76% rename from src/components/code/_code_block.js rename to src/components/code/_code_block.tsx index 56129571c50..14ecd0993dc 100644 --- a/src/components/code/_code_block.js +++ b/src/components/code/_code_block.tsx @@ -1,6 +1,5 @@ -import React, { Component } from 'react'; +import React, { Component, KeyboardEvent, CSSProperties } from 'react'; import { createPortal } from 'react-dom'; -import PropTypes from 'prop-types'; import classNames from 'classnames'; import hljs from 'highlight.js'; @@ -15,6 +14,8 @@ import { EuiFocusTrap } from '../focus_trap'; import { keyCodes } from '../../services'; import { EuiI18n } from '../i18n'; import { EuiInnerText } from '../inner_text'; +import { keysOf } from '../common'; +import { FontSize, PaddingSize } from './code'; const fontSizeToClassNameMap = { s: 'euiCodeBlock--fontSmall', @@ -22,25 +23,57 @@ const fontSizeToClassNameMap = { l: 'euiCodeBlock--fontLarge', }; -export const FONT_SIZES = Object.keys(fontSizeToClassNameMap); +export const FONT_SIZES = keysOf(fontSizeToClassNameMap); -const paddingSizeToClassNameMap = { +const paddingSizeToClassNameMap: { [paddingSize in PaddingSize]: string } = { none: '', s: 'euiCodeBlock--paddingSmall', m: 'euiCodeBlock--paddingMedium', l: 'euiCodeBlock--paddingLarge', }; -export const PADDING_SIZES = Object.keys(paddingSizeToClassNameMap); +export const PADDING_SIZES = keysOf(paddingSizeToClassNameMap); + +interface Props { + className?: string; + fontSize: FontSize; + + /** + * Displays the passed code in an inline format. Also removes any margins set. + */ + inline?: boolean; + + /** + * Displays an icon button to copy the code snippet to the clipboard. + */ + isCopyable: boolean; + + /** + * Sets the syntax highlighting for a specific language + */ + language?: string; + overflowHeight?: number; + paddingSize: PaddingSize; + transparentBackground: boolean; +} + +interface State { + isFullScreen: boolean; +} /** - * This is the base component extended by EuiCode and EuiCodeBlock. These components - * share the same propTypes definition with EuiCodeBlockImpl. + * This is the base component extended by EuiCode and EuiCodeBlock. + * These components share the same propTypes definition with EuiCodeBlockImpl. */ -export class EuiCodeBlockImpl extends Component { - codeTarget = document.createElement('div'); +export class EuiCodeBlockImpl extends Component { + static defaultProps = { + transparentBackground: false, + paddingSize: 'l', + fontSize: 's', + isCopyable: false, + }; - constructor(props) { + constructor(props: Props) { super(props); this.state = { @@ -48,12 +81,18 @@ export class EuiCodeBlockImpl extends Component { }; } + codeTarget = document.createElement('div'); + code: HTMLElement | null = null; + codeFullScreen: HTMLElement | null = null; + highlight = () => { - // 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) + /** + * 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 = this.codeTarget.innerHTML; if (this.code) { @@ -64,7 +103,9 @@ export class EuiCodeBlockImpl extends Component { } if (this.props.language) { - hljs.highlightBlock(this.code); + if (this.code) { + hljs.highlightBlock(this.code); + } if (this.codeFullScreen) { hljs.highlightBlock(this.codeFullScreen); @@ -72,7 +113,7 @@ export class EuiCodeBlockImpl extends Component { } }; - onKeyDown = event => { + onKeyDown = (event: KeyboardEvent) => { if (event.keyCode === keyCodes.ESCAPE) { event.preventDefault(); event.stopPropagation(); @@ -128,7 +169,7 @@ export class EuiCodeBlockImpl extends Component { const codeClasses = classNames('euiCodeBlock__code', language); - const optionalStyles = {}; + const optionalStyles: CSSProperties = {}; if (overflowHeight) { optionalStyles.maxHeight = overflowHeight; @@ -158,14 +199,14 @@ export class EuiCodeBlockImpl extends Component { ); } - function getCopyButton(textToCopy) { - let copyButton; + const getCopyButton = (textToCopy?: string) => { + let copyButton: JSX.Element | undefined; - if (isCopyable) { + if (isCopyable && textToCopy) { copyButton = (
- {copyButton => ( + {(copyButton: string) => ( {copy => ( - {([fullscreenCollapse, fullscreenExpand]) => ( + {([fullscreenCollapse, fullscreenExpand]: string[]) => ( { let codeBlockControls; const copyButton = getCopyButton(textToCopy); @@ -226,17 +267,13 @@ export class EuiCodeBlockImpl extends Component { } return codeBlockControls; - } + }; - const getFullScreenDisplay = codeBlockControls => { + const getFullScreenDisplay = (codeBlockControls?: JSX.Element) => { let fullScreenDisplay; if (this.state.isFullScreen) { - { - /* - Force fullscreen to use large font and padding. - */ - } + // Force fullscreen to use large font and padding. const fullScreenClasses = classNames( 'euiCodeBlock', fontSizeToClassNameMap[fontSize], @@ -299,34 +336,3 @@ export class EuiCodeBlockImpl extends Component { ); } } - -EuiCodeBlockImpl.propTypes = { - children: PropTypes.node, - className: PropTypes.string, - paddingSize: PropTypes.oneOf(PADDING_SIZES), - - /** - * Sets the syntax highlighting for a specific language - */ - language: PropTypes.string, - overflowHeight: PropTypes.number, - fontSize: PropTypes.oneOf(FONT_SIZES), - transparentBackground: PropTypes.bool, - - /** - * Displays the passed code in an inline format. Also removes any margins set. - */ - inline: PropTypes.bool, - - /** - * Displays an icon button to copy the code snippet to the clipboard. - */ - isCopyable: PropTypes.bool, -}; - -EuiCodeBlockImpl.defaultProps = { - transparentBackground: false, - paddingSize: 'l', - fontSize: 's', - isCopyable: false, -}; diff --git a/src/components/code/code.js b/src/components/code/code.js deleted file mode 100644 index fed2a994970..00000000000 --- a/src/components/code/code.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; - -import { EuiCodeBlockImpl } from './_code_block'; - -export const EuiCode = ({ - inline, // eslint-disable-line - ...rest -}) => { - return ; -}; - -EuiCode.propTypes = { - ...EuiCodeBlockImpl.propTypes, -}; diff --git a/src/components/code/code.test.js b/src/components/code/code.test.tsx similarity index 86% rename from src/components/code/code.test.js rename to src/components/code/code.test.tsx index 4dee84b378d..0e91febc1c7 100644 --- a/src/components/code/code.test.js +++ b/src/components/code/code.test.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import { mount } from 'enzyme'; +import { mount, ReactWrapper } from 'enzyme'; import { requiredProps } from '../../test/required_props'; import { EuiCode } from './code'; -function snapshotCodeBlock(component) { +function snapshotCodeBlock(component: ReactWrapper) { // Get the Portal's sibling and return its html const renderedHtml = component.find('Portal + *').html(); const container = document.createElement('div'); diff --git a/src/components/code/code.tsx b/src/components/code/code.tsx new file mode 100644 index 00000000000..60e2477286e --- /dev/null +++ b/src/components/code/code.tsx @@ -0,0 +1,36 @@ +import { CommonProps } from '../common'; + +import React, { FunctionComponent, HTMLAttributes } from 'react'; + +import { EuiCodeBlockImpl } from './_code_block'; + +export type FontSize = 's' | 'm' | 'l'; +export type PaddingSize = 'none' | 's' | 'm' | 'l'; + +export interface EuiCodeSharedProps { + paddingSize?: PaddingSize; + + /** + * Sets the syntax highlighting for a specific language + * @see http://highlightjs.readthedocs.io/en/latest/css-classes-reference.html#language-names-and-aliases + * for options + */ + language?: string; + overflowHeight?: number; + fontSize?: FontSize; + transparentBackground?: boolean; + isCopyable?: boolean; +} + +interface Props extends EuiCodeSharedProps { + inline?: true; +} + +export type EuiCodeProps = CommonProps & Props & HTMLAttributes; + +export const EuiCode: FunctionComponent = ({ + inline, + ...rest +}) => { + return ; +}; diff --git a/src/components/code/code_block.js b/src/components/code/code_block.js deleted file mode 100644 index 3df1bc38251..00000000000 --- a/src/components/code/code_block.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; - -import { EuiCodeBlockImpl } from './_code_block'; - -export const EuiCodeBlock = ({ - inline, // eslint-disable-line - ...rest -}) => { - return ; -}; - -EuiCodeBlock.propTypes = { - ...EuiCodeBlockImpl.propTypes, -}; diff --git a/src/components/code/code_block.test.js b/src/components/code/code_block.test.tsx similarity index 97% rename from src/components/code/code_block.test.js rename to src/components/code/code_block.test.tsx index 66e92dd16fd..8b6c8b7ab4e 100644 --- a/src/components/code/code_block.test.js +++ b/src/components/code/code_block.test.tsx @@ -1,13 +1,13 @@ import React, { useState, useEffect } from 'react'; import ReactDOM from 'react-dom'; -import { mount } from 'enzyme'; +import { mount, ReactWrapper } from 'enzyme'; import html from 'html'; import { requiredProps } from '../../test/required_props'; import { EuiCodeBlock } from './code_block'; import { FONT_SIZES, PADDING_SIZES } from './_code_block'; -function snapshotCodeBlock(component) { +function snapshotCodeBlock(component: ReactWrapper) { // Get the Portal's sibling and return its html const renderedHtml = component.find('Portal + *').html(); const container = document.createElement('div'); diff --git a/src/components/code/code_block.tsx b/src/components/code/code_block.tsx new file mode 100644 index 00000000000..01f766f6db7 --- /dev/null +++ b/src/components/code/code_block.tsx @@ -0,0 +1,20 @@ +import React, { FunctionComponent, HTMLAttributes } from 'react'; +import { CommonProps } from '../common'; + +import { EuiCodeBlockImpl } from './_code_block'; +import { EuiCodeSharedProps } from './code'; + +interface OwnProps extends EuiCodeSharedProps { + inline?: false; +} + +export type EuiCodeBlockProps = CommonProps & + OwnProps & + HTMLAttributes; + +export const EuiCodeBlock: FunctionComponent = ({ + inline, + ...rest +}) => { + return ; +}; diff --git a/src/components/code/html.d.ts b/src/components/code/html.d.ts new file mode 100644 index 00000000000..e097089dcea --- /dev/null +++ b/src/components/code/html.d.ts @@ -0,0 +1 @@ +declare module 'html'; diff --git a/src/components/code/index.d.ts b/src/components/code/index.d.ts deleted file mode 100644 index 480715659dc..00000000000 --- a/src/components/code/index.d.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { CommonProps } from '../common'; - -import { FunctionComponent, HTMLAttributes } from 'react'; - -declare module '@elastic/eui' { - type FontSize = 's' | 'm' | 'l'; - type PaddingSize = 'none' | 's' | 'm' | 'l'; - - // there isn't a specific type for the element, and MDN - // says that it only supports the HTMLElement interface - // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code - type HTMLCodeElement = HTMLElement; - - interface EuiCodeSharedProps { - paddingSize?: PaddingSize; - - /** - * Sets the syntax highlighting for a specific language - * See http://highlightjs.readthedocs.io/en/latest/css-classes-reference.html#language-names-and-aliases - * for options - */ - language?: string; - - overflowHeight?: number; - fontSize?: FontSize; - transparentBackground?: boolean; - isCopyable?: boolean; - } - - /** - * EuiCode type defs - * - * @see './code.js' - */ - - export interface EuiCodeProps extends EuiCodeSharedProps { - inline?: true; - } - - export const EuiCode: FunctionComponent< - CommonProps & EuiCodeProps & HTMLAttributes - >; - - /** - * EuiCodeBlock type defs - * - * @see './code_block.js' - */ - - export interface EuiCodeBlockProps extends EuiCodeSharedProps { - inline?: false; - } - - export const EuiCodeBlock: FunctionComponent< - CommonProps & EuiCodeBlockProps & HTMLAttributes - >; -} diff --git a/src/components/code/index.js b/src/components/code/index.js deleted file mode 100644 index bf0d4cd4a87..00000000000 --- a/src/components/code/index.js +++ /dev/null @@ -1,5 +0,0 @@ -export { EuiCode } from './code'; - -export { EuiCodeBlock } from './code_block'; - -export { EuiCodeBlockImpl } from './_code_block'; diff --git a/src/components/code/index.ts b/src/components/code/index.ts new file mode 100644 index 00000000000..fa0b2253380 --- /dev/null +++ b/src/components/code/index.ts @@ -0,0 +1,3 @@ +export { EuiCode, EuiCodeProps } from './code'; +export { EuiCodeBlock, EuiCodeBlockProps } from './code_block'; +export { EuiCodeBlockImpl } from './_code_block'; diff --git a/src/components/index.d.ts b/src/components/index.d.ts index 02be59523be..24be6528a3f 100644 --- a/src/components/index.d.ts +++ b/src/components/index.d.ts @@ -1,4 +1,3 @@ -/// /// /// /// diff --git a/yarn.lock b/yarn.lock index 69accd29c94..3b78ccecb33 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1241,6 +1241,11 @@ resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== +"@types/highlight.js@^9.12.3": + version "9.12.3" + resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-9.12.3.tgz#b672cfaac25cbbc634a0fd92c515f66faa18dbca" + integrity sha512-pGF/zvYOACZ/gLGWdQH8zSwteQS1epp68yRcVLJMgUck/MjEn/FBYmPub9pXT8C1e4a8YZfHo1CKyV8q1vKUnQ== + "@types/jest-diff@*": version "20.0.1" resolved "https://registry.yarnpkg.com/@types/jest-diff/-/jest-diff-20.0.1.tgz#35cc15b9c4f30a18ef21852e255fdb02f6d59b89"