From 160a6595787451bda0d8e28c7899134e69f415cd Mon Sep 17 00:00:00 2001 From: Renat Kalimulin Date: Thu, 20 Jun 2024 12:30:01 +0300 Subject: [PATCH 1/3] FE: Topics: Save field previews into local storage Resolves: #439 --- .../Topics/Topic/Messages/MessagesTable.tsx | 56 ++++++++++++++++--- frontend/src/lib/hooks/useLocalStorage.ts | 9 ++- 2 files changed, 53 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx b/frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx index 813cabcfa..d92e6b2a0 100644 --- a/frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx +++ b/frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx @@ -2,31 +2,53 @@ import PageLoader from 'components/common/PageLoader/PageLoader'; import { Table } from 'components/common/table/Table/Table.styled'; import TableHeaderCell from 'components/common/table/TableHeaderCell/TableHeaderCell'; import { TopicMessage } from 'generated-sources'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { Button } from 'components/common/Button/Button'; import * as S from 'components/common/NewTable/Table.styled'; import { usePaginateTopics, useIsLiveMode } from 'lib/hooks/useMessagesFilters'; import { useMessageFiltersStore } from 'lib/hooks/useMessageFiltersStore'; +import useAppParams from 'lib/hooks/useAppParams'; +import { RouteParamsClusterTopic } from 'lib/paths'; +import { useLocalStorage } from 'lib/hooks/useLocalStorage'; -import PreviewModal from './PreviewModal'; import Message, { PreviewFilter } from './Message'; +import PreviewModal from './PreviewModal'; export interface MessagesTableProps { messages: TopicMessage[]; isFetching: boolean; } +interface MessagePreviewProps { + [key: string]: { + keyFilters: PreviewFilter[]; + contentFilters: PreviewFilter[]; + }; +} + const MessagesTable: React.FC = ({ messages, isFetching, }) => { const paginate = usePaginateTopics(); - const [previewFor, setPreviewFor] = useState(null); - + const [previewFor, setPreviewFor] = useState<'key' | 'content' | null>(null); const [keyFilters, setKeyFilters] = useState([]); const [contentFilters, setContentFilters] = useState([]); const nextCursor = useMessageFiltersStore((state) => state.nextCursor); const isLive = useIsLiveMode(); + const { topicName } = useAppParams(); + const [messagesPreview, setMessagesPreview] = + useLocalStorage('message-preview', { + [topicName]: { + keyFilters: [], + contentFilters: [], + }, + }); + + useEffect(() => { + setKeyFilters(messagesPreview[topicName]?.keyFilters || []); + setContentFilters(messagesPreview[topicName]?.contentFilters || []); + }, []); return (
@@ -34,11 +56,27 @@ const MessagesTable: React.FC = ({ setPreviewFor(null)} - setFilters={(payload: PreviewFilter[]) => - previewFor === 'key' - ? setKeyFilters(payload) - : setContentFilters(payload) - } + setFilters={(payload: PreviewFilter[]) => { + if (previewFor === 'key') { + setKeyFilters(payload); + setMessagesPreview({ + ...messagesPreview, + [topicName]: { + ...messagesPreview[topicName], + keyFilters: payload, + }, + }); + } else { + setContentFilters(payload); + setMessagesPreview({ + ...messagesPreview, + [topicName]: { + ...messagesPreview[topicName], + contentFilters: payload, + }, + }); + } + }} /> )} diff --git a/frontend/src/lib/hooks/useLocalStorage.ts b/frontend/src/lib/hooks/useLocalStorage.ts index d8945620d..65215fd2c 100644 --- a/frontend/src/lib/hooks/useLocalStorage.ts +++ b/frontend/src/lib/hooks/useLocalStorage.ts @@ -1,9 +1,12 @@ import { LOCAL_STORAGE_KEY_PREFIX } from 'lib/constants'; -import { useState, useEffect } from 'react'; +import { useState, useEffect, Dispatch, SetStateAction } from 'react'; -export const useLocalStorage = (featureKey: string, defaultValue: string) => { +export const useLocalStorage = ( + featureKey: string, + defaultValue: T +): [T, Dispatch>] => { const key = `${LOCAL_STORAGE_KEY_PREFIX}-${featureKey}`; - const [value, setValue] = useState(() => { + const [value, setValue] = useState(() => { const saved = localStorage.getItem(key); if (saved !== null) { From 90b0073a47f9b09021f79f0d06cbb7dcfde63137 Mon Sep 17 00:00:00 2001 From: Renat Kalimulin Date: Thu, 20 Jun 2024 13:57:53 +0300 Subject: [PATCH 2/3] added tests --- .../Messages/__test__/MessagesTable.spec.tsx | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/frontend/src/components/Topics/Topic/Messages/__test__/MessagesTable.spec.tsx b/frontend/src/components/Topics/Topic/Messages/__test__/MessagesTable.spec.tsx index 808dde9e4..77f31cebc 100644 --- a/frontend/src/components/Topics/Topic/Messages/__test__/MessagesTable.spec.tsx +++ b/frontend/src/components/Topics/Topic/Messages/__test__/MessagesTable.spec.tsx @@ -7,6 +7,8 @@ import MessagesTable, { } from 'components/Topics/Topic/Messages/MessagesTable'; import { TopicMessage, TopicMessageTimestampTypeEnum } from 'generated-sources'; import { useIsLiveMode } from 'lib/hooks/useMessagesFilters'; +import useAppParams from 'lib/hooks/useAppParams'; +import { LOCAL_STORAGE_KEY_PREFIX } from 'lib/constants'; export const topicMessagePayload: TopicMessage = { partition: 29, @@ -33,8 +35,16 @@ jest.mock('lib/hooks/useMessagesFilters', () => ({ usePaginateTopics: jest.fn(), })); +jest.mock('lib/hooks/useAppParams', () => ({ + __esModule: true, + default: jest.fn(), +})); + describe('MessagesTable', () => { const renderComponent = (props?: Partial) => { + (useAppParams as jest.Mock).mockImplementation(() => ({ + topicName: 'testTopic', + })); return render( ); @@ -99,4 +109,34 @@ describe('MessagesTable', () => { } }); }); + + describe('should save messages preview into localstorage', () => { + beforeEach(() => { + renderComponent({ messages: mockTopicsMessages, isFetching: false }); + }); + + it('should save messages preview into localstorage', async () => { + const previewButtons = screen.getAllByText('Preview'); + await userEvent.click(previewButtons[0]); + await userEvent.type(screen.getByPlaceholderText('Field'), 'test1'); + await userEvent.type(screen.getByPlaceholderText('Json Path'), 'test2'); + await userEvent.click(screen.getByText('Save')); + await userEvent.click(previewButtons[1]); + await userEvent.type(screen.getByPlaceholderText('Field'), 'test3'); + await userEvent.type(screen.getByPlaceholderText('Json Path'), 'test4'); + await userEvent.click(screen.getByText('Save')); + expect( + global.localStorage.getItem( + `${LOCAL_STORAGE_KEY_PREFIX}-message-preview` + ) + ).toEqual( + JSON.stringify({ + testTopic: { + keyFilters: [{ field: 'test1', path: 'test2' }], + contentFilters: [{ field: 'test3', path: 'test4' }], + }, + }) + ); + }); + }); }); From 06f3368fd6741d981415aee96c3bedbf4df24dac Mon Sep 17 00:00:00 2001 From: Renat Kalimulin Date: Wed, 10 Jul 2024 15:57:47 +0300 Subject: [PATCH 3/3] fixes after review --- .../Topics/Topic/Messages/MessagesTable.tsx | 49 ++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx b/frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx index d92e6b2a0..1751a0e56 100644 --- a/frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx +++ b/frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx @@ -2,7 +2,7 @@ import PageLoader from 'components/common/PageLoader/PageLoader'; import { Table } from 'components/common/table/Table/Table.styled'; import TableHeaderCell from 'components/common/table/TableHeaderCell/TableHeaderCell'; import { TopicMessage } from 'generated-sources'; -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { Button } from 'components/common/Button/Button'; import * as S from 'components/common/NewTable/Table.styled'; import { usePaginateTopics, useIsLiveMode } from 'lib/hooks/useMessagesFilters'; @@ -50,33 +50,38 @@ const MessagesTable: React.FC = ({ setContentFilters(messagesPreview[topicName]?.contentFilters || []); }, []); + const setFilters = useCallback( + (payload: PreviewFilter[]) => { + if (previewFor === 'key') { + setKeyFilters(payload); + setMessagesPreview({ + ...messagesPreview, + [topicName]: { + ...messagesPreview[topicName], + keyFilters: payload, + }, + }); + } else { + setContentFilters(payload); + setMessagesPreview({ + ...messagesPreview, + [topicName]: { + ...messagesPreview[topicName], + contentFilters: payload, + }, + }); + } + }, + [previewFor, messagesPreview, topicName] + ); + return (
{previewFor !== null && ( setPreviewFor(null)} - setFilters={(payload: PreviewFilter[]) => { - if (previewFor === 'key') { - setKeyFilters(payload); - setMessagesPreview({ - ...messagesPreview, - [topicName]: { - ...messagesPreview[topicName], - keyFilters: payload, - }, - }); - } else { - setContentFilters(payload); - setMessagesPreview({ - ...messagesPreview, - [topicName]: { - ...messagesPreview[topicName], - contentFilters: payload, - }, - }); - } - }} + setFilters={setFilters} /> )}