Pārlūkot izejas kodu

Get back messages pagination (#2862)

* initial messages pagination , modification in v2

add minor test in messages table component

Add Messages pagination

add next feature in the Advanced Filters

Messages Table add Back button navigation

remove un-necessary code from the messages table

minor code refactors to remove unnecessary codes

page parameter fix in the query of the advanced Filters

minor change in the test file to keep uniformity of the project

set default table limit for first request messages v1

* pagination fix the initial disable
Mgrdich 2 gadi atpakaļ
vecāks
revīzija
33784e3e83

+ 4 - 0
kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/Filters.tsx

@@ -93,6 +93,8 @@ const Filters: React.FC<FiltersProps> = ({
   const navigate = useNavigate();
   const [searchParams] = useSearchParams();
 
+  const page = searchParams.get('page');
+
   const { data: topic } = useTopicDetails({ clusterName, topicName });
 
   const partitions = topic?.partitions || [];
@@ -202,6 +204,7 @@ const Filters: React.FC<FiltersProps> = ({
       filterQueryType: queryType,
       attempt: nextAttempt,
       limit: PER_PAGE,
+      page: page || 0,
       seekDirection,
       keySerde: keySerde || (searchParams.get('keySerde') as string),
       valueSerde: valueSerde || (searchParams.get('valueSerde') as string),
@@ -392,6 +395,7 @@ const Filters: React.FC<FiltersProps> = ({
     timestamp,
     query,
     seekDirection,
+    page,
   ]);
 
   React.useEffect(() => {

+ 4 - 0
kafka-ui-react-app/src/components/Topics/Topic/Messages/Messages.tsx

@@ -6,6 +6,7 @@ import { useSerdes } from 'lib/hooks/api/topicMessages';
 import useAppParams from 'lib/hooks/useAppParams';
 import { RouteParamsClusterTopic } from 'lib/paths';
 import { getDefaultSerdeName } from 'components/Topics/Topic/Messages/getDefaultSerdeName';
+import { MESSAGES_PER_PAGE } from 'lib/constants';
 
 import MessagesTable from './MessagesTable';
 import FiltersContainer from './Filters/FiltersContainer';
@@ -47,6 +48,9 @@ const Messages: React.FC = () => {
     if (!searchParams.get('valueSerde')) {
       searchParams.set('valueSerde', getDefaultSerdeName(serdes.value || []));
     }
+    if (!searchParams.get('limit')) {
+      searchParams.set('limit', MESSAGES_PER_PAGE);
+    }
     setSearchParams(searchParams);
   }, [serdes]);
 

+ 113 - 61
kafka-ui-react-app/src/components/Topics/Topic/Messages/MessagesTable.tsx

@@ -9,6 +9,10 @@ import {
 } from 'redux/reducers/topicMessages/selectors';
 import TopicMessagesContext from 'components/contexts/TopicMessagesContext';
 import { useAppSelector } from 'lib/hooks/redux';
+import { Button } from 'components/common/Button/Button';
+import { useSearchParams } from 'react-router-dom';
+import { MESSAGES_PER_PAGE } from 'lib/constants';
+import * as S from 'components/common/NewTable/Table.styled';
 
 import PreviewModal from './PreviewModal';
 import Message, { PreviewFilter } from './Message';
@@ -19,75 +23,123 @@ const MessagesTable: React.FC = () => {
   const [keyFilters, setKeyFilters] = useState<PreviewFilter[]>([]);
   const [contentFilters, setContentFilters] = useState<PreviewFilter[]>([]);
 
+  const [searchParams, setSearchParams] = useSearchParams();
+  const page = searchParams.get('page');
   const { isLive } = useContext(TopicMessagesContext);
 
   const messages = useAppSelector(getTopicMessges);
   const isFetching = useAppSelector(getIsTopicMessagesFetching);
+
+  const isTailing = isLive && isFetching;
+
+  // Pagination is disabled in live mode, also we don't want to show the button
+  // if we are fetching the messages or if we are at the end of the topic
+  const isPaginationDisabled = isTailing || isFetching;
+
+  const isNextPageButtonDisabled =
+    isPaginationDisabled || messages.length < Number(MESSAGES_PER_PAGE);
+  const isPrevPageButtonDisabled =
+    isPaginationDisabled || !Number(searchParams.get('page'));
+
+  const handleNextPage = () => {
+    searchParams.set('page', String(Number(page || 0) + 1));
+    setSearchParams(searchParams);
+  };
+
+  const handlePrevPage = () => {
+    searchParams.set('page', String(Number(page || 0) - 1));
+    setSearchParams(searchParams);
+  };
+
   return (
-    <Table isFullwidth>
-      <thead>
-        <tr>
-          <TableHeaderCell> </TableHeaderCell>
-          <TableHeaderCell title="Offset" />
-          <TableHeaderCell title="Partition" />
-          <TableHeaderCell title="Timestamp" />
-          <TableHeaderCell
-            title="Key"
-            previewText={`Preview ${
-              keyFilters.length ? `(${keyFilters.length} selected)` : ''
-            }`}
-            onPreview={() => setPreviewFor('key')}
-          />
-          <TableHeaderCell
-            title="Value"
-            previewText={`Preview ${
-              contentFilters.length ? `(${contentFilters.length} selected)` : ''
-            }`}
-            onPreview={() => setPreviewFor('content')}
-          />
-          <TableHeaderCell> </TableHeaderCell>
+    <>
+      <Table isFullwidth>
+        <thead>
+          <tr>
+            <TableHeaderCell> </TableHeaderCell>
+            <TableHeaderCell title="Offset" />
+            <TableHeaderCell title="Partition" />
+            <TableHeaderCell title="Timestamp" />
+            <TableHeaderCell
+              title="Key"
+              previewText={`Preview ${
+                keyFilters.length ? `(${keyFilters.length} selected)` : ''
+              }`}
+              onPreview={() => setPreviewFor('key')}
+            />
+            <TableHeaderCell
+              title="Value"
+              previewText={`Preview ${
+                contentFilters.length
+                  ? `(${contentFilters.length} selected)`
+                  : ''
+              }`}
+              onPreview={() => setPreviewFor('content')}
+            />
+            <TableHeaderCell> </TableHeaderCell>
 
-          {previewFor !== null && (
-            <PreviewModal
-              values={previewFor === 'key' ? keyFilters : contentFilters}
-              toggleIsOpen={() => setPreviewFor(null)}
-              setFilters={(payload: PreviewFilter[]) =>
-                previewFor === 'key'
-                  ? setKeyFilters(payload)
-                  : setContentFilters(payload)
-              }
+            {previewFor !== null && (
+              <PreviewModal
+                values={previewFor === 'key' ? keyFilters : contentFilters}
+                toggleIsOpen={() => setPreviewFor(null)}
+                setFilters={(payload: PreviewFilter[]) =>
+                  previewFor === 'key'
+                    ? setKeyFilters(payload)
+                    : setContentFilters(payload)
+                }
+              />
+            )}
+          </tr>
+        </thead>
+        <tbody>
+          {messages.map((message: TopicMessage) => (
+            <Message
+              key={[
+                message.offset,
+                message.timestamp,
+                message.key,
+                message.partition,
+              ].join('-')}
+              message={message}
+              keyFilters={keyFilters}
+              contentFilters={contentFilters}
             />
+          ))}
+          {isFetching && isLive && !messages.length && (
+            <tr>
+              <td colSpan={10}>
+                <PageLoader />
+              </td>
+            </tr>
           )}
-        </tr>
-      </thead>
-      <tbody>
-        {messages.map((message: TopicMessage) => (
-          <Message
-            key={[
-              message.offset,
-              message.timestamp,
-              message.key,
-              message.partition,
-            ].join('-')}
-            message={message}
-            keyFilters={keyFilters}
-            contentFilters={contentFilters}
-          />
-        ))}
-        {isFetching && isLive && !messages.length && (
-          <tr>
-            <td colSpan={10}>
-              <PageLoader />
-            </td>
-          </tr>
-        )}
-        {messages.length === 0 && !isFetching && (
-          <tr>
-            <td colSpan={10}>No messages found</td>
-          </tr>
-        )}
-      </tbody>
-    </Table>
+          {messages.length === 0 && !isFetching && (
+            <tr>
+              <td colSpan={10}>No messages found</td>
+            </tr>
+          )}
+        </tbody>
+      </Table>
+      <S.Pagination>
+        <S.Pages>
+          <Button
+            buttonType="secondary"
+            buttonSize="L"
+            disabled={isPrevPageButtonDisabled}
+            onClick={handlePrevPage}
+          >
+            ← Back
+          </Button>
+          <Button
+            buttonType="secondary"
+            buttonSize="L"
+            disabled={isNextPageButtonDisabled}
+            onClick={handleNextPage}
+          >
+            Next →
+          </Button>
+        </S.Pages>
+      </S.Pagination>
+    </>
   );
 };
 

+ 3 - 2
kafka-ui-react-app/src/components/Topics/Topic/Messages/__test__/MessagesTable.spec.tsx

@@ -85,9 +85,10 @@ describe('MessagesTable', () => {
   });
 
   describe('Custom Setup with different props value', () => {
-    it('should check if next click is gone during isLive Param', () => {
+    it('should check if next button and previous is disabled isLive Param', () => {
       setUpComponent(searchParams, { ...contextValue, isLive: true });
-      expect(screen.queryByText(/next/i)).not.toBeInTheDocument();
+      expect(screen.queryByText(/next/i)).toBeDisabled();
+      expect(screen.queryByText(/back/i)).toBeDisabled();
     });
 
     it('should check the display of the loader element', () => {