diff --git a/packages/insomnia/src/ui/components/base/dropdown/dropdown.tsx b/packages/insomnia/src/ui/components/base/dropdown/dropdown.tsx index 2d995b601f..152e5b1554 100644 --- a/packages/insomnia/src/ui/components/base/dropdown/dropdown.tsx +++ b/packages/insomnia/src/ui/components/base/dropdown/dropdown.tsx @@ -101,11 +101,13 @@ export class Dropdown extends PureComponent { continue; } - const match = fuzzyMatch(newFilter, listItem.textContent); + const match = fuzzyMatch(newFilter, listItem.textContent || ''); if (!newFilter || match) { const filterIndex = listItem.getAttribute('data-filter-index'); - filterItems.push(parseInt(filterIndex, 10)); + if (filterIndex) { + filterItems.push(parseInt(filterIndex, 10)); + } } } @@ -131,7 +133,9 @@ export class Dropdown extends PureComponent { for (const li of this._dropdownList.querySelectorAll('li')) { if (li.hasAttribute('data-filter-index')) { const filterIndex = li.getAttribute('data-filter-index'); - items.push(parseInt(filterIndex, 10)); + if (filterIndex) { + items.push(parseInt(filterIndex, 10)); + } } } } diff --git a/packages/insomnia/src/ui/components/editors/body/graph-ql-editor.tsx b/packages/insomnia/src/ui/components/editors/body/graph-ql-editor.tsx index b5b41a1c9a..dd66ef66d0 100644 --- a/packages/insomnia/src/ui/components/editors/body/graph-ql-editor.tsx +++ b/packages/insomnia/src/ui/components/editors/body/graph-ql-editor.tsx @@ -15,7 +15,7 @@ import React, { PureComponent } from 'react'; import ReactDOM from 'react-dom'; import { SetRequired } from 'type-fest'; -import { AUTOBIND_CFG, CONTENT_TYPE_JSON, DEBOUNCE_MILLIS } from '../../../../common/constants'; +import { AUTOBIND_CFG, CONTENT_TYPE_JSON } from '../../../../common/constants'; import { database as db } from '../../../../common/database'; import { hotKeyRefs } from '../../../../common/hotkeys'; import { executeHotKey } from '../../../../common/hotkeys-listener'; @@ -811,7 +811,6 @@ export class GraphQLEditor extends PureComponent { dynamicHeight enableNunjucks uniquenessKey={uniquenessKey ? uniquenessKey + '::variables' : undefined} - debounceMillis={DEBOUNCE_MILLIS * 4} manualPrettify={false} defaultValue={variables} className={className} diff --git a/packages/insomnia/src/ui/components/markdown-editor.tsx b/packages/insomnia/src/ui/components/markdown-editor.tsx index 2cb14559a4..c7fb02320b 100644 --- a/packages/insomnia/src/ui/components/markdown-editor.tsx +++ b/packages/insomnia/src/ui/components/markdown-editor.tsx @@ -86,7 +86,6 @@ export class MarkdownEditor extends PureComponent { enableNunjucks mode={mode || 'text/x-markdown'} placeholder={placeholder} - debounceMillis={300} defaultValue={markdown} onChange={this._handleChange} /> diff --git a/packages/insomnia/src/ui/components/markdown-preview.tsx b/packages/insomnia/src/ui/components/markdown-preview.tsx index ca0946feaf..59fe8af3c7 100644 --- a/packages/insomnia/src/ui/components/markdown-preview.tsx +++ b/packages/insomnia/src/ui/components/markdown-preview.tsx @@ -1,10 +1,7 @@ -import { autoBindMethodsForReact } from 'class-autobind-decorator'; import classnames from 'classnames'; import highlight from 'highlight.js/lib/common'; -import React, { FC, PureComponent } from 'react'; -import ReactDOM from 'react-dom'; +import React, { FC, useEffect, useLayoutEffect, useRef, useState } from 'react'; -import { AUTOBIND_CFG } from '../../common/constants'; import { clickLink } from '../../common/electron-helpers'; import { markdownToHTML } from '../../common/markdown-to-html'; import { HandleRender } from '../../common/render'; @@ -14,122 +11,60 @@ interface Props { markdown: string; handleRender?: HandleRender; className?: string; - debounceMillis?: number; heading?: string; } -interface State { - compiled: string; - renderError: string; -} +export const MarkdownPreview: FC = ({ markdown, className, heading }) => { + const divRef = useRef(null); + const [compiled, setCompiled] = useState(''); + const [error, setError] = useState(''); + const { handleRender } = useGatedNunjucks(); -@autoBindMethodsForReact(AUTOBIND_CFG) -class MarkdownPreviewInternal extends PureComponent { - state: State = { - compiled: '', - renderError: '', - }; - - _compileTimeout: NodeJS.Timeout | null = null; - _preview: HTMLDivElement | null = null; - - /** - * Debounce and compile the markdown (won't debounce first render) - */ - _compileMarkdown(markdown: string) { - if (this._compileTimeout !== null) { - clearTimeout(this._compileTimeout); - } - this._compileTimeout = setTimeout( - async () => { - try { - const { handleRender } = this.props; - const rendered = handleRender ? await handleRender(markdown) : markdown; - const compiled = markdownToHTML(rendered); - this.setState({ - compiled, - renderError: '', - }); - } catch (err) { - this.setState({ - renderError: err.message, - compiled: '', - }); - } - }, - this.state.compiled ? this.props.debounceMillis : 0, - ); - } - - _setPreviewRef(preview: HTMLDivElement) { - this._preview = preview; - } - - _handleClickLink(event: any) { - event.preventDefault(); - clickLink(event.target.getAttribute('href')); - } - - _highlightCodeBlocks() { - if (!this._preview) { + useEffect(() => { + let shouldUpdate = true; + const fn = async () => { + try { + const rendered = handleRender ? await handleRender(markdown) : markdown; + const compiled = markdownToHTML(rendered); + shouldUpdate && setCompiled(compiled); + shouldUpdate && setError(''); + } catch (err) { + shouldUpdate && setCompiled(''); + shouldUpdate && setError(err.message); + } + }; + fn(); + return () => { + shouldUpdate = false; + }; + }, [handleRender, markdown]); + useLayoutEffect(() => { + if (!divRef.current) { return; } - - const el = ReactDOM.findDOMNode(this._preview); - - // @ts-expect-error -- TSCONVERSION - for (const block of el.querySelectorAll('pre > code')) { - highlight.highlightElement(block); + for (const block of divRef.current.querySelectorAll('pre > code')) { + if (block instanceof HTMLElement) { + highlight.highlightElement(block); + } } - - // @ts-expect-error -- TSCONVERSION - for (const a of el.querySelectorAll('a')) { + for (const a of divRef.current.querySelectorAll('a')) { a.title = `Open ${a.getAttribute('href')} in browser`; - a.removeEventListener('click', this._handleClickLink); - a.addEventListener('click', this._handleClickLink); + a.removeEventListener('click', _handleClickLink); + a.addEventListener('click', _handleClickLink); } - } + }, [compiled]); + const _handleClickLink = (event: any) => { + event.preventDefault(); + clickLink(event.target.getAttribute('href')); + }; - componentWillUnmount() { - if (this._compileTimeout !== null) { - clearTimeout(this._compileTimeout); - } - } - - // eslint-disable-next-line camelcase - UNSAFE_componentWillMount() { - this._compileMarkdown(this.props.markdown); - } - - // eslint-disable-next-line camelcase - UNSAFE_componentWillReceiveProps(nextProps: Props) { - this._compileMarkdown(nextProps.markdown); - } - - componentDidUpdate() { - this._highlightCodeBlocks(); - } - - componentDidMount() { - this._highlightCodeBlocks(); - } - - render() { - const { className, heading } = this.props; - const { compiled, renderError } = this.state; - return ( -
- {renderError &&

Failed to render: {renderError}

} -
- {heading ?

{heading}

: null} -
-
+ return ( +
+ {error ?

Failed to render: {error}

: null} +
+ {heading ?

{heading}

: null} +
- ); - } -} - -export const MarkdownPreview: FC> = props => { - const { handleRender } = useGatedNunjucks(); - return ; +
+ ); }; diff --git a/packages/insomnia/src/ui/components/panes/request-pane.tsx b/packages/insomnia/src/ui/components/panes/request-pane.tsx index 1e96f1cf30..64d82e594e 100644 --- a/packages/insomnia/src/ui/components/panes/request-pane.tsx +++ b/packages/insomnia/src/ui/components/panes/request-pane.tsx @@ -301,7 +301,6 @@ export const RequestPane: FC = ({ diff --git a/packages/insomnia/tsconfig.build.json b/packages/insomnia/tsconfig.build.json index 21bb683e54..dad95363ac 100644 --- a/packages/insomnia/tsconfig.build.json +++ b/packages/insomnia/tsconfig.build.json @@ -1,6 +1,7 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { + "lib": ["DOM.Iterable"], "experimentalDecorators": true, "isolatedModules": true, "jsx": "react",