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

feat: validation db modal #14832

Merged
merged 3 commits into from
Jun 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions superset-frontend/src/components/Form/LabeledErrorBoundInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,18 @@ import FormLabel from './FormLabel';
export interface LabeledErrorBoundInputProps {
label?: string;
validationMethods:
| { onBlur: (value: any) => string }
| { onChange: (value: any) => string };
| { onBlur: (value: any) => void }
| { onChange: (value: any) => void };
errorMessage: string | null;
helpText?: string;
required?: boolean;
id?: string;
classname?: string;
[x: string]: any;
}

const StyledInput = styled(Input)`
margin: 8px 0;
margin: ${({ theme }) => `${theme.gridUnit}px 0 ${theme.gridUnit * 2}px`};
`;

const alertIconStyles = (theme: SupersetTheme, hasError: boolean) => css`
Expand All @@ -60,6 +61,12 @@ const alertIconStyles = (theme: SupersetTheme, hasError: boolean) => css`
}
}`}
`;
const StyledFormGroup = styled('div')`
margin-bottom: ${({ theme }) => theme.gridUnit * 5}px;
.ant-form-item {
margin-bottom: 0;
}
`;

const LabeledErrorBoundInput = ({
label,
Expand All @@ -68,9 +75,10 @@ const LabeledErrorBoundInput = ({
helpText,
required = false,
id,
className,
...props
}: LabeledErrorBoundInputProps) => (
<>
<StyledFormGroup className={className}>
<FormLabel htmlFor={id} required={required}>
{label}
</FormLabel>
Expand All @@ -83,7 +91,7 @@ const LabeledErrorBoundInput = ({
>
<StyledInput {...props} {...validationMethods} />
</FormItem>
</>
</StyledFormGroup>
);

export default LabeledErrorBoundInput;
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ import { Tooltip } from 'src/components/Tooltip';
import Icons from 'src/components/Icons';
import ListView, { FilterOperator, Filters } from 'src/components/ListView';
import { commonMenuData } from 'src/views/CRUD/data/common';
import DatabaseModal from 'src/views/CRUD/data/database/DatabaseModal';
import ImportModelsModal from 'src/components/ImportModal/index';
import DatabaseModal from './DatabaseModal';

import { DatabaseObject } from './types';

const PAGE_SIZE = 25;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@
* under the License.
*/
import React, { FormEvent } from 'react';
import cx from 'classnames';
import { SupersetTheme, JsonObject } from '@superset-ui/core';
import { InputProps } from 'antd/lib/input';
import { FormLabel, FormItem } from 'src/components/Form';
import { Input } from 'src/common/components';
import { StyledFormHeader, formScrollableStyles } from './styles';
import ValidatedInput from 'src/components/Form/LabeledErrorBoundInput';
import {
StyledFormHeader,
formScrollableStyles,
validatedFormStyles,
} from './styles';
import { DatabaseForm } from '../types';

export const FormFieldOrder = [
Expand All @@ -33,64 +36,137 @@ export const FormFieldOrder = [
'database_name',
];

const CHANGE_METHOD = {
onChange: 'onChange',
onPropertiesChange: 'onPropertiesChange',
};
interface FieldPropTypes {
required: boolean;
changeMethods: { onParametersChange: (value: any) => string } & {
onChange: (value: any) => string;
};
validationErrors: JsonObject | null;
getValidation: () => void;
}

const hostField = ({
Copy link
Member

Choose a reason for hiding this comment

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

nit: can we move this to a separate files in the next iteration

Copy link
Member Author

Choose a reason for hiding this comment

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

yes for sure

required,
changeMethods,
getValidation,
validationErrors,
}: FieldPropTypes) => (
<ValidatedInput
id="host"
name="host"
required={required}
validationMethods={{ onBlur: getValidation }}
errorMessage={validationErrors?.host}
placeholder="e.g. 127.0.0.1"
className="form-group-w-50"
label="Host"
onChange={changeMethods.onParametersChange}
/>
);
const portField = ({
required,
changeMethods,
getValidation,
validationErrors,
}: FieldPropTypes) => (
<ValidatedInput
id="port"
name="port"
required={required}
validationMethods={{ onBlur: getValidation }}
errorMessage={validationErrors?.port}
placeholder="e.g. 5432"
className="form-group-w-50"
label="Port"
onChange={changeMethods.onParametersChange}
/>
);
const databaseField = ({
required,
changeMethods,
getValidation,
validationErrors,
}: FieldPropTypes) => (
<ValidatedInput
id="database"
name="database"
required={required}
validationMethods={{ onBlur: getValidation }}
errorMessage={validationErrors?.database}
placeholder="e.g. world_population"
label="Database name"
onChange={changeMethods.onParametersChange}
helpText="Copy the name of the PostgreSQL database you are trying to connect to."
/>
);
const usernameField = ({
required,
changeMethods,
getValidation,
validationErrors,
}: FieldPropTypes) => (
<ValidatedInput
id="username"
name="username"
required={required}
validationMethods={{ onBlur: getValidation }}
errorMessage={validationErrors?.username}
placeholder="e.g. Analytics"
label="Username"
onChange={changeMethods.onParametersChange}
/>
);
const passwordField = ({
required,
changeMethods,
getValidation,
validationErrors,
}: FieldPropTypes) => (
<ValidatedInput
id="password"
name="password"
required={required}
validationMethods={{ onBlur: getValidation }}
errorMessage={validationErrors?.password}
placeholder="e.g. ********"
label="Password"
onChange={changeMethods.onParametersChange}
/>
);
const displayField = ({
required,
changeMethods,
getValidation,
validationErrors,
}: FieldPropTypes) => (
<ValidatedInput
id="database_name"
name="database_name"
required={required}
validationMethods={{ onBlur: getValidation }}
errorMessage={validationErrors?.database_name}
placeholder=""
label="Display Name"
onChange={changeMethods.onChange}
helpText="Pick a nickname for this database to display as in Superset."
/>
);

const FORM_FIELD_MAP = {
host: {
description: 'Host',
type: 'text',
className: 'w-50',
placeholder: 'e.g. 127.0.0.1',
changeMethod: CHANGE_METHOD.onPropertiesChange,
},
port: {
description: 'Port',
type: 'text',
className: 'w-50',
placeholder: 'e.g. 5432',
changeMethod: CHANGE_METHOD.onPropertiesChange,
},
database: {
description: 'Database name',
type: 'text',
label:
'Copy the name of the PostgreSQL database you are trying to connect to.',
placeholder: 'e.g. world_population',
changeMethod: CHANGE_METHOD.onPropertiesChange,
},
username: {
description: 'Username',
type: 'text',
placeholder: 'e.g. Analytics',
changeMethod: CHANGE_METHOD.onPropertiesChange,
},
password: {
description: 'Password',
type: 'text',
placeholder: 'e.g. ********',
changeMethod: CHANGE_METHOD.onPropertiesChange,
},
database_name: {
description: 'Display Name',
type: 'text',
label: 'Pick a nickname for this database to display as in Superset.',
changeMethod: CHANGE_METHOD.onChange,
},
query: {
additionalProperties: {},
description: 'Additional parameters',
type: 'object',
changeMethod: CHANGE_METHOD.onPropertiesChange,
},
host: hostField,
port: portField,
database: databaseField,
username: usernameField,
password: passwordField,
database_name: displayField,
};

const DatabaseConnectionForm = ({
dbModel: { name, parameters },
onParametersChange,
onChange,
validationErrors,
getValidation,
}: {
dbModel: DatabaseForm;
onParametersChange: (
Expand All @@ -99,6 +175,8 @@ const DatabaseConnectionForm = ({
onChange: (
event: FormEvent<InputProps> | { target: HTMLInputElement },
) => void;
validationErrors: JsonObject | null;
getValidation: () => void;
}) => (
<>
<StyledFormHeader>
Expand All @@ -107,52 +185,30 @@ const DatabaseConnectionForm = ({
Need help? Learn more about connecting to {name}.
</p>
</StyledFormHeader>
<div css={formScrollableStyles}>
<div
// @ts-ignore
css={(theme: SupersetTheme) => [
Copy link
Member Author

Choose a reason for hiding this comment

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

we have to look into why this isn't using InterpolationWithTheme from @emotion/core and picking up Interpolation from @emotion/react instead. It seems to only happen this way with regular html elements. AntD components use InterpolationWithTheme.

formScrollableStyles,
validatedFormStyles(theme),
]}
>
{parameters &&
FormFieldOrder.filter(
(key: string) =>
Object.keys(parameters.properties).includes(key) ||
key === 'database_name',
).map(field => {
const {
className,
description,
type,
placeholder,
label,
changeMethod,
} = FORM_FIELD_MAP[field];
const onEdit =
changeMethod === CHANGE_METHOD.onChange
? onChange
: onParametersChange;
return (
<FormItem
className={cx(className, `form-group-${className}`)}
key={field}
>
<FormLabel
htmlFor={field}
required={parameters.required.includes(field)}
>
{description}
</FormLabel>
<Input
name={field}
type={type}
id={field}
autoComplete="off"
placeholder={placeholder}
onChange={onEdit}
/>
<p className="helper">{label}</p>
</FormItem>
);
})}
).map(field =>
FORM_FIELD_MAP[field]({
required: parameters.required.includes(field),
changeMethods: { onParametersChange, onChange },
validationErrors,
getValidation,
key: field,
}),
)}
</div>
</>
);

export const FormFieldMap = FORM_FIELD_MAP;

export default DatabaseConnectionForm;
Loading