You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by mi...@apache.org on 2021/08/24 11:20:42 UTC

[superset] branch master updated: chore: Changes the AlertReportModal to use the new Select component (#16144)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 1fc9318  chore: Changes the AlertReportModal to use the new Select component (#16144)
1fc9318 is described below

commit 1fc9318594d9677ed3dada50c65ce90c22361050
Author: Michael S. Molina <70...@users.noreply.github.com>
AuthorDate: Tue Aug 24 08:19:27 2021 -0300

    chore: Changes the AlertReportModal to use the new Select component (#16144)
    
    * chore: Changes the AlertReportModal to use the new Select component
    
    * Gives time to close the select before changing the type
---
 .../src/components/TimezoneSelector/index.tsx      |  16 +-
 .../src/views/CRUD/alert/AlertReportModal.test.jsx |  36 +-
 .../src/views/CRUD/alert/AlertReportModal.tsx      | 418 ++++++++++-----------
 .../CRUD/alert/components/NotificationMethod.tsx   |  20 +-
 4 files changed, 226 insertions(+), 264 deletions(-)

diff --git a/superset-frontend/src/components/TimezoneSelector/index.tsx b/superset-frontend/src/components/TimezoneSelector/index.tsx
index 73c6f1f..48dd100 100644
--- a/superset-frontend/src/components/TimezoneSelector/index.tsx
+++ b/superset-frontend/src/components/TimezoneSelector/index.tsx
@@ -19,8 +19,8 @@
 
 import React, { useEffect, useRef } from 'react';
 import moment from 'moment-timezone';
-
-import { NativeGraySelect as Select } from 'src/components/Select';
+import { t } from '@superset-ui/core';
+import { Select } from 'src/components';
 
 const DEFAULT_TIMEZONE = 'GMT Standard Time';
 const MIN_SELECT_WIDTH = '400px';
@@ -92,12 +92,6 @@ const TIMEZONE_OPTIONS = TIMEZONES.sort(
   offsets: getOffsetKey(zone.name),
 }));
 
