1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

simplify validators, provide error to InfoPanel via React.context

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman 2021-10-25 15:06:30 +03:00
parent 207584d8e9
commit 8f2dfd0d34
5 changed files with 76 additions and 40 deletions

View File

@ -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<any> {
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<DockTabContentProps> {
render() {
const { className, tabId, withEditor, editorValue, editorOnChange } = this.props;
const { error } = this;
if (!tabId) return null;
return (
<div className={cssNames(styles.DockTabContent, className)}>
{this.props.children}
<DockTabContext.Provider value={{ error }}>
{this.props.children}
{withEditor && (
<MonacoEditor
autoFocus
id={tabId}
className={styles.editor}
value={editorValue ?? ""}
onChange={editorOnChange}
onError={error => this.error = error}
ref={monaco => this.editor = monaco}
/>
)}
{withEditor && (
<MonacoEditor
autoFocus
id={tabId}
className={styles.editor}
value={editorValue ?? ""}
onChange={action((value, evt) => {
this.error = ""; // reset first
editorOnChange?.(value, evt);
})}
onError={error => this.error = String(error)}
onModelChange={() => this.error = ""}
ref={monaco => this.editor = monaco}></MonacoEditor>
)}
</DockTabContext.Provider>
</div>
);
}

View File

@ -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<DockTabContextValue>({});
export interface DockTabContextValue {
error?: string;
}

View File

@ -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<InfoPanelProps> = {
@observer
export class InfoPanel extends Component<InfoPanelProps> {
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<InfoPanelProps> {
};
renderErrorIcon(): React.ReactNode {
const { error } = this.props;
if (!error || !this.props.showInlineInfo) {
if (!this.error || !this.props.showInlineInfo) {
return null;
}
return (
<div className="error">
<Icon material="error_outline" tooltip={error}/>
<Icon material="error_outline" tooltip={this.error}/>
</div>
);
}

View File

@ -46,9 +46,10 @@ export interface MonacoEditorProps {
language?: "yaml" | "json"; // configure bundled list of languages in via MonacoWebpackPlugin({languages: []})
options?: Partial<editor.IStandaloneEditorConstructionOptions>; // 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<MonacoEditorProps> = {
@ -129,6 +130,7 @@ export class MonacoEditor extends React.Component<MonacoEditorProps> {
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<MonacoEditorProps> {
}
@action
validate = async (value = this.getValue()): Promise<void> => {
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<MonacoEditorProps> {
bindRef = (elem: HTMLElement) => this.containerElem = elem;
render() {
const { className } = this.props;
return (
<div
data-test-component="monaco-editor"
className={cssNames(styles.MonacoEditor, className)}
className={cssNames(styles.MonacoEditor, this.props.className)}
ref={this.bindRef}
/>
);

View File

@ -21,18 +21,18 @@
import yaml, { YAMLException } from "js-yaml";
export interface MonacoValidator {
(value: string): Promise<void>;
(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) {