Skip to content

Commit

Permalink
Merge branch 'pexdax/db-connection-ui' into arash/errorAlerts
Browse files Browse the repository at this point in the history
  • Loading branch information
AAfghahi authored Jun 22, 2021
2 parents a8bc0f5 + 7c42a7c commit b7254f3
Show file tree
Hide file tree
Showing 12 changed files with 372 additions and 185 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const InteractiveLabeledErrorBoundInput = ({
placeholder,
type,
id,
tooltipText,
}: LabeledErrorBoundInputProps) => {
const [currentValue, setCurrentValue] = useState(value);

Expand All @@ -58,6 +59,8 @@ export const InteractiveLabeledErrorBoundInput = ({
placeholder={placeholder}
type={type}
required
hasTooltip
tooltipText={tooltipText}
/>
);
};
Expand All @@ -66,6 +69,7 @@ InteractiveLabeledErrorBoundInput.args = {
name: 'Username',
placeholder: 'Example placeholder text...',
id: 1,
tooltipText: 'This is a tooltip',
};

InteractiveLabeledErrorBoundInput.argTypes = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/
import React from 'react';
import { render, screen } from 'spec/helpers/testing-library';
import { render, fireEvent, screen } from 'spec/helpers/testing-library';
import LabeledErrorBoundInput from 'src/components/Form/LabeledErrorBoundInput';

const defaultProps = {
Expand All @@ -27,6 +27,8 @@ const defaultProps = {
validationMethods: () => {},
errorMessage: '',
helpText: 'This is a line of example help text',
hasTooltip: false,
tooltipText: 'This is a tooltip',
value: '',
placeholder: 'Example placeholder text...',
type: 'textbox',
Expand Down Expand Up @@ -58,4 +60,19 @@ describe('LabeledErrorBoundInput', () => {
expect(textboxInput).toBeVisible();
expect(errorText).toBeVisible();
});
it('renders a LabledErrorBoundInput with a InfoTooltip', async () => {
defaultProps.hasTooltip = true;
render(<LabeledErrorBoundInput {...defaultProps} />);

const label = screen.getByText(/username/i);
const textboxInput = screen.getByRole('textbox');
const tooltipIcon = screen.getByTestId('info-solid-small');

fireEvent.mouseOver(tooltipIcon);

expect(tooltipIcon).toBeVisible();
expect(label).toBeVisible();
expect(textboxInput).toBeVisible();
expect(await screen.findByText('This is a tooltip')).toBeInTheDocument();
});
});
22 changes: 21 additions & 1 deletion superset-frontend/src/components/Form/LabeledErrorBoundInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import React from 'react';
import { Input } from 'antd';
import { styled, css, SupersetTheme } from '@superset-ui/core';
import InfoTooltip from 'src/components/InfoTooltip';
import FormItem from './FormItem';
import FormLabel from './FormLabel';

Expand All @@ -30,6 +31,8 @@ export interface LabeledErrorBoundInputProps {
errorMessage: string | null;
helpText?: string;
required?: boolean;
hasTooltip?: boolean;
tooltipText?: string | null;
id?: string;
classname?: string;
[x: string]: any;
Expand Down Expand Up @@ -61,6 +64,7 @@ const alertIconStyles = (theme: SupersetTheme, hasError: boolean) => css`
}
}`}
`;

const StyledFormGroup = styled('div')`
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
Expand All @@ -73,20 +77,36 @@ const StyledFormGroup = styled('div')`
}
`;

const infoTooltip = (theme: SupersetTheme) => css`
svg {
vertical-align: bottom;
margin-bottom: ${theme.gridUnit * 0.25}px;
}
`;

const LabeledErrorBoundInput = ({
label,
validationMethods,
errorMessage,
helpText,
required = false,
hasTooltip = false,
tooltipText,
id,
className,
...props
}: LabeledErrorBoundInputProps) => (
<StyledFormGroup className={className}>
<FormLabel htmlFor={id} required={required}>
<FormLabel
htmlFor={id}
required={required}
css={(theme: SupersetTheme) => infoTooltip(theme)}
>
{label}
</FormLabel>
{hasTooltip && (
<InfoTooltip tooltip={`${tooltipText}`} viewBox="0 -6 24 24" />
)}
<FormItem
css={(theme: SupersetTheme) => alertIconStyles(theme, !!errorMessage)}
validateTrigger={Object.keys(validationMethods)}
Expand Down
1 change: 1 addition & 0 deletions superset-frontend/src/components/IconButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ const IconButton = styled(
background-color: ${({ theme }) => theme.colors.grayscale.light5};
color: ${({ theme }) => theme.colors.grayscale.dark2};
border: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
box-shadow: 4px 4px 20px ${({ theme }) => theme.colors.grayscale.light2};
}
`;

Expand Down
5 changes: 3 additions & 2 deletions superset-frontend/src/components/InfoTooltip/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ export interface InfoTooltipProps {
trigger?: string | Array<string>;
overlayStyle?: any;
bgColor?: string;
viewBox?: string;
}

const StyledTooltip = styled(Tooltip)`
cursor: pointer;
path:first-of-type {
fill: #999999;
}
Expand All @@ -65,6 +65,7 @@ export default function InfoTooltip({
trigger = 'hover',
overlayStyle = defaultOverlayStyle,
bgColor = defaultColor,
viewBox = '0 -2 24 24',
}: InfoTooltipProps) {
return (
<StyledTooltip
Expand All @@ -74,7 +75,7 @@ export default function InfoTooltip({
overlayStyle={overlayStyle}
color={bgColor}
>
<Icon name="info-solid-small" />
<Icon name="info-solid-small" viewBox={viewBox} />
</StyledTooltip>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export const FormFieldOrder = [

interface FieldPropTypes {
required: boolean;
hasTooltip?: boolean;
tooltipText?: (valuse: any) => string;
onParametersChange: (value: any) => string;
onParametersUploadFileChange: (value: any) => string;
changeMethods: { onParametersChange: (value: any) => string } & {
Expand Down Expand Up @@ -94,20 +96,35 @@ const CredentialsInfo = ({ changeMethods, isEditMode, db }: FieldPropTypes) => {
</>
)}
{uploadOption === CredentialInfoOptions.copyPaste || isEditMode ? (
<div className="input-container" onChange={changeMethods.onChange}>
<div className="input-container">
<span className="label-select">Service Account</span>
<textarea
className="input-form"
name="encrypted_extra"
name="credentials_info"
value={db?.parameters?.credentials_info}
onChange={changeMethods.onParametersChange}
/>
<span className="label-paste">
Copy and paste the entire service account .json file here
</span>
</div>
) : (
<div className="input-container">
<span className="label-select">Upload Credentials</span>
<div
className="input-container"
css={(theme: SupersetTheme) => infoTooltip(theme)}
>
<span
css={{ display: 'flex', alignItems: 'center' }}
className="label-select"
>
Upload Credentials{' '}
<InfoTooltip
tooltip={t(
'Use the JSON file you automatically downloaded when creating your service account in Google BigQuery.',
)}
/>
</span>

{!fileToUpload && (
<Button
className="input-upload-btn"
Expand All @@ -124,7 +141,7 @@ const CredentialsInfo = ({ changeMethods, isEditMode, db }: FieldPropTypes) => {
setFileToUpload(null);
changeMethods.onParametersChange({
target: {
name: 'encrypted_extra',
name: 'credentials_info',
value: '',
},
});
Expand All @@ -146,7 +163,7 @@ const CredentialsInfo = ({ changeMethods, isEditMode, db }: FieldPropTypes) => {
changeMethods.onParametersChange({
target: {
type: null,
name: 'encrypted_extra',
name: 'credentials_info',
value: await file?.text(),
checked: false,
},
Expand Down Expand Up @@ -174,6 +191,10 @@ const hostField = ({
name="host"
value={db?.parameters?.host}
required={required}
hasTooltip
tooltipText={t(
'This can be either an IP address (e.g. 127.0.0.1) or a domain name (e.g. mydatabase.com).',
)}
validationMethods={{ onBlur: getValidation }}
errorMessage={validationErrors?.host}
placeholder="e.g. 127.0.0.1"
Expand Down Expand Up @@ -329,8 +350,8 @@ const forceSSLField = ({
/>
<span css={toggleStyle}>SSL</span>
<InfoTooltip
tooltip={t('SSL will be enabled in the database connection')}
placement="bottomRight"
tooltip={t('SSL Mode "require" will be used.')}
placement="right"
/>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,10 +341,8 @@ const ExtraOptions = ({
</div>
<div className="helper">
{t(
'JSON string containing additional connection configuration. ' +
'This is used to provide connection information for systems ' +
'like Hive, Presto and BigQuery whih do not conform to the ' +
'username:password syntax normally used by SQLAlchemy.',
'Optional CA_BUNDLE contents to validate HTTPS requests. Only ' +
'available on certain database engines.',
)}
</div>
</StyledInputContainer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
CreateHeaderTitle,
CreateHeaderSubtitle,
StyledFormHeader,
StyledStickyHeader,
} from './styles';
import { DatabaseForm, DatabaseObject } from '../types';

Expand All @@ -50,6 +51,7 @@ const ModalHeader = ({
db,
dbName,
dbModel,
editNewDb,
}: {
isLoading: boolean;
isEditMode: boolean;
Expand All @@ -58,16 +60,17 @@ const ModalHeader = ({
db: Partial<DatabaseObject> | null;
dbName: string;
dbModel: DatabaseForm;
editNewDb?: boolean;
}) => {
const isEditHeader = (
<>
<StyledFormHeader>
<EditHeaderTitle>{db?.backend}</EditHeaderTitle>
<EditHeaderSubtitle>{dbName}</EditHeaderSubtitle>
</>
</StyledFormHeader>
);
const useSqlAlchemyFormHeader = (
<>
<p className="helper"> Step 2 of 2 </p>
<StyledFormHeader>
<p className="helper"> STEP 2 OF 2 </p>
<CreateHeaderTitle>Enter Primary Credentials</CreateHeaderTitle>
<CreateHeaderSubtitle>
Need help? Learn how to connect your database{' '}
Expand All @@ -76,11 +79,11 @@ const ModalHeader = ({
</a>
.
</CreateHeaderSubtitle>
</>
</StyledFormHeader>
);
const hasConnectedDbHeader = (
<StyledFormHeader>
<p className="helper"> Step 3 of 3 </p>
<p className="helper"> STEP 3 OF 3 </p>
<h4>
Your database was successfully connected! Here are some optional
settings for your database
Expand All @@ -98,18 +101,27 @@ const ModalHeader = ({
</StyledFormHeader>
);
const hasDbHeader = (
<StyledFormHeader>
<p className="helper"> Step 2 of 3 </p>
<h4>Enter the required {dbModel.name} credentials</h4>
<p className="helper">
Need help? Learn more about connecting to {dbModel.name}.
</p>
</StyledFormHeader>
<StyledStickyHeader>
<StyledFormHeader>
<p className="helper"> Step 2 of 3 </p>
<h4>Enter the required {dbModel.name} credentials</h4>
<p className="helper">
Need help? Learn more about{' '}
<a
href={documentationLink(db?.engine)}
target="_blank"
rel="noopener noreferrer"
>
connecting to {dbModel.name}.
</a>
</p>
</StyledFormHeader>
</StyledStickyHeader>
);
const noDbHeader = (
<StyledFormHeader>
<div className="select-db">
<p className="helper"> Step 1 of 3 </p>
<p className="helper"> STEP 1 OF 3 </p>
<h4>Select a database to connect</h4>
</div>
</StyledFormHeader>
Expand All @@ -122,10 +134,10 @@ const ModalHeader = ({
if (useSqlAlchemyForm) {
return useSqlAlchemyFormHeader;
}
if (hasConnectedDb) {
if (hasConnectedDb && !editNewDb) {
return hasConnectedDbHeader;
}
if (db) {
if (db || editNewDb) {
return hasDbHeader;
}
return noDbHeader;
Expand Down
Loading

0 comments on commit b7254f3

Please sign in to comment.