This is an automated email from the ASF dual-hosted git repository. enzomartellucci pushed a commit to branch enxdev/fix/visual-regression-p7 in repository https://gitbox.apache.org/repos/asf/superset.git
commit dbbf959d73451af986da64219a96bae48e7a75f0 Author: Enzo Martellucci <enzomartellu...@gmail.com> AuthorDate: Fri Jul 18 17:16:01 2025 +0200 refactor(Modal titles): create reusable ModalTitleWithIcon component with edit mode support and customizable icon - Supports dynamic titles based on edit mode (add/edit) - Allows passing a custom icon with forced iconSize override - Applies consistent styling and spacing for title and icon - Adds data-test attribute for easier testing - Improves code clarity by handling icon rendering and translation internally --- .../SqlLab/components/SaveDatasetModal/index.tsx | 23 +++--- .../src/SqlLab/components/SaveQuery/index.tsx | 11 ++- .../src/components/ModalTitleWithIcon/index.tsx | 84 ++++++++++++++++++++++ .../dashboard/components/PropertiesModal/index.tsx | 3 +- .../explore/components/PropertiesModal/index.tsx | 21 +++--- .../src/features/alerts/AlertReportModal.tsx | 23 ++++-- .../annotationLayers/AnnotationLayerModal.tsx | 35 +++------ .../src/features/cssTemplates/CssTemplateModal.tsx | 36 +++------- .../src/features/databases/DatabaseModal/index.tsx | 55 +++++++------- .../features/databases/UploadDataModel/index.tsx | 3 +- .../features/datasets/DuplicateDatasetModal.tsx | 10 ++- .../src/features/groups/GroupListModal.tsx | 11 ++- .../src/features/rls/RowLevelSecurityModal.tsx | 31 +++----- .../src/features/roles/RoleListAddModal.tsx | 8 ++- .../src/features/tags/BulkTagModal.tsx | 4 +- superset-frontend/src/features/tags/TagModal.tsx | 12 +++- .../src/features/users/UserListModal.tsx | 11 ++- superset-frontend/src/pages/GroupsList/index.tsx | 2 - superset-frontend/src/pages/UserInfo/index.tsx | 1 - 19 files changed, 235 insertions(+), 149 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx index f3d34d75c8..0fa9effb93 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx @@ -18,7 +18,7 @@ */ import { useCallback, useState, FormEvent } from 'react'; - +import { ModalTitleWithIcon } from 'src/components/ModalTitleWithIcon'; import { Radio, RadioChangeEvent } from '@superset-ui/core/components/Radio'; import { AsyncSelect, @@ -27,6 +27,8 @@ import { Modal, Input, type SelectValue, + Icons, + Flex, } from '@superset-ui/core/components'; import { styled, @@ -372,17 +374,16 @@ export const SaveDatasetModal = ({ return ( <Modal show={visible} - title={t('Save or Overwrite Dataset')} + title={ + <ModalTitleWithIcon + title={t('Save or Overwrite Dataset')} + icon={<Icons.SaveOutlined />} + dataTestId="save-or-overwrite-dataset-title" + /> + } onHide={onHide} footer={ - <div - style={{ - display: 'flex', - alignItems: 'center', - justifyContent: 'flex-end', - gap: '8px', - }} - > + <Flex align="center" justify="flex-end" gap="8px"> {isFeatureEnabled(FeatureFlag.EnableTemplateProcessing) && ( <div style={{ display: 'flex', alignItems: 'center' }}> <Checkbox @@ -422,7 +423,7 @@ export const SaveDatasetModal = ({ </Button> </> )} - </div> + </Flex> } > <Styles> diff --git a/superset-frontend/src/SqlLab/components/SaveQuery/index.tsx b/superset-frontend/src/SqlLab/components/SaveQuery/index.tsx index c5918d1b7c..9e1e52e909 100644 --- a/superset-frontend/src/SqlLab/components/SaveQuery/index.tsx +++ b/superset-frontend/src/SqlLab/components/SaveQuery/index.tsx @@ -17,7 +17,6 @@ * under the License. */ import { useState, useEffect, useMemo, ChangeEvent } from 'react'; - import type { DatabaseObject } from 'src/features/databases/types'; import { t, styled } from '@superset-ui/core'; import { @@ -28,6 +27,7 @@ import { Modal, Row, Col, + Icons, } from '@superset-ui/core/components'; import { Menu } from '@superset-ui/core/components/Menu'; import SaveDatasetActionButton from 'src/SqlLab/components/SaveDatasetActionButton'; @@ -43,6 +43,7 @@ import { LOG_ACTIONS_SQLLAB_CREATE_CHART, LOG_ACTIONS_SQLLAB_SAVE_QUERY, } from 'src/logger/LogUtils'; +import { ModalTitleWithIcon } from 'src/components/ModalTitleWithIcon'; interface SaveQueryProps { queryEditorId: string; @@ -224,7 +225,13 @@ const SaveQuery = ({ primaryButtonName={isSaved ? t('Save') : t('Save as')} width="620px" show={showSave} - title={<h4>{t('Save query')}</h4>} + title={ + <ModalTitleWithIcon + title={t('Save query')} + icon={<Icons.SaveOutlined />} + dataTestId="save-query-modal-title" + /> + } footer={ <> <Button diff --git a/superset-frontend/src/components/ModalTitleWithIcon/index.tsx b/superset-frontend/src/components/ModalTitleWithIcon/index.tsx new file mode 100644 index 0000000000..2d3dd4145c --- /dev/null +++ b/superset-frontend/src/components/ModalTitleWithIcon/index.tsx @@ -0,0 +1,84 @@ +import { isValidElement, cloneElement } from 'react'; +import { css, useTheme, t } from '@superset-ui/core'; +import { Typography, Icons } from '@superset-ui/core/components'; + +type EditModeTitleConfig = { + /** Indicates whether the component is in edit mode */ + isEditMode: boolean; + /** Title shown when not in edit mode */ + titleAdd: string; + /** Title shown when in edit mode */ + titleEdit: string; +}; + +type ModalTitleWithIconProps = { + /** + * Optional configuration for dynamic titles based on edit mode. + * If provided, it overrides the static `title` prop. + */ + editModeConfig?: EditModeTitleConfig; + + /** + * Static title used when `editModeConfig` is not provided. + */ + title?: string; + + /** + * Optional icon displayed before the title. + * If not provided and `editModeConfig` is set, a default icon is used: + * - `EditOutlined` if `isEditMode === true` + * - `PlusOutlined` if `isEditMode === false` + */ + icon?: React.ReactNode; + + /** + * Test ID used for end-to-end or unit testing (e.g. Cypress, Testing Library). + */ + dataTestId?: string; + + /** + * Ant Design Typography title level (default: 5) + */ + level?: 1 | 2 | 3 | 4 | 5; +}; + +export const ModalTitleWithIcon = ({ + editModeConfig, + title, + icon, + dataTestId = 'css-template-modal-title', + level = 5, +}: ModalTitleWithIconProps) => { + const theme = useTheme(); + const iconStyles = css` + margin: auto ${theme.sizeUnit * 2}px auto 0; + `; + const titleStyles = css` + && { + margin: 0; + margin-bottom: 0; + } + `; + const renderedTitle = editModeConfig + ? editModeConfig.isEditMode + ? editModeConfig.titleEdit + : editModeConfig.titleAdd + : title; + + const renderedIcon = isValidElement(icon) ? ( + cloneElement(icon, { iconSize: 'l', css: iconStyles }) + ) : editModeConfig ? ( + editModeConfig.isEditMode ? ( + <Icons.EditOutlined iconSize="l" css={iconStyles} /> + ) : ( + <Icons.PlusOutlined iconSize="l" css={iconStyles} /> + ) + ) : null; + + return ( + <Typography.Title level={level} css={titleStyles} data-test={dataTestId}> + {renderedIcon} + {t(renderedTitle || '')} + </Typography.Title> + ); +}; diff --git a/superset-frontend/src/dashboard/components/PropertiesModal/index.tsx b/superset-frontend/src/dashboard/components/PropertiesModal/index.tsx index 0871b63b05..2b4ea97f88 100644 --- a/superset-frontend/src/dashboard/components/PropertiesModal/index.tsx +++ b/superset-frontend/src/dashboard/components/PropertiesModal/index.tsx @@ -64,6 +64,7 @@ import { setDashboardMetadata, } from 'src/dashboard/actions/dashboardState'; import { areObjectsEqual } from 'src/reduxUtils'; +import { ModalTitleWithIcon } from 'src/components/ModalTitleWithIcon'; const StyledJsonEditor = styled(JsonEditor)` border-radius: ${({ theme }) => theme.borderRadius}px; @@ -609,7 +610,7 @@ const PropertiesModal = ({ <Modal show={show} onHide={handleOnCancel} - title={t('Dashboard properties')} + title={<ModalTitleWithIcon title="Dashboard properties" />} footer={ <> <Button diff --git a/superset-frontend/src/explore/components/PropertiesModal/index.tsx b/superset-frontend/src/explore/components/PropertiesModal/index.tsx index 63d7824c90..8926a74ed8 100644 --- a/superset-frontend/src/explore/components/PropertiesModal/index.tsx +++ b/superset-frontend/src/explore/components/PropertiesModal/index.tsx @@ -40,13 +40,12 @@ import { getClientErrorObject, ensureIsArray, useTheme, - css, } from '@superset-ui/core'; -import { Icons } from '@superset-ui/core/components/Icons'; import Chart, { Slice } from 'src/types/Chart'; import withToasts from 'src/components/MessageToasts/withToasts'; import { type TagType } from 'src/components'; import { loadTags } from 'src/components/Tag/utils'; +import { ModalTitleWithIcon } from 'src/components/ModalTitleWithIcon'; export type PropertiesModalProps = { slice: Slice; @@ -69,7 +68,6 @@ function PropertiesModal({ show, addSuccessToast, }: PropertiesModalProps) { - const theme = useTheme(); const [submitting, setSubmitting] = useState(false); const [form] = Form.useForm(); // values of form inputs @@ -246,15 +244,14 @@ function PropertiesModal({ show={show} onHide={onHide} title={ - <span> - <Icons.EditOutlined - css={css` - margin: auto ${theme.sizeUnit * 2}px auto 0; - `} - data-test="edit-alt" - /> - {t('Edit Chart Properties')} - </span> + <ModalTitleWithIcon + editModeConfig={{ + isEditMode: true, + titleAdd: '', + titleEdit: 'Edit Chart Properties', + }} + dataTestId="edit-alt" + /> } footer={ <> diff --git a/superset-frontend/src/features/alerts/AlertReportModal.tsx b/superset-frontend/src/features/alerts/AlertReportModal.tsx index 9376949415..2d797196d7 100644 --- a/superset-frontend/src/features/alerts/AlertReportModal.tsx +++ b/superset-frontend/src/features/alerts/AlertReportModal.tsx @@ -35,6 +35,7 @@ import { SupersetTheme, t, VizType, + useTheme, } from '@superset-ui/core'; import rison from 'rison'; import { useSingleViewResource } from 'src/views/CRUD/hooks'; @@ -51,7 +52,6 @@ import { Switch, TreeSelect, type CheckboxChangeEvent, - Typography, } from '@superset-ui/core/components'; import TimezoneSelector from '@superset-ui/core/components/TimezoneSelector'; import { propertyComparator } from '@superset-ui/core/components/Select/utils'; @@ -81,6 +81,7 @@ import { useSelector } from 'react-redux'; import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes'; import { Icons } from '@superset-ui/core/components/Icons'; import { useOpenerRef } from 'src/hooks/useOpenerRef'; +import { ModalTitleWithIcon } from 'src/components/ModalTitleWithIcon'; import NumberInput from './components/NumberInput'; import { AlertReportCronScheduler } from './components/AlertReportCronScheduler'; import { NotificationMethod } from './components/NotificationMethod'; @@ -250,7 +251,6 @@ export const StyledInputContainer = styled.div` flex: 1; margin-top: 0px; margin-bottom: ${theme.sizeUnit * 4}px; - input::-webkit-outer-spin-button, input::-webkit-inner-spin-button { -webkit-appearance: none; @@ -422,6 +422,7 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({ isReport = false, addSuccessToast, }) => { + const theme = useTheme(); const openerRef = useOpenerRef(show); const currentUser = useSelector<any, UserWithPermissionsAndRoles>( state => state.user, @@ -1455,9 +1456,14 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({ centered openerRef={openerRef} title={ - <Typography.Title level={4} data-test="alert-report-modal-title"> - {getTitleText()} - </Typography.Title> + <ModalTitleWithIcon + editModeConfig={{ + isEditMode, + titleAdd: getTitleText(), + titleEdit: getTitleText(), + }} + dataTestId="alert-report-modal-title" + /> } > <Collapse @@ -1614,7 +1620,12 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({ key={currentAlert?.id} /> </StyledInputContainer> - <div className="inline-container wrap"> + <div + className="inline-container wrap" + css={css` + gap: ${theme.sizeUnit}px; + `} + > <StyledInputContainer css={noMarginBottom}> <div className="control-label" css={inputSpacer}> {t('Trigger Alert If...')} diff --git a/superset-frontend/src/features/annotationLayers/AnnotationLayerModal.tsx b/superset-frontend/src/features/annotationLayers/AnnotationLayerModal.tsx index 2495e520c0..e9871eaeb3 100644 --- a/superset-frontend/src/features/annotationLayers/AnnotationLayerModal.tsx +++ b/superset-frontend/src/features/annotationLayers/AnnotationLayerModal.tsx @@ -18,10 +18,9 @@ */ import { FunctionComponent, useState, useEffect, ChangeEvent } from 'react'; -import { css, styled, t, useTheme } from '@superset-ui/core'; +import { styled, t } from '@superset-ui/core'; import { useSingleViewResource } from 'src/views/CRUD/hooks'; - -import { Icons } from '@superset-ui/core/components/Icons'; +import { ModalTitleWithIcon } from 'src/components/ModalTitleWithIcon'; import { Typography } from '@superset-ui/core/components/Typography'; import { Input, Modal } from '@superset-ui/core/components'; import withToasts from 'src/components/MessageToasts/withToasts'; @@ -94,7 +93,6 @@ const AnnotationLayerModal: FunctionComponent<AnnotationLayerModalProps> = ({ show, layer = null, }) => { - const theme = useTheme(); const [disableSave, setDisableSave] = useState<boolean>(true); const [currentLayer, setCurrentLayer] = useState<AnnotationLayerObject | null>(); @@ -236,27 +234,14 @@ const AnnotationLayerModal: FunctionComponent<AnnotationLayerModalProps> = ({ show={show} width="55%" title={ - <Typography.Title level={4} data-test="annotation-layer-modal-title"> - {isEditMode ? ( - <Icons.EditOutlined - iconSize="l" - css={css` - margin: auto ${theme.sizeUnit * 2}px auto 0; - `} - /> - ) : ( - <Icons.PlusOutlined - iconSize="m" - css={css` - margin: auto ${theme.sizeUnit * 2}px auto 0; - vertical-align: text-top; - `} - /> - )} - {isEditMode - ? t('Edit annotation layer properties') - : t('Add annotation layer')} - </Typography.Title> + <ModalTitleWithIcon + editModeConfig={{ + isEditMode, + titleAdd: 'Add annotation layer', + titleEdit: 'Edit annotation layer properties', + }} + dataTestId="annotation-layer-modal-title" + /> } > <StyledAnnotationLayerTitle> diff --git a/superset-frontend/src/features/cssTemplates/CssTemplateModal.tsx b/superset-frontend/src/features/cssTemplates/CssTemplateModal.tsx index abe5085800..8a6d6f5712 100644 --- a/superset-frontend/src/features/cssTemplates/CssTemplateModal.tsx +++ b/superset-frontend/src/features/cssTemplates/CssTemplateModal.tsx @@ -17,15 +17,12 @@ * under the License. */ import { FunctionComponent, useState, useEffect, ChangeEvent } from 'react'; - -import { css, styled, t, useTheme } from '@superset-ui/core'; +import { css, styled, t } from '@superset-ui/core'; import { useSingleViewResource } from 'src/views/CRUD/hooks'; - -import { Icons } from '@superset-ui/core/components/Icons'; +import { ModalTitleWithIcon } from 'src/components/ModalTitleWithIcon'; import withToasts from 'src/components/MessageToasts/withToasts'; import { Input, CssEditor, Modal } from '@superset-ui/core/components'; import { Typography } from '@superset-ui/core/components/Typography'; - import { OnlyKeyWithType } from 'src/utils/types'; import { TemplateObject } from './types'; @@ -84,7 +81,6 @@ const CssTemplateModal: FunctionComponent<CssTemplateModalProps> = ({ show, cssTemplate = null, }) => { - const theme = useTheme(); const [disableSave, setDisableSave] = useState<boolean>(true); const [currentCssTemplate, setCurrentCssTemplate] = useState<TemplateObject | null>(null); @@ -232,26 +228,14 @@ const CssTemplateModal: FunctionComponent<CssTemplateModalProps> = ({ show={show} width="55%" title={ - <Typography.Title level={4} data-test="css-template-modal-title"> - {isEditMode ? ( - <Icons.EditOutlined - iconSize="l" - css={css` - margin: auto ${theme.sizeUnit * 2}px auto 0; - `} - /> - ) : ( - <Icons.PlusOutlined - iconSize="l" - css={css` - margin: auto ${theme.sizeUnit * 2}px auto 0; - `} - /> - )} - {isEditMode - ? t('Edit CSS template properties') - : t('Add CSS template')} - </Typography.Title> + <ModalTitleWithIcon + editModeConfig={{ + isEditMode, + titleAdd: 'Add CSS template', + titleEdit: 'Edit CSS template properties', + }} + dataTestId="css-template-modal-title" + /> } > <StyledCssTemplateTitle> diff --git a/superset-frontend/src/features/databases/DatabaseModal/index.tsx b/superset-frontend/src/features/databases/DatabaseModal/index.tsx index 70484205aa..1f9c04b4bb 100644 --- a/superset-frontend/src/features/databases/DatabaseModal/index.tsx +++ b/superset-frontend/src/features/databases/DatabaseModal/index.tsx @@ -21,7 +21,6 @@ import { styled, SupersetTheme, getExtensionsRegistry, - css, useTheme, } from '@superset-ui/core'; @@ -69,6 +68,7 @@ import { import { useCommonConf } from 'src/features/databases/state'; import { isEmpty, pick } from 'lodash'; import { OnlyKeyWithType } from 'src/utils/types'; +import { ModalTitleWithIcon } from 'src/components/ModalTitleWithIcon'; import { DatabaseObject, DatabaseForm, @@ -575,7 +575,6 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({ databaseId, dbEngine, }) => { - const theme = useTheme(); const [db, setDB] = useReducer< Reducer<Partial<DatabaseObject> | null, DBReducerActionType> >(dbReducer, null); @@ -1837,7 +1836,12 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({ onHandledPrimaryAction={onSave} primaryButtonName={t('Connect')} show={show} - title={<h4>{t('Connect a database')}</h4>} + title={ + <ModalTitleWithIcon + title="Connect a database" + icon={<Icons.InsertRowAboveOutlined />} + /> + } width="500px" > <ModalHeader @@ -1877,24 +1881,20 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({ centered show={show} title={ - <h4> - {isEditMode ? ( - <Icons.EditOutlined - iconSize="l" - css={css` - margin: auto ${theme.sizeUnit * 2}px auto 0; - `} - /> - ) : ( - <Icons.InsertRowAboveOutlined - iconSize="l" - css={css` - margin: auto ${theme.sizeUnit * 2}px auto 0; - `} - /> - )} - {isEditMode ? t('Edit database') : t('Connect a database')} - </h4> + <ModalTitleWithIcon + editModeConfig={{ + isEditMode, + titleAdd: 'Connect a database', + titleEdit: 'Edit database', + }} + icon={ + isEditMode ? ( + <Icons.EditOutlined iconSize="l" /> + ) : ( + <Icons.InsertRowAboveOutlined iconSize="l" /> + ) + } + /> } footer={modalFooter} maskClosable={false} @@ -2078,15 +2078,10 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({ centered show={show} title={ - <h4> - <Icons.InsertRowAboveOutlined - iconSize="l" - css={css` - margin: auto ${theme.sizeUnit * 2}px auto 0; - `} - /> - {t('Connect a database')} - </h4> + <ModalTitleWithIcon + title="Connect a database" + icon={<Icons.InsertRowAboveOutlined />} + /> } footer={renderModalFooter()} maskClosable={false} diff --git a/superset-frontend/src/features/databases/UploadDataModel/index.tsx b/superset-frontend/src/features/databases/UploadDataModel/index.tsx index 284d837dc7..0564ebb495 100644 --- a/superset-frontend/src/features/databases/UploadDataModel/index.tsx +++ b/superset-frontend/src/features/databases/UploadDataModel/index.tsx @@ -50,6 +50,7 @@ import { Switch, SwitchProps } from '@superset-ui/core/components/Switch'; import { Icons } from '@superset-ui/core/components/Icons'; import rison from 'rison'; import withToasts from 'src/components/MessageToasts/withToasts'; +import { ModalTitleWithIcon } from 'src/components/ModalTitleWithIcon'; import { antDModalNoPaddingStyles, antDModalStyles, @@ -576,7 +577,7 @@ const UploadDataModal: FunctionComponent<UploadDataModalProps> = ({ const UploadTitle: FC = () => { const title = uploadTitles[type] || t('Upload'); - return <h4>{title}</h4>; + return <ModalTitleWithIcon title={title} />; }; return ( diff --git a/superset-frontend/src/features/datasets/DuplicateDatasetModal.tsx b/superset-frontend/src/features/datasets/DuplicateDatasetModal.tsx index f8225ad9cc..9431061b25 100644 --- a/superset-frontend/src/features/datasets/DuplicateDatasetModal.tsx +++ b/superset-frontend/src/features/datasets/DuplicateDatasetModal.tsx @@ -18,7 +18,8 @@ */ import { t } from '@superset-ui/core'; import { FunctionComponent, useEffect, useState, ChangeEvent } from 'react'; -import { Input, FormLabel, Modal } from '@superset-ui/core/components'; +import { Input, FormLabel, Modal, Icons } from '@superset-ui/core/components'; +import { ModalTitleWithIcon } from 'src/components/ModalTitleWithIcon'; import Dataset from 'src/types/Dataset'; interface DuplicateDatasetModalProps { @@ -56,7 +57,12 @@ const DuplicateDatasetModal: FunctionComponent<DuplicateDatasetModalProps> = ({ <Modal show={show} onHide={onHide} - title={t('Duplicate dataset')} + title={ + <ModalTitleWithIcon + title="Duplicate dataset" + icon={<Icons.CopyOutlined />} + /> + } disablePrimaryButton={disableSave} onHandledPrimaryAction={duplicateDataset} primaryButtonName={t('Duplicate')} diff --git a/superset-frontend/src/features/groups/GroupListModal.tsx b/superset-frontend/src/features/groups/GroupListModal.tsx index baf728f5b8..46aa81cf84 100644 --- a/superset-frontend/src/features/groups/GroupListModal.tsx +++ b/superset-frontend/src/features/groups/GroupListModal.tsx @@ -18,6 +18,7 @@ */ import { t } from '@superset-ui/core'; import { useToasts } from 'src/components/MessageToasts/withToasts'; +import { ModalTitleWithIcon } from 'src/components/ModalTitleWithIcon'; import { Actions } from 'src/constants'; import { GroupObject } from 'src/pages/GroupsList'; import { @@ -101,7 +102,15 @@ function GroupListModal({ <FormModal show={show} onHide={onHide} - title={isEditMode ? t('Edit Group') : t('Add Group')} + title={ + <ModalTitleWithIcon + editModeConfig={{ + isEditMode, + titleAdd: 'Add Group', + titleEdit: 'Edit Group', + }} + /> + } onSave={onSave} formSubmitHandler={handleFormSubmit} requiredFields={requiredFields} diff --git a/superset-frontend/src/features/rls/RowLevelSecurityModal.tsx b/superset-frontend/src/features/rls/RowLevelSecurityModal.tsx index 83bd11754e..5b68ac8b3d 100644 --- a/superset-frontend/src/features/rls/RowLevelSecurityModal.tsx +++ b/superset-frontend/src/features/rls/RowLevelSecurityModal.tsx @@ -17,9 +17,9 @@ * under the License. */ -import { css, styled, SupersetClient, useTheme, t } from '@superset-ui/core'; +import { css, styled, SupersetClient, t } from '@superset-ui/core'; import { useCallback, useEffect, useMemo, useState } from 'react'; -import { Icons } from '@superset-ui/core/components/Icons'; +import { ModalTitleWithIcon } from 'src/components/ModalTitleWithIcon'; import { Modal, Select, @@ -28,7 +28,6 @@ import { LabeledErrorBoundInput, Input, } from '@superset-ui/core/components'; -import { Typography } from '@superset-ui/core/components/Typography'; import rison from 'rison'; import { useSingleViewResource } from 'src/views/CRUD/hooks'; import { FILTER_OPTIONS } from './constants'; @@ -125,7 +124,6 @@ const DEFAULT_RULE = { }; function RowLevelSecurityModal(props: RowLevelSecurityModalProps) { - const theme = useTheme(); const { rule, addDangerToast, addSuccessToast, onHide, show } = props; const [currentRule, setCurrentRule] = useState<RLSObject>({ @@ -336,23 +334,14 @@ function RowLevelSecurityModal(props: RowLevelSecurityModalProps) { width="30%" maxWidth="1450px" title={ - <Typography.Title level={4} data-test="rls-modal-title"> - {isEditMode ? ( - <Icons.EditOutlined - css={css` - margin: auto ${theme.sizeUnit * 2}px auto 0; - `} - /> - ) : ( - <Icons.PlusOutlined - iconSize="l" - css={css` - margin: auto ${theme.sizeUnit * 2}px auto 0; - `} - /> - )} - {isEditMode ? t('Edit Rule') : t('Add Rule')} - </Typography.Title> + <ModalTitleWithIcon + editModeConfig={{ + isEditMode, + titleAdd: 'Add Rule', + titleEdit: 'Edit Rule', + }} + dataTestId="rls-modal-title" + /> } > <StyledSectionContainer> diff --git a/superset-frontend/src/features/roles/RoleListAddModal.tsx b/superset-frontend/src/features/roles/RoleListAddModal.tsx index 15f51385c3..6191a3ebcb 100644 --- a/superset-frontend/src/features/roles/RoleListAddModal.tsx +++ b/superset-frontend/src/features/roles/RoleListAddModal.tsx @@ -17,8 +17,9 @@ * under the License. */ import { t } from '@superset-ui/core'; +import { ModalTitleWithIcon } from 'src/components/ModalTitleWithIcon'; import { useToasts } from 'src/components/MessageToasts/withToasts'; -import { FormModal } from '@superset-ui/core/components'; +import { FormModal, Icons } from '@superset-ui/core/components'; import { createRole, updateRolePermissions } from './utils'; import { PermissionsField, RoleNameField } from './RoleFormItems'; import { BaseModalProps, FormattedPermission, RoleForm } from './types'; @@ -34,7 +35,6 @@ function RoleListAddModal({ permissions, }: RoleListAddModalProps) { const { addDangerToast, addSuccessToast } = useToasts(); - const handleFormSubmit = async (values: RoleForm) => { try { const { json: roleResponse } = await createRole(values.roleName); @@ -56,7 +56,9 @@ function RoleListAddModal({ <FormModal show={show} onHide={onHide} - title={t('Add Role')} + title={ + <ModalTitleWithIcon title="Add Role" icon={<Icons.PlusOutlined />} /> + } onSave={onSave} formSubmitHandler={handleFormSubmit} requiredFields={['roleName']} diff --git a/superset-frontend/src/features/tags/BulkTagModal.tsx b/superset-frontend/src/features/tags/BulkTagModal.tsx index 08830dd586..2d24227b14 100644 --- a/superset-frontend/src/features/tags/BulkTagModal.tsx +++ b/superset-frontend/src/features/tags/BulkTagModal.tsx @@ -17,7 +17,7 @@ * under the License. */ import { useState, useEffect, FC } from 'react'; - +import { ModalTitleWithIcon } from 'src/components/ModalTitleWithIcon'; import { t, styled, SupersetClient } from '@superset-ui/core'; import { FormLabel, @@ -94,7 +94,7 @@ const BulkTagModal: FC<BulkTagModalProps> = ({ return ( <Modal - title={t('Bulk tag')} + title={<ModalTitleWithIcon title="Bulk tag" />} show={show} onHide={() => { setTags([]); diff --git a/superset-frontend/src/features/tags/TagModal.tsx b/superset-frontend/src/features/tags/TagModal.tsx index 329f45f281..bb1a52422a 100644 --- a/superset-frontend/src/features/tags/TagModal.tsx +++ b/superset-frontend/src/features/tags/TagModal.tsx @@ -30,6 +30,7 @@ import { import { t, styled, SupersetClient } from '@superset-ui/core'; import { Tag } from 'src/views/CRUD/types'; import { fetchObjectsByTagIds } from 'src/features/tags/tags'; +import { ModalTitleWithIcon } from 'src/components/ModalTitleWithIcon'; const StyledModalBody = styled.div` .ant-select-dropdown { @@ -83,7 +84,6 @@ const TagModal: FC<TagModalProps> = ({ const [description, setDescription] = useState<string>(''); const isEditMode = !!editTag; - const modalTitle = isEditMode ? 'Edit Tag' : 'Create Tag'; const clearResources = () => { setDashboardsToTag([]); @@ -264,7 +264,15 @@ const TagModal: FC<TagModalProps> = ({ return ( <Modal - title={modalTitle} + title={ + <ModalTitleWithIcon + editModeConfig={{ + isEditMode, + titleAdd: 'Create Tag', + titleEdit: 'Edit Tag', + }} + /> + } onHide={() => { if (clearOnHide) clearTagForm(); onHide(); diff --git a/superset-frontend/src/features/users/UserListModal.tsx b/superset-frontend/src/features/users/UserListModal.tsx index f0a104ba3d..916625e448 100644 --- a/superset-frontend/src/features/users/UserListModal.tsx +++ b/superset-frontend/src/features/users/UserListModal.tsx @@ -17,6 +17,7 @@ * under the License. */ import { t } from '@superset-ui/core'; +import { ModalTitleWithIcon } from 'src/components/ModalTitleWithIcon'; import { useToasts } from 'src/components/MessageToasts/withToasts'; import { Checkbox, @@ -120,7 +121,15 @@ function UserListModal({ <FormModal show={show} onHide={onHide} - title={isEditMode ? t('Edit User') : t('Add User')} + title={ + <ModalTitleWithIcon + editModeConfig={{ + isEditMode, + titleAdd: 'Add User', + titleEdit: 'Edit User', + }} + /> + } onSave={onSave} formSubmitHandler={handleFormSubmit} requiredFields={requiredFields} diff --git a/superset-frontend/src/pages/GroupsList/index.tsx b/superset-frontend/src/pages/GroupsList/index.tsx index f497e34330..ef770660f5 100644 --- a/superset-frontend/src/pages/GroupsList/index.tsx +++ b/superset-frontend/src/pages/GroupsList/index.tsx @@ -277,7 +277,6 @@ function GroupsList({ user }: GroupsListProps) { name: ( <> <Icons.PlusOutlined - iconColor={theme.colorText} iconSize="m" css={css` margin: auto ${theme.sizeUnit * 2}px auto 0; @@ -364,7 +363,6 @@ function GroupsList({ user }: GroupsListProps) { buttonText: ( <> <Icons.PlusOutlined - iconColor={theme.colorText} iconSize="m" css={css` margin: auto ${theme.sizeUnit * 2}px auto 0; diff --git a/superset-frontend/src/pages/UserInfo/index.tsx b/superset-frontend/src/pages/UserInfo/index.tsx index c02cc0f2a1..061cba8ce0 100644 --- a/superset-frontend/src/pages/UserInfo/index.tsx +++ b/superset-frontend/src/pages/UserInfo/index.tsx @@ -136,7 +136,6 @@ export function UserInfo({ user }: { user: UserWithPermissionsAndRoles }) { name: ( <> <Icons.FormOutlined - iconColor={theme.colorIcon} iconSize="m" css={css` margin: auto ${theme.sizeUnit * 2}px auto 0;