From 223c5db4aa195e5c182f9e58394edef217049bfa Mon Sep 17 00:00:00 2001 From: Julio Sgarbi Date: Thu, 19 Oct 2023 14:36:43 +0200 Subject: [PATCH 01/24] fix: LEAP-218: Improve performance of search --- src/components/NewTaxonomy/NewTaxonomy.tsx | 52 +++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/components/NewTaxonomy/NewTaxonomy.tsx b/src/components/NewTaxonomy/NewTaxonomy.tsx index 3b91ba4da..587cfc006 100644 --- a/src/components/NewTaxonomy/NewTaxonomy.tsx +++ b/src/components/NewTaxonomy/NewTaxonomy.tsx @@ -4,6 +4,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import { Tooltip } from '../../common/Tooltip/Tooltip'; import './NewTaxonomy.styl'; +import { debounce } from 'lodash'; type TaxonomyPath = string[]; type onAddLabelCallback = (path: string[]) => any; @@ -110,6 +111,7 @@ const NewTaxonomy = ({ // isEditable = true, }: TaxonomyProps) => { const [treeData, setTreeData] = useState([]); + const [filteredTreeData, setFilteredTreeData] = useState([]); const separator = options.pathSeparator; const style = { minWidth: options.minWidth ?? 200, maxWidth: options.maxWidth }; const dropdownWidth = options.dropdownWidth === undefined ? true : +options.dropdownWidth; @@ -120,17 +122,65 @@ const NewTaxonomy = ({ setTreeData(convert(items, { ...options, maxUsagesReached }, value)); }, [items, maxUsagesReached]); + useEffect(() => { + setFilteredTreeData(treeData); + }, [treeData]); + const loadData = useCallback(async (node: any) => { return onLoadData?.(node.value.split(separator)); }, []); + const filterTreeNode = (searchValue: string, treeNode: AntTaxonomyItem) => { + const lowerSearchValue = String(searchValue).toLowerCase(); + + if (!lowerSearchValue) { + return false; + } + + return String(treeNode['title']).toLowerCase().includes(lowerSearchValue); + }; + + const filterTreeData = (treeData: AntTaxonomyItem[], searchValue: string) => { + if (!searchValue) { + return treeData; + } + + const dig = (list: AntTaxonomyItem[], keepAll = false) => { + return list.reduce((total, dataNode) => { + const children = dataNode['children']; + + const match = keepAll || filterTreeNode(searchValue, dataNode); + const childList = dig(children || [], match); + + if (match || childList.length) { + total.push({ + ...dataNode, + isLeaf: undefined, + children: childList, + }); + } + return total; + }, []); + }; + + return dig(treeData); + }; + + const handleSearch = useCallback(debounce(async (inputValue: string) => { + // setFilteredTreeData(filterTreeData(treeData, inputValue)); + }, 2000), [treeData]); + + return ( onChange(null, items.map(item => item.value.split(separator)))} loadData={loadData} treeCheckable + onSearch={handleSearch} + filterTreeNode={false} + treeDefaultExpandAll={true} treeCheckStrictly showCheckedStrategy={TreeSelect.SHOW_ALL} treeExpandAction="click" From f2beff5fd061767439d6eddbeb9290e9fad26320 Mon Sep 17 00:00:00 2001 From: Julio Sgarbi Date: Thu, 19 Oct 2023 16:29:45 +0200 Subject: [PATCH 02/24] add new tests to see how it works between the original ui and our own search --- src/components/NewTaxonomy/NewTaxonomy.tsx | 57 ++++++++++++++-------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/src/components/NewTaxonomy/NewTaxonomy.tsx b/src/components/NewTaxonomy/NewTaxonomy.tsx index 587cfc006..4366a303d 100644 --- a/src/components/NewTaxonomy/NewTaxonomy.tsx +++ b/src/components/NewTaxonomy/NewTaxonomy.tsx @@ -112,6 +112,7 @@ const NewTaxonomy = ({ }: TaxonomyProps) => { const [treeData, setTreeData] = useState([]); const [filteredTreeData, setFilteredTreeData] = useState([]); + const [dropdownOpen, setDropdownOpen] = useState(false); const separator = options.pathSeparator; const style = { minWidth: options.minWidth ?? 200, maxWidth: options.maxWidth }; const dropdownWidth = options.dropdownWidth === undefined ? true : +options.dropdownWidth; @@ -166,29 +167,43 @@ const NewTaxonomy = ({ return dig(treeData); }; - const handleSearch = useCallback(debounce(async (inputValue: string) => { - // setFilteredTreeData(filterTreeData(treeData, inputValue)); - }, 2000), [treeData]); - + const handleSearch = useCallback(debounce(async (e: any) => { + setFilteredTreeData(filterTreeData(treeData, e.target.value)); + }, 0), [treeData]); + + const handleSearch2 = useCallback(debounce(async (e: string) => { + setFilteredTreeData(filterTreeData(treeData, e)); + }, 500), [treeData]); + return ( - onChange(null, items.map(item => item.value.split(separator)))} - loadData={loadData} - treeCheckable - onSearch={handleSearch} - filterTreeNode={false} - treeDefaultExpandAll={true} - treeCheckStrictly - showCheckedStrategy={TreeSelect.SHOW_ALL} - treeExpandAction="click" - dropdownMatchSelectWidth={dropdownWidth} - placeholder={options.placeholder || 'Click to add...'} - style={style} - className="htx-taxonomy" - /> + <> + onChange(null, items.map(item => item.value.split(separator)))} + loadData={loadData} + treeCheckable + filterTreeNode={false} + treeDefaultExpandAll={dropdownOpen} + treeCheckStrictly + onSearch={handleSearch2} + onFocus={() => setDropdownOpen(true)} + onBlur={() => setDropdownOpen(false)} + open={dropdownOpen} + showCheckedStrategy={TreeSelect.SHOW_ALL} + treeExpandAction="click" + dropdownMatchSelectWidth={dropdownWidth} + placeholder={options.placeholder || 'Click to add...'} + style={style} + className="htx-taxonomy" + /> + handleSearch(e)} + onFocus={() => setDropdownOpen(true)} + onBlur={() => setDropdownOpen(false)} + /> + ); }; From 744079aca5d3e38afae578678b7a31f263aa574c Mon Sep 17 00:00:00 2001 From: Julio Sgarbi Date: Thu, 19 Oct 2023 19:31:52 +0200 Subject: [PATCH 03/24] working with external input search --- src/components/NewTaxonomy/NewTaxonomy.tsx | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/components/NewTaxonomy/NewTaxonomy.tsx b/src/components/NewTaxonomy/NewTaxonomy.tsx index 4366a303d..5185af861 100644 --- a/src/components/NewTaxonomy/NewTaxonomy.tsx +++ b/src/components/NewTaxonomy/NewTaxonomy.tsx @@ -1,5 +1,5 @@ import { TreeSelect } from 'antd'; -import React, { useCallback, useEffect, useState } from 'react'; +import React, { createRef, useCallback, useEffect, useState } from 'react'; import { Tooltip } from '../../common/Tooltip/Tooltip'; @@ -118,6 +118,7 @@ const NewTaxonomy = ({ const dropdownWidth = options.dropdownWidth === undefined ? true : +options.dropdownWidth; const maxUsagesReached = !!options.maxUsages && selected.length >= options.maxUsages; const value = selected.map(path => path.join(separator)); + const inputRef = createRef(); useEffect(() => { setTreeData(convert(items, { ...options, maxUsagesReached }, value)); @@ -169,7 +170,7 @@ const NewTaxonomy = ({ const handleSearch = useCallback(debounce(async (e: any) => { setFilteredTreeData(filterTreeData(treeData, e.target.value)); - }, 0), [treeData]); + }, 500), [treeData]); const handleSearch2 = useCallback(debounce(async (e: string) => { setFilteredTreeData(filterTreeData(treeData, e)); @@ -185,11 +186,18 @@ const NewTaxonomy = ({ loadData={loadData} treeCheckable filterTreeNode={false} + showSearch={true} + onDropdownVisibleChange={() => setDropdownOpen(true)} treeDefaultExpandAll={dropdownOpen} treeCheckStrictly onSearch={handleSearch2} - onFocus={() => setDropdownOpen(true)} - onBlur={() => setDropdownOpen(false)} + onFocus={() => { + setDropdownOpen(true); + inputRef.current?.focus(); + }} + onBlur={() => { + // setDropdownOpen(false) + }} open={dropdownOpen} showCheckedStrategy={TreeSelect.SHOW_ALL} treeExpandAction="click" @@ -199,6 +207,10 @@ const NewTaxonomy = ({ className="htx-taxonomy" /> handleSearch(e)} onFocus={() => setDropdownOpen(true)} onBlur={() => setDropdownOpen(false)} From c721203275057e720a95862dd1fe302cb313efea Mon Sep 17 00:00:00 2001 From: Julio Sgarbi Date: Mon, 23 Oct 2023 16:52:40 +0200 Subject: [PATCH 04/24] implements new search and fix UI --- src/components/NewTaxonomy/NewTaxonomy.styl | 9 +++ src/components/NewTaxonomy/NewTaxonomy.tsx | 80 ++++++++++++--------- 2 files changed, 57 insertions(+), 32 deletions(-) diff --git a/src/components/NewTaxonomy/NewTaxonomy.styl b/src/components/NewTaxonomy/NewTaxonomy.styl index f034aa4b3..f850b56df 100644 --- a/src/components/NewTaxonomy/NewTaxonomy.styl +++ b/src/components/NewTaxonomy/NewTaxonomy.styl @@ -1,3 +1,12 @@ :global(.htx-taxonomy-item-color) padding 4px 4px border-radius 2px + +.taxonomy-search-input + width calc(100% - 8px) + height 40px + border-radius 4px + border 1px solid rgba(137, 128, 152, 0.16) + background url("data:image/svg+xml;base64, PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE1Ljc1NSAxNC4yNTVIMTQuOTY1TDE0LjY4NSAxMy45ODVDMTUuNjY1IDEyLjg0NSAxNi4yNTUgMTEuMzY1IDE2LjI1NSA5Ljc1NUMxNi4yNTUgNi4xNjUgMTMuMzQ1IDMuMjU1IDkuNzU1IDMuMjU1QzYuMTY1IDMuMjU1IDMuMjU1IDYuMTY1IDMuMjU1IDkuNzU1QzMuMjU1IDEzLjM0NSA2LjE2NSAxNi4yNTUgOS43NTUgMTYuMjU1QzExLjM2NSAxNi4yNTUgMTIuODQ1IDE1LjY2NSAxMy45ODUgMTQuNjg1TDE0LjI1NSAxNC45NjVWMTUuNzU1TDE5LjI1NSAyMC43NDVMMjAuNzQ1IDE5LjI1NUwxNS43NTUgMTQuMjU1Wk05Ljc1NSAxNC4yNTVDNy4yNjUwMSAxNC4yNTUgNS4yNTUgMTIuMjQ1IDUuMjU1IDkuNzU1QzUuMjU1IDcuMjY1MDEgNy4yNjUwMSA1LjI1NSA5Ljc1NSA1LjI1NUMxMi4yNDUgNS4yNTUgMTQuMjU1IDcuMjY1MDEgMTQuMjU1IDkuNzU1QzE0LjI1NSAxMi4yNDUgMTIuMjQ1IDE0LjI1NSA5Ljc1NSAxNC4yNTVaIiBmaWxsPSIjMDk2REQ5Ii8+Cjwvc3ZnPgo=") center left 4px no-repeat #FAFAFA; + padding 4px 4px 4px 32px + margin 0 4px 14px 4px \ No newline at end of file diff --git a/src/components/NewTaxonomy/NewTaxonomy.tsx b/src/components/NewTaxonomy/NewTaxonomy.tsx index 5185af861..f2216ae3e 100644 --- a/src/components/NewTaxonomy/NewTaxonomy.tsx +++ b/src/components/NewTaxonomy/NewTaxonomy.tsx @@ -1,10 +1,11 @@ import { TreeSelect } from 'antd'; -import React, { createRef, useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { Tooltip } from '../../common/Tooltip/Tooltip'; import './NewTaxonomy.styl'; import { debounce } from 'lodash'; +import { Block } from '../../utils/bem'; type TaxonomyPath = string[]; type onAddLabelCallback = (path: string[]) => any; @@ -112,13 +113,13 @@ const NewTaxonomy = ({ }: TaxonomyProps) => { const [treeData, setTreeData] = useState([]); const [filteredTreeData, setFilteredTreeData] = useState([]); - const [dropdownOpen, setDropdownOpen] = useState(false); + const [expandedKeys, setExpandedKeys] = useState([]); + const separator = options.pathSeparator; const style = { minWidth: options.minWidth ?? 200, maxWidth: options.maxWidth }; const dropdownWidth = options.dropdownWidth === undefined ? true : +options.dropdownWidth; const maxUsagesReached = !!options.maxUsages && selected.length >= options.maxUsages; const value = selected.map(path => path.join(separator)); - const inputRef = createRef(); useEffect(() => { setTreeData(convert(items, { ...options, maxUsagesReached }, value)); @@ -132,7 +133,7 @@ const NewTaxonomy = ({ return onLoadData?.(node.value.split(separator)); }, []); - const filterTreeNode = (searchValue: string, treeNode: AntTaxonomyItem) => { + const filterTreeNode = useCallback((searchValue: string, treeNode: AntTaxonomyItem) => { const lowerSearchValue = String(searchValue).toLowerCase(); if (!lowerSearchValue) { @@ -140,9 +141,9 @@ const NewTaxonomy = ({ } return String(treeNode['title']).toLowerCase().includes(lowerSearchValue); - }; + }, []); - const filterTreeData = (treeData: AntTaxonomyItem[], searchValue: string) => { + const filterTreeData = useCallback((treeData: AntTaxonomyItem[], searchValue: string) => { if (!searchValue) { return treeData; } @@ -166,16 +167,48 @@ const NewTaxonomy = ({ }; return dig(treeData); - }; + }, []); + + const findExpandedKeys = useCallback((data: AntTaxonomyItem[]) => { + const _result: string[] = []; + + const expandedKeys = (list: AntTaxonomyItem[]) => { + list.forEach((item: AntTaxonomyItem) => { + _result.push(item.key); + + if(item.children?.length) { + expandedKeys(item.children); + } + }); + }; + + expandedKeys(data); + + return _result; + }, []); const handleSearch = useCallback(debounce(async (e: any) => { - setFilteredTreeData(filterTreeData(treeData, e.target.value)); - }, 500), [treeData]); + const _filteredData = filterTreeData(treeData, e.target.value); - const handleSearch2 = useCallback(debounce(async (e: string) => { - setFilteredTreeData(filterTreeData(treeData, e)); + setFilteredTreeData(_filteredData); + + if (e.target.value !== '') setExpandedKeys(findExpandedKeys(_filteredData)); + else setExpandedKeys([]); }, 500), [treeData]); + const renderDropdown = (origin: any) => { + return( + <> + ) => handleSearch(e)} + placeholder={'Search'} + name={'taxonomy-search-input'} + /> + {origin} + + ); + }; return ( <> @@ -186,19 +219,11 @@ const NewTaxonomy = ({ loadData={loadData} treeCheckable filterTreeNode={false} - showSearch={true} - onDropdownVisibleChange={() => setDropdownOpen(true)} - treeDefaultExpandAll={dropdownOpen} + showSearch={false} + showArrow={true} + dropdownRender={renderDropdown} + treeExpandedKeys={expandedKeys} treeCheckStrictly - onSearch={handleSearch2} - onFocus={() => { - setDropdownOpen(true); - inputRef.current?.focus(); - }} - onBlur={() => { - // setDropdownOpen(false) - }} - open={dropdownOpen} showCheckedStrategy={TreeSelect.SHOW_ALL} treeExpandAction="click" dropdownMatchSelectWidth={dropdownWidth} @@ -206,15 +231,6 @@ const NewTaxonomy = ({ style={style} className="htx-taxonomy" /> - handleSearch(e)} - onFocus={() => setDropdownOpen(true)} - onBlur={() => setDropdownOpen(false)} - /> ); }; From b498b6e6e5ea749f68ce316c1dd8a100fb150e54 Mon Sep 17 00:00:00 2001 From: Julio Sgarbi Date: Tue, 24 Oct 2023 15:38:34 +0200 Subject: [PATCH 05/24] split taxonomy and taxonomy search --- src/components/NewTaxonomy/NewTaxonomy.styl | 11 +- src/components/NewTaxonomy/NewTaxonomy.tsx | 89 +++------------ .../NewTaxonomy/TaxonomySearch.styl | 8 ++ src/components/NewTaxonomy/TaxonomySearch.tsx | 102 ++++++++++++++++++ 4 files changed, 124 insertions(+), 86 deletions(-) create mode 100644 src/components/NewTaxonomy/TaxonomySearch.styl create mode 100644 src/components/NewTaxonomy/TaxonomySearch.tsx diff --git a/src/components/NewTaxonomy/NewTaxonomy.styl b/src/components/NewTaxonomy/NewTaxonomy.styl index f850b56df..e72402a8b 100644 --- a/src/components/NewTaxonomy/NewTaxonomy.styl +++ b/src/components/NewTaxonomy/NewTaxonomy.styl @@ -1,12 +1,3 @@ :global(.htx-taxonomy-item-color) padding 4px 4px - border-radius 2px - -.taxonomy-search-input - width calc(100% - 8px) - height 40px - border-radius 4px - border 1px solid rgba(137, 128, 152, 0.16) - background url("data:image/svg+xml;base64, PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE1Ljc1NSAxNC4yNTVIMTQuOTY1TDE0LjY4NSAxMy45ODVDMTUuNjY1IDEyLjg0NSAxNi4yNTUgMTEuMzY1IDE2LjI1NSA5Ljc1NUMxNi4yNTUgNi4xNjUgMTMuMzQ1IDMuMjU1IDkuNzU1IDMuMjU1QzYuMTY1IDMuMjU1IDMuMjU1IDYuMTY1IDMuMjU1IDkuNzU1QzMuMjU1IDEzLjM0NSA2LjE2NSAxNi4yNTUgOS43NTUgMTYuMjU1QzExLjM2NSAxNi4yNTUgMTIuODQ1IDE1LjY2NSAxMy45ODUgMTQuNjg1TDE0LjI1NSAxNC45NjVWMTUuNzU1TDE5LjI1NSAyMC43NDVMMjAuNzQ1IDE5LjI1NUwxNS43NTUgMTQuMjU1Wk05Ljc1NSAxNC4yNTVDNy4yNjUwMSAxNC4yNTUgNS4yNTUgMTIuMjQ1IDUuMjU1IDkuNzU1QzUuMjU1IDcuMjY1MDEgNy4yNjUwMSA1LjI1NSA5Ljc1NSA1LjI1NUMxMi4yNDUgNS4yNTUgMTQuMjU1IDcuMjY1MDEgMTQuMjU1IDkuNzU1QzE0LjI1NSAxMi4yNDUgMTIuMjQ1IDE0LjI1NSA5Ljc1NSAxNC4yNTVaIiBmaWxsPSIjMDk2REQ5Ii8+Cjwvc3ZnPgo=") center left 4px no-repeat #FAFAFA; - padding 4px 4px 4px 32px - margin 0 4px 14px 4px \ No newline at end of file + border-radius 2px \ No newline at end of file diff --git a/src/components/NewTaxonomy/NewTaxonomy.tsx b/src/components/NewTaxonomy/NewTaxonomy.tsx index 996a33ad0..e463e473d 100644 --- a/src/components/NewTaxonomy/NewTaxonomy.tsx +++ b/src/components/NewTaxonomy/NewTaxonomy.tsx @@ -1,11 +1,10 @@ import { TreeSelect } from 'antd'; -import React, { useCallback, useEffect, useState } from 'react'; +import React, { ReactNode, useCallback, useEffect, useState } from 'react'; import { Tooltip } from '../../common/Tooltip/Tooltip'; import './NewTaxonomy.styl'; -import { debounce } from 'lodash'; -import { Block } from '../../utils/bem'; +import { TaxonomySearch } from './TaxonomySearch'; type TaxonomyPath = string[]; type onAddLabelCallback = (path: string[]) => any; @@ -22,7 +21,7 @@ type TaxonomyItem = { color?: string, }; -type AntTaxonomyItem = { +export type AntTaxonomyItem = { title: string | JSX.Element, value: string, key: string, @@ -141,82 +140,20 @@ const NewTaxonomy = ({ return onLoadData?.(node.value.split(separator)); }, []); - const filterTreeNode = useCallback((searchValue: string, treeNode: AntTaxonomyItem) => { - const lowerSearchValue = String(searchValue).toLowerCase(); - - if (!lowerSearchValue) { - return false; - } - - return String(treeNode['title']).toLowerCase().includes(lowerSearchValue); - }, []); - - const filterTreeData = useCallback((treeData: AntTaxonomyItem[], searchValue: string) => { - if (!searchValue) { - return treeData; - } - - const dig = (list: AntTaxonomyItem[], keepAll = false) => { - return list.reduce((total, dataNode) => { - const children = dataNode['children']; - - const match = keepAll || filterTreeNode(searchValue, dataNode); - const childList = dig(children || [], match); - - if (match || childList.length) { - total.push({ - ...dataNode, - isLeaf: undefined, - children: childList, - }); - } - return total; - }, []); - }; - - return dig(treeData); - }, []); - - const findExpandedKeys = useCallback((data: AntTaxonomyItem[]) => { - const _result: string[] = []; - - const expandedKeys = (list: AntTaxonomyItem[]) => { - list.forEach((item: AntTaxonomyItem) => { - _result.push(item.key); - - if(item.children?.length) { - expandedKeys(item.children); - } - }); - }; - - expandedKeys(data); - - return _result; + const handleSearch = useCallback((list: AntTaxonomyItem[], expandedKeys: string[]) => { + setExpandedKeys(expandedKeys); + setFilteredTreeData(list); }, []); - const handleSearch = useCallback(debounce(async (e: any) => { - const _filteredData = filterTreeData(treeData, e.target.value); - - setFilteredTreeData(_filteredData); - - if (e.target.value !== '') setExpandedKeys(findExpandedKeys(_filteredData)); - else setExpandedKeys([]); - }, 300), [treeData]); - - const renderDropdown = (origin: any) => { + const renderDropdown = useCallback((origin: ReactNode) => { return( - <> - ) => handleSearch(e)} - placeholder={'Search'} - name={'taxonomy-search-input'} - /> - {origin} - + ); - }; + }, [treeData]); return ( Date: Tue, 24 Oct 2023 17:04:18 +0200 Subject: [PATCH 08/24] change the expandedKeys method from its own method to filterDataTree --- src/components/NewTaxonomy/NewTaxonomy.tsx | 2 +- src/components/NewTaxonomy/TaxonomySearch.tsx | 40 ++++++------------- 2 files changed, 14 insertions(+), 28 deletions(-) diff --git a/src/components/NewTaxonomy/NewTaxonomy.tsx b/src/components/NewTaxonomy/NewTaxonomy.tsx index e463e473d..0405d4413 100644 --- a/src/components/NewTaxonomy/NewTaxonomy.tsx +++ b/src/components/NewTaxonomy/NewTaxonomy.tsx @@ -117,7 +117,7 @@ const NewTaxonomy = ({ }: TaxonomyProps) => { const [treeData, setTreeData] = useState([]); const [filteredTreeData, setFilteredTreeData] = useState([]); - const [expandedKeys, setExpandedKeys] = useState([]); + const [expandedKeys, setExpandedKeys] = useState(); const separator = options.pathSeparator; const style = { minWidth: options.minWidth ?? 200, maxWidth: options.maxWidth }; const dropdownWidth = options.dropdownWidth === undefined ? true : +options.dropdownWidth; diff --git a/src/components/NewTaxonomy/TaxonomySearch.tsx b/src/components/NewTaxonomy/TaxonomySearch.tsx index fd108f8b2..3d4c6f73a 100644 --- a/src/components/NewTaxonomy/TaxonomySearch.tsx +++ b/src/components/NewTaxonomy/TaxonomySearch.tsx @@ -8,7 +8,7 @@ import { debounce } from 'lodash'; type TaxonomySearchProps = { treeData: AntTaxonomyItem[], origin: ReactNode, - onChange: (list: AntTaxonomyItem[], expandedKeys: string[]) => void, + onChange: (list: AntTaxonomyItem[], expandedKeys: string[] | undefined) => void, } const TaxonomySearch = ({ @@ -30,8 +30,12 @@ const TaxonomySearch = ({ // It's running recursively through treeData and its children filtering the content that match with the search value const filterTreeData = useCallback((treeData: AntTaxonomyItem[], searchValue: string) => { + const _expandedKeys: string[] = []; + if (!searchValue) { - return treeData; + return { + filteredDataTree:treeData, + }; } const dig = (list: AntTaxonomyItem[], keepAll = false) => { @@ -42,47 +46,29 @@ const TaxonomySearch = ({ const childList = dig(children || [], match); if (match || childList.length) { + _expandedKeys.push(dataNode.key); + total.push({ ...dataNode, isLeaf: undefined, children: childList, }); } + return total; }, []); }; - return dig(treeData); - }, []); - - // After filter the treeData result, this method is used to check which keys must be expanded - const findExpandedKeys = useCallback((data: AntTaxonomyItem[]) => { - const _result: string[] = []; - - const expandedKeys = (list: AntTaxonomyItem[]) => { - list.forEach((item: AntTaxonomyItem) => { - _result.push(item.key); - - if(item.children?.length) { - expandedKeys(item.children); - } - }); + return { + filteredDataTree:dig(treeData), + expandedKeys:_expandedKeys, }; - - expandedKeys(data); - - return _result; }, []); const handleSearch = useCallback(debounce(async (e: ChangeEvent) => { const _filteredData = filterTreeData(treeData, e.target.value); - let _expandedKeys: string[] = []; - - - if (e.target.value !== '') - _expandedKeys = findExpandedKeys(_filteredData); - onChange(_filteredData, _expandedKeys); + onChange(_filteredData.filteredDataTree, _filteredData.expandedKeys); }, 300), [treeData]); return( From 5018d89bcbca97cc35667719abe4f962c4c3d031 Mon Sep 17 00:00:00 2001 From: Julio Sgarbi Date: Tue, 24 Oct 2023 18:49:18 +0200 Subject: [PATCH 09/24] fix tests --- tests/functional/specs/control_tags/taxonomy.cy.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/functional/specs/control_tags/taxonomy.cy.ts b/tests/functional/specs/control_tags/taxonomy.cy.ts index e94173d59..ecaabad2b 100644 --- a/tests/functional/specs/control_tags/taxonomy.cy.ts +++ b/tests/functional/specs/control_tags/taxonomy.cy.ts @@ -195,7 +195,6 @@ Object.entries(taxonomies).forEach(([title, Taxonomy]) => { cy.get('[data-testid="taxonomy-search"]').type('Section 3.3'); Taxonomy.dropdown.contains('.ant-select-tree-treenode', 'Section 3.3').should('be.visible'); cy.get('[data-testid="taxonomy-search"]').clear(); - Taxonomy.dropdown.contains('.ant-select-tree-treenode', 'Section 3.3').should('not.exist'); cy.get('[data-testid="taxonomy-search"]').type('Section 7.0'); Taxonomy.dropdown.contains('No Data').should('be.visible'); Taxonomy.hasSelected('Book 1 / Chapter 2 / Section 2.1'); From b2584b3d7b5acf5edae853435b24f1f31da9706e Mon Sep 17 00:00:00 2001 From: Julio Sgarbi Date: Thu, 26 Oct 2023 21:46:17 +0200 Subject: [PATCH 10/24] add ff and fix some UX problems --- src/components/NewTaxonomy/NewTaxonomy.tsx | 62 +++++++++----- src/components/NewTaxonomy/TaxonomySearch.tsx | 81 +++++++++++++++---- src/tags/control/Taxonomy/Taxonomy.js | 2 + src/utils/feature-flags.ts | 2 + .../specs/control_tags/taxonomy.cy.ts | 2 + 5 files changed, 114 insertions(+), 35 deletions(-) diff --git a/src/components/NewTaxonomy/NewTaxonomy.tsx b/src/components/NewTaxonomy/NewTaxonomy.tsx index 0405d4413..8c50e7ddf 100644 --- a/src/components/NewTaxonomy/NewTaxonomy.tsx +++ b/src/components/NewTaxonomy/NewTaxonomy.tsx @@ -1,10 +1,10 @@ import { TreeSelect } from 'antd'; -import React, { ReactNode, useCallback, useEffect, useState } from 'react'; +import React, { ReactNode, useCallback, useEffect, useRef, useState } from 'react'; import { Tooltip } from '../../common/Tooltip/Tooltip'; import './NewTaxonomy.styl'; -import { TaxonomySearch } from './TaxonomySearch'; +import { TaxonomySearch, TaxonomySearchRef } from './TaxonomySearch'; type TaxonomyPath = string[]; type onAddLabelCallback = (path: string[]) => any; @@ -55,6 +55,7 @@ type TaxonomyProps = { onDeleteLabel?: onDeleteLabelCallback, options: TaxonomyOptions, isEditable?: boolean, + defaultSearch?: boolean, }; type TaxonomyExtendedOptions = TaxonomyOptions & { @@ -108,6 +109,7 @@ const NewTaxonomy = ({ selected, onChange, onLoadData, + defaultSearch = true, // @todo implement user labels // onAddLabel, // onDeleteLabel, @@ -115,9 +117,10 @@ const NewTaxonomy = ({ // @todo implement readonly mode // isEditable = true, }: TaxonomyProps) => { + const refInput = useRef(null); const [treeData, setTreeData] = useState([]); const [filteredTreeData, setFilteredTreeData] = useState([]); - const [expandedKeys, setExpandedKeys] = useState(); + const [expandedKeys, setExpandedKeys] = useState([]); const separator = options.pathSeparator; const style = { minWidth: options.minWidth ?? 200, maxWidth: options.maxWidth }; const dropdownWidth = options.dropdownWidth === undefined ? true : +options.dropdownWidth; @@ -132,42 +135,61 @@ const NewTaxonomy = ({ setTreeData(convert(items, { ...options, maxUsagesReached }, value)); }, [items, maxUsagesReached]); - useEffect(() => { - setFilteredTreeData(treeData); - }, [treeData]); - const loadData = useCallback(async (node: any) => { return onLoadData?.(node.value.split(separator)); }, []); - const handleSearch = useCallback((list: AntTaxonomyItem[], expandedKeys: string[]) => { - setExpandedKeys(expandedKeys); + const handleSearch = useCallback((list: AntTaxonomyItem[], expandedKeys: React.Key[]) => { setFilteredTreeData(list); + setExpandedKeys(expandedKeys); }, []); const renderDropdown = useCallback((origin: ReactNode) => { - return( - - ); + if (!defaultSearch) { + return ( + + ); + } else { + return ( + <> + {origin} + + ); + } }, [treeData]); + const handleDropdownChange = useCallback((open: boolean) => { + if (open) { + // handleDropdownChange is being called before the dropdown is rendered, 100ms is the time that we have to wait to dropdown be rendered + setTimeout(() => { + refInput.current?.focus(); + }, 100); + } else { + refInput.current?.changeValue(); + } + }, [refInput]); + return ( onChange(null, items.map(item => item.value.split(separator)))} loadData={loadData} treeCheckable - filterTreeNode={false} - showSearch={false} - showArrow={true} + showSearch={defaultSearch} + showArrow={!defaultSearch} dropdownRender={renderDropdown} + onDropdownVisibleChange={handleDropdownChange} treeExpandedKeys={expandedKeys} + onTreeExpand={(expandedKeys: React.Key[]) => { + setExpandedKeys(expandedKeys); + }} treeCheckStrictly showCheckedStrategy={TreeSelect.SHOW_ALL} treeExpandAction="click" diff --git a/src/components/NewTaxonomy/TaxonomySearch.tsx b/src/components/NewTaxonomy/TaxonomySearch.tsx index 3d4c6f73a..8bf0f71a9 100644 --- a/src/components/NewTaxonomy/TaxonomySearch.tsx +++ b/src/components/NewTaxonomy/TaxonomySearch.tsx @@ -1,4 +1,13 @@ -import React, { ChangeEvent, KeyboardEvent, ReactNode, useCallback } from 'react'; +import React, { + ChangeEvent, + KeyboardEvent, + ReactNode, + useCallback, + useEffect, + useImperativeHandle, + useRef, + useState +} from 'react'; import './TaxonomySearch.styl'; import { Block } from '../../utils/bem'; @@ -8,33 +17,68 @@ import { debounce } from 'lodash'; type TaxonomySearchProps = { treeData: AntTaxonomyItem[], origin: ReactNode, - onChange: (list: AntTaxonomyItem[], expandedKeys: string[] | undefined) => void, + onChange: (list: AntTaxonomyItem[], expandedKeys: React.Key[]) => void, } -const TaxonomySearch = ({ +export type TaxonomySearchRef = { + changeValue: () => void, + focus: () => void, +} + +const TaxonomySearch = React.forwardRef(({ origin, treeData, onChange, -}: TaxonomySearchProps) => { +}, ref) => { + useImperativeHandle(ref, (): TaxonomySearchRef => { + return { + changeValue() { + setInputValue(''); + onChange(treeData, []); + }, + focus() { + return inputRef.current?.focus(); + }, + }; + }); + + const inputRef = useRef(); + const [inputValue, setInputValue] = useState(''); + + useEffect(() => { + const _filteredData = filterTreeData(treeData, inputValue); + + onChange(_filteredData.filteredDataTree, _filteredData.expandedKeys); + }, [treeData]); //To filter the treeData items that match with the searchValue const filterTreeNode = useCallback((searchValue: string, treeNode: AntTaxonomyItem) => { const lowerSearchValue = String(searchValue).toLowerCase(); + const lowerResultValue = typeof treeNode.title === 'object' ? treeNode.title.props.children.props.children : treeNode.title; if (!lowerSearchValue) { return false; } - return String(treeNode['title']).toLowerCase().includes(lowerSearchValue); + return String(lowerResultValue).toLowerCase().includes(lowerSearchValue); }, []); + const fillLegacyProps = (dataNode: AntTaxonomyItem) => { + if (!dataNode) { + return dataNode; + } + + return { ...dataNode }; + }; + // It's running recursively through treeData and its children filtering the content that match with the search value const filterTreeData = useCallback((treeData: AntTaxonomyItem[], searchValue: string) => { - const _expandedKeys: string[] = []; + const _expandedKeys: React.Key[] = []; if (!searchValue) { return { - filteredDataTree:treeData, + filteredDataTree: treeData, + expandedKeys: _expandedKeys, }; } @@ -42,12 +86,12 @@ const TaxonomySearch = ({ return list.reduce((total, dataNode) => { const children = dataNode['children']; - const match = keepAll || filterTreeNode(searchValue, dataNode); + const match = keepAll || filterTreeNode(searchValue, fillLegacyProps(dataNode)); const childList = dig(children || [], match); if (match || childList.length) { _expandedKeys.push(dataNode.key); - + total.push({ ...dataNode, isLeaf: undefined, @@ -60,8 +104,8 @@ const TaxonomySearch = ({ }; return { - filteredDataTree:dig(treeData), - expandedKeys:_expandedKeys, + filteredDataTree: dig(treeData), + expandedKeys: _expandedKeys, }; }, []); @@ -71,12 +115,19 @@ const TaxonomySearch = ({ onChange(_filteredData.filteredDataTree, _filteredData.expandedKeys); }, 300), [treeData]); - return( + return ( <> ) => e.stopPropagation()} + onChange={(e: ChangeEvent) => { + setInputValue(e.target.value); + handleSearch(e); + }} + onKeyDown={(e: KeyboardEvent) => { + if (e.key === 'Backspace' || e.key === 'Delete') e.stopPropagation(); + }} placeholder={'Search'} data-testid={'taxonomy-search'} name={'taxonomy-search-input'} @@ -84,6 +135,6 @@ const TaxonomySearch = ({ {origin} ); -}; +}); export { TaxonomySearch }; diff --git a/src/tags/control/Taxonomy/Taxonomy.js b/src/tags/control/Taxonomy/Taxonomy.js index 2193b7522..cedc7b938 100644 --- a/src/tags/control/Taxonomy/Taxonomy.js +++ b/src/tags/control/Taxonomy/Taxonomy.js @@ -23,6 +23,7 @@ import { parseValue } from '../../../utils/data'; import { FF_DEV_2007_DEV_2008, FF_DEV_3617, + FF_LEAP_218, FF_LSDV_4583, FF_TAXONOMY_ASYNC, FF_TAXONOMY_LABELING, @@ -612,6 +613,7 @@ const HtxTaxonomy = observer(({ item }) => { onAddLabel={item.userLabels && item.onAddLabel} onDeleteLabel={item.userLabels && item.onDeleteLabel} options={options} + defaultSearch={!isFF(FF_LEAP_218)} isEditable={!item.isReadOnly()} /> ) : ( diff --git a/src/utils/feature-flags.ts b/src/utils/feature-flags.ts index 1062d5515..104534478 100644 --- a/src/utils/feature-flags.ts +++ b/src/utils/feature-flags.ts @@ -309,6 +309,8 @@ export const FF_DBLCLICK_DELAY = 'fflag_fix_front_lsdv_5248_double_click_delay_2 */ export const FF_TAXONOMY_ASYNC = 'fflag_feat_front_lsdv_5451_async_taxonomy_110823_short'; +export const FF_LEAP_218 = 'fflag_fix_front_leap_218_improve_performance_of_taxonomy_search_short'; + /** * Allow to label NER directly with Taxonomy instead of Labels * @link https://app.launchdarkly.com/default/production/features/fflag_feat_front_lsdv_5452_taxonomy_labeling_110823_short diff --git a/tests/functional/specs/control_tags/taxonomy.cy.ts b/tests/functional/specs/control_tags/taxonomy.cy.ts index ecaabad2b..f80a3049b 100644 --- a/tests/functional/specs/control_tags/taxonomy.cy.ts +++ b/tests/functional/specs/control_tags/taxonomy.cy.ts @@ -17,6 +17,7 @@ import { FF_DEV_2007_DEV_2008, FF_DEV_2100_A, FF_DEV_3617, + FF_LEAP_218, FF_TAXONOMY_ASYNC, FF_TAXONOMY_LABELING } from '../../../../src/utils/feature-flags'; @@ -178,6 +179,7 @@ Object.entries(taxonomies).forEach(([title, Taxonomy]) => { if (Taxonomy.isNew) { LabelStudio.addFeatureFlagsOnPageLoad({ [FF_TAXONOMY_ASYNC]: true, + [FF_LEAP_218]: true, }); } }); From 1add9e0a85033391a083fa6f60afd538eb2a2a99 Mon Sep 17 00:00:00 2001 From: Julio Sgarbi Date: Thu, 26 Oct 2023 21:50:01 +0200 Subject: [PATCH 11/24] remove useless function --- src/components/NewTaxonomy/TaxonomySearch.tsx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/components/NewTaxonomy/TaxonomySearch.tsx b/src/components/NewTaxonomy/TaxonomySearch.tsx index 8bf0f71a9..4021b9597 100644 --- a/src/components/NewTaxonomy/TaxonomySearch.tsx +++ b/src/components/NewTaxonomy/TaxonomySearch.tsx @@ -63,14 +63,6 @@ const TaxonomySearch = React.forwardRef( return String(lowerResultValue).toLowerCase().includes(lowerSearchValue); }, []); - const fillLegacyProps = (dataNode: AntTaxonomyItem) => { - if (!dataNode) { - return dataNode; - } - - return { ...dataNode }; - }; - // It's running recursively through treeData and its children filtering the content that match with the search value const filterTreeData = useCallback((treeData: AntTaxonomyItem[], searchValue: string) => { const _expandedKeys: React.Key[] = []; @@ -86,7 +78,7 @@ const TaxonomySearch = React.forwardRef( return list.reduce((total, dataNode) => { const children = dataNode['children']; - const match = keepAll || filterTreeNode(searchValue, fillLegacyProps(dataNode)); + const match = keepAll || filterTreeNode(searchValue, dataNode); const childList = dig(children || [], match); if (match || childList.length) { From 11bb38f630ce3e85a6067124067df530db930ae7 Mon Sep 17 00:00:00 2001 From: Julio Sgarbi Date: Fri, 27 Oct 2023 16:31:23 +0200 Subject: [PATCH 12/24] expand just the mached value --- src/components/NewTaxonomy/TaxonomySearch.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/NewTaxonomy/TaxonomySearch.tsx b/src/components/NewTaxonomy/TaxonomySearch.tsx index 4021b9597..5db6a76a8 100644 --- a/src/components/NewTaxonomy/TaxonomySearch.tsx +++ b/src/components/NewTaxonomy/TaxonomySearch.tsx @@ -82,7 +82,8 @@ const TaxonomySearch = React.forwardRef( const childList = dig(children || [], match); if (match || childList.length) { - _expandedKeys.push(dataNode.key); + if (!keepAll) + _expandedKeys.push(dataNode.key); total.push({ ...dataNode, From 31b3416790657ecc3db4b393657108deacf713ab Mon Sep 17 00:00:00 2001 From: Julio Sgarbi Date: Mon, 30 Oct 2023 16:17:34 +0100 Subject: [PATCH 13/24] fix small bugs and add comments --- src/components/NewTaxonomy/NewTaxonomy.tsx | 33 +++++----- src/components/NewTaxonomy/TaxonomySearch.tsx | 64 +++++++++---------- 2 files changed, 46 insertions(+), 51 deletions(-) diff --git a/src/components/NewTaxonomy/NewTaxonomy.tsx b/src/components/NewTaxonomy/NewTaxonomy.tsx index 8c50e7ddf..d707f07ca 100644 --- a/src/components/NewTaxonomy/NewTaxonomy.tsx +++ b/src/components/NewTaxonomy/NewTaxonomy.tsx @@ -139,28 +139,25 @@ const NewTaxonomy = ({ return onLoadData?.(node.value.split(separator)); }, []); - const handleSearch = useCallback((list: AntTaxonomyItem[], expandedKeys: React.Key[]) => { + const handleSearch = useCallback((list: AntTaxonomyItem[], expandedKeys: React.Key[] | null) => { setFilteredTreeData(list); - setExpandedKeys(expandedKeys); + if (expandedKeys) + setExpandedKeys(expandedKeys); }, []); const renderDropdown = useCallback((origin: ReactNode) => { - if (!defaultSearch) { - return ( - - ); - } else { - return ( - <> - {origin} - - ); - } + return ( + <> + {!defaultSearch && ( + + )} + {origin} + + ); }, [treeData]); const handleDropdownChange = useCallback((open: boolean) => { diff --git a/src/components/NewTaxonomy/TaxonomySearch.tsx b/src/components/NewTaxonomy/TaxonomySearch.tsx index 5db6a76a8..86e0edf84 100644 --- a/src/components/NewTaxonomy/TaxonomySearch.tsx +++ b/src/components/NewTaxonomy/TaxonomySearch.tsx @@ -1,13 +1,4 @@ -import React, { - ChangeEvent, - KeyboardEvent, - ReactNode, - useCallback, - useEffect, - useImperativeHandle, - useRef, - useState -} from 'react'; +import React, { ChangeEvent, KeyboardEvent, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react'; import './TaxonomySearch.styl'; import { Block } from '../../utils/bem'; @@ -16,8 +7,7 @@ import { debounce } from 'lodash'; type TaxonomySearchProps = { treeData: AntTaxonomyItem[], - origin: ReactNode, - onChange: (list: AntTaxonomyItem[], expandedKeys: React.Key[]) => void, + onChange: (list: AntTaxonomyItem[], expandedKeys: React.Key[] | null) => void, } export type TaxonomySearchRef = { @@ -26,7 +16,6 @@ export type TaxonomySearchRef = { } const TaxonomySearch = React.forwardRef(({ - origin, treeData, onChange, }, ref) => { @@ -48,13 +37,25 @@ const TaxonomySearch = React.forwardRef( useEffect(() => { const _filteredData = filterTreeData(treeData, inputValue); - onChange(_filteredData.filteredDataTree, _filteredData.expandedKeys); + onChange(_filteredData.filteredDataTree, null); }, [treeData]); + // When the treeData is being loaded as async API call, the _treeNode.title_ is not a string but an object, so we have to treat this value to get the title from props.children + const getTitle = useCallback((treeNodeTitle: any): string => { + if (!treeNodeTitle.props) return treeNodeTitle; + + // The title can be on the first level of props.children, but it may be on the second level or deeper + // This recursive function validate where the title is and return to the filterTreeNode + if (typeof treeNodeTitle.props.children === 'object') + return getTitle(treeNodeTitle.props.children); + + return treeNodeTitle.props.children; + }, []); + //To filter the treeData items that match with the searchValue const filterTreeNode = useCallback((searchValue: string, treeNode: AntTaxonomyItem) => { const lowerSearchValue = String(searchValue).toLowerCase(); - const lowerResultValue = typeof treeNode.title === 'object' ? treeNode.title.props.children.props.children : treeNode.title; + const lowerResultValue = getTitle(treeNode.title); if (!lowerSearchValue) { return false; @@ -109,24 +110,21 @@ const TaxonomySearch = React.forwardRef( }, 300), [treeData]); return ( - <> - ) => { - setInputValue(e.target.value); - handleSearch(e); - }} - onKeyDown={(e: KeyboardEvent) => { - if (e.key === 'Backspace' || e.key === 'Delete') e.stopPropagation(); - }} - placeholder={'Search'} - data-testid={'taxonomy-search'} - name={'taxonomy-search-input'} - /> - {origin} - + ) => { + setInputValue(e.target.value); + handleSearch(e); + }} + onKeyDown={(e: KeyboardEvent) => { + if (e.key === 'Backspace' || e.key === 'Delete') e.stopPropagation(); + }} + placeholder={'Search'} + data-testid={'taxonomy-search'} + name={'taxonomy-search-input'} + /> ); }); From 9cc062382d03306e5407a239a3b7026748a2b660 Mon Sep 17 00:00:00 2001 From: Julio Sgarbi Date: Mon, 30 Oct 2023 16:19:48 +0100 Subject: [PATCH 14/24] stop digging if it doesnt have chindrens --- src/components/NewTaxonomy/TaxonomySearch.tsx | 4 ++-- src/utils/feature-flags.ts | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/NewTaxonomy/TaxonomySearch.tsx b/src/components/NewTaxonomy/TaxonomySearch.tsx index 86e0edf84..e4212d322 100644 --- a/src/components/NewTaxonomy/TaxonomySearch.tsx +++ b/src/components/NewTaxonomy/TaxonomySearch.tsx @@ -80,9 +80,9 @@ const TaxonomySearch = React.forwardRef( const children = dataNode['children']; const match = keepAll || filterTreeNode(searchValue, dataNode); - const childList = dig(children || [], match); + const childList = children ? dig(children || [], match) : undefined; - if (match || childList.length) { + if (match || childList?.length) { if (!keepAll) _expandedKeys.push(dataNode.key); diff --git a/src/utils/feature-flags.ts b/src/utils/feature-flags.ts index 104534478..19e0780da 100644 --- a/src/utils/feature-flags.ts +++ b/src/utils/feature-flags.ts @@ -353,6 +353,8 @@ Object.assign(window, { function getFeatureFlags() { return { ...(window.APP_SETTINGS?.feature_flags ?? {}), + [FF_TAXONOMY_ASYNC]: true, + [FF_LEAP_218]: true, // could be used to explicitly set flags for testing, i.e. [FF_DEV_3793]: true }; } From 880dea77b2da9bd71302828d6360d30675d5879e Mon Sep 17 00:00:00 2001 From: Julio Sgarbi Date: Mon, 30 Oct 2023 16:20:04 +0100 Subject: [PATCH 15/24] remove preset ff --- src/utils/feature-flags.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/utils/feature-flags.ts b/src/utils/feature-flags.ts index 19e0780da..104534478 100644 --- a/src/utils/feature-flags.ts +++ b/src/utils/feature-flags.ts @@ -353,8 +353,6 @@ Object.assign(window, { function getFeatureFlags() { return { ...(window.APP_SETTINGS?.feature_flags ?? {}), - [FF_TAXONOMY_ASYNC]: true, - [FF_LEAP_218]: true, // could be used to explicitly set flags for testing, i.e. [FF_DEV_3793]: true }; } From ee653a657590d6876d9807a006cb4ba6f3b15a1b Mon Sep 17 00:00:00 2001 From: Julio Sgarbi Date: Mon, 30 Oct 2023 18:02:21 +0100 Subject: [PATCH 16/24] check if is leaf --- src/components/NewTaxonomy/NewTaxonomy.tsx | 2 +- src/components/NewTaxonomy/TaxonomySearch.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/NewTaxonomy/NewTaxonomy.tsx b/src/components/NewTaxonomy/NewTaxonomy.tsx index d707f07ca..0a73f3aa1 100644 --- a/src/components/NewTaxonomy/NewTaxonomy.tsx +++ b/src/components/NewTaxonomy/NewTaxonomy.tsx @@ -183,7 +183,7 @@ const NewTaxonomy = ({ showArrow={!defaultSearch} dropdownRender={renderDropdown} onDropdownVisibleChange={handleDropdownChange} - treeExpandedKeys={expandedKeys} + treeExpandedKeys={!defaultSearch ? expandedKeys : undefined} onTreeExpand={(expandedKeys: React.Key[]) => { setExpandedKeys(expandedKeys); }} diff --git a/src/components/NewTaxonomy/TaxonomySearch.tsx b/src/components/NewTaxonomy/TaxonomySearch.tsx index e4212d322..ee3b476b2 100644 --- a/src/components/NewTaxonomy/TaxonomySearch.tsx +++ b/src/components/NewTaxonomy/TaxonomySearch.tsx @@ -80,7 +80,7 @@ const TaxonomySearch = React.forwardRef( const children = dataNode['children']; const match = keepAll || filterTreeNode(searchValue, dataNode); - const childList = children ? dig(children || [], match) : undefined; + const childList = children?.length ? dig(children, match) : undefined; if (match || childList?.length) { if (!keepAll) @@ -88,7 +88,7 @@ const TaxonomySearch = React.forwardRef( total.push({ ...dataNode, - isLeaf: undefined, + isLeaf: !childList?.length, children: childList, }); } From 28b12382c90d08bf8cec8172828d5ae76096c8be Mon Sep 17 00:00:00 2001 From: Julio Sgarbi Date: Mon, 30 Oct 2023 22:27:26 +0100 Subject: [PATCH 17/24] Update src/components/NewTaxonomy/NewTaxonomy.tsx Co-authored-by: hlomzik --- src/components/NewTaxonomy/NewTaxonomy.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/NewTaxonomy/NewTaxonomy.tsx b/src/components/NewTaxonomy/NewTaxonomy.tsx index 0a73f3aa1..c011ddee8 100644 --- a/src/components/NewTaxonomy/NewTaxonomy.tsx +++ b/src/components/NewTaxonomy/NewTaxonomy.tsx @@ -162,7 +162,8 @@ const NewTaxonomy = ({ const handleDropdownChange = useCallback((open: boolean) => { if (open) { - // handleDropdownChange is being called before the dropdown is rendered, 100ms is the time that we have to wait to dropdown be rendered + // handleDropdownChange is being called before the dropdown is rendered, + // 100ms is the time that we have to wait to dropdown be rendered setTimeout(() => { refInput.current?.focus(); }, 100); From 5348400f6bf980824d5b772fd17bafcfa5b0a1f8 Mon Sep 17 00:00:00 2001 From: Julio Sgarbi Date: Mon, 30 Oct 2023 22:27:44 +0100 Subject: [PATCH 18/24] Update src/components/NewTaxonomy/TaxonomySearch.tsx Co-authored-by: hlomzik --- src/components/NewTaxonomy/TaxonomySearch.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/NewTaxonomy/TaxonomySearch.tsx b/src/components/NewTaxonomy/TaxonomySearch.tsx index ee3b476b2..9289f9f12 100644 --- a/src/components/NewTaxonomy/TaxonomySearch.tsx +++ b/src/components/NewTaxonomy/TaxonomySearch.tsx @@ -40,7 +40,9 @@ const TaxonomySearch = React.forwardRef( onChange(_filteredData.filteredDataTree, null); }, [treeData]); - // When the treeData is being loaded as async API call, the _treeNode.title_ is not a string but an object, so we have to treat this value to get the title from props.children + // When the treeNode has additional formatting because of `hint` or `color` props, + // the `treeNode.title` is not a string but a react component, + // so we have to look for the title in children (1 or 2 levels deep) const getTitle = useCallback((treeNodeTitle: any): string => { if (!treeNodeTitle.props) return treeNodeTitle; From 0988f49c62f8ecc9a17198103f970843333e5234 Mon Sep 17 00:00:00 2001 From: Julio Sgarbi Date: Mon, 30 Oct 2023 22:28:40 +0100 Subject: [PATCH 19/24] Update src/components/NewTaxonomy/TaxonomySearch.tsx Co-authored-by: hlomzik --- src/components/NewTaxonomy/TaxonomySearch.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/NewTaxonomy/TaxonomySearch.tsx b/src/components/NewTaxonomy/TaxonomySearch.tsx index 9289f9f12..bdf95b2f4 100644 --- a/src/components/NewTaxonomy/TaxonomySearch.tsx +++ b/src/components/NewTaxonomy/TaxonomySearch.tsx @@ -46,8 +46,6 @@ const TaxonomySearch = React.forwardRef( const getTitle = useCallback((treeNodeTitle: any): string => { if (!treeNodeTitle.props) return treeNodeTitle; - // The title can be on the first level of props.children, but it may be on the second level or deeper - // This recursive function validate where the title is and return to the filterTreeNode if (typeof treeNodeTitle.props.children === 'object') return getTitle(treeNodeTitle.props.children); From b3ee9e578b61c853bfd8d25233beaf71a486fc7f Mon Sep 17 00:00:00 2001 From: Julio Sgarbi Date: Mon, 30 Oct 2023 22:28:47 +0100 Subject: [PATCH 20/24] Update src/components/NewTaxonomy/TaxonomySearch.tsx Co-authored-by: hlomzik --- src/components/NewTaxonomy/TaxonomySearch.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/NewTaxonomy/TaxonomySearch.tsx b/src/components/NewTaxonomy/TaxonomySearch.tsx index bdf95b2f4..f7f8ccb5d 100644 --- a/src/components/NewTaxonomy/TaxonomySearch.tsx +++ b/src/components/NewTaxonomy/TaxonomySearch.tsx @@ -52,7 +52,7 @@ const TaxonomySearch = React.forwardRef( return treeNodeTitle.props.children; }, []); - //To filter the treeData items that match with the searchValue + // To filter the treeData items that match with the searchValue const filterTreeNode = useCallback((searchValue: string, treeNode: AntTaxonomyItem) => { const lowerSearchValue = String(searchValue).toLowerCase(); const lowerResultValue = getTitle(treeNode.title); From c3ea39f4272f8cb515d65da28c999535b5bbb1cf Mon Sep 17 00:00:00 2001 From: Julio Sgarbi Date: Mon, 30 Oct 2023 22:28:58 +0100 Subject: [PATCH 21/24] Update src/components/NewTaxonomy/TaxonomySearch.tsx Co-authored-by: hlomzik --- src/components/NewTaxonomy/TaxonomySearch.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/NewTaxonomy/TaxonomySearch.tsx b/src/components/NewTaxonomy/TaxonomySearch.tsx index f7f8ccb5d..612a58e1d 100644 --- a/src/components/NewTaxonomy/TaxonomySearch.tsx +++ b/src/components/NewTaxonomy/TaxonomySearch.tsx @@ -44,7 +44,7 @@ const TaxonomySearch = React.forwardRef( // the `treeNode.title` is not a string but a react component, // so we have to look for the title in children (1 or 2 levels deep) const getTitle = useCallback((treeNodeTitle: any): string => { - if (!treeNodeTitle.props) return treeNodeTitle; + if (typeof treeNodeTitle === 'string') return treeNodeTitle; if (typeof treeNodeTitle.props.children === 'object') return getTitle(treeNodeTitle.props.children); From e387d227672a6d08084e148ce42c44598164222f Mon Sep 17 00:00:00 2001 From: Julio Sgarbi Date: Mon, 30 Oct 2023 22:29:06 +0100 Subject: [PATCH 22/24] Update src/components/NewTaxonomy/NewTaxonomy.tsx Co-authored-by: hlomzik --- src/components/NewTaxonomy/NewTaxonomy.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/NewTaxonomy/NewTaxonomy.tsx b/src/components/NewTaxonomy/NewTaxonomy.tsx index c011ddee8..422a6e5c5 100644 --- a/src/components/NewTaxonomy/NewTaxonomy.tsx +++ b/src/components/NewTaxonomy/NewTaxonomy.tsx @@ -141,8 +141,7 @@ const NewTaxonomy = ({ const handleSearch = useCallback((list: AntTaxonomyItem[], expandedKeys: React.Key[] | null) => { setFilteredTreeData(list); - if (expandedKeys) - setExpandedKeys(expandedKeys); + if (expandedKeys) setExpandedKeys(expandedKeys); }, []); const renderDropdown = useCallback((origin: ReactNode) => { From 065b6cc6efb8161d3c53e0685d62a1f476fde7aa Mon Sep 17 00:00:00 2001 From: Julio Sgarbi Date: Mon, 30 Oct 2023 22:29:55 +0100 Subject: [PATCH 23/24] change changeValue method name to resetValue --- src/components/NewTaxonomy/NewTaxonomy.tsx | 2 +- src/components/NewTaxonomy/TaxonomySearch.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/NewTaxonomy/NewTaxonomy.tsx b/src/components/NewTaxonomy/NewTaxonomy.tsx index 422a6e5c5..625653bba 100644 --- a/src/components/NewTaxonomy/NewTaxonomy.tsx +++ b/src/components/NewTaxonomy/NewTaxonomy.tsx @@ -167,7 +167,7 @@ const NewTaxonomy = ({ refInput.current?.focus(); }, 100); } else { - refInput.current?.changeValue(); + refInput.current?.resetValue(); } }, [refInput]); diff --git a/src/components/NewTaxonomy/TaxonomySearch.tsx b/src/components/NewTaxonomy/TaxonomySearch.tsx index 612a58e1d..1e1f7061e 100644 --- a/src/components/NewTaxonomy/TaxonomySearch.tsx +++ b/src/components/NewTaxonomy/TaxonomySearch.tsx @@ -11,7 +11,7 @@ type TaxonomySearchProps = { } export type TaxonomySearchRef = { - changeValue: () => void, + resetValue: () => void, focus: () => void, } @@ -21,7 +21,7 @@ const TaxonomySearch = React.forwardRef( }, ref) => { useImperativeHandle(ref, (): TaxonomySearchRef => { return { - changeValue() { + resetValue() { setInputValue(''); onChange(treeData, []); }, From e96548a596e942b6972cac91b22161c146ab2c86 Mon Sep 17 00:00:00 2001 From: Julio Sgarbi Date: Mon, 30 Oct 2023 23:09:19 +0100 Subject: [PATCH 24/24] Update src/components/NewTaxonomy/TaxonomySearch.tsx Co-authored-by: hlomzik --- src/components/NewTaxonomy/TaxonomySearch.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/NewTaxonomy/TaxonomySearch.tsx b/src/components/NewTaxonomy/TaxonomySearch.tsx index 1e1f7061e..24e00fe31 100644 --- a/src/components/NewTaxonomy/TaxonomySearch.tsx +++ b/src/components/NewTaxonomy/TaxonomySearch.tsx @@ -119,6 +119,7 @@ const TaxonomySearch = React.forwardRef( handleSearch(e); }} onKeyDown={(e: KeyboardEvent) => { + // to prevent selected items from being deleted if (e.key === 'Backspace' || e.key === 'Delete') e.stopPropagation(); }} placeholder={'Search'}