-const timezoneOptions = TIMEZONE_OPTIONS.map(option => (
-  <Select.Option key={option.value} value={option.value}>
-    {option.label}
-  </Select.Option>
-));
-
 const TimezoneSelector = ({ onTimezoneChange, timezone }: TimezoneProps) => {
   const prevTimezone = useRef(timezone);
   const matchTimezoneToOptions = (timezone: string) =>
@@ -120,12 +114,12 @@ const TimezoneSelector = ({ onTimezoneChange, timezone }: TimezoneProps) => {
 
   return (
     <Select
+      ariaLabel={t('Timezone')}
       css={{ minWidth: MIN_SELECT_WIDTH }} // smallest size for current values
       onChange={onTimezoneChange}
       value={timezone || DEFAULT_TIMEZONE}
-    >
-      {timezoneOptions}
-    </Select>
+      options={TIMEZONE_OPTIONS}
+    />
   );
 };
 
diff --git a/superset-frontend/src/views/CRUD/alert/AlertReportModal.test.jsx b/superset-frontend/src/views/CRUD/alert/AlertReportModal.test.jsx
index 8575a52..1509bff 100644
--- a/superset-frontend/src/views/CRUD/alert/AlertReportModal.test.jsx
+++ b/superset-frontend/src/views/CRUD/alert/AlertReportModal.test.jsx
@@ -24,7 +24,7 @@ import fetchMock from 'fetch-mock';
 import { act } from 'react-dom/test-utils';
 import AlertReportModal from 'src/views/CRUD/alert/AlertReportModal';
 import Modal from 'src/components/Modal';
-import { AsyncSelect, NativeGraySelect as Select } from 'src/components/Select';
+import { Select } from 'src/components';
 import { Switch } from 'src/components/Switch';
 import { Radio } from 'src/components/Radio';
 import TextAreaControl from 'src/explore/components/controls/TextAreaControl';
@@ -161,11 +161,15 @@ describe('AlertReportModal', () => {
     };
 
     const editWrapper = await mountAndWait(props);
-    expect(editWrapper.find(AsyncSelect).at(1).props().value).toEqual({
+    expect(
+      editWrapper.find('[aria-label="Database"]').at(0).props().value,
+    ).toEqual({
       value: 1,
       label: 'test database',
     });
-    expect(editWrapper.find(AsyncSelect).at(2).props().value).toEqual({
+    expect(
+      editWrapper.find('[aria-label="Chart"]').at(0).props().value,
+    ).toEqual({
       value: 1,
       label: 'test chart',
     });
@@ -176,21 +180,9 @@ describe('AlertReportModal', () => {
     expect(wrapper.find('input[name="name"]')).toExist();
   });
 
-  it('renders three async select elements when in report mode', () => {
-    expect(wrapper.find(AsyncSelect)).toExist();
-    expect(wrapper.find(AsyncSelect)).toHaveLength(3);
-  });
-
-  it('renders four async select elements when in alert mode', async () => {
-    const props = {
-      ...mockedProps,
-      isReport: false,
-    };
-
-    const addWrapper = await mountAndWait(props);
-
-    expect(addWrapper.find(AsyncSelect)).toExist();
-    expect(addWrapper.find(AsyncSelect)).toHaveLength(4);
+  it('renders five select elements when in report mode', () => {
+    expect(wrapper.find(Select)).toExist();
+    expect(wrapper.find(Select)).toHaveLength(5);
   });
 
   it('renders Switch element', () => {
@@ -226,12 +218,12 @@ describe('AlertReportModal', () => {
     expect(input.props().value).toEqual('SELECT NaN');
   });
 
-  it('renders two select element when in report mode', () => {
+  it('renders five select element when in report mode', () => {
     expect(wrapper.find(Select)).toExist();
-    expect(wrapper.find(Select)).toHaveLength(2);
+    expect(wrapper.find(Select)).toHaveLength(5);
   });
 
-  it('renders three select elements when in alert mode', async () => {
+  it('renders seven select elements when in alert mode', async () => {
     const props = {
       ...mockedProps,
       isReport: false,
@@ -240,7 +232,7 @@ describe('AlertReportModal', () => {
     const addWrapper = await mountAndWait(props);
 
     expect(addWrapper.find(Select)).toExist();
-    expect(addWrapper.find(Select)).toHaveLength(3);
+    expect(addWrapper.find(Select)).toHaveLength(7);
   });
 
   it('renders value input element when in alert mode', async () => {
diff --git a/superset-frontend/src/views/CRUD/alert/AlertReportModal.tsx b/superset-frontend/src/views/CRUD/alert/AlertReportModal.tsx
index c39783e..a16662d 100644
--- a/superset-frontend/src/views/CRUD/alert/AlertReportModal.tsx
+++ b/superset-frontend/src/views/CRUD/alert/AlertReportModal.tsx
@@ -16,7 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React, { FunctionComponent, useState, useEffect } from 'react';
+import React, {
+  FunctionComponent,
+  useState,
+  useEffect,
+  useMemo,
+  useCallback,
+} from 'react';
 import {
   styled,
   t,
@@ -32,7 +38,7 @@ import { Switch } from 'src/components/Switch';
 import Modal from 'src/components/Modal';
 import TimezoneSelector from 'src/components/TimezoneSelector';
 import { Radio } from 'src/components/Radio';
-import { AsyncSelect, NativeGraySelect as Select } from 'src/components/Select';
+import { Select } from 'src/components';
 import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
 import Owner from 'src/types/Owner';
@@ -215,10 +221,6 @@ const StyledSectionContainer = styled.div`
       }
     }
   }
-
-  .hide-dropdown {
-    display: none;
-  }
 `;
 
 const StyledSectionTitle = styled.div`
@@ -248,7 +250,7 @@ const StyledSwitchContainer = styled.div`
 `;
 
 export const StyledInputContainer = styled.div`
-  flex: 1 1 auto;
+  flex: 1;
   margin: ${({ theme }) => theme.gridUnit * 2}px;
   margin-top: 0;
 
@@ -284,9 +286,7 @@ export const StyledInputContainer = styled.div`
   }
 
   input,
-  textarea,
-  .Select,
-  .ant-select {
+  textarea {
     flex: 1 1 auto;
   }
 
@@ -300,36 +300,24 @@ export const StyledInputContainer = styled.div`
   }
 
   input::placeholder,
-  textarea::placeholder,
-  .Select__placeholder {
+  textarea::placeholder {
     color: ${({ theme }) => theme.colors.grayscale.light1};
   }
 
   textarea,
   input[type='text'],
-  input[type='number'],
-  .Select__control,
-  .ant-select-single .ant-select-selector {
-    padding: ${({ theme }) => theme.gridUnit * 1.5}px
+  input[type='number'] {
+    padding: ${({ theme }) => theme.gridUnit}px
       ${({ theme }) => theme.gridUnit * 2}px;
     border-style: none;
     border: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
     border-radius: ${({ theme }) => theme.gridUnit}px;
 
-    .ant-select-selection-placeholder,
-    .ant-select-selection-item {
-      line-height: 24px;
-    }
-
     &[name='description'] {
       flex: 1 1 auto;
     }
   }
 
-  .Select__control {
-    padding: 2px 0;
-  }
-
   .input-label {
     margin-left: 10px;
   }
@@ -581,94 +569,100 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
   };
 
   // Fetch data to populate form dropdowns
-  const loadOwnerOptions = (input = '') => {
-    const query = rison.encode({ filter: input, page_size: SELECT_PAGE_SIZE });
-    return SupersetClient.get({
-      endpoint: `/api/v1/report/related/owners?q=${query}`,
-    }).then(
-      response =>
-        response.json.result.map((item: any) => ({
-          value: item.value,
-          label: item.text,
-        })),
-      badResponse => [],
-    );
-  };
-
-  const loadSourceOptions = (input = '') => {
-    const query = rison.encode({ filter: input, page_size: SELECT_PAGE_SIZE });
-    return SupersetClient.get({
-      endpoint: `/api/v1/report/related/database?q=${query}`,
-    }).then(
-      response => {
-        const list = response.json.result.map((item: any) => ({
-          value: item.value,
-          label: item.text,
-        }));
-
-        setSourceOptions(list);
-
-        // Find source if current alert has one set
-        if (
-          currentAlert &&
-          currentAlert.database &&
-          !currentAlert.database.label
-        ) {
-          updateAlertState('database', getSourceData());
-        }
+  const loadOwnerOptions = useMemo(
+    () => (input = '', page: number, pageSize: number) => {
+      const query = rison.encode({ filter: input, page, page_size: pageSize });
+      return SupersetClient.get({
+        endpoint: `/api/v1/report/related/owners?q=${query}`,
+      }).then(response => ({
+        data: response.json.result.map(
+          (item: { value: number; text: string }) => ({
+            value: item.value,
+            label: item.text,
+          }),
+        ),
+        totalCount: response.json.count,
+      }));
+    },
+    [],
+  );
 
-        return list;
-      },
-      badResponse => [],
-    );
-  };
+  const getSourceData = useCallback(
+    (db?: MetaObject) => {
+      const database = db || currentAlert?.database;
 
-  const getSourceData = (db?: MetaObject) => {
-    const database = db || currentAlert?.database;
+      if (!database || database.label) {
+        return null;
+      }
 
-    if (!database || database.label) {
-      return null;
-    }
+      let result;
 
-    let result;
+      // Cycle through source options to find the selected option
+      sourceOptions.forEach(source => {
+        if (source.value === database.value || source.value === database.id) {
+          result = source;
+        }
+      });
 
-    // Cycle through source options to find the selected option
-    sourceOptions.forEach(source => {
-      if (source.value === database.value || source.value === database.id) {
-        result = source;
-      }
-    });
+      return result;
+    },
+    [currentAlert?.database, sourceOptions],
+  );
 
-    return result;
+  // Updating alert/report state
+  const updateAlertState = (name: string, value: any) => {
+    setCurrentAlert(currentAlertData => ({
+      ...currentAlertData,
+      [name]: value,
+    }));
   };
 
-  const loadDashboardOptions = (input = '') => {
-    const query = rison.encode({ filter: input, page_size: SELECT_PAGE_SIZE });
-    return SupersetClient.get({
-      endpoint: `/api/v1/report/related/dashboard?q=${query}`,
-    }).then(
-      response => {
-        const list = response.json.result.map((item: any) => ({
-          value: item.value,
-          label: item.text,
-        }));
+  const loadSourceOptions = useMemo(
+    () => (input = '', page: number, pageSize: number) => {
+      const query = rison.encode({ filter: input, page, page_size: pageSize });
+      return SupersetClient.get({
+        endpoint: `/api/v1/report/related/database?q=${query}`,
+      }).then(response => {
+        const list = response.json.result.map(
+          (item: { value: number; text: string }) => ({
+            value: item.value,
+            label: item.text,
+          }),
+        );
+        setSourceOptions(list);
+        return { data: list, totalCount: response.json.count };
+      });
+    },
+    [],
+  );
 
+  const databaseLabel =
+    currentAlert && currentAlert.database && !currentAlert.database.label;
+  useEffect(() => {
+    // Find source if current alert has one set
+    if (databaseLabel) {
+      updateAlertState('database', getSourceData());
+    }
+  }, [databaseLabel, getSourceData]);
+
+  const loadDashboardOptions = useMemo(
+    () => (input = '', page: number, pageSize: number) => {
+      const query = rison.encode({ filter: input, page, page_size: pageSize });
+      return SupersetClient.get({
+        endpoint: `/api/v1/report/related/dashboard?q=${query}`,
+      }).then(response => {
+        const list = response.json.result.map(
+          (item: { value: number; text: string }) => ({
+            value: item.value,
+            label: item.text,
+          }),
+        );
         setDashboardOptions(list);
-
-        // Find source if current alert has one set
-        if (
-          currentAlert &&
-          currentAlert.dashboard &&
-          !currentAlert.dashboard.label
-        ) {
-          updateAlertState('dashboard', getDashboardData());
-        }
-
-        return list;
-      },
-      badResponse => [],
-    );
-  };
+        return { data: list, totalCount: response.json.count };
+      });
+    },
+    [],
+  );
 
   const getDashboardData = (db?: MetaObject) => {
     const dashboard = db || currentAlert?.dashboard;
@@ -689,62 +683,62 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
     return result;
   };
 
-  const loadChartOptions = (input = '') => {
-    const query = rison.encode({ filter: input, page_size: SELECT_PAGE_SIZE });
-    return SupersetClient.get({
-      endpoint: `/api/v1/report/related/chart?q=${query}`,
-    }).then(
-      response => {
-        const list = response.json.result.map((item: any) => ({
-          value: item.value,
-          label: item.text,
-        }));
+  const getChartData = useCallback(
+    (chartData?: MetaObject) => {
+      const chart = chartData || currentAlert?.chart;
 
-        setChartOptions(list);
+      if (!chart || chart.label) {
+        return null;
+      }
 
-        // Find source if current alert has one set
-        if (currentAlert && currentAlert.chart && !currentAlert.chart.label) {
-          updateAlertState('chart', getChartData());
-        }
+      let result;
 
-        return list;
-      },
-      badResponse => [],
-    );
-  };
+      // Cycle through chart options to find the selected option
+      chartOptions.forEach(slice => {
+        if (slice.value === chart.value || slice.value === chart.id) {
+          result = slice;
+        }
+      });
 
-  const getChartData = (chartData?: MetaObject) => {
-    const chart = chartData || currentAlert?.chart;
+      return result;
+    },
+    [chartOptions, currentAlert?.chart],
+  );
 
-    if (!chart || chart.label) {
-      return null;
+  const noChartLabel =
+    currentAlert && currentAlert.chart && !currentAlert.chart.label;
+  useEffect(() => {
+    // Find source if current alert has one set
+    if (noChartLabel) {
+      updateAlertState('chart', getChartData());
     }
+  }, [getChartData, noChartLabel]);
+
+  const loadChartOptions = useMemo(
+    () => (input = '', page: number, pageSize: number) => {
+      const query = rison.encode({ filter: input, page, page_size: pageSize });
+      return SupersetClient.get({
+        endpoint: `/api/v1/report/related/chart?q=${query}`,
+      }).then(response => {
+        const list = response.json.result.map(
+          (item: { value: number; text: string }) => ({
+            value: item.value,
+            label: item.text,
+          }),
+        );
 
-    let result;
-
-    // Cycle through chart options to find the selected option
-    chartOptions.forEach(slice => {
-      if (slice.value === chart.value || slice.value === chart.id) {
-        result = slice;
-      }
-    });
-
-    return result;
-  };
+        setChartOptions(list);
+        return { data: list, totalCount: response.json.count };
+      });
+    },
+    [],
+  );
 
   const getChartVisualizationType = (chart: SelectValue) =>
     SupersetClient.get({
       endpoint: `/api/v1/chart/${chart.value}`,
     }).then(response => setChartVizType(response.json.result.viz_type));
 
-  // Updating alert/report state
-  const updateAlertState = (name: string, value: any) => {
-    setCurrentAlert(currentAlertData => ({
-      ...currentAlertData,
-      [name]: value,
-    }));
-  };
-
   // Handle input/textarea updates
   const onTextChange = (
     event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>,
@@ -775,11 +769,11 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
     updateAlertState('sql', value || '');
   };
 
-  const onOwnersChange = (value: Array<Owner>) => {
+  const onOwnersChange = (value: Array<SelectValue>) => {
     updateAlertState('owners', value || []);
   };
 
-  const onSourceChange = (value: Array<Owner>) => {
+  const onSourceChange = (value: Array<SelectValue>) => {
     updateAlertState('database', value || []);
   };
 
@@ -832,8 +826,8 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
 
   const onContentTypeChange = (event: any) => {
     const { target } = event;
-
-    setContentType(target.value);
+    // Gives time to close the select before changing the type
+    setTimeout(() => setContentType(target.value), 200);
   };
 
   const onFormatChange = (event: any) => {
@@ -1004,19 +998,6 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
     setIsHidden(false);
   }
 
-  // Dropdown options
-  const conditionOptions = CONDITIONS.map(condition => (
-    <Select.Option key={condition.value} value={condition.value}>
-      {condition.label}
-    </Select.Option>
-  ));
-
-  const retentionOptions = RETENTION_OPTIONS.map(option => (
-    <Select.Option key={option.value} value={option.value}>
-      {option.label}
-    </Select.Option>
-  ));
-
   return (
     <StyledModal
       className="no-content-padding"
@@ -1064,13 +1045,22 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
               <span className="required">*</span>
             </div>
             <div data-test="owners-select" className="input-container">
-              <AsyncSelect
+              <Select
+                ariaLabel={t('Owners')}
+                allowClear
+                // TODO Use default page size (100). To do that, the server
+                // needs to send the total number of results. Currently, the
+                // number of results is limited to SELECT_PAGE_SIZE
+                pageSize={SELECT_PAGE_SIZE}
                 name="owners"
-                isMulti
-                value={currentAlert ? currentAlert.owners : []}
-                loadOptions={loadOwnerOptions}
-                defaultOptions // load options on render
-                cacheOptions
+                mode="multiple"
+                value={
+                  (currentAlert?.owners as {
+                    label: string;
+                    value: number;
+                  }[]) || []
+                }
+                options={loadOwnerOptions}
                 onChange={onOwnersChange}
               />
             </div>
@@ -1107,19 +1097,23 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
                   <span className="required">*</span>
                 </div>
                 <div className="input-container">
-                  <AsyncSelect
+                  <Select
+                    ariaLabel={t('Database')}
+                    // TODO Use default page size (100). To do that, the server
+                    // needs to send the total number of results. Currently, the
+                    // number of results is limited to SELECT_PAGE_SIZE
+                    pageSize={SELECT_PAGE_SIZE}
                     name="source"
                     value={
-                      currentAlert?.database
+                      currentAlert?.database?.label &&
+                      currentAlert?.database?.value
                         ? {
                             value: currentAlert.database.value,
                             label: currentAlert.database.label,
                           }
                         : undefined
                     }
-                    loadOptions={loadSourceOptions}
-                    defaultOptions // load options on render
-                    cacheOptions
+                    options={loadSourceOptions}
                     onChange={onSourceChange}
                   />
                 </div>
@@ -1148,21 +1142,14 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
                   </div>
                   <div className="input-container">
                     <Select
+                      ariaLabel={t('Condition')}
                       onChange={onConditionChange}
                       placeholder="Condition"
-                      defaultValue={
-                        currentAlert
-                          ? currentAlert.validator_config_json?.op || undefined
-                          : undefined
-                      }
                       value={
-                        currentAlert
-                          ? currentAlert.validator_config_json?.op || undefined
-                          : undefined
+                        currentAlert?.validator_config_json?.op || undefined
                       }
-                    >
-                      {conditionOptions}
-                    </Select>
+                      options={CONDITIONS}
+                    />
                   </div>
                 </StyledInputContainer>
                 <StyledInputContainer>
@@ -1224,21 +1211,12 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
               </div>
               <div className="input-container">
                 <Select
+                  ariaLabel={t('Log retention')}
+                  placeholder={t('Log retention')}
                   onChange={onLogRetentionChange}
-                  placeholder
-                  defaultValue={
-                    currentAlert
-                      ? currentAlert.log_retention || DEFAULT_RETENTION
-                      : DEFAULT_RETENTION
-                  }
-                  value={
-                    currentAlert
-                      ? currentAlert.log_retention || DEFAULT_RETENTION
-                      : DEFAULT_RETENTION
-                  }
-                >
-                  {retentionOptions}
-                </Select>
+                  value={currentAlert?.log_retention || DEFAULT_RETENTION}
+                  options={RETENTION_OPTIONS}
+                />
               </div>
             </StyledInputContainer>
             <StyledInputContainer>
@@ -1284,44 +1262,46 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
               <StyledRadio value="dashboard">{t('Dashboard')}</StyledRadio>
               <StyledRadio value="chart">{t('Chart')}</StyledRadio>
             </Radio.Group>
-            <AsyncSelect
-              className={
-                contentType === 'chart'
-                  ? 'async-select'
-                  : 'hide-dropdown async-select'
-              }
+            <Select
+              ariaLabel={t('Chart')}
+              css={{
+                display: contentType === 'chart' ? 'inline' : 'none',
+              }}
+              // TODO Use default page size (100). To do that, the server
+              // needs to send the total number of results. Currently, the
+              // number of results is limited to SELECT_PAGE_SIZE
+              pageSize={SELECT_PAGE_SIZE}
               name="chart"
               value={
-                currentAlert && currentAlert.chart
+                currentAlert?.chart?.label && currentAlert?.chart?.value
                   ? {
                       value: currentAlert.chart.value,
                       label: currentAlert.chart.label,
                     }
                   : undefined
               }
-              loadOptions={loadChartOptions}
-              defaultOptions // load options on render
-              cacheOptions
+              options={loadChartOptions}
               onChange={onChartChange}
             />
-            <AsyncSelect
-              className={
-                contentType === 'dashboard'
-                  ? 'async-select'
-                  : 'hide-dropdown async-select'
-              }
+            <Select
+              ariaLabel={t('Dashboard')}
+              css={{
+                display: contentType === 'dashboard' ? 'inline' : 'none',
+              }}
+              // TODO Use default page size (100). To do that, the server
+              // needs to send the total number of results. Currently, the
+              // number of results is limited to SELECT_PAGE_SIZE
+              pageSize={SELECT_PAGE_SIZE}
               name="dashboard"
               value={
-                currentAlert && currentAlert.dashboard
+                currentAlert?.dashboard?.label && currentAlert?.dashboard?.value
                   ? {
                       value: currentAlert.dashboard.value,
                       label: currentAlert.dashboard.label,
                     }
                   : undefined
               }
-              loadOptions={loadDashboardOptions}
-              defaultOptions // load options on render
-              cacheOptions
+              options={loadDashboardOptions}
               onChange={onDashboardChange}
             />
             {formatOptionEnabled && (
diff --git a/superset-frontend/src/views/CRUD/alert/components/NotificationMethod.tsx b/superset-frontend/src/views/CRUD/alert/components/NotificationMethod.tsx
index 0e4b1b7..905e85e 100644
--- a/superset-frontend/src/views/CRUD/alert/components/NotificationMethod.tsx
+++ b/superset-frontend/src/views/CRUD/alert/components/NotificationMethod.tsx
@@ -18,7 +18,7 @@
  */
 import React, { FunctionComponent, useState } from 'react';
 import { styled, t, useTheme } from '@superset-ui/core';
-import { NativeGraySelect as Select } from 'src/components/Select';
+import { Select } from 'src/components';
 import Icons from 'src/components/Icons';
 import { StyledInputContainer } from '../AlertReportModal';
 
@@ -116,26 +116,22 @@ export const NotificationMethod: FunctionComponent<NotificationMethodProps> = ({
     setRecipientValue(recipients);
   }
 
-  const methodOptions = (options || []).map((method: NotificationMethod) => (
-    <Select.Option key={method} value={method}>
-      {t(method)}
-    </Select.Option>
-  ));
-
   return (
     <StyledNotificationMethod>
       <div className="inline-container">
         <StyledInputContainer>
           <div className="input-container">
             <Select
+              ariaLabel={t('Delivery method')}
               data-test="select-delivery-method"
               onChange={onMethodChange}
-              placeholder="Select Delivery Method"
-              defaultValue={method}
+              placeholder={t('Select Delivery Method')}
+              options={(options || []).map((method: NotificationMethod) => ({
+                label: method,
+                value: method,
+              }))}
               value={method}
-            >
-              {methodOptions}
-            </Select>
+            />
           </div>
         </StyledInputContainer>
         {method !== undefined && !!onRemove ? (