You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by kg...@apache.org on 2021/08/18 10:24:15 UTC
[superset] branch master updated: chore(explore): make
metric/column search input clearable (#16320)
This is an automated email from the ASF dual-hosted git repository.
kgabryje 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 2c595b0 chore(explore): make metric/column search input clearable (#16320)
2c595b0 is described below
commit 2c595b09eabb28e22a8bb3866d1fc940c6373753
Author: Kamil Gabryjelski <ka...@gmail.com>
AuthorDate: Wed Aug 18 12:23:23 2021 +0200
chore(explore): make metric/column search input clearable (#16320)
* chore(explore): make metric/column search input clearable
* Fix typo
* Fix test
---
.../DatasourcePanel/DatasourcePanel.test.tsx | 2 +-
.../explore/components/DatasourcePanel/index.tsx | 379 ++++++++++++---------
2 files changed, 211 insertions(+), 170 deletions(-)
diff --git a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx
index 0d06595..3448433 100644
--- a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx
+++ b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx
@@ -116,7 +116,7 @@ test('should render 0 search results', async () => {
const searchInput = screen.getByPlaceholderText('Search Metrics & Columns');
search('nothing', searchInput);
- expect(await screen.findByText('Showing 0 of 0')).toBeInTheDocument();
+ expect(await screen.findAllByText('Showing 0 of 0')).toHaveLength(2);
});
test('should search and render matching columns', async () => {
diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx
index bc436cf..2094583 100644
--- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx
+++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx
@@ -16,12 +16,19 @@
* specific language governing permissions and limitations
* under the License.
*/
-import React, { useEffect, useMemo, useRef, useState } from 'react';
-import { styled, t } from '@superset-ui/core';
-import Collapse from 'src/components/Collapse';
+import React, {
+ useCallback,
+ useEffect,
+ useMemo,
+ useRef,
+ useState,
+} from 'react';
import { ControlConfig, DatasourceMeta } from '@superset-ui/chart-controls';
import { debounce } from 'lodash';
import { matchSorter, rankings } from 'match-sorter';
+import { css, styled, t } from '@superset-ui/core';
+import Collapse from 'src/components/Collapse';
+import { Input } from 'src/common/components';
import { FAST_DEBOUNCE } from 'src/constants';
import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags';
import { ExploreActions } from 'src/explore/actions/exploreActions';
@@ -55,36 +62,39 @@ const ButtonContainer = styled.div`
`;
const DatasourceContainer = styled.div`
- background-color: ${({ theme }) => theme.colors.grayscale.light4};
- position: relative;
- height: 100%;
- display: flex;
- flex-direction: column;
- max-height: 100%;
- .ant-collapse {
- height: auto;
- }
- .field-selections {
- padding: ${({ theme }) => `0 0 ${4 * theme.gridUnit}px`};
- overflow: auto;
- }
- .field-length {
- margin-bottom: ${({ theme }) => theme.gridUnit * 2}px;
- font-size: ${({ theme }) => theme.typography.sizes.s}px;
- color: ${({ theme }) => theme.colors.grayscale.light1};
- }
- .form-control.input-md {
- width: calc(100% - ${({ theme }) => theme.gridUnit * 4}px);
- margin: ${({ theme }) => theme.gridUnit * 2}px auto;
- }
- .type-label {
- font-weight: ${({ theme }) => theme.typography.weights.light};
- font-size: ${({ theme }) => theme.typography.sizes.s}px;
- color: ${({ theme }) => theme.colors.grayscale.base};
- }
- .Control {
- padding-bottom: 0;
- }
+ ${({ theme }) => css`
+ background-color: ${theme.colors.grayscale.light4};
+ position: relative;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ max-height: 100%;
+ .ant-collapse {
+ height: auto;
+ }
+ .field-selections {
+ padding: 0 0 ${4 * theme.gridUnit}px;
+ overflow: auto;
+ }
+ .field-length {
+ margin-bottom: ${theme.gridUnit * 2}px;
+ font-size: ${theme.typography.sizes.s}px;
+ color: ${theme.colors.grayscale.light1};
+ }
+ .form-control.input-md {
+ width: calc(100% - ${theme.gridUnit * 4}px);
+ height: ${theme.gridUnit * 8}px;
+ margin: ${theme.gridUnit * 2}px auto;
+ }
+ .type-label {
+ font-weight: ${theme.typography.weights.light};
+ font-size: ${theme.typography.sizes.s}px;
+ color: ${theme.colors.grayscale.base};
+ }
+ .Control {
+ padding-bottom: 0;
+ }
+ `};
`;
const LabelWrapper = styled.div`
@@ -183,59 +193,62 @@ export default function DataSourcePanel({
const DEFAULT_MAX_COLUMNS_LENGTH = 50;
const DEFAULT_MAX_METRICS_LENGTH = 50;
- const search = debounce((value: string) => {
- if (value === '') {
- setList({ columns, metrics });
- return;
- }
- setList({
- columns: matchSorter(columns, value, {
- keys: [
- {
- key: 'verbose_name',
- threshold: rankings.CONTAINS,
- },
- {
- key: 'column_name',
- threshold: rankings.CONTAINS,
- },
- {
- key: item =>
- [item.description, item.expression].map(
- x => x?.replace(/[_\n\s]+/g, ' ') || '',
- ),
- threshold: rankings.CONTAINS,
- maxRanking: rankings.CONTAINS,
- },
- ],
- keepDiacritics: true,
- }),
- metrics: matchSorter(metrics, value, {
- keys: [
- {
- key: 'verbose_name',
- threshold: rankings.CONTAINS,
- },
- {
- key: 'metric_name',
- threshold: rankings.CONTAINS,
- },
- {
- key: item =>
- [item.description, item.expression].map(
- x => x?.replace(/[_\n\s]+/g, ' ') || '',
- ),
- threshold: rankings.CONTAINS,
- maxRanking: rankings.CONTAINS,
- },
- ],
- keepDiacritics: true,
- baseSort: (a, b) =>
- Number(b.item.is_certified) - Number(a.item.is_certified) ||
- String(a.rankedValue).localeCompare(b.rankedValue),
- }),
- });
- }, FAST_DEBOUNCE);
+ const search = useCallback(
+ debounce((value: string) => {
+ if (value === '') {
+ setList({ columns, metrics });
+ return;
+ }
+ setList({
+ columns: matchSorter(columns, value, {
+ keys: [
+ {
+ key: 'verbose_name',
+ threshold: rankings.CONTAINS,
+ },
+ {
+ key: 'column_name',
+ threshold: rankings.CONTAINS,
+ },
+ {
+ key: item =>
+ [item.description, item.expression].map(
+ x => x?.replace(/[_\n\s]+/g, ' ') || '',
+ ),
+ threshold: rankings.CONTAINS,
+ maxRanking: rankings.CONTAINS,
+ },
+ ],
+ keepDiacritics: true,
+ }),
+ metrics: matchSorter(metrics, value, {
+ keys: [
+ {
+ key: 'verbose_name',
+ threshold: rankings.CONTAINS,
+ },
+ {
+ key: 'metric_name',
+ threshold: rankings.CONTAINS,
+ },
+ {
+ key: item =>
+ [item.description, item.expression].map(
+ x => x?.replace(/[_\n\s]+/g, ' ') || '',
+ ),
+ threshold: rankings.CONTAINS,
+ maxRanking: rankings.CONTAINS,
+ },
+ ],
+ keepDiacritics: true,
+ baseSort: (a, b) =>
+ Number(b.item.is_certified) - Number(a.item.is_certified) ||
+ String(a.rankedValue).localeCompare(b.rankedValue),
+ }),
+ });
+ }, FAST_DEBOUNCE),
+ [columns, metrics],
+ );
useEffect(() => {
setList({
@@ -245,93 +258,121 @@ export default function DataSourcePanel({
setInputValue('');
}, [columns, datasource, metrics]);
- const metricSlice = showAllMetrics
- ? lists.metrics
- : lists.metrics.slice(0, DEFAULT_MAX_COLUMNS_LENGTH);
- const columnSlice = showAllColumns
- ? lists.columns
- : lists.columns.slice(0, DEFAULT_MAX_METRICS_LENGTH);
+ const metricSlice = useMemo(
+ () =>
+ showAllMetrics
+ ? lists.metrics
+ : lists.metrics.slice(0, DEFAULT_MAX_METRICS_LENGTH),
+ [lists.metrics, showAllMetrics],
+ );
+ const columnSlice = useMemo(
+ () =>
+ showAllColumns
+ ? lists.columns
+ : lists.columns.slice(0, DEFAULT_MAX_COLUMNS_LENGTH),
+ [lists.columns, showAllColumns],
+ );
- const mainBody = (
- <>
- <input
- type="text"
- onChange={evt => {
- setInputValue(evt.target.value);
- search(evt.target.value);
- }}
- value={inputValue}
- className="form-control input-md"
- placeholder={t('Search Metrics & Columns')}
- />
- <div className="field-selections">
- <Collapse
- bordered
- defaultActiveKey={['metrics', 'column']}
- expandIconPosition="right"
- ghost
- >
- <Collapse.Panel
- header={<span className="header">{t('Metrics')}</span>}
- key="metrics"
+ const mainBody = useMemo(
+ () => (
+ <>
+ <Input
+ allowClear
+ onChange={evt => {
+ setInputValue(evt.target.value);
+ search(evt.target.value);
+ }}
+ value={inputValue}
+ className="form-control input-md"
+ placeholder={t('Search Metrics & Columns')}
+ />
+ <div className="field-selections">
+ <Collapse
+ bordered
+ defaultActiveKey={['metrics', 'column']}
+ expandIconPosition="right"
+ ghost
>
- <div className="field-length">
- {t(`Showing %s of %s`, metricSlice.length, lists.metrics.length)}
- </div>
- {metricSlice.map(m => (
- <LabelContainer key={m.metric_name} className="column">
- {enableExploreDnd ? (
- <DatasourcePanelDragOption
- value={m}
- type={DndItemType.Metric}
- />
- ) : (
- <StyledMetricOption metric={m} showType />
+ <Collapse.Panel
+ header={<span className="header">{t('Metrics')}</span>}
+ key="metrics"
+ >
+ <div className="field-length">
+ {t(
+ `Showing %s of %s`,
+ metricSlice.length,
+ lists.metrics.length,
)}
- </LabelContainer>
- ))}
- {lists.metrics.length > DEFAULT_MAX_METRICS_LENGTH ? (
- <ButtonContainer>
- <Button onClick={() => setShowAllMetrics(!showAllMetrics)}>
- {showAllMetrics ? t('Show less...') : t('Show all...')}
- </Button>
- </ButtonContainer>
- ) : (
- <></>
- )}
- </Collapse.Panel>
- <Collapse.Panel
- header={<span className="header">{t('Columns')}</span>}
- key="column"
- >
- <div className="field-length">
- {t(`Showing %s of %s`, columnSlice.length, lists.columns.length)}
- </div>
- {columnSlice.map(col => (
- <LabelContainer key={col.column_name} className="column">
- {enableExploreDnd ? (
- <DatasourcePanelDragOption
- value={col}
- type={DndItemType.Column}
- />
- ) : (
- <StyledColumnOption column={col} showType />
+ </div>
+ {metricSlice.map(m => (
+ <LabelContainer key={m.metric_name} className="column">
+ {enableExploreDnd ? (
+ <DatasourcePanelDragOption
+ value={m}
+ type={DndItemType.Metric}
+ />
+ ) : (
+ <StyledMetricOption metric={m} showType />
+ )}
+ </LabelContainer>
+ ))}
+ {lists.metrics.length > DEFAULT_MAX_METRICS_LENGTH ? (
+ <ButtonContainer>
+ <Button onClick={() => setShowAllMetrics(!showAllMetrics)}>
+ {showAllMetrics ? t('Show less...') : t('Show all...')}
+ </Button>
+ </ButtonContainer>
+ ) : (
+ <></>
+ )}
+ </Collapse.Panel>
+ <Collapse.Panel
+ header={<span className="header">{t('Columns')}</span>}
+ key="column"
+ >
+ <div className="field-length">
+ {t(
+ `Showing %s of %s`,
+ columnSlice.length,
+ lists.columns.length,
)}
- </LabelContainer>
- ))}
- {lists.columns.length > DEFAULT_MAX_COLUMNS_LENGTH ? (
- <ButtonContainer>
- <Button onClick={() => setShowAllColumns(!showAllColumns)}>
- {showAllColumns ? t('Show Less...') : t('Show all...')}
- </Button>
- </ButtonContainer>
- ) : (
- <></>
- )}
- </Collapse.Panel>
- </Collapse>
- </div>
- </>
+ </div>
+ {columnSlice.map(col => (
+ <LabelContainer key={col.column_name} className="column">
+ {enableExploreDnd ? (
+ <DatasourcePanelDragOption
+ value={col}
+ type={DndItemType.Column}
+ />
+ ) : (
+ <StyledColumnOption column={col} showType />
+ )}
+ </LabelContainer>
+ ))}
+ {lists.columns.length > DEFAULT_MAX_COLUMNS_LENGTH ? (
+ <ButtonContainer>
+ <Button onClick={() => setShowAllColumns(!showAllColumns)}>
+ {showAllColumns ? t('Show Less...') : t('Show all...')}
+ </Button>
+ </ButtonContainer>
+ ) : (
+ <></>
+ )}
+ </Collapse.Panel>
+ </Collapse>
+ </div>
+ </>
+ ),
+ [
+ columnSlice,
+ inputValue,
+ lists.columns.length,
+ lists.metrics.length,
+ metricSlice,
+ search,
+ showAllColumns,
+ showAllMetrics,
+ ],
);
return (