Skip to content
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

Split setup in advanced and regular for data sources and destinations #4160

Merged
merged 12 commits into from
Oct 6, 2019
2 changes: 1 addition & 1 deletion client/app/components/CreateSourceDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class CreateSourceDialog extends React.Component {
const fields = helper.getFields(selectedType);
const helpTriggerType = `${helpTriggerPrefix}${toUpper(selectedType.type)}`;
return (
<div className="p-5">
<div>
<div className="d-flex justify-content-center align-items-center">
<img
className="p-5"
Expand Down
47 changes: 38 additions & 9 deletions client/app/components/dynamic-form/DynamicForm.jsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import React from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import Form from 'antd/lib/form';
import Input from 'antd/lib/input';
import InputNumber from 'antd/lib/input-number';
import Checkbox from 'antd/lib/checkbox';
import Button from 'antd/lib/button';
import Upload from 'antd/lib/upload';
import Icon from 'antd/lib/icon';
import { includes, isFunction } from 'lodash';
import { includes, isFunction, filter, difference, isEmpty, some, isNumber, isBoolean } from 'lodash';
import Select from 'antd/lib/select';
import notification from '@/services/notification';
import Collapse from '@/components/Collapse';
import AceEditorInput from '@/components/AceEditorInput';
import { toHuman } from '@/filters';
import { Field, Action, AntdForm } from '../proptypes';
import helper from './dynamicFormHelper';

import './DynamicForm.less';

const fieldRules = ({ type, required, minLength }) => {
const requiredRule = required;
const minLengthRule = minLength && includes(['text', 'email', 'password'], type);
Expand Down Expand Up @@ -51,9 +56,14 @@ class DynamicForm extends React.Component {
constructor(props) {
super(props);

const hasFilledExtraField = some(props.fields, (field) => {
const { extra, initialValue } = field;
return extra && (!isEmpty(initialValue) || isNumber(initialValue) || isBoolean(initialValue) && initialValue);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ranbena this seemed the simplest without separating this and deep evaluating the values with their defaults. I've also tried the last one and my opinion is that it's an unnecessary extra complexity.

});
this.state = {
isSubmitting: false,
inProgressActions: [],
showExtraFields: hasFilledExtraField,
};

this.actionCallbacks = this.props.actions.reduce((acc, cur) => ({
Expand Down Expand Up @@ -157,7 +167,7 @@ class DynamicForm extends React.Component {
renderField(field, props) {
const { getFieldDecorator } = this.props.form;
const { name, type, initialValue } = field;
const fieldLabel = field.title || helper.toHuman(name);
const fieldLabel = field.title || toHuman(name);

const options = {
rules: fieldRules(field),
Expand All @@ -183,11 +193,11 @@ class DynamicForm extends React.Component {
return getFieldDecorator(name, options)(<Input {...props} />);
}

renderFields() {
return this.props.fields.map((field) => {
renderFields(fields) {
return fields.map((field) => {
const FormItem = Form.Item;
const { name, title, type, readOnly, autoFocus, contentAfter } = field;
const fieldLabel = title || helper.toHuman(name);
const fieldLabel = title || toHuman(name);
const { feedbackIcons, form } = this.props;

const formItemProps = {
Expand Down Expand Up @@ -239,16 +249,35 @@ class DynamicForm extends React.Component {
const submitProps = {
type: 'primary',
htmlType: 'submit',
className: 'w-100',
className: 'w-100 m-t-20',
disabled: this.state.isSubmitting,
loading: this.state.isSubmitting,
};
const { id, hideSubmitButton, saveText } = this.props;
const { id, hideSubmitButton, saveText, fields } = this.props;
const { showExtraFields } = this.state;
const saveButton = !hideSubmitButton;
const extraFields = filter(fields, { extra: true });
const regularFields = difference(fields, extraFields);

return (
<Form id={id} layout="vertical" onSubmit={this.handleSubmit}>
{this.renderFields()}
<Form id={id} className="dynamic-form" layout="vertical" onSubmit={this.handleSubmit}>
{this.renderFields(regularFields)}
{!isEmpty(extraFields) && (
<div className="extra-options">
<Button
type="dashed"
block
className="extra-options-button"
onClick={() => this.setState({ showExtraFields: !showExtraFields })}
>
Additional Settings
<i className={cx('fa m-l-5', { 'fa-caret-up': showExtraFields, 'fa-caret-down': !showExtraFields })} />
</Button>
<Collapse collapsed={!showExtraFields} className="extra-options-content">
{this.renderFields(extraFields)}
</Collapse>
</div>
)}
{saveButton && <Button {...submitProps}>{saveText}</Button>}
{this.renderActions()}
</Form>
Expand Down
29 changes: 29 additions & 0 deletions client/app/components/dynamic-form/DynamicForm.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
@import '~@/assets/less/ant';

.dynamic-form{
.extra-options {
margin: 25px 0 10px;
}

.extra-options-button {
&, &:focus, &:hover {
height: 40px;
font-weight: 500;
background-color: @btn-danger-bg;
border-color: @btn-danger-border;
color: @btn-default-color;
}

&:focus, &:hover {
background-color: fade(@btn-danger-bg, 15%);
}
}

.extra-options-content {
margin-top: 15px;

.ant-form-item:last-of-type {
margin-bottom: 0 !important;
}
}
}
7 changes: 2 additions & 5 deletions client/app/components/dynamic-form/dynamicFormHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ function orderedInputs(properties, order, targetOptions) {
type: properties[key].type,
placeholder: properties[key].default && properties[key].default.toString(),
required: properties[key].required,
extra: properties[key].extra,
initialValue: targetOptions[key],
};

Expand Down Expand Up @@ -42,6 +43,7 @@ function normalizeSchema(configurationSchema) {
}

prop.required = includes(configurationSchema.required, name);
prop.extra = includes(configurationSchema.extra_options, name);
});

configurationSchema.order = configurationSchema.order || [];
Expand Down Expand Up @@ -90,10 +92,6 @@ function updateTargetWithValues(target, values) {
});
}

function toHuman(text) {
return text.replace(/_/g, ' ').replace(/(?:^|\s)\S/g, a => a.toUpperCase());
}

function getBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
Expand All @@ -106,6 +104,5 @@ function getBase64(file) {
export default {
getFields,
updateTargetWithValues,
toHuman,
getBase64,
};
1 change: 1 addition & 0 deletions client/app/components/proptypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export const Field = PropTypes.shape({
content: PropTypes.node,
mode: PropTypes.string,
required: PropTypes.bool,
extra: PropTypes.bool,
readOnly: PropTypes.bool,
autoFocus: PropTypes.bool,
minLength: PropTypes.number,
Expand Down
3 changes: 2 additions & 1 deletion redash/destinations/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ def configuration_schema(cls):
"title": "Subject Template"
}
},
"required": ["addresses"]
"required": ["addresses"],
"extra_options": ["subject_template"]
}

@classmethod
Expand Down
3 changes: 3 additions & 0 deletions redash/query_runner/athena.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def configuration_schema(cls):
},
},
'required': ['region', 's3_staging_dir'],
'extra_options': ['glue'],
'order': ['region', 's3_staging_dir', 'schema', 'work_group'],
'secret': ['aws_secret_key']
}
Expand All @@ -101,6 +102,8 @@ def configuration_schema(cls):
'title': 'KMS Key',
},
})
schema['extra_options'].append('encryption_option')
schema['extra_options'].append('kms_key')

if ASSUME_ROLE:
del schema['properties']['aws_access_key']
Expand Down
1 change: 1 addition & 0 deletions redash/query_runner/clickhouse.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def configuration_schema(cls):
}
},
"required": ["dbname"],
"extra_options": ["timeout"],
"secret": ["password"]
}

Expand Down