From 2c595b09eabb28e22a8bb3866d1fc940c6373753 Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Wed, 18 Aug 2021 12:23:23 +0200 Subject: [PATCH] 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 +- .../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 0d065958339b..34484334768c 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 bc436cf66e80..209458335c55 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 = ( - <> - { - setInputValue(evt.target.value); - search(evt.target.value); - }} - value={inputValue} - className="form-control input-md" - placeholder={t('Search Metrics & Columns')} - /> -
- - {t('Metrics')}} - key="metrics" + const mainBody = useMemo( + () => ( + <> + { + setInputValue(evt.target.value); + search(evt.target.value); + }} + value={inputValue} + className="form-control input-md" + placeholder={t('Search Metrics & Columns')} + /> +
+ -
- {t(`Showing %s of %s`, metricSlice.length, lists.metrics.length)} -
- {metricSlice.map(m => ( - - {enableExploreDnd ? ( - - ) : ( - + {t('Metrics')}} + key="metrics" + > +
+ {t( + `Showing %s of %s`, + metricSlice.length, + lists.metrics.length, )} - - ))} - {lists.metrics.length > DEFAULT_MAX_METRICS_LENGTH ? ( - - - - ) : ( - <> - )} - - {t('Columns')}} - key="column" - > -
- {t(`Showing %s of %s`, columnSlice.length, lists.columns.length)} -
- {columnSlice.map(col => ( - - {enableExploreDnd ? ( - - ) : ( - +
+ {metricSlice.map(m => ( + + {enableExploreDnd ? ( + + ) : ( + + )} + + ))} + {lists.metrics.length > DEFAULT_MAX_METRICS_LENGTH ? ( + + + + ) : ( + <> + )} +
+ {t('Columns')}} + key="column" + > +
+ {t( + `Showing %s of %s`, + columnSlice.length, + lists.columns.length, )} - - ))} - {lists.columns.length > DEFAULT_MAX_COLUMNS_LENGTH ? ( - - - - ) : ( - <> - )} - - -
- +
+ {columnSlice.map(col => ( + + {enableExploreDnd ? ( + + ) : ( + + )} + + ))} + {lists.columns.length > DEFAULT_MAX_COLUMNS_LENGTH ? ( + + + + ) : ( + <> + )} +
+
+
+ + ), + [ + columnSlice, + inputValue, + lists.columns.length, + lists.metrics.length, + metricSlice, + search, + showAllColumns, + showAllMetrics, + ], ); return (