This is an automated email from the ASF dual-hosted git repository.

maximebeauchemin pushed a commit to branch alerts_refactor
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 82f782cc4265f8e906675299a42d01a69c922205
Author: Maxime Beauchemin <[email protected]>
AuthorDate: Tue Jan 14 23:28:19 2025 -0800

    chore: refactor Alert-related components
    
    Chiseling at https://github.com/apache/superset/pull/31590 that has gotten 
big / unruly, in this PR is a refactor of Alert-related components, going 
vanilla AntD.
    
    Also. Deprecating colors.alerts since it's ambiguous/redundant with 
warning/error and does not exist in antd-v5
---
 .../packages/superset-ui-core/src/style/index.tsx  |   7 -
 .../src/ReactCalendar.jsx                          |   8 +-
 .../spec/helpers/{theming.ts => theming.tsx}       |   7 +-
 superset-frontend/src/components/Alert/index.tsx   |  46 +--
 .../src/components/AlteredSliceTag/index.tsx       |   2 +-
 .../ErrorMessage/DatabaseErrorMessage.tsx          |  28 +-
 .../ErrorMessage/DatasetNotFoundErrorMessage.tsx   |  12 +-
 .../components/ErrorMessage/ErrorAlert.stories.tsx | 151 +++++++++
 .../components/ErrorMessage/ErrorAlert.test.tsx    | 232 +++++---------
 .../src/components/ErrorMessage/ErrorAlert.tsx     | 342 +++++++--------------
 .../ErrorMessage/ErrorMessageWithStackTrace.tsx    |  37 +--
 .../ErrorMessage/InvalidSQLErrorMessage.test.tsx   | 136 ++++----
 .../ErrorMessage/InvalidSQLErrorMessage.tsx        |  16 +-
 .../ErrorMessage/OAuth2RedirectMessage.tsx         |   9 +-
 .../ErrorMessage/ParameterErrorMessage.test.tsx    |   2 +-
 .../ErrorMessage/ParameterErrorMessage.tsx         |  15 +-
 .../ErrorMessage/TimeoutErrorMessage.tsx           |  15 +-
 .../src/components/Label/Label.stories.tsx         |   1 -
 superset-frontend/src/components/Label/index.tsx   |  15 +-
 .../components/WarningIconWithTooltip/index.tsx    |   2 +-
 .../DashboardBuilder/DashboardWrapper.tsx          |   2 +-
 .../src/explore/components/ControlHeader.tsx       |   9 +-
 .../explore/components/ControlPanelsContainer.tsx  |   6 +-
 .../src/explore/components/ExploreAlert.tsx        |   8 +-
 .../controls/ColorSchemeControl/index.tsx          |   2 +-
 .../FormattingPopoverContent.tsx                   |   4 +-
 .../features/alerts/components/AlertStatusIcon.tsx |   4 +-
 superset-frontend/src/theme/index.ts               |   9 -
 28 files changed, 481 insertions(+), 646 deletions(-)

