From 8f2dfd0d34041b65b5730bd212792b245b266301 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 25 Oct 2021 15:06:30 +0300 Subject: [PATCH] simplify validators, provide error to InfoPanel via React.context Signed-off-by: Roman --- .../components/dock/dock-tab-content.tsx | 41 +++++++++++-------- .../components/dock/dock-tab-context.tsx | 28 +++++++++++++ src/renderer/components/dock/info-panel.tsx | 13 ++++-- .../monaco-editor/monaco-editor.tsx | 26 +++++------- .../monaco-editor/monaco-validators.ts | 8 ++-- 5 files changed, 76 insertions(+), 40 deletions(-) create mode 100644 src/renderer/components/dock/dock-tab-context.tsx diff --git a/src/renderer/components/dock/dock-tab-content.tsx b/src/renderer/components/dock/dock-tab-content.tsx index 268468669a..02a6e6ebde 100644 --- a/src/renderer/components/dock/dock-tab-content.tsx +++ b/src/renderer/components/dock/dock-tab-content.tsx @@ -20,20 +20,21 @@ */ import styles from "./dock-tab-content.module.css"; +import throttle from "lodash/throttle"; import React from "react"; +import { action, makeObservable, observable, reaction } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; -import { makeObservable, observable, reaction } from "mobx"; import { dockStore, TabId } from "./dock.store"; import { cssNames } from "../../utils"; -import { MonacoEditor } from "../monaco-editor"; -import throttle from "lodash/throttle"; +import { MonacoEditor, MonacoEditorProps } from "../monaco-editor"; +import { DockTabContext } from "./dock-tab-context"; export interface DockTabContentProps extends React.HTMLAttributes { tabId: TabId; className?: string; withEditor?: boolean; - editorValue?: string; - editorOnChange?(value: string): void; + editorValue?: MonacoEditorProps["value"]; + editorOnChange?: MonacoEditorProps["onChange"]; } @observer @@ -56,24 +57,30 @@ export class DockTabContent extends React.Component { render() { const { className, tabId, withEditor, editorValue, editorOnChange } = this.props; + const { error } = this; if (!tabId) return null; return (
- {this.props.children} + + {this.props.children} - {withEditor && ( - this.error = error} - ref={monaco => this.editor = monaco} - /> - )} + {withEditor && ( + { + this.error = ""; // reset first + editorOnChange?.(value, evt); + })} + onError={error => this.error = String(error)} + onModelChange={() => this.error = ""} + ref={monaco => this.editor = monaco}> + )} +
); } diff --git a/src/renderer/components/dock/dock-tab-context.tsx b/src/renderer/components/dock/dock-tab-context.tsx new file mode 100644 index 0000000000..aaea2ff9be --- /dev/null +++ b/src/renderer/components/dock/dock-tab-context.tsx @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import React from "react"; + +export const DockTabContext = React.createContext({}); + +export interface DockTabContextValue { + error?: string; +} diff --git a/src/renderer/components/dock/info-panel.tsx b/src/renderer/components/dock/info-panel.tsx index 5cb16d0e62..9275ca72aa 100644 --- a/src/renderer/components/dock/info-panel.tsx +++ b/src/renderer/components/dock/info-panel.tsx @@ -29,6 +29,7 @@ import { Icon } from "../icon"; import { Spinner } from "../spinner"; import { dockStore, TabId } from "./dock.store"; import { Notifications } from "../notifications"; +import { DockTabContext, DockTabContextValue } from "./dock-tab-context"; export interface InfoPanelProps { tabId: TabId; @@ -59,6 +60,12 @@ const defaultProps: Partial = { @observer export class InfoPanel extends Component { static defaultProps = defaultProps as object; + static contextType = DockTabContext; + declare context: DockTabContextValue; + + get error() { + return this.props.error ?? this.context.error; + } @observable waiting = false; @@ -103,15 +110,13 @@ export class InfoPanel extends Component { }; renderErrorIcon(): React.ReactNode { - const { error } = this.props; - - if (!error || !this.props.showInlineInfo) { + if (!this.error || !this.props.showInlineInfo) { return null; } return (
- +
); } diff --git a/src/renderer/components/monaco-editor/monaco-editor.tsx b/src/renderer/components/monaco-editor/monaco-editor.tsx index a01336339e..1292545d3a 100644 --- a/src/renderer/components/monaco-editor/monaco-editor.tsx +++ b/src/renderer/components/monaco-editor/monaco-editor.tsx @@ -46,9 +46,10 @@ export interface MonacoEditorProps { language?: "yaml" | "json"; // configure bundled list of languages in via MonacoWebpackPlugin({languages: []}) options?: Partial; // customize editor's initialization options onChange?(value: string, evt: editor.IModelContentChangedEvent): void; // catch latest value updates - onError?(error?: string): void; // provide syntax validation errors, etc. + onError?(error?: Error | unknown): void; // provide syntax validation error, etc. onDidLayoutChange?(info: editor.EditorLayoutInfo): void; onDidContentSizeChange?(evt: editor.IContentSizeChangedEvent): void; + onModelChange?(model: editor.ITextModel, prev?: editor.ITextModel): void; } export const defaultEditorProps: Partial = { @@ -129,6 +130,7 @@ export class MonacoEditor extends React.Component { this.restoreViewState(model); this.editor.layout(); this.editor.focus(); // keep focus in editor, e.g. when clicking between dock-tabs + this.props.onModelChange?.(model, oldModel); this.validateLazy(); }; @@ -254,21 +256,17 @@ export class MonacoEditor extends React.Component { } @action - validate = async (value = this.getValue()): Promise => { - const { language } = this.props; - const validators: MonacoValidator[] = []; - const syntaxValidator: MonacoValidator = monacoValidators[language]; - - if (syntaxValidator) { - validators.push(syntaxValidator); - } + validate = (value = this.getValue()) => { + const validators: MonacoValidator[] = [ + monacoValidators[this.props.language], // parsing syntax check + ].filter(Boolean); for (const validate of validators) { try { - await validate(value); + validate(value); } catch (error) { - error = String(error); - this.props.onError?.(error); // emit error outside via callback + logger.silly(`[MONACO]: validation error`, error); + this.props.onError?.(error); // emit error outside } } }; @@ -279,12 +277,10 @@ export class MonacoEditor extends React.Component { bindRef = (elem: HTMLElement) => this.containerElem = elem; render() { - const { className } = this.props; - return (
); diff --git a/src/renderer/components/monaco-editor/monaco-validators.ts b/src/renderer/components/monaco-editor/monaco-validators.ts index 78c617b9fa..936bb97cb8 100644 --- a/src/renderer/components/monaco-editor/monaco-validators.ts +++ b/src/renderer/components/monaco-editor/monaco-validators.ts @@ -21,18 +21,18 @@ import yaml, { YAMLException } from "js-yaml"; export interface MonacoValidator { - (value: string): Promise; + (value: string): void; } -export async function yamlValidator(value: string) { +export function yamlValidator(value: string) { try { - await yaml.load(value); + yaml.load(value); } catch (error) { throw String(error as YAMLException); } } -export async function jsonValidator(value: string) { +export function jsonValidator(value: string) { try { JSON.parse(value); } catch (error) {