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:
parent
207584d8e9
commit
8f2dfd0d34
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
28
src/renderer/components/dock/dock-tab-context.tsx
Normal file
28
src/renderer/components/dock/dock-tab-context.tsx
Normal 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;
|
||||
}
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user