mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-20 01:28:45 +01:00
fix(ui): convert yaml-editor to functional component (#24921)
Signed-off-by: Mike <mike.kangaroo.world@gmail.com> Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Co-authored-by: Alexandre Gaudreault <alexandre_gaudreault@intuit.com>
This commit is contained in:
@@ -2,114 +2,100 @@ import {ErrorNotification, NotificationType} from 'argo-ui';
|
||||
import * as jsYaml from 'js-yaml';
|
||||
import * as monacoEditor from 'monaco-editor';
|
||||
import * as React from 'react';
|
||||
import {useContext, useState, useRef} from 'react';
|
||||
|
||||
import {Consumer} from '../../context';
|
||||
import {Context} from '../../context';
|
||||
import {MonacoEditor} from '../monaco-editor';
|
||||
|
||||
const jsonMergePatch = require('json-merge-patch');
|
||||
require('./yaml-editor.scss');
|
||||
|
||||
export class YamlEditor<T> extends React.Component<
|
||||
{
|
||||
input: T;
|
||||
hideModeButtons?: boolean;
|
||||
initialEditMode?: boolean;
|
||||
vScrollbar?: boolean;
|
||||
enableWordWrap?: boolean;
|
||||
onSave?: (patch: string, patchType: string) => Promise<any>;
|
||||
onCancel?: () => any;
|
||||
minHeight?: number;
|
||||
},
|
||||
{
|
||||
editing: boolean;
|
||||
}
|
||||
> {
|
||||
private model: monacoEditor.editor.ITextModel;
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {editing: props.initialEditMode};
|
||||
}
|
||||
|
||||
public render() {
|
||||
const props = this.props;
|
||||
const yaml = props.input ? jsYaml.dump(props.input) : '';
|
||||
|
||||
return (
|
||||
<div className='yaml-editor'>
|
||||
{!props.hideModeButtons && (
|
||||
<div className='yaml-editor__buttons'>
|
||||
{(this.state.editing && (
|
||||
<Consumer>
|
||||
{ctx => (
|
||||
<React.Fragment>
|
||||
<button
|
||||
onClick={async () => {
|
||||
try {
|
||||
const updated = jsYaml.load(this.model.getLinesContent().join('\n'));
|
||||
const patch = jsonMergePatch.generate(props.input, updated);
|
||||
try {
|
||||
const unmounted = await this.props.onSave(JSON.stringify(patch || {}), 'application/merge-patch+json');
|
||||
if (unmounted !== true) {
|
||||
this.setState({editing: false});
|
||||
}
|
||||
} catch (e) {
|
||||
ctx.notifications.show({
|
||||
content: (
|
||||
<div className='yaml-editor__error'>
|
||||
<ErrorNotification title='Unable to save changes' e={e} />
|
||||
</div>
|
||||
),
|
||||
type: NotificationType.Error
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
ctx.notifications.show({
|
||||
content: <ErrorNotification title='Unable to validate changes' e={e} />,
|
||||
type: NotificationType.Error
|
||||
});
|
||||
}
|
||||
}}
|
||||
className='argo-button argo-button--base'>
|
||||
Save
|
||||
</button>{' '}
|
||||
<button
|
||||
onClick={() => {
|
||||
this.model.setValue(jsYaml.dump(props.input));
|
||||
this.setState({editing: !this.state.editing});
|
||||
if (props.onCancel) {
|
||||
props.onCancel();
|
||||
}
|
||||
}}
|
||||
className='argo-button argo-button--base-o'>
|
||||
Cancel
|
||||
</button>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Consumer>
|
||||
)) || (
|
||||
<button onClick={() => this.setState({editing: true})} className='argo-button argo-button--base'>
|
||||
Edit
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<MonacoEditor
|
||||
minHeight={props.minHeight}
|
||||
vScrollBar={props.vScrollbar}
|
||||
editor={{
|
||||
input: {text: yaml, language: 'yaml'},
|
||||
options: {
|
||||
readOnly: !this.state.editing,
|
||||
minimap: {enabled: false},
|
||||
wordWrap: props.enableWordWrap ? 'on' : 'off'
|
||||
},
|
||||
getApi: api => {
|
||||
this.model = api.getModel() as monacoEditor.editor.ITextModel;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
interface YamlEditorProps<T> {
|
||||
input: T;
|
||||
hideModeButtons?: boolean;
|
||||
initialEditMode?: boolean;
|
||||
vScrollbar?: boolean;
|
||||
enableWordWrap?: boolean;
|
||||
onSave?: (patch: string, patchType: string) => Promise<any>;
|
||||
onCancel?: () => any;
|
||||
minHeight?: number;
|
||||
}
|
||||
|
||||
export function YamlEditor<T>(props: YamlEditorProps<T>) {
|
||||
const ctx = useContext(Context);
|
||||
const [editing, setEditing] = useState(!!props.initialEditMode);
|
||||
const modelRef = useRef<monacoEditor.editor.ITextModel | null>(null);
|
||||
|
||||
const yamlText = props.input ? jsYaml.dump(props.input) : '';
|
||||
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
const updated = jsYaml.load(modelRef.current!.getLinesContent().join('\n'));
|
||||
const patch = jsonMergePatch.generate(props.input, updated);
|
||||
try {
|
||||
const unmounted = await props.onSave?.(JSON.stringify(patch || {}), 'application/merge-patch+json');
|
||||
if (unmounted !== true) {
|
||||
setEditing(false);
|
||||
}
|
||||
} catch (e) {
|
||||
ctx.notifications.show({
|
||||
content: (
|
||||
<div className='yaml-editor__error'>
|
||||
<ErrorNotification title='Unable to save changes' e={e} />
|
||||
</div>
|
||||
),
|
||||
type: NotificationType.Error
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
ctx.notifications.show({
|
||||
content: <ErrorNotification title='Unable to validate changes' e={e} />,
|
||||
type: NotificationType.Error
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
modelRef.current?.setValue(jsYaml.dump(props.input));
|
||||
setEditing(false);
|
||||
props.onCancel?.();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='yaml-editor'>
|
||||
{!props.hideModeButtons && (
|
||||
<div className='yaml-editor__buttons'>
|
||||
{editing ? (
|
||||
<>
|
||||
<button onClick={handleSave} className='argo-button argo-button--base'>
|
||||
Save
|
||||
</button>{' '}
|
||||
<button onClick={handleCancel} className='argo-button argo-button--base-o'>
|
||||
Cancel
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<button onClick={() => setEditing(true)} className='argo-button argo-button--base'>
|
||||
Edit
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<MonacoEditor
|
||||
minHeight={props.minHeight}
|
||||
vScrollBar={props.vScrollbar}
|
||||
editor={{
|
||||
input: {text: yamlText, language: 'yaml'},
|
||||
options: {
|
||||
readOnly: !editing,
|
||||
minimap: {enabled: false},
|
||||
wordWrap: props.enableWordWrap ? 'on' : 'off'
|
||||
},
|
||||
getApi: api => {
|
||||
modelRef.current = api.getModel() as monacoEditor.editor.ITextModel;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user