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 styles from "./dock-tab-content.module.css";
|
||||||
|
import throttle from "lodash/throttle";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { action, makeObservable, observable, reaction } from "mobx";
|
||||||
import { disposeOnUnmount, observer } from "mobx-react";
|
import { disposeOnUnmount, observer } from "mobx-react";
|
||||||
import { makeObservable, observable, reaction } from "mobx";
|
|
||||||
import { dockStore, TabId } from "./dock.store";
|
import { dockStore, TabId } from "./dock.store";
|
||||||
import { cssNames } from "../../utils";
|
import { cssNames } from "../../utils";
|
||||||
import { MonacoEditor } from "../monaco-editor";
|
import { MonacoEditor, MonacoEditorProps } from "../monaco-editor";
|
||||||
import throttle from "lodash/throttle";
|
import { DockTabContext } from "./dock-tab-context";
|
||||||
|
|
||||||
export interface DockTabContentProps extends React.HTMLAttributes<any> {
|
export interface DockTabContentProps extends React.HTMLAttributes<any> {
|
||||||
tabId: TabId;
|
tabId: TabId;
|
||||||
className?: string;
|
className?: string;
|
||||||
withEditor?: boolean;
|
withEditor?: boolean;
|
||||||
editorValue?: string;
|
editorValue?: MonacoEditorProps["value"];
|
||||||
editorOnChange?(value: string): void;
|
editorOnChange?: MonacoEditorProps["onChange"];
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
@ -56,11 +57,13 @@ export class DockTabContent extends React.Component<DockTabContentProps> {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { className, tabId, withEditor, editorValue, editorOnChange } = this.props;
|
const { className, tabId, withEditor, editorValue, editorOnChange } = this.props;
|
||||||
|
const { error } = this;
|
||||||
|
|
||||||
if (!tabId) return null;
|
if (!tabId) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cssNames(styles.DockTabContent, className)}>
|
<div className={cssNames(styles.DockTabContent, className)}>
|
||||||
|
<DockTabContext.Provider value={{ error }}>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
|
|
||||||
{withEditor && (
|
{withEditor && (
|
||||||
@ -69,11 +72,15 @@ export class DockTabContent extends React.Component<DockTabContentProps> {
|
|||||||
id={tabId}
|
id={tabId}
|
||||||
className={styles.editor}
|
className={styles.editor}
|
||||||
value={editorValue ?? ""}
|
value={editorValue ?? ""}
|
||||||
onChange={editorOnChange}
|
onChange={action((value, evt) => {
|
||||||
onError={error => this.error = error}
|
this.error = ""; // reset first
|
||||||
ref={monaco => this.editor = monaco}
|
editorOnChange?.(value, evt);
|
||||||
/>
|
})}
|
||||||
|
onError={error => this.error = String(error)}
|
||||||
|
onModelChange={() => this.error = ""}
|
||||||
|
ref={monaco => this.editor = monaco}></MonacoEditor>
|
||||||
)}
|
)}
|
||||||
|
</DockTabContext.Provider>
|
||||||
</div>
|
</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 { Spinner } from "../spinner";
|
||||||
import { dockStore, TabId } from "./dock.store";
|
import { dockStore, TabId } from "./dock.store";
|
||||||
import { Notifications } from "../notifications";
|
import { Notifications } from "../notifications";
|
||||||
|
import { DockTabContext, DockTabContextValue } from "./dock-tab-context";
|
||||||
|
|
||||||
export interface InfoPanelProps {
|
export interface InfoPanelProps {
|
||||||
tabId: TabId;
|
tabId: TabId;
|
||||||
@ -59,6 +60,12 @@ const defaultProps: Partial<InfoPanelProps> = {
|
|||||||
@observer
|
@observer
|
||||||
export class InfoPanel extends Component<InfoPanelProps> {
|
export class InfoPanel extends Component<InfoPanelProps> {
|
||||||
static defaultProps = defaultProps as object;
|
static defaultProps = defaultProps as object;
|
||||||
|
static contextType = DockTabContext;
|
||||||
|
declare context: DockTabContextValue;
|
||||||
|
|
||||||
|
get error() {
|
||||||
|
return this.props.error ?? this.context.error;
|
||||||
|
}
|
||||||
|
|
||||||
@observable waiting = false;
|
@observable waiting = false;
|
||||||
|
|
||||||
@ -103,15 +110,13 @@ export class InfoPanel extends Component<InfoPanelProps> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
renderErrorIcon(): React.ReactNode {
|
renderErrorIcon(): React.ReactNode {
|
||||||
const { error } = this.props;
|
if (!this.error || !this.props.showInlineInfo) {
|
||||||
|
|
||||||
if (!error || !this.props.showInlineInfo) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="error">
|
<div className="error">
|
||||||
<Icon material="error_outline" tooltip={error}/>
|
<Icon material="error_outline" tooltip={this.error}/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,9 +46,10 @@ export interface MonacoEditorProps {
|
|||||||
language?: "yaml" | "json"; // configure bundled list of languages in via MonacoWebpackPlugin({languages: []})
|
language?: "yaml" | "json"; // configure bundled list of languages in via MonacoWebpackPlugin({languages: []})
|
||||||
options?: Partial<editor.IStandaloneEditorConstructionOptions>; // customize editor's initialization options
|
options?: Partial<editor.IStandaloneEditorConstructionOptions>; // customize editor's initialization options
|
||||||
onChange?(value: string, evt: editor.IModelContentChangedEvent): void; // catch latest value updates
|
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;
|
onDidLayoutChange?(info: editor.EditorLayoutInfo): void;
|
||||||
onDidContentSizeChange?(evt: editor.IContentSizeChangedEvent): void;
|
onDidContentSizeChange?(evt: editor.IContentSizeChangedEvent): void;
|
||||||
|
onModelChange?(model: editor.ITextModel, prev?: editor.ITextModel): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultEditorProps: Partial<MonacoEditorProps> = {
|
export const defaultEditorProps: Partial<MonacoEditorProps> = {
|
||||||
@ -129,6 +130,7 @@ export class MonacoEditor extends React.Component<MonacoEditorProps> {
|
|||||||
this.restoreViewState(model);
|
this.restoreViewState(model);
|
||||||
this.editor.layout();
|
this.editor.layout();
|
||||||
this.editor.focus(); // keep focus in editor, e.g. when clicking between dock-tabs
|
this.editor.focus(); // keep focus in editor, e.g. when clicking between dock-tabs
|
||||||
|
this.props.onModelChange?.(model, oldModel);
|
||||||
this.validateLazy();
|
this.validateLazy();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -254,21 +256,17 @@ export class MonacoEditor extends React.Component<MonacoEditorProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
validate = async (value = this.getValue()): Promise<void> => {
|
validate = (value = this.getValue()) => {
|
||||||
const { language } = this.props;
|
const validators: MonacoValidator[] = [
|
||||||
const validators: MonacoValidator[] = [];
|
monacoValidators[this.props.language], // parsing syntax check
|
||||||
const syntaxValidator: MonacoValidator = monacoValidators[language];
|
].filter(Boolean);
|
||||||
|
|
||||||
if (syntaxValidator) {
|
|
||||||
validators.push(syntaxValidator);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const validate of validators) {
|
for (const validate of validators) {
|
||||||
try {
|
try {
|
||||||
await validate(value);
|
validate(value);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
error = String(error);
|
logger.silly(`[MONACO]: validation error`, error);
|
||||||
this.props.onError?.(error); // emit error outside via callback
|
this.props.onError?.(error); // emit error outside
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -279,12 +277,10 @@ export class MonacoEditor extends React.Component<MonacoEditorProps> {
|
|||||||
bindRef = (elem: HTMLElement) => this.containerElem = elem;
|
bindRef = (elem: HTMLElement) => this.containerElem = elem;
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { className } = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-test-component="monaco-editor"
|
data-test-component="monaco-editor"
|
||||||
className={cssNames(styles.MonacoEditor, className)}
|
className={cssNames(styles.MonacoEditor, this.props.className)}
|
||||||
ref={this.bindRef}
|
ref={this.bindRef}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -21,18 +21,18 @@
|
|||||||
import yaml, { YAMLException } from "js-yaml";
|
import yaml, { YAMLException } from "js-yaml";
|
||||||
|
|
||||||
export interface MonacoValidator {
|
export interface MonacoValidator {
|
||||||
(value: string): Promise<void>;
|
(value: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function yamlValidator(value: string) {
|
export function yamlValidator(value: string) {
|
||||||
try {
|
try {
|
||||||
await yaml.load(value);
|
yaml.load(value);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw String(error as YAMLException);
|
throw String(error as YAMLException);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function jsonValidator(value: string) {
|
export function jsonValidator(value: string) {
|
||||||
try {
|
try {
|
||||||
JSON.parse(value);
|
JSON.parse(value);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user