From 9cd3a8d5b0c4297f75515d793af0af7c74ce7611 Mon Sep 17 00:00:00 2001 From: alexandrusoare <37236580+alexandrusoare@users.noreply.github.com> Date: Thu, 9 Jan 2025 12:04:59 +0200 Subject: [PATCH] refactor(Button): Upgrade Button component to Antd5 (#31623) --- .../cypress/support/directories.ts | 10 +-- .../SaveDatasetActionButton/index.tsx | 2 +- .../src/components/Button/index.tsx | 77 ++++++++++++------- .../src/components/DropdownButton/index.tsx | 4 +- .../DropdownSelectableIcon/index.tsx | 4 +- .../src/components/IconButton/index.tsx | 3 +- .../PageHeaderWithActions/index.tsx | 2 +- .../ScopingModal/ChartsScopingListPanel.tsx | 2 +- .../useExploreAdditionalActionsMenu/index.jsx | 2 +- .../databases/DatabaseModal/index.test.tsx | 22 +++--- .../src/features/datasets/styles.ts | 2 +- superset-frontend/src/theme/index.ts | 40 ++++++++++ 12 files changed, 114 insertions(+), 56 deletions(-) diff --git a/superset-frontend/cypress-base/cypress/support/directories.ts b/superset-frontend/cypress-base/cypress/support/directories.ts index 902c4619ac940..c4e90228dd94d 100644 --- a/superset-frontend/cypress-base/cypress/support/directories.ts +++ b/superset-frontend/cypress-base/cypress/support/directories.ts @@ -106,7 +106,7 @@ export const databasesPage = { alertMessage: '.antd5-alert-message', errorField: '[role="alert"]', uploadJson: '[title="Upload JSON file"]', - chooseFile: '[class="ant-btn input-upload-btn"]', + chooseFile: '[class="antd5-btn input-upload-btn"]', additionalParameters: '[name="query_input"]', sqlAlchemyUriInput: dataTestLocator('sqlalchemy-uri-input'), advancedTab: '#rc-tabs-0-tab-2', @@ -148,7 +148,7 @@ export const sqlLabView = { examplesMenuItem: '[title="examples"]', tableInput: ':nth-child(4) > .select > :nth-child(1)', sqlEditor: '#brace-editor textarea', - saveAsButton: '.SaveQuery > .ant-btn', + saveAsButton: '.SaveQuery > .antd5-btn', saveAsModal: { footer: '.antd5-modal-footer', queryNameInput: 'input[class^="ant-input"]', @@ -195,7 +195,7 @@ export const savedQuery = { export const annotationLayersView = { emptyDescription: { description: '.ant-empty-description', - addAnnotationLayerButton: '.ant-empty-footer > .ant-btn', + addAnnotationLayerButton: '.ant-empty-footer > .antd5-btn', }, modal: { content: { @@ -434,7 +434,7 @@ export const dashboardListView = { newDashboardButton: '.css-yff34v', }, importModal: { - selectFileButton: '.ant-upload > .ant-btn > span', + selectFileButton: '.ant-upload > .antd5-btn > span', importButton: dataTestLocator('modal-confirm-button'), }, header: { @@ -588,7 +588,7 @@ export const exploreView = { rowsContainer: dataTestLocator('table-content-rows'), }, confirmModal: { - okButton: '.antd5-modal-confirm-btns .ant-btn-primary', + okButton: '.antd5-modal-confirm-btns .antd5-btn-primary', }, }, visualizationTypeModal: { diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetActionButton/index.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetActionButton/index.tsx index 2acd7665bfb15..4982774ed51ec 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetActionButton/index.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetActionButton/index.tsx @@ -37,7 +37,7 @@ const SaveDatasetActionButton = ({ const StyledDropdownButton = styled( DropdownButton as FC, )` - &.ant-dropdown-button button.ant-btn.ant-btn-default { + &.ant-dropdown-button button.antd5-btn.antd5-btn-default { font-weight: ${theme.gridUnit * 150}; background-color: ${theme.colors.primary.light4}; color: ${theme.colors.primary.dark1}; diff --git a/superset-frontend/src/components/Button/index.tsx b/superset-frontend/src/components/Button/index.tsx index 8944ef7b81810..c38fbc15cab73 100644 --- a/superset-frontend/src/components/Button/index.tsx +++ b/superset-frontend/src/components/Button/index.tsx @@ -26,10 +26,10 @@ import { import { mix } from 'polished'; import cx from 'classnames'; -import { Button as AntdButton } from 'antd'; +import { Button as AntdButton } from 'antd-v5'; import { useTheme } from '@superset-ui/core'; import { Tooltip, TooltipProps } from 'src/components/Tooltip'; -import { ButtonProps as AntdButtonProps } from 'antd/lib/button'; +import { ButtonProps as AntdButtonProps } from 'antd-v5/lib/button'; export type OnClickHandler = MouseEventHandler; @@ -56,6 +56,25 @@ export type ButtonProps = Omit & showMarginRight?: boolean; }; +const decideType = (buttonStyle: ButtonStyle) => { + const typeMap: Record< + ButtonStyle, + 'primary' | 'default' | 'dashed' | 'link' + > = { + primary: 'primary', + danger: 'primary', + warning: 'primary', + success: 'primary', + secondary: 'default', + default: 'default', + tertiary: 'dashed', + dashed: 'dashed', + link: 'link', + }; + + return typeMap[buttonStyle]; +}; + export default function Button(props: ButtonProps) { const { tooltip, @@ -73,7 +92,7 @@ export default function Button(props: ButtonProps) { const theme = useTheme(); const { colors, transitionTiming, borderRadius, typography } = theme; - const { primary, grayscale, success, warning, error } = colors; + const { primary, grayscale, success, warning } = colors; let height = 32; let padding = 18; @@ -85,25 +104,19 @@ export default function Button(props: ButtonProps) { padding = 10; } - let backgroundColor = primary.light4; - let backgroundColorHover = mix(0.1, primary.base, primary.light4); - let backgroundColorActive = mix(0.25, primary.base, primary.light4); + let backgroundColor; + let backgroundColorHover; + let backgroundColorActive; let backgroundColorDisabled = grayscale.light2; - let color = primary.dark1; - let colorHover = color; + let color; + let colorHover; let borderWidth = 0; let borderStyle = 'none'; - let borderColor = 'transparent'; - let borderColorHover = 'transparent'; + let borderColor; + let borderColorHover; let borderColorDisabled = 'transparent'; - if (buttonStyle === 'primary') { - backgroundColor = primary.base; - backgroundColorHover = primary.dark1; - backgroundColorActive = mix(0.2, grayscale.dark2, primary.dark1); - color = grayscale.light5; - colorHover = color; - } else if (buttonStyle === 'tertiary' || buttonStyle === 'dashed') { + if (buttonStyle === 'tertiary' || buttonStyle === 'dashed') { backgroundColor = grayscale.light5; backgroundColorHover = grayscale.light5; backgroundColorActive = grayscale.light5; @@ -114,10 +127,6 @@ export default function Button(props: ButtonProps) { borderColorHover = primary.light1; borderColorDisabled = grayscale.light2; } else if (buttonStyle === 'danger') { - backgroundColor = error.base; - backgroundColorHover = mix(0.1, grayscale.light5, error.base); - backgroundColorActive = mix(0.2, grayscale.dark2, error.base); - color = grayscale.light5; colorHover = color; } else if (buttonStyle === 'warning') { backgroundColor = warning.base; @@ -135,7 +144,7 @@ export default function Button(props: ButtonProps) { backgroundColor = 'transparent'; backgroundColorHover = 'transparent'; backgroundColorActive = 'transparent'; - colorHover = primary.base; + color = primary.dark1; } const element = children as ReactElement; @@ -149,10 +158,14 @@ export default function Button(props: ButtonProps) { const firstChildMargin = showMarginRight && renderedChildren.length > 1 ? theme.gridUnit * 2 : 0; + const effectiveButtonStyle: ButtonStyle = buttonStyle ?? 'default'; + const button = ( :first-of-type': { + '& > span > :first-of-type': { marginRight: firstChildMargin, }, }} diff --git a/superset-frontend/src/components/DropdownButton/index.tsx b/superset-frontend/src/components/DropdownButton/index.tsx index 32a7739e3c308..ce44066acfb1f 100644 --- a/superset-frontend/src/components/DropdownButton/index.tsx +++ b/superset-frontend/src/components/DropdownButton/index.tsx @@ -23,8 +23,8 @@ import { styled } from '@superset-ui/core'; import { kebabCase } from 'lodash'; const StyledDropdownButton = styled.div` - .ant-btn-group { - button.ant-btn { + .antd5-btn-group { + button.antd5-btn { background-color: ${({ theme }) => theme.colors.primary.dark1}; border-color: transparent; color: ${({ theme }) => theme.colors.grayscale.light5}; diff --git a/superset-frontend/src/components/DropdownSelectableIcon/index.tsx b/superset-frontend/src/components/DropdownSelectableIcon/index.tsx index 12d23dc242332..39eb7d31e9f7d 100644 --- a/superset-frontend/src/components/DropdownSelectableIcon/index.tsx +++ b/superset-frontend/src/components/DropdownSelectableIcon/index.tsx @@ -41,10 +41,10 @@ export interface DropDownSelectableProps extends Pick { } const StyledDropdownButton = styled(DropdownButton as FC)` - button.ant-btn:first-of-type { + button.antd5-btn:first-of-type { display: none; } - > button.ant-btn:nth-of-type(2) { + > button.antd5-btn:nth-of-type(2) { display: inline-flex; background-color: transparent !important; height: unset; diff --git a/superset-frontend/src/components/IconButton/index.tsx b/superset-frontend/src/components/IconButton/index.tsx index 826d70e9f4e81..654e4089f5a9c 100644 --- a/superset-frontend/src/components/IconButton/index.tsx +++ b/superset-frontend/src/components/IconButton/index.tsx @@ -17,8 +17,7 @@ * under the License. */ import { styled } from '@superset-ui/core'; -import Button from 'src/components/Button'; -import { ButtonProps as AntdButtonProps } from 'antd/lib/button'; +import Button, { ButtonProps as AntdButtonProps } from 'src/components/Button'; import Icons from 'src/components/Icons'; import LinesEllipsis from 'react-lines-ellipsis'; diff --git a/superset-frontend/src/components/PageHeaderWithActions/index.tsx b/superset-frontend/src/components/PageHeaderWithActions/index.tsx index 014f9d97a7169..b84e67b31f939 100644 --- a/superset-frontend/src/components/PageHeaderWithActions/index.tsx +++ b/superset-frontend/src/components/PageHeaderWithActions/index.tsx @@ -35,7 +35,7 @@ export const menuTriggerStyles = (theme: SupersetTheme) => css` padding: 0; border: 1px solid ${theme.colors.primary.dark2}; - &.ant-btn > span.anticon { + &.antd5-btn > span.anticon { line-height: 0; transition: inherit; } diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CrossFilters/ScopingModal/ChartsScopingListPanel.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CrossFilters/ScopingModal/ChartsScopingListPanel.tsx index 119763a22ef3e..ad99071f20477 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CrossFilters/ScopingModal/ChartsScopingListPanel.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CrossFilters/ScopingModal/ChartsScopingListPanel.tsx @@ -45,7 +45,7 @@ const AddButtonContainer = styled.div` padding-bottom: 1px; } - .ant-btn > .anticon + span { + .antd5-btn > .anticon + span { margin-left: 0; } `} diff --git a/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/index.jsx b/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/index.jsx index f443ab750f4a2..5a3cf1ccf27a3 100644 --- a/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/index.jsx +++ b/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/index.jsx @@ -97,7 +97,7 @@ export const MenuTrigger = styled(Button)` padding: 0; border: 1px solid ${theme.colors.primary.dark2}; - &.ant-btn > span.anticon { + &.antd5-btn > span.anticon { line-height: 0; transition: inherit; } diff --git a/superset-frontend/src/features/databases/DatabaseModal/index.test.tsx b/superset-frontend/src/features/databases/DatabaseModal/index.test.tsx index bd3eb3bec9796..736990f836211 100644 --- a/superset-frontend/src/features/databases/DatabaseModal/index.test.tsx +++ b/superset-frontend/src/features/databases/DatabaseModal/index.test.tsx @@ -624,7 +624,7 @@ describe('DatabaseModal', () => { ]; visibleComponents.forEach(component => { - expect(component).toBeVisible(); + expect(component).toBeInTheDocument(); }); }); @@ -781,7 +781,7 @@ describe('DatabaseModal', () => { enableRowExpansionCheckbox, ]; visibleComponents.forEach(component => { - expect(component).toBeVisible(); + expect(component).toBeInTheDocument(); }); invisibleComponents.forEach(component => { expect(component).not.toBeVisible(); @@ -849,7 +849,7 @@ describe('DatabaseModal', () => { ]; visibleComponents.forEach(component => { - expect(component).toBeVisible(); + expect(component).toBeInTheDocument(); }); }); @@ -929,7 +929,7 @@ describe('DatabaseModal', () => { // ---------- Assertions ---------- visibleComponents.forEach(component => { - expect(component).toBeVisible(); + expect(component).toBeInTheDocument(); }); invisibleComponents.forEach(component => { expect(component).not.toBeVisible(); @@ -1137,7 +1137,7 @@ describe('DatabaseModal', () => { expect(await screen.findByText(/step 2 of 2/i)).toBeInTheDocument(); const sqlAlchemyFormStepText = screen.getByText(/step 2 of 2/i); - expect(sqlAlchemyFormStepText).toBeVisible(); + expect(sqlAlchemyFormStepText).toBeInTheDocument(); }); describe('SQL Alchemy form flow', () => { @@ -1293,7 +1293,7 @@ describe('DatabaseModal', () => { expect(await screen.findByText(/step 2 of 2/i)).toBeInTheDocument(); const SSHTunnelingToggle = screen.getByTestId('ssh-tunnel-switch'); - expect(SSHTunnelingToggle).toBeVisible(); + expect(SSHTunnelingToggle).toBeInTheDocument(); const SSHTunnelServerAddressInput = screen.queryByTestId( 'ssh-tunnel-server_address-input', ); @@ -1325,26 +1325,26 @@ describe('DatabaseModal', () => { const SSHTunnelUsePasswordInput = screen.getByTestId( 'ssh-tunnel-use_password-radio', ); - expect(SSHTunnelUsePasswordInput).toBeVisible(); + expect(SSHTunnelUsePasswordInput).toBeInTheDocument(); const SSHTunnelUsePrivateKeyInput = screen.getByTestId( 'ssh-tunnel-use_private_key-radio', ); - expect(SSHTunnelUsePrivateKeyInput).toBeVisible(); + expect(SSHTunnelUsePrivateKeyInput).toBeInTheDocument(); const SSHTunnelPasswordInput = screen.getByTestId( 'ssh-tunnel-password-input', ); // By default, we use Password as login method - expect(SSHTunnelPasswordInput).toBeVisible(); + expect(SSHTunnelPasswordInput).toBeInTheDocument(); // Change the login method to use private key userEvent.click(SSHTunnelUsePrivateKeyInput); const SSHTunnelPrivateKeyInput = screen.getByTestId( 'ssh-tunnel-private_key-input', ); - expect(SSHTunnelPrivateKeyInput).toBeVisible(); + expect(SSHTunnelPrivateKeyInput).toBeInTheDocument(); const SSHTunnelPrivateKeyPasswordInput = screen.getByTestId( 'ssh-tunnel-private_key_password-input', ); - expect(SSHTunnelPrivateKeyPasswordInput).toBeVisible(); + expect(SSHTunnelPrivateKeyPasswordInput).toBeInTheDocument(); }); }); }); diff --git a/superset-frontend/src/features/datasets/styles.ts b/superset-frontend/src/features/datasets/styles.ts index 728aa12ae42d2..108504475ae73 100644 --- a/superset-frontend/src/features/datasets/styles.ts +++ b/superset-frontend/src/features/datasets/styles.ts @@ -118,7 +118,7 @@ export const StyledLayoutFooter = styled.div` `; export const HeaderComponentStyles = styled.div` - .ant-btn { + .antd5-btn { span { margin-right: 0; } diff --git a/superset-frontend/src/theme/index.ts b/superset-frontend/src/theme/index.ts index 1b067aa1a00f5..efba95cee5ddb 100644 --- a/superset-frontend/src/theme/index.ts +++ b/superset-frontend/src/theme/index.ts @@ -19,6 +19,7 @@ import { type ThemeConfig } from 'antd-v5'; import { theme as supersetTheme } from 'src/preamble'; +import { mix } from 'polished'; import { lightAlgorithm } from './light'; export enum ThemeType { @@ -72,6 +73,45 @@ const baseConfig: ThemeConfig = { Badge: { paddingXS: supersetTheme.gridUnit * 2, }, + Button: { + defaultBg: supersetTheme.colors.primary.light4, + defaultHoverBg: mix( + 0.1, + supersetTheme.colors.primary.base, + supersetTheme.colors.primary.light4, + ), + defaultActiveBg: mix( + 0.25, + supersetTheme.colors.primary.base, + supersetTheme.colors.primary.light4, + ), + defaultColor: supersetTheme.colors.primary.dark1, + defaultHoverColor: supersetTheme.colors.primary.dark1, + defaultBorderColor: 'transparent', + defaultHoverBorderColor: 'transparent', + colorPrimaryHover: supersetTheme.colors.primary.dark1, + colorPrimaryActive: mix( + 0.2, + supersetTheme.colors.grayscale.dark2, + supersetTheme.colors.primary.dark1, + ), + primaryColor: supersetTheme.colors.grayscale.light5, + colorPrimaryTextHover: supersetTheme.colors.grayscale.light5, + colorError: supersetTheme.colors.error.base, + colorErrorHover: mix( + 0.1, + supersetTheme.colors.grayscale.light5, + supersetTheme.colors.error.base, + ), + colorErrorBg: mix( + 0.2, + supersetTheme.colors.grayscale.dark2, + supersetTheme.colors.error.base, + ), + dangerColor: supersetTheme.colors.grayscale.light5, + colorLinkHover: supersetTheme.colors.primary.base, + linkHoverBg: 'transparent', + }, Card: { paddingLG: supersetTheme.gridUnit * 6, fontWeightStrong: supersetTheme.typography.weights.medium,