-
Notifications
You must be signed in to change notification settings - Fork 707
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New deployment forms: add json-schema-based table #5412
Changes from 7 commits
c2b71e8
41b2ab9
bf0fd45
c23a1d6
e73bffd
73e9fff
47866f2
37e3a6f
d63b338
d46d171
d58e282
7bfb69d
2f5029d
ed6552a
bde921a
cd21b80
9655a5f
98d7fce
486cc47
ebf442c
57dfb67
7b12f70
b698179
c6462b8
035172a
07bfec2
f919322
a5fb3b2
f48ec48
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,25 +10,24 @@ import AvailablePackageDetailExcerpt from "components/Catalog/AvailablePackageDe | |
import Alert from "components/js/Alert"; | ||
import Column from "components/js/Column"; | ||
import Row from "components/js/Row"; | ||
import LoadingWrapper from "components/LoadingWrapper"; | ||
import PackageHeader from "components/PackageHeader/PackageHeader"; | ||
import { push } from "connected-react-router"; | ||
import { | ||
AvailablePackageReference, | ||
ReconciliationOptions, | ||
} from "gen/kubeappsapis/core/packages/v1alpha1/packages"; | ||
import { Plugin } from "gen/kubeappsapis/core/plugins/v1alpha1/plugins"; | ||
import { useEffect, useState } from "react"; | ||
import { useEffect, useRef, useState } from "react"; | ||
import { useDispatch, useSelector } from "react-redux"; | ||
import * as ReactRouter from "react-router-dom"; | ||
import "react-tabs/style/react-tabs.css"; | ||
import { Action } from "redux"; | ||
import { ThunkDispatch } from "redux-thunk"; | ||
import { Kube } from "shared/Kube"; | ||
import { FetchError, IStoreState } from "shared/types"; | ||
import * as url from "shared/url"; | ||
import { getPluginsRequiringSA, k8sObjectNameRegex } from "shared/utils"; | ||
import DeploymentFormBody from "../DeploymentFormBody/DeploymentFormBody"; | ||
import LoadingWrapper from "../LoadingWrapper/LoadingWrapper"; | ||
import DeploymentFormBody from "./DeploymentFormBody"; | ||
interface IRouteParams { | ||
cluster: string; | ||
namespace: string; | ||
|
@@ -63,6 +62,7 @@ export default function DeploymentForm() { | |
const [valuesModified, setValuesModified] = useState(false); | ||
const [serviceAccountList, setServiceAccountList] = useState([] as string[]); | ||
const [reconciliationOptions, setReconciliationOptions] = useState({} as ReconciliationOptions); | ||
const formRef = useRef<HTMLFormElement>(null); | ||
|
||
const error = apps.error || selectedPackage.error; | ||
|
||
|
@@ -209,7 +209,7 @@ export default function DeploymentForm() { | |
</Column> | ||
<Column span={9}> | ||
{error && <Alert theme="danger">An error occurred: {error.message}</Alert>} | ||
<form onSubmit={handleDeploy}> | ||
<form onSubmit={handleDeploy} ref={formRef}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a hacky way to pass information from the children to the parent container in React. We need to manually control the form submission downstream. |
||
<CdsFormGroup | ||
validate={true} | ||
className="deployment-form" | ||
|
@@ -267,6 +267,7 @@ export default function DeploymentForm() { | |
setValues={handleValuesChange} | ||
appValues={appValues} | ||
setValuesModified={setValuesModifiedTrue} | ||
formRef={formRef} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was introduced in one of my previous attempts when working on this. The idea is to trigger a plain normal HTML form "submit" event from a child, this way we leverage the built-in HTML validations (like Anyway, just to double-check, I'm trying locally to replace the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
/> | ||
</form> | ||
</Column> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
// Copyright 2019-2022 the Kubeapps contributors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import { CdsRadio, CdsRadioGroup } from "@cds/react/radio"; | ||
import Column from "components/js/Column"; | ||
import Row from "components/js/Row"; | ||
import monaco from "monaco-editor/esm/vs/editor/editor.api"; // for types only | ||
import { useEffect, useState } from "react"; | ||
import { MonacoDiffEditor } from "react-monaco-editor"; | ||
import { useSelector } from "react-redux"; | ||
import { IStoreState } from "shared/types"; | ||
|
||
export interface IAdvancedDeploymentForm { | ||
valuesFromTheParentContainer?: string; | ||
handleValuesChange: (value: string) => void; | ||
children?: JSX.Element; | ||
valuesFromTheDeployedPackage: string; | ||
valuesFromTheAvailablePackage: string; | ||
deploymentEvent: string; | ||
} | ||
|
||
export default function AdvancedDeploymentForm(props: IAdvancedDeploymentForm) { | ||
const { | ||
config: { theme }, | ||
} = useSelector((state: IStoreState) => state); | ||
const { | ||
handleValuesChange, | ||
valuesFromTheParentContainer, | ||
valuesFromTheDeployedPackage, | ||
valuesFromTheAvailablePackage, | ||
deploymentEvent, | ||
} = props; | ||
|
||
const [usePackageDefaults, setUsePackageDefaults] = useState( | ||
deploymentEvent === "upgrade" ? false : true, | ||
); | ||
const [useDiffEditor, setUseDiffEditor] = useState(true); | ||
const [diffValues, setDiffValues] = useState(valuesFromTheAvailablePackage); | ||
|
||
const diffEditorOptions = { | ||
renderSideBySide: false, | ||
automaticLayout: true, | ||
}; | ||
|
||
const onChange = (value: string | undefined, _ev: any) => { | ||
// debouncing is not required as the diff calculation happens in a webworker | ||
handleValuesChange(value || ""); | ||
}; | ||
|
||
useEffect(() => { | ||
if (!useDiffEditor) { | ||
setDiffValues(valuesFromTheParentContainer || ""); | ||
} else if (!usePackageDefaults) { | ||
setDiffValues(valuesFromTheDeployedPackage); | ||
} else { | ||
setDiffValues(valuesFromTheAvailablePackage); | ||
} | ||
}, [ | ||
usePackageDefaults, | ||
useDiffEditor, | ||
valuesFromTheAvailablePackage, | ||
valuesFromTheDeployedPackage, | ||
valuesFromTheParentContainer, | ||
]); | ||
|
||
const editorDidMount = (editor: monaco.editor.IStandaloneDiffEditor, m: typeof monaco) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here we are just creating some commands in the palette and context menu (when you right-click on the editor). |
||
// Add "go to the next change" action | ||
editor.addAction({ | ||
id: "goToNextChange", | ||
label: "Go to the next change", | ||
keybindings: [m.KeyMod.Alt | m.KeyCode.KeyG], | ||
contextMenuGroupId: "9_cutcopypaste", | ||
run: () => { | ||
const lineChanges = editor?.getLineChanges() as monaco.editor.ILineChange[]; | ||
lineChanges.some(lineChange => { | ||
const currentPosition = editor?.getPosition() as monaco.Position; | ||
if (currentPosition.lineNumber < lineChange.modifiedEndLineNumber) { | ||
// Set the cursor to the next change | ||
editor?.setPosition({ | ||
lineNumber: lineChange.modifiedEndLineNumber, | ||
column: 1, | ||
}); | ||
// Scroll to the next change | ||
editor?.revealPositionInCenter({ | ||
lineNumber: lineChange.modifiedEndLineNumber, | ||
column: 1, | ||
}); | ||
// Return true to stop the loop | ||
return true; | ||
} | ||
return false; | ||
}); | ||
}, | ||
}); | ||
// Add "go to the previous change" action | ||
editor.addAction({ | ||
id: "goToPreviousChange", | ||
label: "Go to the previous change", | ||
keybindings: [m.KeyMod.Alt | m.KeyCode.KeyF], | ||
contextMenuGroupId: "9_cutcopypaste", | ||
run: () => { | ||
const lineChanges = editor?.getLineChanges() as monaco.editor.ILineChange[]; | ||
lineChanges.some(lineChange => { | ||
const currentPosition = editor?.getPosition() as monaco.Position; | ||
if (currentPosition.lineNumber > lineChange.modifiedEndLineNumber) { | ||
// Set the cursor to the next change | ||
editor?.setPosition({ | ||
lineNumber: lineChange.modifiedEndLineNumber, | ||
column: 1, | ||
}); | ||
// Scroll to the next change | ||
editor?.revealPositionInCenter({ | ||
lineNumber: lineChange.modifiedEndLineNumber, | ||
column: 1, | ||
}); | ||
// Return true to stop the loop | ||
return true; | ||
} | ||
return false; | ||
}); | ||
}, | ||
}); | ||
|
||
// Add the "toggle deployed/package default values" action | ||
if (deploymentEvent === "upgrade") { | ||
editor.addAction({ | ||
id: "useDefaultsFalse", | ||
label: "Use default values", | ||
keybindings: [m.KeyMod.Alt | m.KeyCode.KeyD], | ||
contextMenuGroupId: "9_cutcopypaste", | ||
run: () => { | ||
setUsePackageDefaults(false); | ||
}, | ||
}); | ||
editor.addAction({ | ||
id: "useDefaultsTrue", | ||
label: "Use package values", | ||
keybindings: [m.KeyMod.Alt | m.KeyCode.KeyV], | ||
contextMenuGroupId: "9_cutcopypaste", | ||
run: () => { | ||
setUsePackageDefaults(true); | ||
}, | ||
}); | ||
} | ||
}; | ||
|
||
return ( | ||
<div className="deployment-form-tabs-data"> | ||
<> | ||
<Row> | ||
<Column span={3}> | ||
<CdsRadioGroup layout="vertical"> | ||
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */} | ||
<label>Enable diff editor:</label> | ||
<CdsRadio> | ||
<label htmlFor="diff-compare-enable-true">Yes</label> | ||
<input | ||
id="diff-compare-enable-true" | ||
type="radio" | ||
name="true" | ||
checked={useDiffEditor} | ||
onChange={e => { | ||
setUseDiffEditor(e.target.checked); | ||
}} | ||
/> | ||
</CdsRadio> | ||
<CdsRadio> | ||
<label htmlFor="diff-compare-enable-false">No</label> | ||
<input | ||
id="diff-compare-enable-false" | ||
type="radio" | ||
name="deployed" | ||
checked={!useDiffEditor} | ||
onChange={e => { | ||
setUseDiffEditor(!e.target.checked); | ||
}} | ||
/> | ||
</CdsRadio> | ||
</CdsRadioGroup> | ||
</Column> | ||
{deploymentEvent === "upgrade" ? ( | ||
<> | ||
<Column span={3}> | ||
<CdsRadioGroup layout="vertical"> | ||
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */} | ||
<label>Values to compare against:</label> | ||
<CdsRadio> | ||
<label htmlFor="diff-compare-values-package">Package values</label> | ||
<input | ||
id="diff-compare-values-package" | ||
type="radio" | ||
name="package" | ||
checked={usePackageDefaults} | ||
onChange={e => { | ||
setUsePackageDefaults(e.target.checked); | ||
}} | ||
/> | ||
</CdsRadio> | ||
<CdsRadio> | ||
<label htmlFor="diff-compare-values-deployed">Deployed values</label> | ||
<input | ||
id="diff-compare-values-deployed" | ||
type="radio" | ||
name="deployed" | ||
checked={!usePackageDefaults} | ||
onChange={e => { | ||
setUsePackageDefaults(!e.target.checked); | ||
}} | ||
/> | ||
</CdsRadio> | ||
</CdsRadioGroup> | ||
</Column> | ||
</> | ||
) : ( | ||
<></> | ||
)} | ||
</Row> | ||
</> | ||
<br /> | ||
<MonacoDiffEditor | ||
value={valuesFromTheParentContainer} | ||
original={diffValues} | ||
className="editor" | ||
height="90vh" | ||
language="yaml" | ||
theme={theme === "dark" ? "vs-dark" : "light"} | ||
options={diffEditorOptions} | ||
onChange={onChange} | ||
editorDidMount={editorDidMount} | ||
/> | ||
</div> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to dev-deps as they are not required in the production build