Issue #1059 - Use ApplicationParameters panel on ApplicationCreatePanel (#2197)

This commit is contained in:
Alexander Matyushentsev 2019-08-22 15:53:16 -07:00 committed by GitHub
parent f9286cfab9
commit a10dd3f184
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 79 additions and 84 deletions

View file

@ -1,10 +1,11 @@
import {Checkbox, DataLoader, DropDownMenu, FormField, Select} from 'argo-ui';
import * as deepMerge from 'deepmerge';
import * as React from 'react';
import {FieldApi, Form, FormApi, FormField as ReactFormField, Text, TextArea} from 'react-form';
import {AutocompleteField, CheckboxField, clusterTitle, TagsInputField, YamlEditor} from '../../../shared/components';
import {FieldApi, Form, FormApi, FormField as ReactFormField, Text} from 'react-form';
import {AutocompleteField, clusterTitle, YamlEditor} from '../../../shared/components';
import * as models from '../../../shared/models';
import {services} from '../../../shared/services';
import { ApplicationParameters } from '../application-parameters/application-parameters';
const jsonMergePatch = require('json-merge-patch');
@ -176,80 +177,55 @@ export const ApplicationCreatePanel = (props: {
);
const typePanel = () => (
<div className='white-box'>
<DataLoader
input={{repoURL: app.spec.source.repoURL, path: app.spec.source.path, targetRevision: app.spec.source.targetRevision }}
load={async (src) => {
if (src.repoURL && src.path) {
return services.repos.appDetails(src.repoURL, src.path, src.targetRevision).catch(() => ({
type: 'Directory',
details: {},
}));
} else {
return {
type: 'Directory',
details: {},
};
}
}}>
{(details: models.RepoAppDetails) => {
const type = explicitPathType && explicitPathType.path === app.spec.source.path && explicitPathType.type || details.type;
return (
<React.Fragment>
<DropDownMenu anchor={() => (<p>{type} <i className='fa fa-caret-down'/></p>)} items={appTypes.map(
(item) => ({ title: item.type, action: () => {
setExplicitPathType({ type: item.type, path: app.spec.source.path });
normalizeTypeFields(api, item.type);
}}))}
/>
{type === 'Directory' && (
<FormField formApi={api}
label='Include subdirectories' field='spec.source.directory.recurse' component={CheckboxField}/>
) || type === 'Kustomize' && (
<div className='argo-form-row'>
<FormField formApi={api} label='Name Prefix' field='spec.source.kustomize.namePrefix' component={Text} />
</div>
) || type === 'Ksonnet' && (
<div className='argo-form-row'>
<FormField formApi={api} label='Environment' field='spec.source.ksonnet.environment'
component={AutocompleteField} componentProps={{
items: details.ksonnet && Object.keys(details.ksonnet.environments) || [],
}}
/>
</div>
) || type === 'Helm' && (
<React.Fragment>
<div className='argo-form-row'>
<FormField formApi={api} label='Values Files'
field='spec.source.helm.valueFiles'
componentProps={{
options: details.helm && details.helm.valueFiles,
noTagsLabel: 'No values files selected',
}} component={TagsInputField}/>
</div>
<div className='argo-form-row'>
<label>Values</label>
<pre><FormField formApi={api}
field='spec.source.helm.values'
component={TextArea}/></pre>
</div>
{details.helm && details.helm.values && (
<div className='argo-form-row'>
<label>values.yaml</label>
<pre>{details.helm.values}</pre>
</div>
)}
</React.Fragment>
) || type === 'Plugin' && (
<div className='argo-form-row'>
<FormField formApi={api} label='Name' field='spec.source.plugin.name' component={Text} />
</div>
)}
</React.Fragment>
);
}}
</DataLoader>
</div>
<DataLoader
input={{repoURL: app.spec.source.repoURL, path: app.spec.source.path, targetRevision: app.spec.source.targetRevision }}
load={async (src) => {
if (src.repoURL && src.path) {
return services.repos.appDetails(src.repoURL, src.path, src.targetRevision).catch(() => ({
type: 'Directory',
details: {},
}));
} else {
return {
type: 'Directory',
details: {},
};
}
}}>
{(details: models.RepoAppDetails) => {
const type = explicitPathType && explicitPathType.path === app.spec.source.path && explicitPathType.type || details.type;
if (details.type !== type) {
switch (type) {
case 'Helm':
details = {type, path: details.path, helm: { name: '', valueFiles: [], path: '', parameters: [] } };
break;
case 'Kustomize':
details = {type, path: details.path, kustomize: { path: '' } };
break;
case 'Ksonnet':
details = {type, path: details.path, ksonnet: { name: '', path: '', environments: {}, parameters: []} };
break;
// Directory or Plugin
default:
details = {type, path: details.path, directory: {} };
break;
}
}
return (
<React.Fragment>
<DropDownMenu anchor={() => (<p>{type} <i className='fa fa-caret-down'/></p>)} items={appTypes.map(
(item) => ({ title: item.type, action: () => {
setExplicitPathType({ type: item.type, path: app.spec.source.path });
normalizeTypeFields(api, item.type);
}}))}
/>
<ApplicationParameters noReadonlyMode={true} application={app} details={details} save={async (updatedApp) => {
api.setAllValues(updatedApp);
}} />
</React.Fragment>
);
}}
</DataLoader>
);
return (

View file

@ -27,7 +27,7 @@ function overridesFirst(first: { overrideIndex: number}, second: { overrideIndex
return first.overrideIndex - second.overrideIndex;
}
function getParamsEditableItems<T extends { name: string, value: string }>(
function getParamsEditableItems(
app: models.Application,
title: string,
fieldsPath: string,
@ -84,7 +84,13 @@ function getParamsEditableItems<T extends { name: string, value: string }>(
}).map((item, i) => ({...item, before: i === 0 && <p style={{ marginTop: '1em' }}>{title}</p> || null }));
}
export const ApplicationParameters = (props: { application: models.Application, details: models.RepoAppDetails, save?: (application: models.Application) => Promise<any> }) => {
export const ApplicationParameters = (props: {
application: models.Application,
details: models.RepoAppDetails,
save?: (application: models.Application) => Promise<any>,
noReadonlyMode?: boolean,
}) => {
const app = props.application;
const source = props.application.spec.source;
const [removedOverrides, setRemovedOverrides] = React.useState(new Array<boolean>());
@ -222,6 +228,6 @@ export const ApplicationParameters = (props: { application: models.Application,
await props.save(input);
setRemovedOverrides(new Array<boolean>());
})}
values={app} title={props.details.type.toLocaleUpperCase()} items={attributes} />
values={app} title={props.details.type.toLocaleUpperCase()} items={attributes} noReadonlyMode={props.noReadonlyMode} />
);
};

View file

@ -252,7 +252,7 @@ export const ApplicationsList = (props: RouteComponentProps<{}>) => {
</DataLoader>
)}
</ObservableQuery>
<SlidingPanel isMiddle={true} isShown={!!appInput} onClose={() => ctx.navigation.goto('.', { new: null })} header={
<SlidingPanel isShown={!!appInput} onClose={() => ctx.navigation.goto('.', { new: null })} header={
<div>
<button className='argo-button argo-button--base'
onClick={() => createApi && createApi.submitForm(null)}>
@ -262,7 +262,9 @@ export const ApplicationsList = (props: RouteComponentProps<{}>) => {
</button>
</div>
}>
{appInput && <ApplicationCreatePanel getFormApi={setCreateApi} createApp={ async (app) => {
{appInput && <ApplicationCreatePanel getFormApi={(api) => {
setCreateApi(api);
}} createApp={ async (app) => {
try {
await services.applications.create(app);
ctx.navigation.goto('.', { new: null });

View file

@ -21,6 +21,7 @@ export interface EditablePanelProps<T> {
save?: (input: T) => Promise<any>;
items: EditablePanelItem[];
onModeSwitch?: () => any;
noReadonlyMode?: boolean;
}
interface EditablePanelState { edit: boolean; saving: boolean; }
@ -33,7 +34,13 @@ export class EditablePanel<T = {}> extends React.Component<EditablePanelProps<T>
constructor(props: EditablePanelProps<T>) {
super(props);
this.state = { edit: false, saving: false };
this.state = { edit: !!props.noReadonlyMode, saving: false };
}
public UNSAFE_componentWillReceiveProps(nextProps: EditablePanelProps<T>) {
if (JSON.stringify(this.props.values) !== JSON.stringify(nextProps.values)) {
this.formApi.setAllValues(nextProps.values);
}
}
public render() {
@ -41,7 +48,7 @@ export class EditablePanel<T = {}> extends React.Component<EditablePanelProps<T>
<Consumer>{(ctx) => (
<div className={classNames('white-box editable-panel', {'editable-panel--disabled': this.state.saving})}>
<div className='white-box__details'>
{this.props.save && (
{!this.props.noReadonlyMode && this.props.save && (
<div className='editable-panel__buttons'>
{!this.state.edit && (
<button onClick={() => {
@ -78,7 +85,11 @@ export class EditablePanel<T = {}> extends React.Component<EditablePanelProps<T>
))}
</React.Fragment>
) || (
<Form getApi={(api) => this.formApi = api} onSubmit={async (input) => {
<Form getApi={(api) => this.formApi = api} formDidUpdate={async (form) => {
if (this.props.noReadonlyMode && this.props.save) {
await this.props.save(form.values as any);
}
}} onSubmit={async (input) => {
try {
this.setState({ saving: true });
await this.props.save(input as any);