Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add types to markdown editor #3703

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
"remark-emoji": "^2.1.0",
"remark-highlight.js": "^5.2.0",
"remark-parse": "^7.0.2",
"remark-rehype": "^6.0.0",
"remark-rehype": "^7.0.0",
"resize-observer-polyfill": "^1.5.0",
"tabbable": "^3.0.0",
"text-diff": "^1.0.1",
Expand Down
2 changes: 1 addition & 1 deletion scripts/babel/fetch-i18n-strings.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ const files = glob.sync(
'**/*.@(js|ts|tsx)',
{ cwd: srcDir, realpath: true },
).filter(filepath => {
if (filepath.endsWith('index.d.ts')) return false;
if (filepath.endsWith('.d.ts')) return false;
if (filepath.endsWith('test.ts')) return false;
if (filepath.endsWith('test.tsx')) return false;
if (filepath.endsWith('test.js')) return false;
Expand Down
8 changes: 8 additions & 0 deletions src/components/markdown_editor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,11 @@ export {
defaultProcessingPlugins,
} from './markdown_editor';
export { EuiMarkdownContext } from './markdown_context';
export {
EuiMarkdownParseError,
EuiMarkdownAstNode,
EuiMarkdownAstNodePosition,
EuiMarkdownFormatting,
EuiMarkdownEditorUiPlugin,
RemarkRehypeHandler,
} from './markdown_types';
19 changes: 8 additions & 11 deletions src/components/markdown_editor/markdown_actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,12 @@ class MarkdownActions {
...defaults,
...incomingStyle,
};
const editor = document.getElementById(this.editorID);
const editor = document.getElementById(
this.editorID
) as HTMLTextAreaElement;

if (editor) {
editor.focus();
// @ts-ignore TODO
styleSelectedText(editor, outgoingStyle);
}
}
Expand Down Expand Up @@ -262,15 +263,11 @@ export function insertText(
* https://hustle.bizongo.in/simulate-react-on-change-on-controlled-components-baa336920e04
*/
const inputEvent = new Event('input', { bubbles: true });
const nativeInputValueSetter =
// @ts-ignore TODO
Object.getOwnPropertyDescriptor(
// @ts-ignore TODO
window.HTMLTextAreaElement.prototype,
'value'
).set;
// @ts-ignore TODO
nativeInputValueSetter.call(textarea, before + text + after);
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLTextAreaElement.prototype,
'value'
)!.set;
nativeInputValueSetter!.call(textarea, before + text + after);
textarea.dispatchEvent(inputEvent);
}

