This is an automated email from the ASF dual-hosted git repository. maximebeauchemin pushed a commit to branch fire-alert in repository https://gitbox.apache.org/repos/asf/superset.git
commit ad23b18b1024b6926fb281ea55ebe46a3597e06e Author: Maxime Beauchemin <[email protected]> AuthorDate: Wed Sep 10 00:26:03 2025 -0700 refactor: Migrate key AlertReportModal fields to ModalFormField pattern Migrate critical form fields in AlertReportModal to use the modern ModalFormField pattern for consistent styling and dynamic validation feedback: - Database field (Sections.Content validation) - SQL Query field (Sections.Content validation) - Trigger Alert If... condition (Sections.Alert validation) - Value threshold (Sections.Alert validation) - Timezone (Sections.Schedule validation) - Log retention (Sections.Schedule validation) - Working timeout/Grace period (Sections.Schedule validation) Benefits: - Dynamic asterisk coloring: subtle by default, red on validation errors - Consistent form field styling across all Superset modals - Better accessibility and user experience - Proper tooltip integration in ModalFormField - Cleaner code by removing custom StyledInputContainer usage - Removed unused imports and CSS for cleaner codebase This continues the migration from custom form styling to the standard ModalFormField pattern used by Chart/Dashboard properties modals. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> --- .../src/features/alerts/AlertReportModal.tsx | 283 +++++++++++---------- 1 file changed, 143 insertions(+), 140 deletions(-) diff --git a/superset-frontend/src/features/alerts/AlertReportModal.tsx b/superset-frontend/src/features/alerts/AlertReportModal.tsx index ace9501303..79dc5d5a02 100644 --- a/superset-frontend/src/features/alerts/AlertReportModal.tsx +++ b/superset-frontend/src/features/alerts/AlertReportModal.tsx @@ -32,7 +32,6 @@ import { FeatureFlag, styled, SupersetClient, - SupersetTheme, t, VizType, useTheme, @@ -44,7 +43,6 @@ import { Checkbox, Collapse, CollapseLabelInModal, - InfoTooltip, Input, InputNumber, Select, @@ -300,10 +298,6 @@ const StyledNotificationMethodWrapper = styled.div` } `; -const inputSpacer = (theme: SupersetTheme) => css` - margin-right: ${theme.sizeUnit * 3}px; -`; - type NotificationAddStatus = 'active' | 'disabled' | 'hidden'; interface NotificationMethodAddProps { @@ -1499,40 +1493,46 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({ ), children: ( <div> - <StyledInputContainer> - <div className="control-label"> - {t('Database')} - <span className="required">*</span> - </div> - <div className="input-container"> - <AsyncSelect - ariaLabel={t('Database')} - name="source" - placeholder={t('Select database')} - value={ - currentAlert?.database?.label && - currentAlert?.database?.value - ? { - value: currentAlert.database.value, - label: currentAlert.database.label, - } - : undefined - } - options={loadSourceOptions} - onChange={onSourceChange} - /> - </div> - </StyledInputContainer> - <StyledInputContainer> - <div className="control-label"> - {t('SQL Query')} - <InfoTooltip - tooltip={t( - 'The result of this query must be a value capable of numeric interpretation e.g. 1, 1.0, or "1" (compatible with Python\'s float() function).', - )} - /> - <span className="required">*</span> - </div> + <ModalFormField + label={t('Database')} + required + error={ + validationStatus[Sections.Content]?.hasErrors && + !currentAlert?.database + ? t('Database is required') + : undefined + } + > + <AsyncSelect + ariaLabel={t('Database')} + name="source" + placeholder={t('Select database')} + value={ + currentAlert?.database?.label && + currentAlert?.database?.value + ? { + value: currentAlert.database.value, + label: currentAlert.database.label, + } + : undefined + } + options={loadSourceOptions} + onChange={onSourceChange} + /> + </ModalFormField> + <ModalFormField + label={t('SQL Query')} + required + tooltip={t( + 'The result of this query must be a value capable of numeric interpretation e.g. 1, 1.0, or "1" (compatible with Python\'s float() function).', + )} + error={ + validationStatus[Sections.Content]?.hasErrors && + !resource?.sql + ? t('SQL Query is required') + : undefined + } + > <TextAreaControl name="sql" language="sql" @@ -1544,57 +1544,60 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({ initialValue={resource?.sql} key={currentAlert?.id} /> - </StyledInputContainer> + </ModalFormField> <div className="inline-container wrap" css={css` gap: ${theme.sizeUnit}px; `} > - <StyledInputContainer css={noMarginBottom}> - <div className="control-label" css={inputSpacer}> - {t('Trigger Alert If...')} - <span className="required">*</span> - </div> - <div className="input-container"> - <Select - ariaLabel={t('Condition')} - onChange={onConditionChange} - placeholder={t('Condition')} - value={ - currentAlert?.validator_config_json?.op || - undefined - } - options={CONDITIONS} - /> - </div> - </StyledInputContainer> - <StyledInputContainer css={noMarginBottom}> - <div className="control-label"> - {t('Value')}{' '} - {!conditionNotNull && ( - <span className="required">*</span> - )} - </div> - <div className="input-container"> - <InputNumber - disabled={conditionNotNull} - type="number" - name="threshold" - value={ - currentAlert?.validator_config_json - ?.threshold !== undefined && - !conditionNotNull - ? currentAlert.validator_config_json - .threshold - : '' - } - min={0} - placeholder={t('Value')} - onChange={onThresholdChange} - /> - </div> - </StyledInputContainer> + <ModalFormField + label={t('Trigger Alert If...')} + required + error={ + validationStatus[Sections.Alert]?.hasErrors && + !currentAlert?.validator_config_json?.op + ? t('Condition is required') + : undefined + } + > + <Select + ariaLabel={t('Condition')} + onChange={onConditionChange} + placeholder={t('Condition')} + value={ + currentAlert?.validator_config_json?.op || + undefined + } + options={CONDITIONS} + /> + </ModalFormField> + <ModalFormField + label={t('Value')} + required={!conditionNotNull} + error={ + validationStatus[Sections.Alert]?.hasErrors && + !conditionNotNull && + !currentAlert?.validator_config_json?.threshold + ? t('Value is required') + : undefined + } + > + <InputNumber + disabled={conditionNotNull} + type="number" + name="threshold" + value={ + currentAlert?.validator_config_json + ?.threshold !== undefined && !conditionNotNull + ? currentAlert.validator_config_json.threshold + : '' + } + min={0} + placeholder={t('Value')} + onChange={onThresholdChange} + /> + </ModalFormField> </div> </div> ), @@ -1791,66 +1794,66 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({ value={currentAlert?.crontab || ''} onChange={newVal => updateAlertState('crontab', newVal)} /> - <StyledInputContainer> - <div className="control-label"> - {t('Timezone')} <span className="required">*</span> - </div> + <ModalFormField + label={t('Timezone')} + required + error={ + validationStatus[Sections.Schedule]?.hasErrors && + !currentAlert?.timezone + ? t('Timezone is required') + : undefined + } + > <TimezoneSelector onTimezoneChange={onTimezoneChange} timezone={currentAlert?.timezone} minWidth="100%" /> - </StyledInputContainer> - <StyledInputContainer> - <div className="control-label"> - {t('Log retention')} - <span className="required">*</span> - </div> - <div className="input-container"> - <Select - ariaLabel={t('Log retention')} - placeholder={t('Log retention')} - onChange={onLogRetentionChange} - value={currentAlert?.log_retention} - options={RETENTION_OPTIONS} - sortComparator={propertyComparator('value')} - /> - </div> - </StyledInputContainer> - <StyledInputContainer css={noMarginBottom}> - {isReport ? ( - <> - <div className="control-label"> - {t('Working timeout')} - <span className="required">*</span> - </div> - <div className="input-container"> - <NumberInput - min={1} - name="working_timeout" - value={currentAlert?.working_timeout || ''} - placeholder={t('Time in seconds')} - onChange={onTimeoutVerifyChange} - timeUnit={t('seconds')} - /> - </div> - </> - ) : ( - <> - <div className="control-label">{t('Grace period')}</div> - <div className="input-container"> - <NumberInput - min={1} - name="grace_period" - value={currentAlert?.grace_period || ''} - placeholder={t('Time in seconds')} - onChange={onTimeoutVerifyChange} - timeUnit={t('seconds')} - /> - </div> - </> - )} - </StyledInputContainer> + </ModalFormField> + <ModalFormField + label={t('Log retention')} + required + error={ + validationStatus[Sections.Schedule]?.hasErrors && + !currentAlert?.log_retention + ? t('Log retention is required') + : undefined + } + > + <Select + ariaLabel={t('Log retention')} + placeholder={t('Log retention')} + onChange={onLogRetentionChange} + value={currentAlert?.log_retention} + options={RETENTION_OPTIONS} + sortComparator={propertyComparator('value')} + /> + </ModalFormField> + <ModalFormField + label={isReport ? t('Working timeout') : t('Grace period')} + required={isReport} + error={ + validationStatus[Sections.Schedule]?.hasErrors && + isReport && + !currentAlert?.working_timeout + ? t('Working timeout is required') + : undefined + } + bottomSpacing={false} + > + <NumberInput + min={1} + name={isReport ? 'working_timeout' : 'grace_period'} + value={ + isReport + ? currentAlert?.working_timeout || '' + : currentAlert?.grace_period || '' + } + placeholder={t('Time in seconds')} + onChange={onTimeoutVerifyChange} + timeUnit={t('seconds')} + /> + </ModalFormField> </> ), },
