This is an automated email from the ASF dual-hosted git repository. rusackas pushed a commit to branch SO-164 in repository https://gitbox.apache.org/repos/asf/superset.git
commit 29a001140af62dca088b1266b34efd52403dcd99 Author: Isaac Jaynes <[email protected]> AuthorDate: Thu Dec 11 13:36:09 2025 -0800 SO-164: Added validation to db credential input --- .../src/components/Form/LabeledErrorBoundInput.tsx | 10 +++++ .../superset-ui-core/src/components/Form/types.ts | 2 + .../DatabaseConnectionForm/EncryptedField.tsx | 49 ++++++++++++++-------- .../src/features/databases/DatabaseModal/styles.ts | 25 ++++++----- 4 files changed, 55 insertions(+), 31 deletions(-) diff --git a/superset-frontend/packages/superset-ui-core/src/components/Form/LabeledErrorBoundInput.tsx b/superset-frontend/packages/superset-ui-core/src/components/Form/LabeledErrorBoundInput.tsx index 2095cca2f03..5d23490f9b1 100644 --- a/superset-frontend/packages/superset-ui-core/src/components/Form/LabeledErrorBoundInput.tsx +++ b/superset-frontend/packages/superset-ui-core/src/components/Form/LabeledErrorBoundInput.tsx @@ -28,6 +28,10 @@ const StyledInput = styled(Input)` margin: ${({ theme }) => `${theme.sizeUnit}px 0 ${theme.sizeUnit * 2}px`}; `; +const StyledTextArea = styled(Input.TextArea)` + margin: ${({ theme }) => `${theme.sizeUnit}px 0 ${theme.sizeUnit * 2}px`}; +`; + const StyledInputPassword = styled(Input.Password)` margin: ${({ theme }) => `${theme.sizeUnit}px 0 ${theme.sizeUnit * 2}px`}; `; @@ -98,6 +102,12 @@ export const LabeledErrorBoundInput = ({ } role="textbox" /> + ) : props.renderAsTextArea ? ( + <StyledTextArea + css={props.textAreaCss} + {...props} + {...validationMethods} + /> ) : ( <StyledInput {...props} {...validationMethods} /> )} diff --git a/superset-frontend/packages/superset-ui-core/src/components/Form/types.ts b/superset-frontend/packages/superset-ui-core/src/components/Form/types.ts index ffff65dc35a..be9fc7e2268 100644 --- a/superset-frontend/packages/superset-ui-core/src/components/Form/types.ts +++ b/superset-frontend/packages/superset-ui-core/src/components/Form/types.ts @@ -31,5 +31,7 @@ export interface LabeledErrorBoundInputProps { classname?: string; visibilityToggle?: boolean; isValidating?: boolean; + renderAsTextArea?: boolean; + textAreaCss?: any; [x: string]: any; } diff --git a/superset-frontend/src/features/databases/DatabaseModal/DatabaseConnectionForm/EncryptedField.tsx b/superset-frontend/src/features/databases/DatabaseModal/DatabaseConnectionForm/EncryptedField.tsx index 5c1eec0a302..e4a3edd5a8e 100644 --- a/superset-frontend/src/features/databases/DatabaseModal/DatabaseConnectionForm/EncryptedField.tsx +++ b/superset-frontend/src/features/databases/DatabaseModal/DatabaseConnectionForm/EncryptedField.tsx @@ -20,17 +20,21 @@ import { useState, useEffect } from 'react'; import { t } from '@apache-superset/core/translation'; import { SupersetTheme } from '@apache-superset/core/theme'; import { - Input, Button, FormLabel, Select, Upload, type UploadFile, + LabeledErrorBoundInput as ValidatedInput, } from '@superset-ui/core/components'; import { Icons } from '@superset-ui/core/components/Icons'; import { useToasts } from 'src/components/MessageToasts/withToasts'; import { DatabaseParameters, Engines, FieldPropTypes } from '../../types'; -import { infoTooltip, CredentialInfoForm } from '../styles'; +import { + infoTooltip, + CredentialInfoForm, + CredentialInfoFormTextArea, +} from '../styles'; enum CredentialInfoOptions { JsonUpload, @@ -53,6 +57,9 @@ export const EncryptedField = ({ editNewDb, isPublic = true, setIsPublic, + isValidating, + validationErrors, + getValidation, }: FieldPropTypes) => { const [fileList, setFileList] = useState<UploadFile[]>([]); const [uploadOption, setUploadOption] = useState<number>( @@ -168,22 +175,27 @@ export const EncryptedField = ({ (uploadOption === CredentialInfoOptions.CopyPaste || isEditMode || editNewDb) ? ( - <div className="input-container"> - <FormLabel>{t('Service Account')}</FormLabel> - <Input.TextArea - className="input-form" - name={encryptedField} - value={ - typeof encryptedValue === 'boolean' - ? String(encryptedValue) - : encryptedValue - } - onChange={changeMethods.onParametersChange} - placeholder={t( - 'Paste content of service credentials JSON file here', - )} - /> - </div> + <ValidatedInput + id={encryptedField} + name={encryptedField} + required={false} + isValidating={isValidating} + value={ + typeof encryptedValue === 'boolean' + ? String(encryptedValue) + : encryptedValue + } + validationMethods={{ onBlur: getValidation }} + errorMessage={validationErrors?.query} + placeholder={t('Paste content of service credentials JSON file here')} + label={t('Service Account')} + onChange={changeMethods.onParametersChange} + helpText={t('Add service credentials')} + renderAsTextArea + textAreaCss={(theme: SupersetTheme) => + CredentialInfoFormTextArea(theme) + } + /> ) : ( showCredentialsInfo && ( <div @@ -220,6 +232,7 @@ export const EncryptedField = ({ }, }); setFileList(info.fileList); + getValidation(); } catch { setFileList([]); addDangerToast( diff --git a/superset-frontend/src/features/databases/DatabaseModal/styles.ts b/superset-frontend/src/features/databases/DatabaseModal/styles.ts index 6e065acebb6..545d5182078 100644 --- a/superset-frontend/src/features/databases/DatabaseModal/styles.ts +++ b/superset-frontend/src/features/databases/DatabaseModal/styles.ts @@ -383,6 +383,18 @@ export const EditHeaderSubtitle = styled.div` font-weight: ${({ theme }) => theme.fontWeightStrong}; `; +export const CredentialInfoFormTextArea = (theme: SupersetTheme) => css` + height: 100px; + width: 100%; + border: 1px solid ${theme.colorBorder}; + border-radius: ${theme.borderRadius}px; + resize: vertical; + padding: ${theme.sizeUnit * 1.5}px ${theme.sizeUnit * 2}px; + &::placeholder { + color: ${theme.colorTextPlaceholder}; + } +`; + export const CredentialInfoForm = styled.div` margin-top: ${({ theme }) => theme.sizeUnit * 4}px; @@ -413,19 +425,6 @@ export const CredentialInfoForm = styled.div` margin: ${({ theme }) => theme.sizeUnit * 4}px 0; display: flex; flex-direction: column; -} - } - .input-form { - height: 100px; - width: 100%; - border: 1px solid ${({ theme }) => theme.colorBorder}; - border-radius: ${({ theme }) => theme.borderRadius}px; - resize: vertical; - padding: ${({ theme }) => theme.sizeUnit * 1.5}px - ${({ theme }) => theme.sizeUnit * 2}px; - &::placeholder { - color: ${({ theme }) => theme.colorTextPlaceholder}; - } } .input-container {