Expand Down
29 changes: 13 additions & 16 deletions src/components/markdown_editor/markdown_editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,9 @@ import React, {
import unified, { PluggableList, Processor } from 'unified';
import { VFileMessage } from 'vfile-message';
import classNames from 'classnames';
// @ts-ignore TODO
import emoji from 'remark-emoji';
import markdown from 'remark-parse';
// @ts-ignore TODO
import remark2rehype from 'remark-rehype';
// @ts-ignore TODO
import highlight from 'remark-highlight.js';
import rehype2react from 'rehype-react';

Expand All @@ -49,9 +46,13 @@ import { EuiMarkdownFormat } from './markdown_format';
import { EuiMarkdownEditorDropZone } from './markdown_editor_drop_zone';
import { htmlIdGenerator } from '../../services/accessibility';
import { EuiLink } from '../link';
import { EuiCodeBlock } from '../code';
import { EuiCodeBlock, EuiCodeBlockProps } from '../code';
import { MARKDOWN_MODE, MODE_EDITING, MODE_VIEWING } from './markdown_modes';
import { EuiMarkdownEditorUiPlugin } from './markdown_types';
import {
EuiMarkdownAstNode,
EuiMarkdownEditorUiPlugin,
EuiMarkdownParseError,
} from './markdown_types';
import { EuiOverlayMask } from '../overlay_mask';
import { EuiModal } from '../modal';
import { ContextShape, EuiMarkdownContext } from './markdown_context';
Expand Down Expand Up @@ -83,7 +84,7 @@ export const defaultProcessingPlugins: PluggableList = [
createElement: createElement,
components: {
a: EuiLink,
code: (props: any) =>
code: (props: EuiCodeBlockProps) =>
// if has classNames is a codeBlock using highlight js
props.className ? (
<EuiCodeBlock {...props} />
Expand Down Expand Up @@ -121,15 +122,15 @@ type CommonMarkdownEditorProps = HTMLAttributes<HTMLDivElement> &
/** array of toolbar plugins **/
uiPlugins?: EuiMarkdownEditorUiPlugin[];

/** Errors to buble up */
errors?: any;
/** Errors to bubble up */
errors?: EuiMarkdownParseError[];

/** callback triggered when parsing results are available **/
onParse?: (
error: any | null,
error: EuiMarkdownParseError | null,
data: {
messages: VFileMessage[];
ast: any;
ast: EuiMarkdownAstNode;
}
) => void;
};
Expand Down Expand Up @@ -193,7 +194,7 @@ export const EuiMarkdownEditor: FunctionComponent<
}, [parsingPluginList]);

const [parsed, parseError] = useMemo<
[any | null, VFileMessage | null]
[any | null, EuiMarkdownParseError | null]
>(() => {
try {
const parsed = parser.processSync(value);
Expand Down Expand Up @@ -242,7 +243,7 @@ export const EuiMarkdownEditor: FunctionComponent<
const getCursorNode = () => {
const { selectionStart } = textareaRef.current!;

let node: any = parsed.contents;
let node: EuiMarkdownAstNode = parsed.contents;

outer: while (true) {
if (node.children) {
Expand Down Expand Up @@ -335,21 +336,17 @@ export const EuiMarkdownEditor: FunctionComponent<
{createElement(pluginEditorPlugin.editor!, {
node:
selectedNode &&
// @ts-ignore TODO
selectedNode.type === pluginEditorPlugin.name
? selectedNode
: null,
onCancel: () => setPluginEditorPlugin(undefined),
onSave: markdown => {
if (
selectedNode &&
// @ts-ignore TODO
selectedNode.type === pluginEditorPlugin.name
) {
textareaRef.current!.setSelectionRange(
// @ts-ignore TODO
selectedNode.position.start.offset,
// @ts-ignore TODO
selectedNode.position.end.offset
);
}
Expand Down
7 changes: 5 additions & 2 deletions src/components/markdown_editor/markdown_editor_drop_zone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ import React, { FunctionComponent } from 'react';
import classNames from 'classnames';
import { useDropzone } from 'react-dropzone';
import { EuiMarkdownEditorFooter } from './markdown_editor_footer';
import { EuiMarkdownEditorUiPlugin } from './markdown_types';
import {
EuiMarkdownEditorUiPlugin,
EuiMarkdownParseError,
} from './markdown_types';

interface EuiMarkdownEditorDropZoneProps {
uiPlugins: EuiMarkdownEditorUiPlugin[];
errors: any;
errors: EuiMarkdownParseError[];
}

export const EuiMarkdownEditorDropZone: FunctionComponent<
Expand Down
9 changes: 6 additions & 3 deletions src/components/markdown_editor/markdown_editor_footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ import { EuiOverlayMask } from '../overlay_mask';
import { EuiTitle } from '../title';
import { EuiModal, EuiModalBody, EuiModalHeader } from '../modal';
import { EuiI18n } from '../i18n';
import { EuiMarkdownEditorUiPlugin } from './markdown_types';
import {
EuiMarkdownEditorUiPlugin,
EuiMarkdownParseError,
} from './markdown_types';
import { EuiPopover, EuiPopoverTitle } from '../popover';
import { EuiText } from '../text';
import { EuiSpacer } from '../spacer';
Expand All @@ -41,7 +44,7 @@ interface EuiMarkdownEditorFooterProps {
uiPlugins: EuiMarkdownEditorUiPlugin[];
isUploadingFiles: boolean;
openFiles: () => void;
errors: any;
errors: EuiMarkdownParseError[];
}

export const EuiMarkdownEditorFooter: FunctionComponent<
Expand Down Expand Up @@ -97,7 +100,7 @@ export const EuiMarkdownEditorFooter: FunctionComponent<
default="Errors"
/>
</EuiPopoverTitle>
{errors.map((message: any, idx: any) => (
{errors.map((message, idx) => (
<EuiText key={idx}>{message.toString()}</EuiText>
))}
</div>
Expand Down
11 changes: 8 additions & 3 deletions src/components/markdown_editor/markdown_editor_toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@
* under the License.
*/

import React, { FunctionComponent, HTMLAttributes, useContext } from 'react';
import React, {
FunctionComponent,
HTMLAttributes,
MouseEventHandler,
useContext,
} from 'react';
import { CommonProps } from '../common';
import { EuiButtonEmpty, EuiButtonIcon } from '../button';
import { EuiFlexItem, EuiFlexGroup } from '../flex';
Expand All @@ -27,15 +32,15 @@ import { MARKDOWN_MODE, MODE_VIEWING } from './markdown_modes';
import { EuiMarkdownEditorUiPlugin } from './markdown_types';
import { EuiMarkdownContext } from './markdown_context';
import MarkdownActions from './markdown_actions';
// @ts-ignore TODO
// @ts-ignore a react svg
import MarkdownCheckmarkIcon from './icons/markdown_checkmark';

export type EuiMarkdownEditorToolbarProps = HTMLAttributes<HTMLDivElement> &
CommonProps & {
selectedNode?: null | any;
markdownActions: MarkdownActions;
viewMode: MARKDOWN_MODE;
onClickPreview: any;
onClickPreview: MouseEventHandler<HTMLButtonElement>;
uiPlugins: EuiMarkdownEditorUiPlugin[];
};

Expand Down
33 changes: 27 additions & 6 deletions src/components/markdown_editor/markdown_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@

import { ComponentType, ReactNode } from 'react';
import { VFile } from 'vfile';
// eslint-disable-next-line import/no-unresolved
import { Node as UnistNode, Position as UnistPosition } from 'unist';
import { Parser } from 'remark-parse';
import { VFileMessage } from 'vfile-message';
import { IconType } from '../icon';

export interface RemarkParser {
Parser: any;
Parser: typeof Parser;
tokenizeInline: Function;
file: VFile;
}
Expand All @@ -38,12 +42,17 @@ export interface RemarkTokenizer {

notInLink?: boolean;
}
export interface RemarkRehypeHandler {
(h: any, node: any): any;
interface RehypeNode {}
interface RemarkRehypeHandlerCallback {
(
node: UnistPosition,
tagName: string,
props: Object,
children: RehypeNode[]
): RehypeNode;
}
export interface AstNodePosition {
start: { line: number; column: number; offset: number };
end: { line: number; column: number; offset: number };
export interface RemarkRehypeHandler {
(h: RemarkRehypeHandlerCallback, node: UnistNode): RehypeNode;
}

export interface EuiMarkdownEditorUiPluginEditorProps {
Expand Down Expand Up @@ -90,3 +99,15 @@ export interface EuiMarkdownFormatting {
orderedList?: boolean;
trimFirst?: boolean;
}

export interface EuiMarkdownAstNode {
type: string;
children?: EuiMarkdownAstNode[];
position: EuiMarkdownAstNodePosition;
}
export interface EuiMarkdownAstNodePosition {
start: { line: number; column: number; offset: number };
end: { line: number; column: number; offset: number };
}

export type EuiMarkdownParseError = string | VFileMessage | Error;
13 changes: 6 additions & 7 deletions src/components/markdown_editor/plugins/markdown_checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,16 @@
*/

import React, { FunctionComponent, useContext } from 'react';
// @ts-ignore TODO
import all from 'mdast-util-to-hast/lib/all';
import { EuiCheckbox } from '../../form/checkbox';
import { EuiMarkdownContext } from '../markdown_context';
import { htmlIdGenerator } from '../../../services/accessibility';
import {
AstNodePosition,
RemarkParser,
EuiMarkdownAstNodePosition,
RemarkRehypeHandler,
RemarkTokenizer,
} from '../markdown_types';
import { Plugin } from 'unified';

interface CheckboxNodeDetails {
type: 'checkboxPlugin';
Expand All @@ -37,7 +36,7 @@ interface CheckboxNodeDetails {
isChecked: boolean;
}

function CheckboxParser(this: RemarkParser) {
const CheckboxParser: Plugin = function CheckboxParser() {
const Parser = this.Parser;
const tokenizers = Parser.prototype.blockTokenizers;
const methods = Parser.prototype.blockMethods;
Expand Down Expand Up @@ -80,13 +79,13 @@ function CheckboxParser(this: RemarkParser) {

tokenizers.checkbox = tokenizeCheckbox;
methods.splice(methods.indexOf('list'), 0, 'checkbox'); // Run it just before default `list` plugin to inject our own idea of checkboxes.
}
};

const checkboxMarkdownHandler: RemarkRehypeHandler = (h, node) => {
return h(node.position, 'checkboxPlugin', node, all(h, node));
return h(node.position!, 'checkboxPlugin', node, all(h, node));
};
const CheckboxMarkdownRenderer: FunctionComponent<
CheckboxNodeDetails & { position: AstNodePosition }
CheckboxNodeDetails & { position: EuiMarkdownAstNodePosition }
> = ({ position, lead, label, isChecked, children }) => {
const { replaceNode } = useContext(EuiMarkdownContext);
return (
Expand Down
13 changes: 6 additions & 7 deletions src/components/markdown_editor/plugins/markdown_tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,15 @@
*/

import React, { FunctionComponent } from 'react';
// @ts-ignore TODO
import all from 'mdast-util-to-hast/lib/all';
import {
AstNodePosition,
RemarkParser,
EuiMarkdownAstNodePosition,
RemarkRehypeHandler,
RemarkTokenizer,
} from '../markdown_types';
import { EuiToolTip } from '../../tool_tip';
import { EuiCodeBlock } from '../../code';
import { Plugin } from 'unified';

interface TooltipNodeDetails {
type: 'tooltipPlugin';
Expand All @@ -52,7 +51,7 @@ const tooltipPlugin = {
),
};

function TooltipParser(this: RemarkParser) {
const TooltipParser: Plugin = function TooltipParser() {
const Parser = this.Parser;
const tokenizers = Parser.prototype.inlineTokenizers;
const methods = Parser.prototype.inlineMethods;
Expand Down Expand Up @@ -135,13 +134,13 @@ function TooltipParser(this: RemarkParser) {

tokenizers.tooltip = tokenizeTooltip;
methods.splice(methods.indexOf('text'), 0, 'tooltip');
}
};

const tooltipMarkdownHandler: RemarkRehypeHandler = (h, node) => {
return h(node.position, 'tooltipPlugin', node, all(h, node));
return h(node.position!, 'tooltipPlugin', node, all(h, node));
};
const tooltipMarkdownRenderer: FunctionComponent<
TooltipNodeDetails & { position: AstNodePosition }
TooltipNodeDetails & { position: EuiMarkdownAstNodePosition }
> = ({ content, children }) => {
return (
<EuiToolTip content={content}>
Expand Down
Loading