diff --git a/superset-frontend/packages/superset-ui-core/src/style/index.tsx 
b/superset-frontend/packages/superset-ui-core/src/style/index.tsx
index ee0b6e10ac..c4964172be 100644
--- a/superset-frontend/packages/superset-ui-core/src/style/index.tsx
+++ b/superset-frontend/packages/superset-ui-core/src/style/index.tsx
@@ -98,13 +98,6 @@ const defaultTheme = {
       light2: '#FAEDEE',
     },
     warning: {
-      base: '#FF7F44',
-      dark1: '#BF5E33',
-      dark2: '#7F3F21',
-      light1: '#FEC0A1',
-      light2: '#FFF2EC',
-    },
-    alert: {
       base: '#FCC700',
       dark1: '#BC9501',
       dark2: '#7D6300',
diff --git 
a/superset-frontend/plugins/legacy-plugin-chart-calendar/src/ReactCalendar.jsx 
b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/ReactCalendar.jsx
index f3fcc807d8..46cb1de852 100644
--- 
a/superset-frontend/plugins/legacy-plugin-chart-calendar/src/ReactCalendar.jsx
+++ 
b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/ReactCalendar.jsx
@@ -157,13 +157,13 @@ export default styled(Calendar)`
     }
 
     .cal-heatmap-container .q1 {
-      background-color: ${theme.colors.alert.light2};
-      fill: ${theme.colors.alert.light2};
+      background-color: ${theme.colors.warning.light2};
+      fill: ${theme.colors.warning.light2};
     }
 
     .cal-heatmap-container .q2 {
-      background-color: ${theme.colors.alert.light1};
-      fill: ${theme.colors.alert.light1};
+      background-color: ${theme.colors.warning.light1};
+      fill: ${theme.colors.warning.light1};
     }
 
     .cal-heatmap-container .q3 {
diff --git a/superset-frontend/spec/helpers/theming.ts 
b/superset-frontend/spec/helpers/theming.tsx
similarity index 85%
rename from superset-frontend/spec/helpers/theming.ts
rename to superset-frontend/spec/helpers/theming.tsx
index e364b41345..c54d46a384 100644
--- a/superset-frontend/spec/helpers/theming.ts
+++ b/superset-frontend/spec/helpers/theming.tsx
@@ -18,9 +18,11 @@
  */
 import { shallow as enzymeShallow, mount as enzymeMount } from 'enzyme';
 // eslint-disable-next-line no-restricted-imports
-import { supersetTheme } from '@superset-ui/core';
 import { ReactElement } from 'react';
+import { render } from '@testing-library/react';
+import { ThemeProvider, supersetTheme } from '@superset-ui/core';
 import { ProviderWrapper } from './ProviderWrapper';
+import '@testing-library/jest-dom';
 
 type optionsType = {
   wrappingComponentProps?: any;
@@ -55,3 +57,6 @@ export function styledShallow(
     },
   });
 }
+
+export const renderWithTheme = (component: JSX.Element) =>
+  render(<ThemeProvider theme={supersetTheme}>{component}</ThemeProvider>);
diff --git a/superset-frontend/src/components/Alert/index.tsx 
b/superset-frontend/src/components/Alert/index.tsx
index 6a85739950..c567091aea 100644
--- a/superset-frontend/src/components/Alert/index.tsx
+++ b/superset-frontend/src/components/Alert/index.tsx
@@ -19,8 +19,6 @@
 import { PropsWithChildren } from 'react';
 import { Alert as AntdAlert } from 'antd-v5';
 import { AlertProps as AntdAlertProps } from 'antd-v5/lib/alert';
-import { css, useTheme } from '@superset-ui/core';
-import Icons from 'src/components/Icons';
 
 export type AlertProps = PropsWithChildren<
   Omit<AntdAlertProps, 'children'> & { roomBelow?: boolean }
@@ -32,59 +30,17 @@ export default function Alert(props: AlertProps) {
     description,
     showIcon = true,
     closable = true,
-    roomBelow = false,
     children,
   } = props;
 
-  const theme = useTheme();
-  const { colors } = theme;
-  const { alert: alertColor, error, info, success } = colors;
-
-  let baseColor = info;
-  let AlertIcon = Icons.InfoSolid;
-  if (type === 'error') {
-    baseColor = error;
-    AlertIcon = Icons.ErrorSolid;
-  } else if (type === 'warning') {
-    baseColor = alertColor;
-    AlertIcon = Icons.AlertSolid;
-  } else if (type === 'success') {
-    baseColor = success;
-    AlertIcon = Icons.CircleCheckSolid;
-  }
-
   return (
     <AntdAlert
       role="alert"
       aria-live={type === 'error' ? 'assertive' : 'polite'}
       showIcon={showIcon}
-      icon={
-        showIcon && (
-          <span
-            role="img"
-            aria-label={`${type} icon`}
-            style={{
-              color: baseColor.base,
-            }}
-          >
-            <AlertIcon />
-          </span>
-        )
-      }
-      closeIcon={closable && <Icons.XSmall aria-label="close icon" />}
+      closeIcon={closable}
       message={children || 'Default message'}
       description={description}
-      css={css`
-        margin-bottom: ${roomBelow ? theme.gridUnit * 4 : 0}px;
-        a {
-          text-decoration: underline;
-        }
-        .antd5-alert-message {
-          font-weight: ${description
-            ? theme.typography.weights.bold
-            : 'inherit'};
-        }
-      `}
       {...props}
     />
   );
diff --git a/superset-frontend/src/components/AlteredSliceTag/index.tsx 
b/superset-frontend/src/components/AlteredSliceTag/index.tsx
index 9aca6b46b8..46fc58b3ff 100644
--- a/superset-frontend/src/components/AlteredSliceTag/index.tsx
+++ b/superset-frontend/src/components/AlteredSliceTag/index.tsx
@@ -221,7 +221,7 @@ const AlteredSliceTag: FC<AlteredSliceTagProps> = props => {
         <Label
           icon={<Icons.Warning iconSize="m" />}
           className="label"
-          type="alert"
+          type="warning"
           onClick={() => {}}
         >
           {t('Altered')}
diff --git 
a/superset-frontend/src/components/ErrorMessage/DatabaseErrorMessage.tsx 
b/superset-frontend/src/components/ErrorMessage/DatabaseErrorMessage.tsx
index fed692f9e6..7e9e69ea7e 100644
--- a/superset-frontend/src/components/ErrorMessage/DatabaseErrorMessage.tsx
+++ b/superset-frontend/src/components/ErrorMessage/DatabaseErrorMessage.tsx
@@ -34,12 +34,16 @@ interface DatabaseErrorExtra {
 
 function DatabaseErrorMessage({
   error,
-  source = 'dashboard',
+  source,
   subtitle,
 }: ErrorMessageComponentProps<DatabaseErrorExtra | null>) {
   const { extra, level, message } = error;
 
-  const isVisualization = ['dashboard', 'explore'].includes(source);
+  const isVisualization = ['dashboard', 'explore'].includes(source || '');
+  const [firstLine, ...remainingLines] = message.split('\n');
+  const alertMessage = firstLine;
+  const alertDescription =
+    remainingLines.length > 0 ? remainingLines.join('\n') : null;
 
   const body = extra && (
     <>
@@ -75,23 +79,13 @@ function DatabaseErrorMessage({
     </>
   );
 
-  const copyText = extra?.issue_codes
-    ? t('%(message)s\nThis may be triggered by: \n%(issues)s', {
-        message,
-        issues: extra.issue_codes
-          .map(issueCode => issueCode.message)
-          .join('\n'),
-      })
-    : message;
-
   return (
     <ErrorAlert
-      title={t('%s Error', extra?.engine_name || t('DB engine'))}
-      subtitle={subtitle}
-      level={level}
-      source={source}
-      copyText={copyText}
-      body={body}
+      errorType={t('%s Error', extra?.engine_name || t('DB engine'))}
+      message={alertMessage}
+      description={alertDescription}
+      type={level}
+      descriptionDetails={body}
     />
   );
 }
diff --git 
a/superset-frontend/src/components/ErrorMessage/DatasetNotFoundErrorMessage.tsx 
b/superset-frontend/src/components/ErrorMessage/DatasetNotFoundErrorMessage.tsx
index 31f86bfcfc..238b345c34 100644
--- 
a/superset-frontend/src/components/ErrorMessage/DatasetNotFoundErrorMessage.tsx
+++ 
b/superset-frontend/src/components/ErrorMessage/DatasetNotFoundErrorMessage.tsx
@@ -23,19 +23,15 @@ import ErrorAlert from './ErrorAlert';
 
 function DatasetNotFoundErrorMessage({
   error,
-  source = 'dashboard',
   subtitle,
 }: ErrorMessageComponentProps) {
   const { level, message } = error;
-
   return (
     <ErrorAlert
-      title={t('Missing dataset')}
-      subtitle={subtitle}
-      level={level}
-      source={source}
-      copyText={message}
-      body={null}
+      errorType={t('Missing dataset')}
+      message={subtitle}
+      description={message}
+      type={level}
     />
   );
 }
diff --git 
a/superset-frontend/src/components/ErrorMessage/ErrorAlert.stories.tsx 
b/superset-frontend/src/components/ErrorMessage/ErrorAlert.stories.tsx
new file mode 100644
index 0000000000..b7595c00fa
--- /dev/null
+++ b/superset-frontend/src/components/ErrorMessage/ErrorAlert.stories.tsx
@@ -0,0 +1,151 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { Meta, StoryFn } from '@storybook/react';
+import { Layout, Row, Col, Card } from 'antd-v5';
+import ErrorAlert from './ErrorAlert';
+
+const { Content } = Layout;
+
+const longDescription = `This is a detailed description to test long content 
display.
+Line breaks are included here to demonstrate pre-wrap styling.
+This is useful for verbose error messages.`;
+
+const sqlErrorDescription = `SQL Error: Syntax error near unexpected token.
+Please check your query and ensure it follows the correct syntax.`;
+
+const detailsExample = `Additional details about the issue are provided here.
+This content is shown when the user clicks "Show more".`;
+
+const ErrorCard: React.FC<{ children: React.ReactNode }> = ({ children }) => (
+  <Card>{children}</Card>
+);
+
+export default {
+  title: 'Components/ErrorAlert',
+  component: ErrorAlert,
+} as Meta;
+
+export const Gallery: StoryFn = () => (
+  <Layout>
+    <Content style={{ padding: '24px' }}>
+      <h2>Non-Compact Errors</h2>
+      <Row gutter={[16, 16]}>
+        <Col xs={48} sm={24} md={16} lg={16} xl={12}>
+          <ErrorCard>
+            <ErrorAlert message="Only message props was passed here" />
+          </ErrorCard>
+        </Col>
+        <Col xs={48} sm={24} md={16} lg={16} xl={12}>
+          <ErrorCard>
+            <ErrorAlert
+              errorType="Database Connection Error"
+              type="warning"
+              message="Failed to connect to database"
+              descriptionDetails={detailsExample}
+              descriptionDetailsCollapsed
+            />
+          </ErrorCard>
+        </Col>
+        <Col xs={48} sm={24} md={16} lg={16} xl={12}>
+          <ErrorCard>
+            <ErrorAlert
+              errorType="Error"
+              message="SQL Syntax Error - No defaults set here"
+              description={sqlErrorDescription}
+              descriptionDetails={detailsExample}
+              descriptionDetailsCollapsed
+              descriptionPre
+            />
+          </ErrorCard>
+        </Col>
+        <Col xs={48} sm={24} md={16} lg={16} xl={12}>
+          <ErrorCard>
+            <ErrorAlert
+              errorType="Error"
+              message="See the details below"
+              type="error"
+              description={longDescription}
+              descriptionDetails={detailsExample}
+              descriptionDetailsCollapsed={false}
+            />
+          </ErrorCard>
+        </Col>
+        <Col xs={48} sm={24} md={16} lg={16} xl={12}>
+          <ErrorCard>
+            <ErrorAlert
+              errorType="Informational Warning"
+              message="This is a non-pre-wrap styled description"
+              type="info"
+              description={longDescription}
+              descriptionDetails={detailsExample}
+              descriptionDetailsCollapsed={false}
+              descriptionPre={false}
+            />
+          </ErrorCard>
+        </Col>
+        <Col xs={24} sm={12} md={8} lg={8} xl={6}>
+          <ErrorCard>
+            <ErrorAlert
+              errorType="Error"
+              message="Something went wrong"
+              type="error"
+            />
+          </ErrorCard>
+        </Col>
+        <Col xs={24} sm={12} md={8} lg={8} xl={6}>
+          <ErrorCard>
+            <ErrorAlert
+              errorType="Warning"
+              message="Be cautious"
+              type="warning"
+            />
+          </ErrorCard>
+        </Col>
+      </Row>
+      <h2>Compact Errors (with Modal)</h2>
+      <Row gutter={[16, 16]}>
+        <Col xs={24} sm={12} md={8} lg={8} xl={6}>
+          <ErrorCard>
+            <ErrorAlert
+              errorType="Error"
+              message="Compact mode example"
+              type="error"
+              compact
+              descriptionDetailsCollapsed
+              description={sqlErrorDescription}
+              descriptionDetails={detailsExample}
+            />
+          </ErrorCard>
+        </Col>
+        <Col xs={24} sm={12} md={8} lg={8} xl={6}>
+          <ErrorCard>
+            <ErrorAlert
+              errorType="Warning"
+              message="Compact mode example"
+              type="warning"
+              compact
+              descriptionDetails={detailsExample}
+              descriptionDetailsCollapsed
+            />
+          </ErrorCard>
+        </Col>
+      </Row>
+    </Content>
+  </Layout>
+);
diff --git a/superset-frontend/src/components/ErrorMessage/ErrorAlert.test.tsx 
b/superset-frontend/src/components/ErrorMessage/ErrorAlert.test.tsx
index 9f9fef9b75..7407927875 100644
--- a/superset-frontend/src/components/ErrorMessage/ErrorAlert.test.tsx
+++ b/superset-frontend/src/components/ErrorMessage/ErrorAlert.test.tsx
@@ -17,168 +17,76 @@
  * under the License.
  */
 
-import userEvent from '@testing-library/user-event';
-import { render, screen } from 'spec/helpers/testing-library';
-import { ErrorLevel, ErrorSource, supersetTheme } from '@superset-ui/core';
-import { isCurrentUserBot } from 'src/utils/isBot';
+import { screen, fireEvent } from '@testing-library/react';
+import { renderWithTheme } from 'spec/helpers/theming';
 import ErrorAlert from './ErrorAlert';
 
-jest.mock(
-  'src/components/Icons/Icon',
-  () =>
-    ({ fileName }: { fileName: string }) => (
-      <span role="img" aria-label={fileName.replace('_', '-')} />
-    ),
-);
-
-jest.mock('src/utils/isBot', () => ({
-  isCurrentUserBot: jest.fn(),
-}));
-
-const mockedProps = {
-  body: 'Error body',
-  level: 'warning' as ErrorLevel,
-  copyText: 'Copy text',
-  subtitle: 'Error subtitle',
-  title: 'Error title',
-  source: 'dashboard' as ErrorSource,
-  description: 'we are unable to connect db.',
-};
-
-beforeEach(() => {
-  (isCurrentUserBot as jest.Mock).mockReturnValue(false);
-});
-
-afterEach(() => {
-  jest.clearAllMocks();
-});
-
-test('should render', () => {
-  const { container } = render(<ErrorAlert {...mockedProps} />);
-  expect(container).toBeInTheDocument();
-});
-
-test('should render warning icon', () => {
-  render(<ErrorAlert {...mockedProps} />);
-  expect(
-    screen.getByRole('img', { name: 'warning-solid' }),
-  ).toBeInTheDocument();
-});
-
-test('should render error icon', () => {
-  const errorProps = {
-    ...mockedProps,
-    level: 'error' as ErrorLevel,
-  };
-  render(<ErrorAlert {...errorProps} />);
-  expect(screen.getByRole('img', { name: 'error-solid' })).toBeInTheDocument();
-});
-
-test('should render the error title', () => {
-  const titleProps = {
-    ...mockedProps,
-    source: 'explore' as ErrorSource,
-  };
-  render(<ErrorAlert {...titleProps} />);
-  expect(screen.getByText('Error title')).toBeInTheDocument();
-});
-
-test('should render the error description', () => {
-  render(<ErrorAlert {...mockedProps} />, { useRedux: true });
-  expect(screen.getByText('we are unable to connect db.')).toBeInTheDocument();
-});
-
-test('should render the error subtitle', () => {
-  render(<ErrorAlert {...mockedProps} />, { useRedux: true });
-  const button = screen.getByText('See more');
-  userEvent.click(button);
-  expect(screen.getByText('Error subtitle')).toBeInTheDocument();
-});
-
-test('should render the error body', () => {
-  render(<ErrorAlert {...mockedProps} />, { useRedux: true });
-  const button = screen.getByText('See more');
-  userEvent.click(button);
-  expect(screen.getByText('Error body')).toBeInTheDocument();
-});
-
-test('should render the See more button', () => {
-  const seemoreProps = {
-    ...mockedProps,
-    source: 'explore' as ErrorSource,
-  };
-  render(<ErrorAlert {...seemoreProps} />);
-  expect(screen.getByRole('button')).toBeInTheDocument();
-  expect(screen.getByText('See more')).toBeInTheDocument();
-});
-
-test('should render the error subtitle and body defaultly for the screen 
capture request', () => {
-  const seemoreProps = {
-    ...mockedProps,
-    source: 'explore' as ErrorSource,
-  };
-  (isCurrentUserBot as jest.Mock).mockReturnValue(true);
-  render(<ErrorAlert {...seemoreProps} />);
-  expect(screen.getByText('Error subtitle')).toBeInTheDocument();
-  expect(screen.getByText('Error body')).toBeInTheDocument();
-});
-
-test('should render the modal', () => {
-  render(<ErrorAlert {...mockedProps} />, { useRedux: true });
-  const button = screen.getByText('See more');
-  userEvent.click(button);
-  expect(screen.getByRole('dialog')).toBeInTheDocument();
-  expect(screen.getByText('Close')).toBeInTheDocument();
-});
-
-test('should NOT render the modal', () => {
-  const expandableProps = {
-    ...mockedProps,
-    source: 'explore' as ErrorSource,
-  };
-  render(<ErrorAlert {...expandableProps} />, { useRedux: true });
-  const button = screen.getByText('See more');
-  userEvent.click(button);
-  expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
-});
-
-test('should render the See less button', () => {
-  const expandableProps = {
-    ...mockedProps,
-    source: 'explore' as ErrorSource,
-  };
-  render(<ErrorAlert {...expandableProps} />);
-  const button = screen.getByText('See more');
-  userEvent.click(button);
-  expect(screen.getByText('See less')).toBeInTheDocument();
-  expect(screen.queryByText('See more')).not.toBeInTheDocument();
-});
-
-test('should render the Copy button', () => {
-  render(<ErrorAlert {...mockedProps} />, { useRedux: true });
-  const button = screen.getByText('See more');
-  userEvent.click(button);
-  expect(screen.getByText('Copy message')).toBeInTheDocument();
-});
-
-test('should render with warning theme', () => {
-  render(<ErrorAlert {...mockedProps} />);
-  expect(screen.getByRole('alert')).toHaveStyle(
-    `
-      backgroundColor: ${supersetTheme.colors.warning.light2};
-    `,
-  );
-});
-
-test('should render with error theme', () => {
-  const errorProps = {
-    ...mockedProps,
-    level: 'error' as ErrorLevel,
-  };
-  render(<ErrorAlert {...errorProps} />);
-  expect(screen.getByRole('alert')).toHaveStyle(
-    `
-      backgroundColor: ${supersetTheme.colors.error.light2};
-    `,
-  );
+describe('ErrorAlert', () => {
+  it('renders the error message correctly', () => {
+    renderWithTheme(
+      <ErrorAlert
+        errorType="Error"
+        message="Something went wrong"
+        type="error"
+      />,
+    );
+
+    expect(screen.getByText('Error')).toBeInTheDocument();
+    expect(screen.getByText('Something went wrong')).toBeInTheDocument();
+  });
+
+  it('renders the description when provided', () => {
+    const description = 'This is a detailed description';
+    renderWithTheme(
+      <ErrorAlert
+        errorType="Error"
+        message="Something went wrong"
+        type="error"
+        description={description}
+      />,
+    );
+
+    expect(screen.getByText(description)).toBeInTheDocument();
+  });
+
+  it('toggles description details visibility when show more/less is clicked', 
() => {
+    const descriptionDetails = 'Additional details about the error.';
+    renderWithTheme(
+      <ErrorAlert
+        errorType="Error"
+        message="Something went wrong"
+        type="error"
+        descriptionDetails={descriptionDetails}
+        descriptionDetailsCollapsed
+      />,
+    );
+
+    const showMoreButton = screen.getByText('See more');
+    expect(showMoreButton).toBeInTheDocument();
+
+    fireEvent.click(showMoreButton);
+    expect(screen.getByText(descriptionDetails)).toBeInTheDocument();
+
+    const showLessButton = screen.getByText('See less');
+    fireEvent.click(showLessButton);
+    expect(screen.queryByText(descriptionDetails)).not.toBeInTheDocument();
+  });
+
+  it('renders compact mode with a tooltip and modal', () => {
+    renderWithTheme(
+      <ErrorAlert
+        errorType="Error"
+        message="Compact mode example"
+        type="error"
+        compact
+        descriptionDetails="Detailed description in compact mode."
+      />,
+    );
+
+    const iconTrigger = screen.getByText('Error');
+    expect(iconTrigger).toBeInTheDocument();
+
+    fireEvent.click(iconTrigger);
+    expect(screen.getByText('Compact mode example')).toBeInTheDocument();
+  });
 });
diff --git a/superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx 
b/superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx
index e441505beb..2d2c3d50b6 100644
--- a/superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx
+++ b/superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx
@@ -16,250 +16,128 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { useState, ReactNode } from 'react';
-import {
-  ErrorLevel,
-  ErrorSource,
-  styled,
-  useTheme,
-  t,
-} from '@superset-ui/core';
-import { noOp } from 'src/utils/common';
-import Modal from 'src/components/Modal';
-import Button from 'src/components/Button';
-import { isCurrentUserBot } from 'src/utils/isBot';
-
-import Icons from 'src/components/Icons';
-import CopyToClipboard from '../CopyToClipboard';
-
-const ErrorAlertDiv = styled.div<{ level: ErrorLevel }>`
-  align-items: center;
-  background-color: ${({ level, theme }) => theme.colors[level].light2};
-  border-radius: ${({ theme }) => theme.borderRadius}px;
-  border: 1px solid ${({ level, theme }) => theme.colors[level].base};
-  color: ${({ level, theme }) => theme.colors[level].dark2};
-  padding: ${({ theme }) => 2 * theme.gridUnit}px;
-  width: 100%;
-
-  .top-row {
-    display: flex;
-    justify-content: space-between;
-  }
-
-  .error-body {
-    padding-top: ${({ theme }) => theme.gridUnit}px;
-    padding-left: ${({ theme }) => 8 * theme.gridUnit}px;
-  }
-
-  .icon {
-    margin-right: ${({ theme }) => 2 * theme.gridUnit}px;
-  }
-
-  .link {
-    color: ${({ level, theme }) => theme.colors[level].dark2};
-    text-decoration: underline;
-    &:focus-visible {
-      border: 1px solid ${({ theme }) => theme.colors.primary.base};
-      padding: ${({ theme }) => theme.gridUnit / 2}px;
-      margin: -${({ theme }) => theme.gridUnit / 2 + 1}px;
-      border-radius: ${({ theme }) => theme.borderRadius}px;
-  }
-`;
-
-const ErrorModal = styled(Modal)<{ level: ErrorLevel }>`
-  color: ${({ level, theme }) => theme.colors[level].dark2};
-  overflow-wrap: break-word;
-
-  .antd5-modal-header {
-    background-color: ${({ level, theme }) => theme.colors[level].light2};
-    padding: ${({ theme }) => 4 * theme.gridUnit}px;
-  }
-
-  .icon {
-    margin-right: ${({ theme }) => 2 * theme.gridUnit}px;
-  }
-
-  .header {
-    display: flex;
-    align-items: center;
-    font-size: ${({ theme }) => theme.typography.sizes.l}px;
-  }
-`;
-
-const LeftSideContent = styled.div`
-  align-items: center;
-  display: flex;
-`;
-
-interface ErrorAlertProps {
-  body: ReactNode;
-  copyText?: string;
-  level: ErrorLevel;
-  source?: ErrorSource;
-  subtitle: ReactNode;
-  title: ReactNode;
-  description?: string;
+import { useState } from 'react';
+import { Modal, Tooltip } from 'antd';
+import { ExclamationCircleOutlined, WarningOutlined } from '@ant-design/icons';
+import Alert from 'src/components/Alert';
+import { t, useTheme } from '@superset-ui/core';
+
+export interface ErrorAlertProps {
+  errorType?: string; // Strong text on the first line
+  message: React.ReactNode | string; // Text shown on the first line
+  type?: 'warning' | 'error' | 'info'; // Allows only 'warning' or 'error'
+  description?: React.ReactNode; // Text shown under the first line, not 
collapsible
+  descriptionDetails?: React.ReactNode | string; // Text shown under the first 
line, collapsible
+  descriptionDetailsCollapsed?: boolean; // Hides the collapsible section 
unless "Show more" is clicked, default true
+  descriptionPre?: boolean; // Uses pre-style to break lines, default true
+  compact?: boolean; // Shows the error icon with tooltip and modal, default 
false
+  children?: React.ReactNode; // Additional content to show in the modal
+  closable?: boolean; // Show close button, default true
+  showIcon?: boolean; // Show icon, default true
 }
 
-export default function ErrorAlert({
-  body,
-  copyText,
-  level = 'error',
-  source = 'dashboard',
-  subtitle,
-  title,
+const ErrorAlert: React.FC<ErrorAlertProps> = ({
+  errorType = t('Error'),
+  message,
+  type = 'error',
   description,
-}: ErrorAlertProps) {
-  const theme = useTheme();
-
-  const [isModalOpen, setIsModalOpen] = useState(false);
-  const [isBodyExpanded, setIsBodyExpanded] = useState(isCurrentUserBot());
+  descriptionDetails,
+  descriptionDetailsCollapsed = true,
+  descriptionPre = true,
+  compact = false,
+  children,
+  closable = true,
+  showIcon = true,
+}) => {
+  const [isDescriptionVisible, setIsDescriptionVisible] = useState(
+    !descriptionDetailsCollapsed,
+  );
+  const [showModal, setShowModal] = useState(false);
 
-  const isExpandable =
-    isCurrentUserBot() || ['explore', 'sqllab'].includes(source);
-  const iconColor = theme.colors[level].base;
+  const toggleDescription = () => {
+    setIsDescriptionVisible(!isDescriptionVisible);
+  };
 
-  return (
-    <ErrorAlertDiv level={level} role="alert">
-      <div className="top-row">
-        <LeftSideContent>
-          {level === 'error' ? (
-            <Icons.ErrorSolid className="icon" iconColor={iconColor} />
-          ) : (
-            <Icons.WarningSolid className="icon" iconColor={iconColor} />
+  const theme = useTheme();
+  const renderTrigger = () => {
+    const icon =
+      type === 'warning' ? <WarningOutlined /> : <ExclamationCircleOutlined />;
+    const color =
+      type === 'warning' ? theme.colors.warning.base : theme.colors.error.base;
+    return (
+      <div style={{ cursor: 'pointer' }}>
+        <span style={{ color }}>{icon} </span>
+        {errorType}
+      </div>
+    );
+  };
+  const preStyle = {
+    whiteSpace: 'pre-wrap',
+    // fontFamily: theme.antd.fontFamilyCode,
+  };
+  const renderDescription = () => (
+    <div>
+      {description && (
+        <p style={descriptionPre ? preStyle : {}} data-testid="description">
+          {description}
+        </p>
+      )}
+      {descriptionDetails && (
+        <div>
+          {isDescriptionVisible && (
+            <p style={descriptionPre ? preStyle : {}}>{descriptionDetails}</p>
           )}
-          <strong>{title}</strong>
-        </LeftSideContent>
-        {!isExpandable && !description && (
           <span
             role="button"
             tabIndex={0}
-            className="link"
-            onClick={() => setIsModalOpen(true)}
-            onKeyDown={event => {
-              if (event.key === 'Enter') {
-                setIsModalOpen(true);
-              }
-            }}
+            onClick={toggleDescription}
+            style={{ textDecoration: 'underline', cursor: 'pointer' }}
           >
-            {t('See more')}
+            {isDescriptionVisible ? t('See less') : t('See more')}
           </span>
-        )}
-      </div>
-      {description && (
-        <div className="error-body">
-          <p>{description}</p>
-          {!isExpandable && (
-            <span
-              role="button"
-              tabIndex={0}
-              className="link"
-              onClick={() => setIsModalOpen(true)}
-              onKeyDown={event => {
-                if (event.key === 'Enter') {
-                  setIsModalOpen(true);
-                }
-              }}
-            >
-              {t('See more')}
-            </span>
-          )}
         </div>
       )}
-      {isExpandable ? (
-        <div className="error-body">
-          <p>{subtitle}</p>
-          {body && (
-            <>
-              {!isBodyExpanded && (
-                <span
-                  role="button"
-                  tabIndex={0}
-                  className="link"
-                  onClick={() => setIsBodyExpanded(true)}
-                  onKeyDown={event => {
-                    if (event.key === 'Enter') {
-                      setIsBodyExpanded(true);
-                    }
-                  }}
-                >
-                  {t('See more')}
-                </span>
-              )}
-              {isBodyExpanded && (
-                <>
-                  <br />
-                  {body}
-                  <span
-                    role="button"
-                    tabIndex={0}
-                    className="link"
-                    onClick={() => setIsBodyExpanded(false)}
-                    onKeyDown={event => {
-                      if (event.key === 'Enter') {
-                        setIsBodyExpanded(false);
-                      }
-                    }}
-                  >
-                    {t('See less')}
-                  </span>
-                </>
-              )}
-            </>
-          )}
-        </div>
-      ) : (
-        <ErrorModal
-          level={level}
-          show={isModalOpen}
-          onHide={() => setIsModalOpen(false)}
-          destroyOnClose
-          title={
-            <div className="header">
-              {level === 'error' ? (
-                <Icons.ErrorSolid className="icon" iconColor={iconColor} />
-              ) : (
-                <Icons.WarningSolid className="icon" iconColor={iconColor} />
-              )}
-              <div className="title">{title}</div>
-            </div>
-          }
-          footer={
-            <>
-              {copyText && (
-                <CopyToClipboard
-                  text={copyText}
-                  shouldShowText={false}
-                  wrapped={false}
-                  copyNode={<Button onClick={noOp}>{t('Copy 
message')}</Button>}
-                />
-              )}
-              <Button
-                cta
-                buttonStyle="primary"
-                onClick={() => setIsModalOpen(false)}
-                tabIndex={0}
-                onKeyDown={event => {
-                  if (event.key === 'Enter') {
-                    setIsModalOpen(false);
-                  }
-                }}
-              >
-                {t('Close')}
-              </Button>
-            </>
-          }
-        >
-          <>
-            <p>{subtitle}</p>
-            {/* This break was in the original design of the modal but
-            the spacing looks really off if there is only
-            subtitle or a body */}
-            {subtitle && body && <br />}
-            {body}
-          </>
-        </ErrorModal>
+    </div>
+  );
+
+  const renderAlert = () => (
+    <Alert
+      description={renderDescription()}
+      type={type}
+      showIcon
+      closable={closable}
+    >
+      <strong>{errorType}</strong>
+      {message && (
+        <>
+          : <span>{message}</span>
+        </>
       )}
-    </ErrorAlertDiv>
+    </Alert>
   );
-}
+
+  if (compact) {
+    return (
+      <>
+        <Tooltip title={`${errorType}: ${message}`}>
+          <span role="button" onClick={() => setShowModal(true)} tabIndex={0}>
+            {renderTrigger()}
+          </span>
+        </Tooltip>
+        <Modal
+          title={errorType}
+          visible={showModal}
+          onCancel={() => setShowModal(false)}
+          footer={null}
+        >
+          {renderAlert()}
+          {children}
+        </Modal>
+      </>
+    );
+  }
+
+  return renderAlert();
+};
+
+export default ErrorAlert;
diff --git 
a/superset-frontend/src/components/ErrorMessage/ErrorMessageWithStackTrace.tsx 
b/superset-frontend/src/components/ErrorMessage/ErrorMessageWithStackTrace.tsx
index 9597b16006..596d336bf8 100644
--- 
a/superset-frontend/src/components/ErrorMessage/ErrorMessageWithStackTrace.tsx
+++ 
b/superset-frontend/src/components/ErrorMessage/ErrorMessageWithStackTrace.tsx
@@ -32,6 +32,7 @@ type Props = {
   stackTrace?: string;
   source?: ErrorSource;
   description?: string;
+  descriptionDetails?: ReactNode;
   errorMitigationFunction?: () => void;
   fallback?: ReactNode;
 };
@@ -45,6 +46,7 @@ export default function ErrorMessageWithStackTrace({
   stackTrace,
   source,
   description,
+  descriptionDetails,
   fallback,
 }: Props) {
   // Check if a custom error message component was registered for this message
@@ -66,28 +68,27 @@ export default function ErrorMessageWithStackTrace({
   if (fallback) {
     return <>{fallback}</>;
   }
+  const computedDescriptionDetails =
+    descriptionDetails ||
+    (link || stackTrace ? (
+      <>
+        {link && (
+          <a href={link} target="_blank" rel="noopener noreferrer">
+            (Request Access)
+          </a>
+        )}
+        <br />
+        {stackTrace && <pre>{stackTrace}</pre>}
+      </>
+    ) : undefined);
 
   return (
     <ErrorAlert
-      level="warning"
-      title={title}
-      subtitle={subtitle}
-      copyText={copyText}
+      type="warning"
+      errorType={title}
+      message={subtitle}
       description={description}
-      source={source}
-      body={
-        link || stackTrace ? (
-          <>
-            {link && (
-              <a href={link} target="_blank" rel="noopener noreferrer">
-                (Request Access)
-              </a>
-            )}
-            <br />
-            {stackTrace && <pre>{stackTrace}</pre>}
-          </>
-        ) : undefined
-      }
+      descriptionDetails={computedDescriptionDetails}
     />
   );
 }
diff --git 
a/superset-frontend/src/components/ErrorMessage/InvalidSQLErrorMessage.test.tsx 
b/superset-frontend/src/components/ErrorMessage/InvalidSQLErrorMessage.test.tsx
index 38f4355679..79d2cff228 100644
--- 
a/superset-frontend/src/components/ErrorMessage/InvalidSQLErrorMessage.test.tsx
+++ 
b/superset-frontend/src/components/ErrorMessage/InvalidSQLErrorMessage.test.tsx
@@ -1,20 +1,19 @@
 /**
  * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
+ * or more contributor license agreements. See the NOTICE file
  * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 import { render } from '@testing-library/react';
@@ -44,83 +43,66 @@ const defaultProps = {
   subtitle: 'Test subtitle',
 };
 
-const setup = (overrides = {}) => (
-  <ThemeProvider theme={supersetTheme}>
-    <InvalidSQLErrorMessage {...defaultProps} {...overrides} />;
-  </ThemeProvider>
-);
-
-// Mock the ErrorAlert component
-jest.mock('./ErrorAlert', () => ({
-  __esModule: true,
-  default: ({
-    title,
-    subtitle,
-    level,
-    source,
-    body,
-  }: {
-    title: React.ReactNode;
-    subtitle?: React.ReactNode;
-    level: ErrorLevel;
-    source: ErrorSource;
-    body: React.ReactNode;
-  }) => (
-    <div data-test="error-alert">
-      <div data-test="title">{title}</div>
-      <div data-test="subtitle">{subtitle}</div>
-      <div data-test="level">{level}</div>
-      <div data-test="source">{source}</div>
-      <div data-test="body">{body}</div>
-    </div>
-  ),
-}));
+const renderComponent = (overrides = {}) =>
+  render(
+    <ThemeProvider theme={supersetTheme}>
+      <InvalidSQLErrorMessage {...defaultProps} {...overrides} />
+    </ThemeProvider>,
+  );
 
 describe('InvalidSQLErrorMessage', () => {
-  it('renders ErrorAlert with correct props', () => {
-    const { getByTestId } = render(setup());
+  it('renders the error message with correct properties', () => {
+    const { getByText } = renderComponent();
 
-    expect(getByTestId('error-alert')).toBeInTheDocument();
-    expect(getByTestId('title')).toHaveTextContent('Unable to parse SQL');
-    expect(getByTestId('subtitle')).toHaveTextContent('Test subtitle');
-    expect(getByTestId('level')).toHaveTextContent('error');
-    expect(getByTestId('source')).toHaveTextContent('test');
+    // Validate main properties
+    expect(getByText('Unable to parse SQL')).toBeInTheDocument();
+    expect(getByText('Test subtitle')).toBeInTheDocument();
+    expect(getByText('SELECT * FFROM table')).toBeInTheDocument();
   });
 
-  it('displays the error line and column indicator', () => {
-    const { getByTestId } = render(setup());
+  it('displays the SQL error line and column indicator', () => {
+    const { getByText, container } = renderComponent();
+
+    // Validate SQL and caret indicator
+    expect(getByText('SELECT * FFROM table')).toBeInTheDocument();
 
-    const body = getByTestId('body');
-    expect(body).toContainHTML('<pre>SELECT * FFROM table</pre>');
-    expect(body).toContainHTML('<pre>         ^</pre>');
+    // Check for caret (`^`) under the error column
+    const preTags = container.querySelectorAll('pre');
+    const secondPre = preTags[1];
+    console.log({ secondPre });
+    expect(secondPre).toHaveTextContent('^');
   });
 
-  it('handles missing line number', () => {
-    const { getByTestId } = render(
-      setup({
-        error: {
-          ...defaultProps.error,
-          extra: { ...defaultProps.error.extra, line: null },
-        },
-      }),
-    );
+  it('handles missing line number gracefully', () => {
+    const overrides = {
+      error: {
+        ...defaultProps.error,
+        extra: { ...defaultProps.error.extra, line: null },
+      },
+    };
+    const { getByText, container } = renderComponent(overrides);
+
+    // Check that the full SQL is displayed
+    expect(getByText('SELECT * FFROM table')).toBeInTheDocument();
 
-    const body = getByTestId('body');
-    expect(body).toBeEmptyDOMElement();
+    // Validate absence of caret indicator
+    const caret = container.querySelector('pre');
+    expect(caret).not.toHaveTextContent('^');
   });
+  it('handles missing column number gracefully', () => {
+    const overrides = {
+      error: {
+        ...defaultProps.error,
+        extra: { ...defaultProps.error.extra, column: null },
+      },
+    };
+    const { getByText, container } = renderComponent(overrides);
 
-  it('handles missing column number', () => {
-    const { getByTestId } = render(
-      setup({
-        error: {
-          ...defaultProps.error,
-          extra: { ...defaultProps.error.extra, column: null },
-        },
-      }),
-    );
+    // Check that the full SQL is displayed
+    expect(getByText('SELECT * FFROM table')).toBeInTheDocument();
 
-    const body = getByTestId('body');
-    expect(body).toHaveTextContent('SELECT * FFROM table');
-    expect(body).not.toHaveTextContent('^');
+    // Validate absence of caret indicator
+    const caret = container.querySelector('pre');
+    expect(caret).not.toHaveTextContent('^');
   });
 });
diff --git 
a/superset-frontend/src/components/ErrorMessage/InvalidSQLErrorMessage.tsx 
b/superset-frontend/src/components/ErrorMessage/InvalidSQLErrorMessage.tsx
index c7b701772d..21236e92a0 100644
--- a/superset-frontend/src/components/ErrorMessage/InvalidSQLErrorMessage.tsx
+++ b/superset-frontend/src/components/ErrorMessage/InvalidSQLErrorMessage.tsx
@@ -40,21 +40,23 @@ function InvalidSQLErrorMessage({
 
   const { sql, line, column } = extra;
   const lines = sql.split('\n');
-  const errorLine = line !== null ? lines[line - 1] : null;
+  let errorLine;
+  if (line !== null) errorLine = lines[line - 1];
+  else if (lines.length > 0) {
+    errorLine = lines[0];
+  }
   const body = errorLine && (
     <>
       <pre>{errorLine}</pre>
       {column !== null && <pre>{' '.repeat(column - 1)}^</pre>}
     </>
   );
-
   return (
     <ErrorAlert
-      title={t('Unable to parse SQL')}
-      subtitle={subtitle}
-      level={level}
-      source={source}
-      body={body}
+      errorType={t('Unable to parse SQL')}
+      message={subtitle}
+      type={level}
+      description={body}
     />
   );
 }
diff --git 
a/superset-frontend/src/components/ErrorMessage/OAuth2RedirectMessage.tsx 
b/superset-frontend/src/components/ErrorMessage/OAuth2RedirectMessage.tsx
index 6b1c6731b9..0e2bad17d1 100644
--- a/superset-frontend/src/components/ErrorMessage/OAuth2RedirectMessage.tsx
+++ b/superset-frontend/src/components/ErrorMessage/OAuth2RedirectMessage.tsx
@@ -168,11 +168,10 @@ function OAuth2RedirectMessage({
 
   return (
     <ErrorAlert
-      title={t('Authorization needed')}
-      subtitle={subtitle}
-      level={level}
-      source={source}
-      body={body}
+      errorType={t('Authorization needed')}
+      message={subtitle}
+      type={level}
+      description={body}
     />
   );
 }
diff --git 
a/superset-frontend/src/components/ErrorMessage/ParameterErrorMessage.test.tsx 
b/superset-frontend/src/components/ErrorMessage/ParameterErrorMessage.test.tsx
index b4d1324059..173820dc90 100644
--- 
a/superset-frontend/src/components/ErrorMessage/ParameterErrorMessage.test.tsx
+++ 
b/superset-frontend/src/components/ErrorMessage/ParameterErrorMessage.test.tsx
@@ -51,7 +51,7 @@ const mockedProps = {
     message: 'Error message',
   },
   source: 'dashboard' as ErrorSource,
-  subtitle: 'Error message',
+  subtitle: 'Error message subtitle',
 };
 
 test('should render', () => {
diff --git 
a/superset-frontend/src/components/ErrorMessage/ParameterErrorMessage.tsx 
b/superset-frontend/src/components/ErrorMessage/ParameterErrorMessage.tsx
index f854b34d02..5fbf31d1ff 100644
--- a/superset-frontend/src/components/ErrorMessage/ParameterErrorMessage.tsx
+++ b/superset-frontend/src/components/ErrorMessage/ParameterErrorMessage.tsx
@@ -107,18 +107,13 @@ function ParameterErrorMessage({
     </>
   );
 
-  const copyText = `${message}
-${triggerMessage}
-${extra.issue_codes.map(issueCode => issueCode.message).join('\n')}`;
-
   return (
     <ErrorAlert
-      title={t('Parameter error')}
-      subtitle={subtitle}
-      level={level}
-      source={source}
-      copyText={copyText}
-      body={body}
+      errorType={t('Parameter error')}
+      type={level}
+      message={message}
+      description={subtitle}
+      descriptionDetails={body}
     />
   );
 }
diff --git 
a/superset-frontend/src/components/ErrorMessage/TimeoutErrorMessage.tsx 
b/superset-frontend/src/components/ErrorMessage/TimeoutErrorMessage.tsx
index b4280db391..f0df59e147 100644
--- a/superset-frontend/src/components/ErrorMessage/TimeoutErrorMessage.tsx
+++ b/superset-frontend/src/components/ErrorMessage/TimeoutErrorMessage.tsx
@@ -88,19 +88,12 @@ function TimeoutErrorMessage({
     </>
   );
 
-  const copyText = t('%(subtitle)s\nThis may be triggered by:\n %(issue)s', {
-    subtitle,
-    issue: extra.issue_codes.map(issueCode => issueCode.message).join('\n'),
-  });
-
   return (
     <ErrorAlert
-      title={t('Timeout error')}
-      subtitle={subtitle}
-      level={level}
-      source={source}
-      copyText={copyText}
-      body={body}
+      errorType={t('Timeout error')}
+      message={subtitle}
+      type={level}
+      descriptionDetails={body}
     />
   );
 }
diff --git a/superset-frontend/src/components/Label/Label.stories.tsx 
b/superset-frontend/src/components/Label/Label.stories.tsx
index 20225811d3..b19666e1fc 100644
--- a/superset-frontend/src/components/Label/Label.stories.tsx
+++ b/superset-frontend/src/components/Label/Label.stories.tsx
@@ -30,7 +30,6 @@ export default {
 // Explicitly type the options array as an array of `Type`
 export const options: Type[] = [
   'default',
-  'alert',
   'info',
   'success',
   'warning',
diff --git a/superset-frontend/src/components/Label/index.tsx 
b/superset-frontend/src/components/Label/index.tsx
index b745084076..13766fec86 100644
--- a/superset-frontend/src/components/Label/index.tsx
+++ b/superset-frontend/src/components/Label/index.tsx
@@ -31,7 +31,6 @@ import PublishedLabel from 
'src/components/Label/reusable/PublishedLabel';
 export type OnClickHandler = MouseEventHandler<HTMLElement>;
 
 export type Type =
-  | 'alert'
   | 'success'
   | 'warning'
   | 'danger'
@@ -64,16 +63,8 @@ export default function Label(props: LabelProps) {
     icon,
     ...rest
   } = props;
-  const {
-    alert,
-    primary,
-    secondary,
-    grayscale,
-    success,
-    warning,
-    error,
-    info,
-  } = colors;
+  const { primary, secondary, grayscale, success, warning, error, info } =
+    colors;
 
   let baseColor;
   if (type === 'primary') {
@@ -82,8 +73,6 @@ export default function Label(props: LabelProps) {
     baseColor = secondary;
   } else if (type === 'success') {
     baseColor = success;
-  } else if (type === 'alert') {
-    baseColor = alert;
   } else if (type === 'warning') {
     baseColor = warning;
   } else if (type === 'danger') {
diff --git a/superset-frontend/src/components/WarningIconWithTooltip/index.tsx 
b/superset-frontend/src/components/WarningIconWithTooltip/index.tsx
index 1e3bae090f..546047728c 100644
--- a/superset-frontend/src/components/WarningIconWithTooltip/index.tsx
+++ b/superset-frontend/src/components/WarningIconWithTooltip/index.tsx
@@ -38,7 +38,7 @@ function WarningIconWithTooltip({
       title={<SafeMarkdown source={warningMarkdown} />}
     >
       <Icons.AlertSolid
-        iconColor={theme.colors.alert.base}
+        iconColor={theme.colors.warning.base}
         iconSize={size}
         css={{ marginRight: marginRight ?? theme.gridUnit * 2 }}
       />
diff --git 
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.tsx
 
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.tsx
index 3f563fc428..1cb8746598 100644
--- 
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.tsx
+++ 
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.tsx
@@ -106,7 +106,7 @@ const StyledDiv = styled.div`
     }
 
     i.warning {
-      color: ${theme.colors.alert.base};
+      color: ${theme.colors.warning.base};
     }
   `}
 `;
diff --git a/superset-frontend/src/explore/components/ControlHeader.tsx 
b/superset-frontend/src/explore/components/ControlHeader.tsx
index 0959015634..4778f80ac3 100644
--- a/superset-frontend/src/explore/components/ControlHeader.tsx
+++ b/superset-frontend/src/explore/components/ControlHeader.tsx
@@ -79,8 +79,8 @@ const ControlHeader: FC<ControlHeaderProps> = ({
       return 'unset';
     }
 
-    return colors.alert.base;
-  }, [colors.error.base, colors.alert.base, validationErrors.length]);
+    return colors.warning.base;
+  }, [colors.error.base, colors.warning.base, validationErrors.length]);
 
   if (!label) {
     return null;
@@ -151,7 +151,10 @@ const ControlHeader: FC<ControlHeaderProps> = ({
           {warning && (
             <span>
               <Tooltip id="error-tooltip" placement="top" title={warning}>
-                <Icons.AlertSolid iconColor={colors.alert.base} iconSize="s" />
+                <Icons.AlertSolid
+                  iconColor={colors.warning.base}
+                  iconSize="s"
+                />
               </Tooltip>{' '}
             </span>
           )}
diff --git 
a/superset-frontend/src/explore/components/ControlPanelsContainer.tsx 
b/superset-frontend/src/explore/components/ControlPanelsContainer.tsx
index 4490cd64a0..27a8f2dd65 100644
--- a/superset-frontend/src/explore/components/ControlPanelsContainer.tsx
+++ b/superset-frontend/src/explore/components/ControlPanelsContainer.tsx
@@ -587,7 +587,7 @@ export const ControlPanelsContainer = (props: 
ControlPanelsContainerProps) => {
 
     const errorColor = sectionHasHadNoErrors.current[sectionId]
       ? colors.error.base
-      : colors.alert.base;
+      : colors.warning.base;
 
     const PanelHeader = () => (
       <span data-test="collapsible-control-panel-header">
@@ -747,7 +747,7 @@ export const ControlPanelsContainer = (props: 
ControlPanelsContainerProps) => {
 
     const errorColor = dataTabHasHadNoErrors.current
       ? colors.error.base
-      : colors.alert.base;
+      : colors.warning.base;
 
     return (
       <>
@@ -777,7 +777,7 @@ export const ControlPanelsContainer = (props: 
ControlPanelsContainerProps) => {
     );
   }, [
     colors.error.base,
-    colors.alert.base,
+    colors.warning.base,
     dataTabHasHadNoErrors,
     props.errorMessage,
   ]);
diff --git a/superset-frontend/src/explore/components/ExploreAlert.tsx 
b/superset-frontend/src/explore/components/ExploreAlert.tsx
index ae7200e984..15c7ad3b4b 100644
--- a/superset-frontend/src/explore/components/ExploreAlert.tsx
+++ b/superset-frontend/src/explore/components/ExploreAlert.tsx
@@ -58,16 +58,16 @@ const AlertContainer = styled.div`
     }
 
     &.alert-type-warning {
-      border-color: ${theme.colors.alert.base};
-      background-color: ${theme.colors.alert.light2};
+      border-color: ${theme.colors.warning.base};
+      background-color: ${theme.colors.warning.light2};
 
       p {
-        color: ${theme.colors.alert.dark2};
+        color: ${theme.colors.warning.dark2};
       }
 
       & a:hover,
       & span[role='button']:hover {
-        color: ${theme.colors.alert.dark1};
+        color: ${theme.colors.warning.dark1};
       }
     }
   `}
diff --git 
a/superset-frontend/src/explore/components/controls/ColorSchemeControl/index.tsx
 
b/superset-frontend/src/explore/components/controls/ColorSchemeControl/index.tsx
index fd9dc85f0f..43946b40ee 100644
--- 
a/superset-frontend/src/explore/components/controls/ColorSchemeControl/index.tsx
+++ 
b/superset-frontend/src/explore/components/controls/ColorSchemeControl/index.tsx
@@ -69,7 +69,7 @@ export interface ColorSchemeControlProps {
 }
 
 const StyledAlert = styled(Icons.AlertSolid)`
-  color: ${({ theme }) => theme.colors.alert.base};
+  color: ${({ theme }) => theme.colors.warning.base};
 `;
 
 const CUSTOM_LABEL_ALERT = t(
diff --git 
a/superset-frontend/src/explore/components/controls/ConditionalFormattingControl/FormattingPopoverContent.tsx
 
b/superset-frontend/src/explore/components/controls/ConditionalFormattingControl/FormattingPopoverContent.tsx
index 79b0829dad..8e68bc0209 100644
--- 
a/superset-frontend/src/explore/components/controls/ConditionalFormattingControl/FormattingPopoverContent.tsx
+++ 
b/superset-frontend/src/explore/components/controls/ConditionalFormattingControl/FormattingPopoverContent.tsx
@@ -41,10 +41,10 @@ const JustifyEnd = styled.div`
 
 const colorSchemeOptions = (theme: SupersetTheme) => [
   { value: theme.colors.success.light1, label: t('success') },
-  { value: theme.colors.alert.light1, label: t('alert') },
+  { value: theme.colors.warning.light1, label: t('alert') },
   { value: theme.colors.error.light1, label: t('error') },
   { value: theme.colors.success.dark1, label: t('success dark') },
-  { value: theme.colors.alert.dark1, label: t('alert dark') },
+  { value: theme.colors.warning.dark1, label: t('alert dark') },
   { value: theme.colors.error.dark1, label: t('error dark') },
 ];
 
diff --git 
a/superset-frontend/src/features/alerts/components/AlertStatusIcon.tsx 
b/superset-frontend/src/features/alerts/components/AlertStatusIcon.tsx
index f3f743264c..d558e5a437 100644
--- a/superset-frontend/src/features/alerts/components/AlertStatusIcon.tsx
+++ b/superset-frontend/src/features/alerts/components/AlertStatusIcon.tsx
@@ -34,11 +34,11 @@ function getStatusColor(
     case AlertState.Success:
       return isReportEnabled
         ? theme.colors.success.base
-        : theme.colors.alert.base;
+        : theme.colors.warning.base;
     case AlertState.Noop:
       return theme.colors.success.base;
     case AlertState.Grace:
-      return theme.colors.alert.base;
+      return theme.colors.warning.base;
     default:
       return theme.colors.grayscale.base;
   }
diff --git a/superset-frontend/src/theme/index.ts 
b/superset-frontend/src/theme/index.ts
index 7b61bfdeca..80b971cc8f 100644
--- a/superset-frontend/src/theme/index.ts
+++ b/superset-frontend/src/theme/index.ts
@@ -56,15 +56,6 @@ const baseConfig: ThemeConfig = {
     zIndexPopupBase: supersetTheme.zIndex.max,
   },
   components: {
-    Alert: {
-      borderRadius: supersetTheme.borderRadius,
-      colorBgContainer: supersetTheme.colors.grayscale.light5,
-      colorBorder: supersetTheme.colors.grayscale.light3,
-      fontSize: supersetTheme.typography.sizes.m,
-      fontSizeLG: supersetTheme.typography.sizes.m,
-      fontSizeIcon: supersetTheme.typography.sizes.l,
-      colorText: supersetTheme.colors.grayscale.dark1,
-    },
     Avatar: {
       containerSize: 32,
       fontSize: supersetTheme.typography.sizes.s,

Reply via email to