From c5b032799c329ddb0b66ac1019935e19cb209a03 Mon Sep 17 00:00:00 2001 From: Vadim Ogievetsky Date: Thu, 29 Feb 2024 15:45:50 -0800 Subject: [PATCH] Web console: add table and column search (#15990) * Make a search * fix snapshot * added message when not found --- .../__snapshots__/column-tree.spec.tsx.snap | 77 ++++++++++ .../column-tree/column-tree.scss | 28 ++-- .../column-tree/column-tree.tsx | 142 +++++++++++++++--- 3 files changed, 220 insertions(+), 27 deletions(-) diff --git a/web-console/src/views/workbench-view/column-tree/__snapshots__/column-tree.spec.tsx.snap b/web-console/src/views/workbench-view/column-tree/__snapshots__/column-tree.spec.tsx.snap index 93e3c83d041..53ebf6d8ea2 100644 --- a/web-console/src/views/workbench-view/column-tree/__snapshots__/column-tree.spec.tsx.snap +++ b/web-console/src/views/workbench-view/column-tree/__snapshots__/column-tree.spec.tsx.snap @@ -23,6 +23,83 @@ exports[`ColumnTree matches snapshot 1`] = ` sys + + + + + + + + } + defaultIsOpen={false} + disabled={false} + fill={false} + hasBackdrop={false} + hoverCloseDelay={300} + hoverOpenDelay={150} + inheritDarkTheme={true} + interactionKind="click" + matchTargetWidth={false} + minimal={false} + openOnTargetFocus={true} + position="bottom-left" + positioningStrategy="absolute" + shouldReturnFocusOnClose={false} + targetTagName="span" + transitionDuration={300} + usePortal={true} + > + + + + } + value="" + />
diff --git a/web-console/src/views/workbench-view/column-tree/column-tree.scss b/web-console/src/views/workbench-view/column-tree/column-tree.scss index 20e6dc9d239..2371c145b59 100644 --- a/web-console/src/views/workbench-view/column-tree/column-tree.scss +++ b/web-console/src/views/workbench-view/column-tree/column-tree.scss @@ -34,18 +34,19 @@ } .schema-selector { - position: absolute; - top: 0; - select { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } } + .search-box { + margin: 2px 8px; + } + .tree-container { position: absolute; - top: 40px; + top: 78px; bottom: 0; width: 100%; overflow: auto; @@ -57,11 +58,20 @@ .highlight { animation: druid-glow 1s infinite alternate; } - } - .#{$bp-ns}-popover2-target { - width: 188px; - display: inline-block; - cursor: pointer; + .#{$bp-ns}-popover2-target { + width: 188px; + display: inline-block; + cursor: pointer; + } + + .message-box { + position: absolute; + top: 45%; + left: 50%; + transform: translate(-50%, -50%); + width: 80%; + text-align: center; + } } } diff --git a/web-console/src/views/workbench-view/column-tree/column-tree.tsx b/web-console/src/views/workbench-view/column-tree/column-tree.tsx index eee22449c9b..839bd2b5ca4 100644 --- a/web-console/src/views/workbench-view/column-tree/column-tree.tsx +++ b/web-console/src/views/workbench-view/column-tree/column-tree.tsx @@ -17,7 +17,19 @@ */ import type { TreeNodeInfo } from '@blueprintjs/core'; -import { Classes, HTMLSelect, Icon, Menu, MenuItem, Position, Tree } from '@blueprintjs/core'; +import { + Button, + ButtonGroup, + Classes, + HTMLSelect, + Icon, + InputGroup, + Menu, + MenuDivider, + MenuItem, + Position, + Tree, +} from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; import { Popover2 } from '@blueprintjs/popover2'; import type { SqlExpression } from '@druid-toolkit/query'; @@ -37,7 +49,14 @@ import React from 'react'; import { Deferred, Loader } from '../../../components'; import type { ColumnMetadata } from '../../../utils'; -import { copyAndAlert, dataTypeToIcon, groupBy, oneOf, prettyPrintSql } from '../../../utils'; +import { + copyAndAlert, + dataTypeToIcon, + groupBy, + oneOf, + prettyPrintSql, + tickIcon, +} from '../../../utils'; import { ComplexMenuItems, @@ -81,6 +100,16 @@ interface HandleColumnClickOptions { onQueryChange: (query: SqlQuery, run: boolean) => void; } +type SearchMode = 'tables-and-columns' | 'tables-only' | 'columns-only'; + +const SEARCH_MODES: SearchMode[] = ['tables-and-columns', 'tables-only', 'columns-only']; + +const SEARCH_MDOE_TITLE: Record = { + 'tables-and-columns': 'Tables and columns', + 'tables-only': 'Tables only', + 'columns-only': 'Columns only', +}; + function handleColumnShow(options: HandleColumnClickOptions): void { const { columnSchema, @@ -147,6 +176,14 @@ export interface ColumnTreeState { columnTree?: TreeNodeInfo[]; currentSchemaSubtree?: TreeNodeInfo[]; selectedTreeIndex: number; + searchString: string; + searchMode: SearchMode; + prevSearchHash?: string; +} + +function computeSearchHash(searchString: string, searchMode: SearchMode): string { + if (!searchString) return ''; + return `${searchString.toLowerCase()}_${searchMode}`; } export function getJoinColumns(parsedQuery: SqlQuery, _table: string) { @@ -181,8 +218,15 @@ export class ColumnTree extends React.PureComponent r.TABLE_SCHEMA, @@ -190,12 +234,27 @@ export class ColumnTree extends React.PureComponent + (searchMode === 'tables-and-columns' && + (r.TABLE_NAME.toLowerCase().includes(lowerSearchString) || + r.COLUMN_NAME.toLowerCase().includes(lowerSearchString))) || + (searchMode === 'tables-only' && + r.TABLE_NAME.toLowerCase().includes(lowerSearchString)) || + (searchMode === 'columns-only' && + r.COLUMN_NAME.toLowerCase().includes(lowerSearchString)), + ) + : metadata, r => r.TABLE_NAME, (metadata, tableName): TreeNodeInfo => ({ id: tableName, icon: IconNames.TH, className: tableName === highlightTable ? 'highlight' : undefined, + isExpanded: + isSearching && + (searchMode === 'columns-only' || + !tableName.toLowerCase().includes(lowerSearchString)), label: ( -1) { const treeNodes = columnTree[selectedTreeIndex].childNodes; - if (treeNodes) { - if (defaultTable) { - expandedNode = treeNodes.findIndex(node => { - return node.id === defaultTable; - }); - } + if (treeNodes && defaultTable) { + expandedNode = treeNodes.findIndex(node => { + return node.id === defaultTable; + }); } } @@ -536,6 +593,7 @@ export class ColumnTree extends React.PureComponent - {columnTree.map((treeNode, i) => ( + {columnTree?.map((treeNode, i) => ( @@ -570,6 +629,46 @@ export class ColumnTree extends React.PureComponent { + this.setState({ searchString: e.target.value.substring(0, 100) }); + }} + rightElement={ + + {searchString !== '' && ( +
);