From 09a8973b01ee61f5c1104d6ccca24eee8b48c23d Mon Sep 17 00:00:00 2001 From: Alexander Date: Thu, 29 Apr 2021 14:16:16 +0300 Subject: [PATCH 01/13] Implement topics' filtration --- .../src/components/Topics/List/List.tsx | 49 +++++++++---- .../src/components/Topics/List/ListHeader.tsx | 71 +++++++++++++++++++ .../src/redux/actions/thunks/topics.ts | 4 ++ 3 files changed, 112 insertions(+), 12 deletions(-) create mode 100644 kafka-ui-react-app/src/components/Topics/List/ListHeader.tsx diff --git a/kafka-ui-react-app/src/components/Topics/List/List.tsx b/kafka-ui-react-app/src/components/Topics/List/List.tsx index 54d4b7854ad..b4e253abc1a 100644 --- a/kafka-ui-react-app/src/components/Topics/List/List.tsx +++ b/kafka-ui-react-app/src/components/Topics/List/List.tsx @@ -1,3 +1,5 @@ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ import React from 'react'; import { TopicWithDetailedInfo, @@ -12,8 +14,10 @@ import { FetchTopicsListParams } from 'redux/actions'; import ClusterContext from 'components/contexts/ClusterContext'; import PageLoader from 'components/common/PageLoader/PageLoader'; import Pagination from 'components/common/Pagination/Pagination'; +import { TopicColumnsToSort } from 'generated-sources'; import ListItem from './ListItem'; +import ListHeader from './ListHeader'; interface Props { areTopicsFetching: boolean; @@ -41,10 +45,15 @@ const List: React.FC = ({ const { isReadOnly } = React.useContext(ClusterContext); const { clusterName } = useParams<{ clusterName: ClusterName }>(); const { page, perPage } = usePagination(); + const [orderBy, setOrderBy] = React.useState< + TopicColumnsToSort | undefined + >(); + const [search, setSearch] = React.useState(''); + let debounceTimeout: NodeJS.Timeout; React.useEffect(() => { - fetchTopicsList({ clusterName, page, perPage }); - }, [fetchTopicsList, clusterName, page, perPage]); + fetchTopicsList({ clusterName, page, perPage, orderBy, search }); + }, [fetchTopicsList, clusterName, page, perPage, orderBy, search]); const [showInternal, setShowInternal] = React.useState(true); @@ -52,14 +61,23 @@ const List: React.FC = ({ setShowInternal(!showInternal); }, [showInternal]); + const handleSearch = (e: React.ChangeEvent) => { + if (debounceTimeout) window.clearTimeout(debounceTimeout); + debounceTimeout = setTimeout(() => setSearch(e.target.value), 300); + }; + + React.useEffect(() => () => { + if (debounceTimeout) window.clearTimeout(debounceTimeout); + }); + const items = showInternal ? topics : externalTopics; return (
{showInternal ? `All Topics` : `External Topics`}
-
-
+
+
= ({
-
+
+

+ handleSearch(e)} + /> + + + +

+
+
{!isReadOnly && ( = ({
- - - - - - - + {items.map((topic) => ( diff --git a/kafka-ui-react-app/src/components/Topics/List/ListHeader.tsx b/kafka-ui-react-app/src/components/Topics/List/ListHeader.tsx new file mode 100644 index 00000000000..cabab0f4c3f --- /dev/null +++ b/kafka-ui-react-app/src/components/Topics/List/ListHeader.tsx @@ -0,0 +1,71 @@ +import { TopicColumnsToSort } from 'generated-sources'; +import React from 'react'; +import cx from 'classnames'; + +export interface ListHeaderProps { + orderBy: TopicColumnsToSort | undefined; + setOrderBy: React.Dispatch< + React.SetStateAction + >; +} + +const ListHeader: React.FC = ({ orderBy, setOrderBy }) => { + return ( + + + + + + + + ); +}; + +export default ListHeader; diff --git a/kafka-ui-react-app/src/redux/actions/thunks/topics.ts b/kafka-ui-react-app/src/redux/actions/thunks/topics.ts index 7ce6bbe6a9c..52409911fc3 100644 --- a/kafka-ui-react-app/src/redux/actions/thunks/topics.ts +++ b/kafka-ui-react-app/src/redux/actions/thunks/topics.ts @@ -7,6 +7,7 @@ import { TopicCreation, TopicUpdate, TopicConfig, + TopicColumnsToSort, } from 'generated-sources'; import { PromiseThunkResult, @@ -30,6 +31,9 @@ export interface FetchTopicsListParams { clusterName: ClusterName; page?: number; perPage?: number; + showInternal?: boolean; + search?: string; + orderBy?: TopicColumnsToSort; } export const fetchTopicsList = ( From 970f4d418dcc55a25f37e1dfac41abbfcc030c67 Mon Sep 17 00:00:00 2001 From: Alexander Date: Thu, 29 Apr 2021 14:43:25 +0300 Subject: [PATCH 02/13] Test ListHeader --- .../src/components/Topics/List/List.tsx | 6 +- .../src/components/Topics/List/ListHeader.tsx | 104 +++++++++--------- .../Topics/List/__tests__/ListHeader.spec.tsx | 27 +++++ .../__snapshots__/ListHeader.spec.tsx.snap | 79 +++++++++++++ 4 files changed, 160 insertions(+), 56 deletions(-) create mode 100644 kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx create mode 100644 kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap diff --git a/kafka-ui-react-app/src/components/Topics/List/List.tsx b/kafka-ui-react-app/src/components/Topics/List/List.tsx index b4e253abc1a..97313fdad7b 100644 --- a/kafka-ui-react-app/src/components/Topics/List/List.tsx +++ b/kafka-ui-react-app/src/components/Topics/List/List.tsx @@ -1,5 +1,3 @@ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ import React from 'react'; import { TopicWithDetailedInfo, @@ -120,9 +118,7 @@ const List: React.FC = ({ ) : (
Topic NameTotal PartitionsOut of sync replicasType
setOrderBy(TopicColumnsToSort.NAME)} + > + Topic Name{' '} + + {orderBy === TopicColumnsToSort.NAME ? ( + + ) : ( + + )} + + setOrderBy(TopicColumnsToSort.TOTAL_PARTITIONS)} + > + Total Partitions{' '} + + {orderBy === TopicColumnsToSort.TOTAL_PARTITIONS ? ( + + ) : ( + + )} + + setOrderBy(TopicColumnsToSort.OUT_OF_SYNC_REPLICAS)} + > + Out of sync replicas{' '} + + {orderBy === TopicColumnsToSort.OUT_OF_SYNC_REPLICAS ? ( + + ) : ( + + )} + + Type
- - - + {items.map((topic) => ( = ({ orderBy, setOrderBy }) => { return ( - - + + - + - + - - - + onClick={() => setOrderBy(TopicColumnsToSort.OUT_OF_SYNC_REPLICAS)} + > + Out of sync replicas{' '} + + {orderBy === TopicColumnsToSort.OUT_OF_SYNC_REPLICAS ? ( + + ) : ( + + )} + + + + + + ); }; diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx b/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx new file mode 100644 index 00000000000..56518650f0c --- /dev/null +++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx @@ -0,0 +1,27 @@ +import ListHeader from 'components/Topics/List/ListHeader'; +import { mount } from 'enzyme'; +import { TopicColumnsToSort } from 'generated-sources'; +import React from 'react'; + +describe('ListHeader', () => { + const setOrderBy = jest.fn(); + const component = mount( +
setOrderBy(TopicColumnsToSort.NAME)} - > - Topic Name{' '} - - {orderBy === TopicColumnsToSort.NAME ? ( - - ) : ( - +
- setOrderBy(TopicColumnsToSort.TOTAL_PARTITIONS)} - > - Total Partitions{' '} - - {orderBy === TopicColumnsToSort.TOTAL_PARTITIONS ? ( - - ) : ( - + onClick={() => setOrderBy(TopicColumnsToSort.NAME)} + > + Topic Name{' '} + + {orderBy === TopicColumnsToSort.NAME ? ( + + ) : ( + + )} + + - setOrderBy(TopicColumnsToSort.OUT_OF_SYNC_REPLICAS)} - > - Out of sync replicas{' '} - - {orderBy === TopicColumnsToSort.OUT_OF_SYNC_REPLICAS ? ( - - ) : ( - + onClick={() => setOrderBy(TopicColumnsToSort.TOTAL_PARTITIONS)} + > + Total Partitions{' '} + + {orderBy === TopicColumnsToSort.TOTAL_PARTITIONS ? ( + + ) : ( + + )} + + - Type
Type
+ +
+ ); + it('matches the snapshot', () => { + expect(component).toMatchSnapshot(); + }); + + describe('on column click', () => { + it('calls setOrderBy', () => { + component.find('th').at(0).simulate('click'); + expect(setOrderBy).toHaveBeenCalledTimes(1); + expect(setOrderBy).toHaveBeenCalledWith(TopicColumnsToSort.NAME); + }); + it('matches the snapshot', () => { + expect(component.find('th').at(0)).toMatchSnapshot(); + }); + }); +}); diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap new file mode 100644 index 00000000000..eb4879a58b0 --- /dev/null +++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap @@ -0,0 +1,79 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ListHeader matches the snapshot 1`] = ` + + + + + + + + + + + + +
+ Topic Name + + + + + + Total Partitions + + + + + + Out of sync replicas + + + + + + Type + + +
+`; + +exports[`ListHeader on column click matches the snapshot 1`] = ` + + Topic Name + + + + + +`; From b3d617b9cae3984c05c83f50493f0c3bad15fe83 Mon Sep 17 00:00:00 2001 From: Alexander Date: Thu, 29 Apr 2021 16:58:00 +0300 Subject: [PATCH 03/13] Add snapshots for the List component --- .../Topics/List/__tests__/List.spec.tsx | 45 ++-- .../Topics/List/__tests__/ListHeader.spec.tsx | 12 +- .../__snapshots__/List.spec.tsx.snap | 230 ++++++++++++++++++ .../__snapshots__/ListHeader.spec.tsx.snap | 2 +- 4 files changed, 266 insertions(+), 23 deletions(-) create mode 100644 kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/List.spec.tsx b/kafka-ui-react-app/src/components/Topics/List/__tests__/List.spec.tsx index 11dd2b00b65..8e6fc6d2a1f 100644 --- a/kafka-ui-react-app/src/components/Topics/List/__tests__/List.spec.tsx +++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/List.spec.tsx @@ -33,29 +33,32 @@ describe('List', () => { }); describe('when it does not have readonly flag', () => { + const component = mount( + + + + + + ); it('renders the Add a Topic button', () => { - const component = mount( - - - - - - ); expect(component.exists('Link')).toBeTruthy(); }); + it('matches the snapshot', () => { + expect(component).toMatchSnapshot(); + }); }); }); diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx b/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx index 56518650f0c..246926fec80 100644 --- a/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx +++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx @@ -19,9 +19,19 @@ describe('ListHeader', () => { component.find('th').at(0).simulate('click'); expect(setOrderBy).toHaveBeenCalledTimes(1); expect(setOrderBy).toHaveBeenCalledWith(TopicColumnsToSort.NAME); + component.find('th').at(1).simulate('click'); + expect(setOrderBy).toHaveBeenCalledTimes(2); + expect(setOrderBy).toHaveBeenCalledWith( + TopicColumnsToSort.TOTAL_PARTITIONS + ); + component.find('th').at(2).simulate('click'); + expect(setOrderBy).toHaveBeenCalledTimes(3); + expect(setOrderBy).toHaveBeenCalledWith( + TopicColumnsToSort.OUT_OF_SYNC_REPLICAS + ); }); it('matches the snapshot', () => { - expect(component.find('th').at(0)).toMatchSnapshot(); + expect(component.find('th').at(2)).toMatchSnapshot(); }); }); }); diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap new file mode 100644 index 00000000000..a847654fe66 --- /dev/null +++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap @@ -0,0 +1,230 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`List when it does not have readonly flag matches the snapshot 1`] = ` + + + +
+ + + +
+
+
+
+ + +
+
+
+

+ + + + +

+
+
+ + + + Add a Topic + + + +
+
+
+
+ + + + + + + + + + + + + + + + + +
+ Topic Name + + + + + + Total Partitions + + + + + + Out of sync replicas + + + + + + Type + + +
+ No topics found +
+ + + +
+
+
+
+
+`; diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap index eb4879a58b0..6404097b459 100644 --- a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap +++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap @@ -66,7 +66,7 @@ exports[`ListHeader on column click matches the snapshot 1`] = ` className="is-clickable" onClick={[Function]} > - Topic Name + Out of sync replicas Date: Sat, 1 May 2021 11:23:26 +0300 Subject: [PATCH 04/13] Add List tests --- .../components/Topics/List/__tests__/List.spec.tsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/List.spec.tsx b/kafka-ui-react-app/src/components/Topics/List/__tests__/List.spec.tsx index 8e6fc6d2a1f..bcf300e0f0a 100644 --- a/kafka-ui-react-app/src/components/Topics/List/__tests__/List.spec.tsx +++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/List.spec.tsx @@ -33,6 +33,8 @@ describe('List', () => { }); describe('when it does not have readonly flag', () => { + const mockFetch = jest.fn(); + jest.useFakeTimers(); const component = mount( { topics={[]} externalTopics={[]} totalPages={1} - fetchTopicsList={jest.fn()} + fetchTopicsList={mockFetch} deleteTopic={jest.fn()} clearTopicMessages={jest.fn()} /> @@ -60,5 +62,15 @@ describe('List', () => { it('matches the snapshot', () => { expect(component).toMatchSnapshot(); }); + + it('calls fetchTopicsList on input', () => { + const input = component.find('input').at(1); + input.simulate('change', { target: { value: 't' } }); + expect(setTimeout).toHaveBeenCalledTimes(1); + expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 300); + setTimeout(() => { + expect(mockFetch).toHaveBeenCalledTimes(1); + }, 301); + }); }); }); From 99c35a9c7cb30c2c89e82288ef4bd250edee7162 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 1 May 2021 11:50:25 +0300 Subject: [PATCH 05/13] Add more tests for the ListHeader --- .../Topics/List/__tests__/ListHeader.spec.tsx | 10 +++ .../__snapshots__/ListHeader.spec.tsx.snap | 85 +++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx b/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx index 246926fec80..ae1bd7aabef 100644 --- a/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx +++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx @@ -31,6 +31,16 @@ describe('ListHeader', () => { ); }); it('matches the snapshot', () => { + expect(component.find('th').at(0)).toMatchSnapshot(); + component.find('th').at(0).simulate('click'); + expect(component.find('th').at(0)).toMatchSnapshot(); + + expect(component.find('th').at(1)).toMatchSnapshot(); + component.find('th').at(1).simulate('click'); + expect(component.find('th').at(1)).toMatchSnapshot(); + + expect(component.find('th').at(2)).toMatchSnapshot(); + component.find('th').at(2).simulate('click'); expect(component.find('th').at(2)).toMatchSnapshot(); }); }); diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap index 6404097b459..af02227a3b1 100644 --- a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap +++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap @@ -62,6 +62,91 @@ exports[`ListHeader matches the snapshot 1`] = ` `; exports[`ListHeader on column click matches the snapshot 1`] = ` + + Topic Name + + + + + +`; + +exports[`ListHeader on column click matches the snapshot 2`] = ` + + Topic Name + + + + + +`; + +exports[`ListHeader on column click matches the snapshot 3`] = ` + + Total Partitions + + + + + +`; + +exports[`ListHeader on column click matches the snapshot 4`] = ` + + Total Partitions + + + + + +`; + +exports[`ListHeader on column click matches the snapshot 5`] = ` + + Out of sync replicas + + + + + +`; + +exports[`ListHeader on column click matches the snapshot 6`] = ` Date: Sat, 1 May 2021 11:58:46 +0300 Subject: [PATCH 06/13] Fix ListHeader tests --- .../Topics/List/__tests__/ListHeader.spec.tsx | 32 +++++++-- .../__snapshots__/ListHeader.spec.tsx.snap | 65 ++----------------- 2 files changed, 32 insertions(+), 65 deletions(-) diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx b/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx index ae1bd7aabef..2af0c28d216 100644 --- a/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx +++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx @@ -5,7 +5,7 @@ import React from 'react'; describe('ListHeader', () => { const setOrderBy = jest.fn(); - const component = mount( + let component = mount(
@@ -31,16 +31,34 @@ describe('ListHeader', () => { ); }); it('matches the snapshot', () => { - expect(component.find('th').at(0)).toMatchSnapshot(); - component.find('th').at(0).simulate('click'); + component = mount( + + +
+ ); expect(component.find('th').at(0)).toMatchSnapshot(); - expect(component.find('th').at(1)).toMatchSnapshot(); - component.find('th').at(1).simulate('click'); + component = mount( + + +
+ ); expect(component.find('th').at(1)).toMatchSnapshot(); - expect(component.find('th').at(2)).toMatchSnapshot(); - component.find('th').at(2).simulate('click'); + component = mount( + + +
+ ); expect(component.find('th').at(2)).toMatchSnapshot(); }); }); diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap index af02227a3b1..b53afb6c3b3 100644 --- a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap +++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap @@ -63,7 +63,7 @@ exports[`ListHeader matches the snapshot 1`] = ` exports[`ListHeader on column click matches the snapshot 1`] = ` Topic Name @@ -72,7 +72,7 @@ exports[`ListHeader on column click matches the snapshot 1`] = ` className="icon is-small" >
@@ -80,41 +80,7 @@ exports[`ListHeader on column click matches the snapshot 1`] = ` exports[`ListHeader on column click matches the snapshot 2`] = ` - Topic Name - - - - - -`; - -exports[`ListHeader on column click matches the snapshot 3`] = ` - - Total Partitions - - - - - -`; - -exports[`ListHeader on column click matches the snapshot 4`] = ` - Total Partitions @@ -123,32 +89,15 @@ exports[`ListHeader on column click matches the snapshot 4`] = ` className="icon is-small" > - - -`; - -exports[`ListHeader on column click matches the snapshot 5`] = ` - - Out of sync replicas - - - `; -exports[`ListHeader on column click matches the snapshot 6`] = ` +exports[`ListHeader on column click matches the snapshot 3`] = ` Out of sync replicas @@ -157,7 +106,7 @@ exports[`ListHeader on column click matches the snapshot 6`] = ` className="icon is-small" > From 578d7899594f4b996c536ac510766e3ef5239def Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 3 May 2021 17:33:38 +0300 Subject: [PATCH 07/13] Fix the table header --- .../src/components/Topics/List/List.tsx | 28 +++---- .../src/components/Topics/List/ListHeader.tsx | 77 +++++------------ .../components/Topics/List/ListHeaderCell.tsx | 31 +++++++ .../Topics/List/__tests__/ListHeader.spec.tsx | 2 +- .../__snapshots__/List.spec.tsx.snap | 73 +++++++++-------- .../__snapshots__/ListHeader.spec.tsx.snap | 82 ++++++++++--------- 6 files changed, 151 insertions(+), 142 deletions(-) create mode 100644 kafka-ui-react-app/src/components/Topics/List/ListHeaderCell.tsx diff --git a/kafka-ui-react-app/src/components/Topics/List/List.tsx b/kafka-ui-react-app/src/components/Topics/List/List.tsx index 97313fdad7b..e24264c4f20 100644 --- a/kafka-ui-react-app/src/components/Topics/List/List.tsx +++ b/kafka-ui-react-app/src/components/Topics/List/List.tsx @@ -13,6 +13,7 @@ import ClusterContext from 'components/contexts/ClusterContext'; import PageLoader from 'components/common/PageLoader/PageLoader'; import Pagination from 'components/common/Pagination/Pagination'; import { TopicColumnsToSort } from 'generated-sources'; +import { useDebouncedCallback } from 'use-debounce'; import ListItem from './ListItem'; import ListHeader from './ListHeader'; @@ -43,14 +44,17 @@ const List: React.FC = ({ const { isReadOnly } = React.useContext(ClusterContext); const { clusterName } = useParams<{ clusterName: ClusterName }>(); const { page, perPage } = usePagination(); - const [orderBy, setOrderBy] = React.useState< - TopicColumnsToSort | undefined - >(); + const [orderBy, setOrderBy] = React.useState(null); const [search, setSearch] = React.useState(''); - let debounceTimeout: NodeJS.Timeout; React.useEffect(() => { - fetchTopicsList({ clusterName, page, perPage, orderBy, search }); + fetchTopicsList({ + clusterName, + page, + perPage, + orderBy: orderBy || undefined, + search, + }); }, [fetchTopicsList, clusterName, page, perPage, orderBy, search]); const [showInternal, setShowInternal] = React.useState(true); @@ -59,14 +63,10 @@ const List: React.FC = ({ setShowInternal(!showInternal); }, [showInternal]); - const handleSearch = (e: React.ChangeEvent) => { - if (debounceTimeout) window.clearTimeout(debounceTimeout); - debounceTimeout = setTimeout(() => setSearch(e.target.value), 300); - }; - - React.useEffect(() => () => { - if (debounceTimeout) window.clearTimeout(debounceTimeout); - }); + const handleSearch = useDebouncedCallback( + (e) => setSearch(e.target.value), + 300 + ); const items = showInternal ? topics : externalTopics; @@ -94,7 +94,7 @@ const List: React.FC = ({ className="input" type="text" placeholder="Search by Topic Name" - onChange={(e) => handleSearch(e)} + onChange={handleSearch} /> diff --git a/kafka-ui-react-app/src/components/Topics/List/ListHeader.tsx b/kafka-ui-react-app/src/components/Topics/List/ListHeader.tsx index 29cc49df6da..a901c8e053a 100644 --- a/kafka-ui-react-app/src/components/Topics/List/ListHeader.tsx +++ b/kafka-ui-react-app/src/components/Topics/List/ListHeader.tsx @@ -1,68 +1,35 @@ import { TopicColumnsToSort } from 'generated-sources'; import React from 'react'; -import cx from 'classnames'; + +import ListHeaderCell from './ListHeaderCell'; export interface ListHeaderProps { - orderBy: TopicColumnsToSort | undefined; - setOrderBy: React.Dispatch< - React.SetStateAction - >; + orderBy: TopicColumnsToSort | null; + setOrderBy: React.Dispatch>; } const ListHeader: React.FC = ({ orderBy, setOrderBy }) => { return ( - setOrderBy(TopicColumnsToSort.NAME)} - > - Topic Name{' '} - - {orderBy === TopicColumnsToSort.NAME ? ( - - ) : ( - - )} - - - setOrderBy(TopicColumnsToSort.TOTAL_PARTITIONS)} - > - Total Partitions{' '} - - {orderBy === TopicColumnsToSort.TOTAL_PARTITIONS ? ( - - ) : ( - - )} - - - setOrderBy(TopicColumnsToSort.OUT_OF_SYNC_REPLICAS)} - > - Out of sync replicas{' '} - - {orderBy === TopicColumnsToSort.OUT_OF_SYNC_REPLICAS ? ( - - ) : ( - - )} - - + + + Type diff --git a/kafka-ui-react-app/src/components/Topics/List/ListHeaderCell.tsx b/kafka-ui-react-app/src/components/Topics/List/ListHeaderCell.tsx new file mode 100644 index 00000000000..2d5c3db7858 --- /dev/null +++ b/kafka-ui-react-app/src/components/Topics/List/ListHeaderCell.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { TopicColumnsToSort } from 'generated-sources'; +import cx from 'classnames'; + +export interface ListHeaderProps { + value: TopicColumnsToSort; + title: string; + orderBy: TopicColumnsToSort | null; + setOrderBy: React.Dispatch>; +} + +const ListHeaderCell: React.FC = ({ + value, + title, + orderBy, + setOrderBy, +}) => { + return ( + setOrderBy(value)} + > + {title} + + {orderBy === value ? : ''} + + + ); +}; + +export default ListHeaderCell; diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx b/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx index 2af0c28d216..267b77dbaee 100644 --- a/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx +++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx @@ -7,7 +7,7 @@ describe('ListHeader', () => { const setOrderBy = jest.fn(); let component = mount( - +
); it('matches the snapshot', () => { diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap index a847654fe66..88773ea104b 100644 --- a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap +++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap @@ -133,52 +133,59 @@ exports[`List when it does not have readonly flag matches the snapshot 1`] = ` className="table is-fullwidth" > - - Topic Name - - - - - - +
+ - Total Partitions - - - - - - + + - Out of sync replicas - - - - - + + Type diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap index b53afb6c3b3..a8cd910e039 100644 --- a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap +++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap @@ -3,52 +3,59 @@ exports[`ListHeader matches the snapshot 1`] = ` - - - + + @@ -67,12 +74,11 @@ exports[`ListHeader on column click matches the snapshot 1`] = ` onClick={[Function]} > Topic Name - @@ -84,12 +90,11 @@ exports[`ListHeader on column click matches the snapshot 2`] = ` onClick={[Function]} > Total Partitions - @@ -101,12 +106,11 @@ exports[`ListHeader on column click matches the snapshot 3`] = ` onClick={[Function]} > Out of sync replicas - From 6d42d186b319be98677b37deb57aab4cb10e5688 Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 3 May 2021 17:41:53 +0300 Subject: [PATCH 08/13] Fix the icon --- .../components/Topics/List/ListHeaderCell.tsx | 2 +- .../__tests__/__snapshots__/List.spec.tsx.snap | 18 +++++++++++++++--- .../__snapshots__/ListHeader.spec.tsx.snap | 18 +++++++++++++++--- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/kafka-ui-react-app/src/components/Topics/List/ListHeaderCell.tsx b/kafka-ui-react-app/src/components/Topics/List/ListHeaderCell.tsx index 2d5c3db7858..2f1dc312799 100644 --- a/kafka-ui-react-app/src/components/Topics/List/ListHeaderCell.tsx +++ b/kafka-ui-react-app/src/components/Topics/List/ListHeaderCell.tsx @@ -22,7 +22,7 @@ const ListHeaderCell: React.FC = ({ > {title} - {orderBy === value ? : ''} + ); diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap index 88773ea104b..c90e51c6ab6 100644 --- a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap +++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap @@ -151,7 +151,11 @@ exports[`List when it does not have readonly flag matches the snapshot 1`] = ` Topic Name + > + + + > + + + > + + + > + + + > + + - - + + + - - + + + + + - - + + + + + - - - - - - + + + + + - - - - - - - - - - - - - - - -
- Topic Name - - - - - + + - Total Partitions - - - - - + + - Out of sync replicas - - - - - Type diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap index a8cd910e039..d690100af22 100644 --- a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap +++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap @@ -21,7 +21,11 @@ exports[`ListHeader matches the snapshot 1`] = ` Topic Name + > + + From a56259f39439515f713c003b1cd5fec85a08d27e Mon Sep 17 00:00:00 2001 From: Alexander Date: Thu, 6 May 2021 15:52:43 +0300 Subject: [PATCH 09/13] Make SortableColumn and Search reusable --- .../src/components/Topics/List/List.tsx | 49 ++++-- .../src/components/Topics/List/ListHeader.tsx | 40 ----- .../Topics/List/__tests__/ListHeader.spec.tsx | 65 ------- .../__snapshots__/List.spec.tsx.snap | 164 +++++++++--------- .../__snapshots__/ListHeader.spec.tsx.snap | 129 -------------- .../src/components/common/Search/Search.tsx | 29 ++++ .../SortableColumnHeader.tsx} | 8 +- .../__tests__/SortableColumnHeader.spec.tsx | 37 ++++ .../SortableColumnHeader.spec.tsx.snap | 59 +++++++ 9 files changed, 242 insertions(+), 338 deletions(-) delete mode 100644 kafka-ui-react-app/src/components/Topics/List/ListHeader.tsx delete mode 100644 kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx delete mode 100644 kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/ListHeader.spec.tsx.snap create mode 100644 kafka-ui-react-app/src/components/common/Search/Search.tsx rename kafka-ui-react-app/src/components/{Topics/List/ListHeaderCell.tsx => common/table/SortableCulumnHeader/SortableColumnHeader.tsx} (71%) create mode 100644 kafka-ui-react-app/src/components/common/table/__tests__/SortableColumnHeader.spec.tsx create mode 100644 kafka-ui-react-app/src/components/common/table/__tests__/__snapshots__/SortableColumnHeader.spec.tsx.snap diff --git a/kafka-ui-react-app/src/components/Topics/List/List.tsx b/kafka-ui-react-app/src/components/Topics/List/List.tsx index e24264c4f20..636fc91f6d6 100644 --- a/kafka-ui-react-app/src/components/Topics/List/List.tsx +++ b/kafka-ui-react-app/src/components/Topics/List/List.tsx @@ -13,10 +13,10 @@ import ClusterContext from 'components/contexts/ClusterContext'; import PageLoader from 'components/common/PageLoader/PageLoader'; import Pagination from 'components/common/Pagination/Pagination'; import { TopicColumnsToSort } from 'generated-sources'; -import { useDebouncedCallback } from 'use-debounce'; +import SortableColumnHeader from 'components/common/table/SortableCulumnHeader/SortableColumnHeader'; +import Search from 'components/common/Search/Search'; import ListItem from './ListItem'; -import ListHeader from './ListHeader'; interface Props { areTopicsFetching: boolean; @@ -63,10 +63,7 @@ const List: React.FC = ({ setShowInternal(!showInternal); }, [showInternal]); - const handleSearch = useDebouncedCallback( - (e) => setSearch(e.target.value), - 300 - ); + const handleSearch = (value: string) => setSearch(value); const items = showInternal ? topics : externalTopics; @@ -89,17 +86,10 @@ const List: React.FC = ({
-

- - - - -

+
{!isReadOnly && ( @@ -118,7 +108,30 @@ const List: React.FC = ({ ) : (
- + + + + + + + + + {items.map((topic) => ( >; -} - -const ListHeader: React.FC = ({ orderBy, setOrderBy }) => { - return ( - - - - - - - - - - ); -}; - -export default ListHeader; diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx b/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx deleted file mode 100644 index 267b77dbaee..00000000000 --- a/kafka-ui-react-app/src/components/Topics/List/__tests__/ListHeader.spec.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import ListHeader from 'components/Topics/List/ListHeader'; -import { mount } from 'enzyme'; -import { TopicColumnsToSort } from 'generated-sources'; -import React from 'react'; - -describe('ListHeader', () => { - const setOrderBy = jest.fn(); - let component = mount( -
Type
Type
- -
- ); - it('matches the snapshot', () => { - expect(component).toMatchSnapshot(); - }); - - describe('on column click', () => { - it('calls setOrderBy', () => { - component.find('th').at(0).simulate('click'); - expect(setOrderBy).toHaveBeenCalledTimes(1); - expect(setOrderBy).toHaveBeenCalledWith(TopicColumnsToSort.NAME); - component.find('th').at(1).simulate('click'); - expect(setOrderBy).toHaveBeenCalledTimes(2); - expect(setOrderBy).toHaveBeenCalledWith( - TopicColumnsToSort.TOTAL_PARTITIONS - ); - component.find('th').at(2).simulate('click'); - expect(setOrderBy).toHaveBeenCalledTimes(3); - expect(setOrderBy).toHaveBeenCalledWith( - TopicColumnsToSort.OUT_OF_SYNC_REPLICAS - ); - }); - it('matches the snapshot', () => { - component = mount( - - -
- ); - expect(component.find('th').at(0)).toMatchSnapshot(); - - component = mount( - - -
- ); - expect(component.find('th').at(1)).toMatchSnapshot(); - - component = mount( - - -
- ); - expect(component.find('th').at(2)).toMatchSnapshot(); - }); - }); -}); diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap index c90e51c6ab6..8fcb04591f2 100644 --- a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap +++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap @@ -84,23 +84,28 @@ exports[`List when it does not have readonly flag matches the snapshot 1`] = `
-

- - - - -

+ + + +

+
- -
- - Topic Name - - - - - - Total Partitions - - - - - - Out of sync replicas - - - - - Type + + - -
+ Type + + +
- -
- Topic Name - - - - - Total Partitions - - - - - Out of sync replicas - - - - - Type - - -
-`; - -exports[`ListHeader on column click matches the snapshot 1`] = ` - - Topic Name - - - - -`; - -exports[`ListHeader on column click matches the snapshot 2`] = ` - - Total Partitions - - - - -`; - -exports[`ListHeader on column click matches the snapshot 3`] = ` - - Out of sync replicas - - - - -`; diff --git a/kafka-ui-react-app/src/components/common/Search/Search.tsx b/kafka-ui-react-app/src/components/common/Search/Search.tsx new file mode 100644 index 00000000000..6ddbf66306f --- /dev/null +++ b/kafka-ui-react-app/src/components/common/Search/Search.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { useDebouncedCallback } from 'use-debounce'; + +interface SearchProps { + handleSearch: (value: string) => void; + placeholder: string; +} + +const Search: React.FC = ({ handleSearch, placeholder }) => { + const onChange = useDebouncedCallback( + (e) => handleSearch(e.target.value), + 300 + ); + return ( +

+ + + + +

+ ); +}; + +export default Search; diff --git a/kafka-ui-react-app/src/components/Topics/List/ListHeaderCell.tsx b/kafka-ui-react-app/src/components/common/table/SortableCulumnHeader/SortableColumnHeader.tsx similarity index 71% rename from kafka-ui-react-app/src/components/Topics/List/ListHeaderCell.tsx rename to kafka-ui-react-app/src/components/common/table/SortableCulumnHeader/SortableColumnHeader.tsx index 2f1dc312799..f6e147a1020 100644 --- a/kafka-ui-react-app/src/components/Topics/List/ListHeaderCell.tsx +++ b/kafka-ui-react-app/src/components/common/table/SortableCulumnHeader/SortableColumnHeader.tsx @@ -1,12 +1,12 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import React from 'react'; -import { TopicColumnsToSort } from 'generated-sources'; import cx from 'classnames'; export interface ListHeaderProps { - value: TopicColumnsToSort; + value: any; title: string; - orderBy: TopicColumnsToSort | null; - setOrderBy: React.Dispatch>; + orderBy: any; + setOrderBy: React.Dispatch>; } const ListHeaderCell: React.FC = ({ diff --git a/kafka-ui-react-app/src/components/common/table/__tests__/SortableColumnHeader.spec.tsx b/kafka-ui-react-app/src/components/common/table/__tests__/SortableColumnHeader.spec.tsx new file mode 100644 index 00000000000..a9421ac7fad --- /dev/null +++ b/kafka-ui-react-app/src/components/common/table/__tests__/SortableColumnHeader.spec.tsx @@ -0,0 +1,37 @@ +import SortableColumnHeader from 'components/common/table/SortableCulumnHeader/SortableColumnHeader'; +import { mount } from 'enzyme'; +import { TopicColumnsToSort } from 'generated-sources'; +import React from 'react'; + +describe('ListHeader', () => { + const setOrderBy = jest.fn(); + const component = mount( + + + + + + +
+ ); + it('matches the snapshot', () => { + expect(component).toMatchSnapshot(); + }); + + describe('on column click', () => { + it('calls setOrderBy', () => { + component.find('th').simulate('click'); + expect(setOrderBy).toHaveBeenCalledTimes(1); + expect(setOrderBy).toHaveBeenCalledWith(TopicColumnsToSort.NAME); + }); + + it('matches the snapshot', () => { + expect(component).toMatchSnapshot(); + }); + }); +}); diff --git a/kafka-ui-react-app/src/components/common/table/__tests__/__snapshots__/SortableColumnHeader.spec.tsx.snap b/kafka-ui-react-app/src/components/common/table/__tests__/__snapshots__/SortableColumnHeader.spec.tsx.snap new file mode 100644 index 00000000000..48e0159cc04 --- /dev/null +++ b/kafka-ui-react-app/src/components/common/table/__tests__/__snapshots__/SortableColumnHeader.spec.tsx.snap @@ -0,0 +1,59 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ListHeader matches the snapshot 1`] = ` + + + + + + + + +
+ Name + + + +
+`; + +exports[`ListHeader on column click matches the snapshot 1`] = ` + + + + + + + + +
+ Name + + + +
+`; From a489df78c2f4752086a73007962b8f6a97278029 Mon Sep 17 00:00:00 2001 From: Alexander Date: Thu, 6 May 2021 16:55:56 +0300 Subject: [PATCH 10/13] Move search and orderBy to the store --- .../src/components/Topics/List/List.tsx | 18 ++++--- .../components/Topics/List/ListContainer.ts | 8 +++ .../Topics/List/__tests__/List.spec.tsx | 8 +++ .../__snapshots__/List.spec.tsx.snap | 10 ++-- .../SortableColumnHeader.tsx | 24 ++++----- .../redux/actions/__test__/actions.spec.ts | 19 +++++++ .../src/redux/actions/actions.ts | 9 ++++ .../src/redux/interfaces/topic.ts | 3 ++ .../reducers/topics/__test__/reducer.spec.ts | 51 +++++++++++++++---- .../src/redux/reducers/topics/reducer.ts | 14 +++++ .../src/redux/reducers/topics/selectors.ts | 10 ++++ 11 files changed, 141 insertions(+), 33 deletions(-) diff --git a/kafka-ui-react-app/src/components/Topics/List/List.tsx b/kafka-ui-react-app/src/components/Topics/List/List.tsx index 636fc91f6d6..7172c9fbb7e 100644 --- a/kafka-ui-react-app/src/components/Topics/List/List.tsx +++ b/kafka-ui-react-app/src/components/Topics/List/List.tsx @@ -30,6 +30,10 @@ interface Props { clusterName: ClusterName, partitions?: number[] ): void; + search: string; + orderBy: TopicColumnsToSort | null; + setTopicsSearch(search: string): void; + setTopicsOrderBy(orderBy: TopicColumnsToSort | null): void; } const List: React.FC = ({ @@ -40,12 +44,14 @@ const List: React.FC = ({ fetchTopicsList, deleteTopic, clearTopicMessages, + search, + orderBy, + setTopicsSearch, + setTopicsOrderBy, }) => { const { isReadOnly } = React.useContext(ClusterContext); const { clusterName } = useParams<{ clusterName: ClusterName }>(); const { page, perPage } = usePagination(); - const [orderBy, setOrderBy] = React.useState(null); - const [search, setSearch] = React.useState(''); React.useEffect(() => { fetchTopicsList({ @@ -63,7 +69,7 @@ const List: React.FC = ({ setShowInternal(!showInternal); }, [showInternal]); - const handleSearch = (value: string) => setSearch(value); + const handleSearch = (value: string) => setTopicsSearch(value); const items = showInternal ? topics : externalTopics; @@ -114,19 +120,19 @@ const List: React.FC = ({ value={TopicColumnsToSort.NAME} title="Topic Name" orderBy={orderBy} - setOrderBy={setOrderBy} + setOrderBy={setTopicsOrderBy} /> Type diff --git a/kafka-ui-react-app/src/components/Topics/List/ListContainer.ts b/kafka-ui-react-app/src/components/Topics/List/ListContainer.ts index d5d5c5f416d..029f26aea5b 100644 --- a/kafka-ui-react-app/src/components/Topics/List/ListContainer.ts +++ b/kafka-ui-react-app/src/components/Topics/List/ListContainer.ts @@ -4,12 +4,16 @@ import { fetchTopicsList, deleteTopic, clearTopicMessages, + setTopicsSearchAction, + setTopicsOrderByAction, } from 'redux/actions'; import { getTopicList, getExternalTopicList, getAreTopicsFetching, getTopicListTotalPages, + getTopicsSearch, + getTopicsOrderBy, } from 'redux/reducers/topics/selectors'; import List from './List'; @@ -19,12 +23,16 @@ const mapStateToProps = (state: RootState) => ({ topics: getTopicList(state), externalTopics: getExternalTopicList(state), totalPages: getTopicListTotalPages(state), + search: getTopicsSearch(state), + orderBy: getTopicsOrderBy(state), }); const mapDispatchToProps = { fetchTopicsList, deleteTopic, clearTopicMessages, + setTopicsSearch: setTopicsSearchAction, + setTopicsOrderBy: setTopicsOrderByAction, }; export default connect(mapStateToProps, mapDispatchToProps)(List); diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/List.spec.tsx b/kafka-ui-react-app/src/components/Topics/List/__tests__/List.spec.tsx index bcf300e0f0a..9ebfdc680dc 100644 --- a/kafka-ui-react-app/src/components/Topics/List/__tests__/List.spec.tsx +++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/List.spec.tsx @@ -24,6 +24,10 @@ describe('List', () => { fetchTopicsList={jest.fn()} deleteTopic={jest.fn()} clearTopicMessages={jest.fn()} + search="" + orderBy={null} + setTopicsSearch={jest.fn()} + setTopicsOrderBy={jest.fn()} /> @@ -52,6 +56,10 @@ describe('List', () => { fetchTopicsList={mockFetch} deleteTopic={jest.fn()} clearTopicMessages={jest.fn()} + search="" + orderBy={null} + setTopicsSearch={jest.fn()} + setTopicsOrderBy={jest.fn()} /> diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap index 8fcb04591f2..c45e088011c 100644 --- a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap +++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap @@ -30,6 +30,10 @@ exports[`List when it does not have readonly flag matches the snapshot 1`] = ` deleteTopic={[MockFunction]} externalTopics={Array []} fetchTopicsList={[MockFunction]} + orderBy={null} + search="" + setTopicsOrderBy={[MockFunction]} + setTopicsSearch={[MockFunction]} topics={Array []} totalPages={1} > @@ -141,7 +145,7 @@ exports[`List when it does not have readonly flag matches the snapshot 1`] = ` @@ -161,7 +165,7 @@ exports[`List when it does not have readonly flag matches the snapshot 1`] = ` @@ -181,7 +185,7 @@ exports[`List when it does not have readonly flag matches the snapshot 1`] = ` diff --git a/kafka-ui-react-app/src/components/common/table/SortableCulumnHeader/SortableColumnHeader.tsx b/kafka-ui-react-app/src/components/common/table/SortableCulumnHeader/SortableColumnHeader.tsx index f6e147a1020..5fc7fca68c0 100644 --- a/kafka-ui-react-app/src/components/common/table/SortableCulumnHeader/SortableColumnHeader.tsx +++ b/kafka-ui-react-app/src/components/common/table/SortableCulumnHeader/SortableColumnHeader.tsx @@ -14,18 +14,16 @@ const ListHeaderCell: React.FC = ({ title, orderBy, setOrderBy, -}) => { - return ( - setOrderBy(value)} - > - {title} - - - - - ); -}; +}) => ( + setOrderBy(value)} + > + {title} + + + + +); export default ListHeaderCell; diff --git a/kafka-ui-react-app/src/redux/actions/__test__/actions.spec.ts b/kafka-ui-react-app/src/redux/actions/__test__/actions.spec.ts index e39fdb2a9c4..4b23d249b30 100644 --- a/kafka-ui-react-app/src/redux/actions/__test__/actions.spec.ts +++ b/kafka-ui-react-app/src/redux/actions/__test__/actions.spec.ts @@ -3,6 +3,7 @@ import { schemaVersionsPayload, } from 'redux/reducers/schemas/__test__/fixtures'; import * as actions from 'redux/actions'; +import { TopicColumnsToSort } from 'generated-sources'; describe('Actions', () => { describe('fetchClusterStatsAction', () => { @@ -131,4 +132,22 @@ describe('Actions', () => { }); }); }); + + describe('setTopicsSearchAction', () => { + it('creartes SET_TOPICS_SEARCH', () => { + expect(actions.setTopicsSearchAction('test')).toEqual({ + type: 'SET_TOPICS_SEARCH', + payload: 'test', + }); + }); + }); + + describe('setTopicsOrderByAction', () => { + it('creartes SET_TOPICS_ORDER_BY', () => { + expect(actions.setTopicsOrderByAction(TopicColumnsToSort.NAME)).toEqual({ + type: 'SET_TOPICS_ORDER_BY', + payload: TopicColumnsToSort.NAME, + }); + }); + }); }); diff --git a/kafka-ui-react-app/src/redux/actions/actions.ts b/kafka-ui-react-app/src/redux/actions/actions.ts index 9b9649e2199..ac8f4503c8b 100644 --- a/kafka-ui-react-app/src/redux/actions/actions.ts +++ b/kafka-ui-react-app/src/redux/actions/actions.ts @@ -17,6 +17,7 @@ import { ConsumerGroupDetails, SchemaSubject, CompatibilityLevelCompatibilityEnum, + TopicColumnsToSort, } from 'generated-sources'; export const fetchClusterStatsAction = createAsyncAction( @@ -180,3 +181,11 @@ export const deleteConnectorAction = createAsyncAction( 'DELETE_CONNECTOR__SUCCESS', 'DELETE_CONNECTOR__FAILURE' )(); + +export const setTopicsSearchAction = createAction( + 'SET_TOPICS_SEARCH' +)(); + +export const setTopicsOrderByAction = createAction( + 'SET_TOPICS_ORDER_BY' +)(); diff --git a/kafka-ui-react-app/src/redux/interfaces/topic.ts b/kafka-ui-react-app/src/redux/interfaces/topic.ts index e2835c4b5d3..fb5278c1704 100644 --- a/kafka-ui-react-app/src/redux/interfaces/topic.ts +++ b/kafka-ui-react-app/src/redux/interfaces/topic.ts @@ -5,6 +5,7 @@ import { TopicConfig, TopicCreation, GetTopicMessagesRequest, + TopicColumnsToSort, } from 'generated-sources'; export type TopicName = Topic['name']; @@ -45,6 +46,8 @@ export interface TopicsState { allNames: TopicName[]; totalPages: number; messages: TopicMessage[]; + search: string; + orderBy: TopicColumnsToSort | null; } export type TopicFormFormattedParams = TopicCreation['configs']; diff --git a/kafka-ui-react-app/src/redux/reducers/topics/__test__/reducer.spec.ts b/kafka-ui-react-app/src/redux/reducers/topics/__test__/reducer.spec.ts index d4841b137b2..0e064db94fa 100644 --- a/kafka-ui-react-app/src/redux/reducers/topics/__test__/reducer.spec.ts +++ b/kafka-ui-react-app/src/redux/reducers/topics/__test__/reducer.spec.ts @@ -1,4 +1,10 @@ -import { deleteTopicAction, clearMessagesTopicAction } from 'redux/actions'; +import { TopicColumnsToSort } from 'generated-sources'; +import { + deleteTopicAction, + clearMessagesTopicAction, + setTopicsSearchAction, + setTopicsOrderByAction, +} from 'redux/actions'; import reducer from 'redux/reducers/topics/reducer'; const topic = { @@ -13,21 +19,44 @@ const state = { allNames: [topic.name], messages: [], totalPages: 1, + search: '', + orderBy: null, }; describe('topics reducer', () => { - it('deletes the topic from the list on DELETE_TOPIC__SUCCESS', () => { - expect(reducer(state, deleteTopicAction.success(topic.name))).toEqual({ - byName: {}, - allNames: [], - messages: [], - totalPages: 1, + describe('delete topic', () => { + it('deletes the topic from the list on DELETE_TOPIC__SUCCESS', () => { + expect(reducer(state, deleteTopicAction.success(topic.name))).toEqual({ + ...state, + byName: {}, + allNames: [], + }); + }); + + it('delete topic messages on CLEAR_TOPIC_MESSAGES__SUCCESS', () => { + expect( + reducer(state, clearMessagesTopicAction.success(topic.name)) + ).toEqual(state); + }); + }); + + describe('search topics', () => { + it('sets the search string', () => { + expect(reducer(state, setTopicsSearchAction('test'))).toEqual({ + ...state, + search: 'test', + }); }); }); - it('delete topic messages on CLEAR_TOPIC_MESSAGES__SUCCESS', () => { - expect( - reducer(state, clearMessagesTopicAction.success(topic.name)) - ).toEqual(state); + describe('order topics', () => { + it('sets the orderBy', () => { + expect( + reducer(state, setTopicsOrderByAction(TopicColumnsToSort.NAME)) + ).toEqual({ + ...state, + orderBy: TopicColumnsToSort.NAME, + }); + }); }); }); diff --git a/kafka-ui-react-app/src/redux/reducers/topics/reducer.ts b/kafka-ui-react-app/src/redux/reducers/topics/reducer.ts index 0e3f7366e3c..85e1956f749 100644 --- a/kafka-ui-react-app/src/redux/reducers/topics/reducer.ts +++ b/kafka-ui-react-app/src/redux/reducers/topics/reducer.ts @@ -8,6 +8,8 @@ export const initialState: TopicsState = { allNames: [], totalPages: 1, messages: [], + search: '', + orderBy: null, }; const transformTopicMessages = ( @@ -59,6 +61,18 @@ const reducer = (state = initialState, action: Action): TopicsState => { messages: [], }; } + case getType(actions.setTopicsSearchAction): { + return { + ...state, + search: action.payload, + }; + } + case getType(actions.setTopicsOrderByAction): { + return { + ...state, + orderBy: action.payload, + }; + } default: return state; } diff --git a/kafka-ui-react-app/src/redux/reducers/topics/selectors.ts b/kafka-ui-react-app/src/redux/reducers/topics/selectors.ts index 1da0cf9110a..cb815c835ba 100644 --- a/kafka-ui-react-app/src/redux/reducers/topics/selectors.ts +++ b/kafka-ui-react-app/src/redux/reducers/topics/selectors.ts @@ -123,3 +123,13 @@ export const getTopicConfigByParamName = createSelector( return byParamName; } ); + +export const getTopicsSearch = createSelector( + topicsState, + (state) => state.search +); + +export const getTopicsOrderBy = createSelector( + topicsState, + (state) => state.orderBy +); From 31f908528c01a1cb42f676193e6464405a382b4c Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 12 May 2021 09:57:13 +0300 Subject: [PATCH 11/13] Show search value in the input field --- kafka-ui-react-app/src/components/Topics/List/List.tsx | 1 + .../List/__tests__/__snapshots__/List.spec.tsx.snap | 2 ++ .../src/components/common/Search/Search.tsx | 10 ++++++++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/kafka-ui-react-app/src/components/Topics/List/List.tsx b/kafka-ui-react-app/src/components/Topics/List/List.tsx index 7172c9fbb7e..99784bb869f 100644 --- a/kafka-ui-react-app/src/components/Topics/List/List.tsx +++ b/kafka-ui-react-app/src/components/Topics/List/List.tsx @@ -95,6 +95,7 @@ const List: React.FC = ({
diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap index c45e088011c..27706ba2831 100644 --- a/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap +++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/__snapshots__/List.spec.tsx.snap @@ -91,12 +91,14 @@ exports[`List when it does not have readonly flag matches the snapshot 1`] = `

void; - placeholder: string; + placeholder?: string; + value: string; } -const Search: React.FC = ({ handleSearch, placeholder }) => { +const Search: React.FC = ({ + handleSearch, + placeholder = 'Search', + value, +}) => { const onChange = useDebouncedCallback( (e) => handleSearch(e.target.value), 300 @@ -18,6 +23,7 @@ const Search: React.FC = ({ handleSearch, placeholder }) => { type="text" placeholder={placeholder} onChange={onChange} + defaultValue={value} /> From bc820211911e458394496aa01d6d415477e2e054 Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 12 May 2021 10:24:30 +0300 Subject: [PATCH 12/13] Add tests for the Search component --- .../common/Search/__tests__/Search.spec.tsx | 26 +++++++++++++++++++ .../__snapshots__/Search.spec.tsx.snap | 22 ++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 kafka-ui-react-app/src/components/common/Search/__tests__/Search.spec.tsx create mode 100644 kafka-ui-react-app/src/components/common/Search/__tests__/__snapshots__/Search.spec.tsx.snap diff --git a/kafka-ui-react-app/src/components/common/Search/__tests__/Search.spec.tsx b/kafka-ui-react-app/src/components/common/Search/__tests__/Search.spec.tsx new file mode 100644 index 00000000000..56dba40c394 --- /dev/null +++ b/kafka-ui-react-app/src/components/common/Search/__tests__/Search.spec.tsx @@ -0,0 +1,26 @@ +import { shallow } from 'enzyme'; +import Search from 'components/common/Search/Search'; +import React from 'react'; + +jest.mock('use-debounce', () => ({ + useDebouncedCallback: (fn: (e: Event) => void) => fn, +})); + +describe('Search', () => { + const handleSearch = jest.fn(); + const component = shallow( + + ); + it('matches the snapshot', () => { + expect(component).toMatchSnapshot(); + }); + + it('calls handleSearch on input', () => { + component.find('input').simulate('change', { target: { value: 'test' } }); + expect(handleSearch).toHaveBeenCalledTimes(1); + }); +}); diff --git a/kafka-ui-react-app/src/components/common/Search/__tests__/__snapshots__/Search.spec.tsx.snap b/kafka-ui-react-app/src/components/common/Search/__tests__/__snapshots__/Search.spec.tsx.snap new file mode 100644 index 00000000000..a3fc0be27ad --- /dev/null +++ b/kafka-ui-react-app/src/components/common/Search/__tests__/__snapshots__/Search.spec.tsx.snap @@ -0,0 +1,22 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Search matches the snapshot 1`] = ` +

+ + + + +

+`; From ac757f0ee91d3585bf739b552e03e4c5bf155a5f Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 12 May 2021 10:36:20 +0300 Subject: [PATCH 13/13] Test Search placeholder default value --- .../common/Search/__tests__/Search.spec.tsx | 19 ++++++++++---- .../__snapshots__/Search.spec.tsx.snap | 25 +++++++++++++++++-- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/kafka-ui-react-app/src/components/common/Search/__tests__/Search.spec.tsx b/kafka-ui-react-app/src/components/common/Search/__tests__/Search.spec.tsx index 56dba40c394..017a30c8feb 100644 --- a/kafka-ui-react-app/src/components/common/Search/__tests__/Search.spec.tsx +++ b/kafka-ui-react-app/src/components/common/Search/__tests__/Search.spec.tsx @@ -8,19 +8,28 @@ jest.mock('use-debounce', () => ({ describe('Search', () => { const handleSearch = jest.fn(); - const component = shallow( + let component = shallow( ); - it('matches the snapshot', () => { - expect(component).toMatchSnapshot(); - }); - it('calls handleSearch on input', () => { component.find('input').simulate('change', { target: { value: 'test' } }); expect(handleSearch).toHaveBeenCalledTimes(1); }); + + describe('when placeholder is provided', () => { + it('matches the snapshot', () => { + expect(component).toMatchSnapshot(); + }); + }); + + describe('when placeholder is not provided', () => { + component = shallow(); + it('matches the snapshot', () => { + expect(component).toMatchSnapshot(); + }); + }); }); diff --git a/kafka-ui-react-app/src/components/common/Search/__tests__/__snapshots__/Search.spec.tsx.snap b/kafka-ui-react-app/src/components/common/Search/__tests__/__snapshots__/Search.spec.tsx.snap index a3fc0be27ad..c2026138ee0 100644 --- a/kafka-ui-react-app/src/components/common/Search/__tests__/__snapshots__/Search.spec.tsx.snap +++ b/kafka-ui-react-app/src/components/common/Search/__tests__/__snapshots__/Search.spec.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Search matches the snapshot 1`] = ` +exports[`Search when placeholder is not provided matches the snapshot 1`] = `

@@ -8,7 +8,28 @@ exports[`Search matches the snapshot 1`] = ` className="input" defaultValue="" onChange={[Function]} - placeholder="Search bt the Topic name" + placeholder="Search" + type="text" + /> + + + +

+`; + +exports[`Search when placeholder is provided matches the snapshot 1`] = ` +

+