Separating ksqlDb tables and streams by tabs (#2145)
* separating ksqlDb tables and streams by tabs * adding tests
This commit is contained in:
parent
41fd765d83
commit
0528c3a28f
12 changed files with 239 additions and 135 deletions
|
@ -9,7 +9,7 @@ const KsqlDb: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route
|
<Route
|
||||||
index
|
path="/*"
|
||||||
element={
|
element={
|
||||||
<BreadcrumbRoute>
|
<BreadcrumbRoute>
|
||||||
<List />
|
<List />
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PageLoader from 'components/common/PageLoader/PageLoader';
|
||||||
|
import { KsqlStreamDescription, KsqlTableDescription } from 'generated-sources';
|
||||||
|
import { useTableState } from 'lib/hooks/useTableState';
|
||||||
|
import { SmartTable } from 'components/common/SmartTable/SmartTable';
|
||||||
|
import { TableColumn } from 'components/common/SmartTable/TableColumn';
|
||||||
|
import { KsqlDescription } from 'redux/interfaces/ksqlDb';
|
||||||
|
import { ksqlRowData } from 'components/KsqlDb/List/KsqlDbItem/utils/ksqlRowData';
|
||||||
|
|
||||||
|
export enum KsqlDbItemType {
|
||||||
|
Tables = 'tables',
|
||||||
|
Streams = 'streams',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RowsType {
|
||||||
|
tables: KsqlTableDescription[];
|
||||||
|
streams: KsqlStreamDescription[];
|
||||||
|
}
|
||||||
|
export interface KsqlDbItemProps {
|
||||||
|
type: KsqlDbItemType;
|
||||||
|
fetching: boolean;
|
||||||
|
rows: RowsType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type KsqlDescriptionAccessor = keyof KsqlDescription;
|
||||||
|
|
||||||
|
export interface HeadersType {
|
||||||
|
Header: string;
|
||||||
|
accessor: KsqlDescriptionAccessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface KsqlTableState {
|
||||||
|
name: string;
|
||||||
|
topic: string;
|
||||||
|
keyFormat: string;
|
||||||
|
valueFormat: string;
|
||||||
|
isWindowed: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const headers: HeadersType[] = [
|
||||||
|
{ Header: 'Name', accessor: 'name' },
|
||||||
|
{ Header: 'Topic', accessor: 'topic' },
|
||||||
|
{ Header: 'Key Format', accessor: 'keyFormat' },
|
||||||
|
{ Header: 'Value Format', accessor: 'valueFormat' },
|
||||||
|
{ Header: 'Is Windowed', accessor: 'isWindowed' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const KsqlDbItem: React.FC<KsqlDbItemProps> = ({ type, fetching, rows }) => {
|
||||||
|
const preparedRows = rows[type]?.map(ksqlRowData) || [];
|
||||||
|
const tableState = useTableState<KsqlTableState, string>(preparedRows, {
|
||||||
|
idSelector: ({ name }) => name,
|
||||||
|
totalPages: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (fetching) {
|
||||||
|
return <PageLoader />;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<SmartTable
|
||||||
|
tableState={tableState}
|
||||||
|
isFullwidth
|
||||||
|
placeholder="No tables or streams found"
|
||||||
|
hoverable
|
||||||
|
>
|
||||||
|
<TableColumn title="Name" field="name" />
|
||||||
|
<TableColumn title="Topic" field="topic" />
|
||||||
|
<TableColumn title="Key Format" field="keyFormat" />
|
||||||
|
<TableColumn title="Value Format" field="valueFormat" />
|
||||||
|
<TableColumn title="Is Windowed" field="isWindowed" />
|
||||||
|
</SmartTable>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default KsqlDbItem;
|
|
@ -0,0 +1,66 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { render, WithRoute } from 'lib/testHelpers';
|
||||||
|
import { clusterKsqlDbTablesPath } from 'lib/paths';
|
||||||
|
import KsqlDbItem, {
|
||||||
|
KsqlDbItemProps,
|
||||||
|
KsqlDbItemType,
|
||||||
|
} from 'components/KsqlDb/List/KsqlDbItem/KsqlDbItem';
|
||||||
|
import { screen } from '@testing-library/dom';
|
||||||
|
import { fetchKsqlDbTablesPayload } from 'redux/reducers/ksqlDb/__test__/fixtures';
|
||||||
|
|
||||||
|
describe('KsqlDbItem', () => {
|
||||||
|
const tablesPathname = clusterKsqlDbTablesPath();
|
||||||
|
|
||||||
|
const component = (props: Partial<KsqlDbItemProps> = {}) => (
|
||||||
|
<WithRoute path={tablesPathname}>
|
||||||
|
<KsqlDbItem
|
||||||
|
type={KsqlDbItemType.Tables}
|
||||||
|
fetching={false}
|
||||||
|
rows={{ tables: [], streams: [] }}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</WithRoute>
|
||||||
|
);
|
||||||
|
|
||||||
|
it('renders progressbar when fetching tables and streams', () => {
|
||||||
|
render(component({ fetching: true }), {
|
||||||
|
initialEntries: [clusterKsqlDbTablesPath()],
|
||||||
|
});
|
||||||
|
expect(screen.getByRole('progressbar')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
it('show no text if no data found', () => {
|
||||||
|
render(component({}), {
|
||||||
|
initialEntries: [clusterKsqlDbTablesPath()],
|
||||||
|
});
|
||||||
|
expect(screen.getByText('No tables or streams found')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
it('renders with tables', () => {
|
||||||
|
render(
|
||||||
|
component({
|
||||||
|
rows: {
|
||||||
|
tables: fetchKsqlDbTablesPayload.tables,
|
||||||
|
streams: [],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
initialEntries: [clusterKsqlDbTablesPath()],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
expect(screen.getByRole('table').querySelectorAll('td')).toHaveLength(10);
|
||||||
|
});
|
||||||
|
it('renders with streams', () => {
|
||||||
|
render(
|
||||||
|
component({
|
||||||
|
type: KsqlDbItemType.Streams,
|
||||||
|
rows: {
|
||||||
|
tables: [],
|
||||||
|
streams: fetchKsqlDbTablesPayload.streams,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
initialEntries: [clusterKsqlDbTablesPath()],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
expect(screen.getByRole('table').querySelectorAll('td')).toHaveLength(10);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { KsqlDescription } from 'redux/interfaces/ksqlDb';
|
||||||
|
import { KsqlTableState } from 'components/KsqlDb/List/KsqlDbItem/KsqlDbItem';
|
||||||
|
|
||||||
|
export const ksqlRowData = (data: KsqlDescription): KsqlTableState => {
|
||||||
|
return {
|
||||||
|
name: data.name || '',
|
||||||
|
topic: data.topic || '',
|
||||||
|
keyFormat: data.keyFormat || '',
|
||||||
|
valueFormat: data.valueFormat || '',
|
||||||
|
isWindowed: 'isWindowed' in data ? String(data.isWindowed) : '-',
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,44 +1,32 @@
|
||||||
import React, { FC, useEffect } from 'react';
|
import React, { FC } from 'react';
|
||||||
import useAppParams from 'lib/hooks/useAppParams';
|
import useAppParams from 'lib/hooks/useAppParams';
|
||||||
import * as Metrics from 'components/common/Metrics';
|
import * as Metrics from 'components/common/Metrics';
|
||||||
import PageLoader from 'components/common/PageLoader/PageLoader';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
import ListItem from 'components/KsqlDb/List/ListItem';
|
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
|
||||||
import { fetchKsqlDbTables } from 'redux/reducers/ksqlDb/ksqlDbSlice';
|
|
||||||
import { getKsqlDbTables } from 'redux/reducers/ksqlDb/selectors';
|
import { getKsqlDbTables } from 'redux/reducers/ksqlDb/selectors';
|
||||||
import { clusterKsqlDbQueryRelativePath, ClusterNameRoute } from 'lib/paths';
|
import {
|
||||||
|
clusterKsqlDbQueryRelativePath,
|
||||||
|
ClusterNameRoute,
|
||||||
|
clusterKsqlDbStreamsPath,
|
||||||
|
clusterKsqlDbTablesPath,
|
||||||
|
clusterKsqlDbStreamsRelativePath,
|
||||||
|
clusterKsqlDbTablesRelativePath,
|
||||||
|
} from 'lib/paths';
|
||||||
import PageHeading from 'components/common/PageHeading/PageHeading';
|
import PageHeading from 'components/common/PageHeading/PageHeading';
|
||||||
import { Table } from 'components/common/table/Table/Table.styled';
|
|
||||||
import TableHeaderCell from 'components/common/table/TableHeaderCell/TableHeaderCell';
|
|
||||||
import { Button } from 'components/common/Button/Button';
|
import { Button } from 'components/common/Button/Button';
|
||||||
import { KsqlDescription } from 'redux/interfaces/ksqlDb';
|
import Navbar from 'components/common/Navigation/Navbar.styled';
|
||||||
|
import { NavLink, Route, Routes, Navigate } from 'react-router-dom';
|
||||||
|
import { fetchKsqlDbTables } from 'redux/reducers/ksqlDb/ksqlDbSlice';
|
||||||
|
|
||||||
export type KsqlDescriptionAccessor = keyof KsqlDescription;
|
import KsqlDbItem, { KsqlDbItemType } from './KsqlDbItem/KsqlDbItem';
|
||||||
|
|
||||||
interface HeadersType {
|
|
||||||
Header: string;
|
|
||||||
accessor: KsqlDescriptionAccessor;
|
|
||||||
}
|
|
||||||
const headers: HeadersType[] = [
|
|
||||||
{ Header: 'Type', accessor: 'type' },
|
|
||||||
{ Header: 'Name', accessor: 'name' },
|
|
||||||
{ Header: 'Topic', accessor: 'topic' },
|
|
||||||
{ Header: 'Key Format', accessor: 'keyFormat' },
|
|
||||||
{ Header: 'Value Format', accessor: 'valueFormat' },
|
|
||||||
{ Header: 'Is Windowed', accessor: 'isWindowed' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const accessors = headers.map((header) => header.accessor);
|
|
||||||
|
|
||||||
const List: FC = () => {
|
const List: FC = () => {
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
const { clusterName } = useAppParams<ClusterNameRoute>();
|
const { clusterName } = useAppParams<ClusterNameRoute>();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const { rows, fetching, tablesCount, streamsCount } =
|
const { rows, fetching, tablesCount, streamsCount } =
|
||||||
useSelector(getKsqlDbTables);
|
useSelector(getKsqlDbTables);
|
||||||
|
|
||||||
useEffect(() => {
|
React.useEffect(() => {
|
||||||
dispatch(fetchKsqlDbTables(clusterName));
|
dispatch(fetchKsqlDbTables(clusterName));
|
||||||
}, [clusterName, dispatch]);
|
}, [clusterName, dispatch]);
|
||||||
|
|
||||||
|
@ -68,31 +56,48 @@ const List: FC = () => {
|
||||||
</Metrics.Section>
|
</Metrics.Section>
|
||||||
</Metrics.Wrapper>
|
</Metrics.Wrapper>
|
||||||
<div>
|
<div>
|
||||||
{fetching ? (
|
<Navbar role="navigation">
|
||||||
<PageLoader />
|
<NavLink
|
||||||
) : (
|
to={clusterKsqlDbTablesPath(clusterName)}
|
||||||
<Table isFullwidth>
|
className={({ isActive }) => (isActive ? 'is-active' : '')}
|
||||||
<thead>
|
end
|
||||||
<tr>
|
>
|
||||||
{headers.map(({ Header, accessor }) => (
|
Tables
|
||||||
<TableHeaderCell title={Header} key={accessor} />
|
</NavLink>
|
||||||
))}
|
<NavLink
|
||||||
</tr>
|
to={clusterKsqlDbStreamsPath(clusterName)}
|
||||||
</thead>
|
className={({ isActive }) => (isActive ? 'is-active' : '')}
|
||||||
<tbody>
|
end
|
||||||
{rows.map((row) => (
|
>
|
||||||
<ListItem key={row.name} accessors={accessors} data={row} />
|
Streams
|
||||||
))}
|
</NavLink>
|
||||||
{rows.length === 0 && (
|
</Navbar>
|
||||||
<tr>
|
<Routes>
|
||||||
<td colSpan={headers.length + 1}>
|
<Route
|
||||||
No tables or streams found
|
index
|
||||||
</td>
|
element={<Navigate to={clusterKsqlDbTablesRelativePath} />}
|
||||||
</tr>
|
/>
|
||||||
)}
|
<Route
|
||||||
</tbody>
|
path={clusterKsqlDbTablesRelativePath}
|
||||||
</Table>
|
element={
|
||||||
)}
|
<KsqlDbItem
|
||||||
|
type={KsqlDbItemType.Tables}
|
||||||
|
fetching={fetching}
|
||||||
|
rows={rows}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path={clusterKsqlDbStreamsRelativePath}
|
||||||
|
element={
|
||||||
|
<KsqlDbItem
|
||||||
|
type={KsqlDbItemType.Streams}
|
||||||
|
fetching={fetching}
|
||||||
|
rows={rows}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { KsqlDescription } from 'redux/interfaces/ksqlDb';
|
|
||||||
import { KsqlDescriptionAccessor } from 'components/KsqlDb/List/List';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
accessors: KsqlDescriptionAccessor[];
|
|
||||||
data: KsqlDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ListItem: React.FC<Props> = ({ accessors, data }) => {
|
|
||||||
return (
|
|
||||||
<tr>
|
|
||||||
{accessors.map((accessor) => (
|
|
||||||
<td key={accessor}>{data[accessor]?.toString()}</td>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ListItem;
|
|
|
@ -1,32 +1,22 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import List from 'components/KsqlDb/List/List';
|
import List from 'components/KsqlDb/List/List';
|
||||||
import { clusterKsqlDbPath } from 'lib/paths';
|
import { render } from 'lib/testHelpers';
|
||||||
import { render, WithRoute } from 'lib/testHelpers';
|
|
||||||
import fetchMock from 'fetch-mock';
|
import fetchMock from 'fetch-mock';
|
||||||
import { screen, waitForElementToBeRemoved } from '@testing-library/dom';
|
import { screen } from '@testing-library/dom';
|
||||||
|
|
||||||
const clusterName = 'local';
|
|
||||||
|
|
||||||
const renderComponent = () => {
|
const renderComponent = () => {
|
||||||
render(
|
render(<List />);
|
||||||
<WithRoute path={clusterKsqlDbPath()}>
|
|
||||||
<List />
|
|
||||||
</WithRoute>,
|
|
||||||
{ initialEntries: [clusterKsqlDbPath(clusterName)] }
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('KsqlDb List', () => {
|
describe('KsqlDb List', () => {
|
||||||
afterEach(() => fetchMock.reset());
|
afterEach(() => fetchMock.reset());
|
||||||
it('renders placeholder on empty data', async () => {
|
it('renders List component with Tables and Streams tabs', async () => {
|
||||||
fetchMock.post(
|
|
||||||
{
|
|
||||||
url: `/api/clusters/${clusterName}/ksql`,
|
|
||||||
},
|
|
||||||
{ data: [] }
|
|
||||||
);
|
|
||||||
renderComponent();
|
renderComponent();
|
||||||
await waitForElementToBeRemoved(() => screen.getByRole('progressbar'));
|
|
||||||
expect(screen.getByText('No tables or streams found')).toBeInTheDocument();
|
const Tables = screen.getByTitle('Tables');
|
||||||
|
const Streams = screen.getByTitle('Streams');
|
||||||
|
|
||||||
|
expect(Tables).toBeInTheDocument();
|
||||||
|
expect(Streams).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { clusterKsqlDbPath } from 'lib/paths';
|
|
||||||
import { render, WithRoute } from 'lib/testHelpers';
|
|
||||||
import { screen } from '@testing-library/dom';
|
|
||||||
import ListItem from 'components/KsqlDb/List/ListItem';
|
|
||||||
import { KsqlDescription } from 'redux/interfaces/ksqlDb';
|
|
||||||
|
|
||||||
const clusterName = 'local';
|
|
||||||
|
|
||||||
const renderComponent = ({
|
|
||||||
accessors,
|
|
||||||
data,
|
|
||||||
}: {
|
|
||||||
accessors: (keyof KsqlDescription)[];
|
|
||||||
data: Record<string, string>;
|
|
||||||
}) => {
|
|
||||||
render(
|
|
||||||
<WithRoute path={clusterKsqlDbPath()}>
|
|
||||||
<ListItem accessors={accessors} data={data} />
|
|
||||||
</WithRoute>,
|
|
||||||
{ initialEntries: [clusterKsqlDbPath(clusterName)] }
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('KsqlDb List Item', () => {
|
|
||||||
it('renders placeholder on one data', async () => {
|
|
||||||
renderComponent({
|
|
||||||
accessors: ['accessors' as keyof KsqlDescription],
|
|
||||||
data: { accessors: 'accessors text' },
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(screen.getByText('accessors text')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -233,9 +233,18 @@ export type RouterParamsClusterConnectConnector = {
|
||||||
// KsqlDb
|
// KsqlDb
|
||||||
export const clusterKsqlDbRelativePath = 'ksqldb';
|
export const clusterKsqlDbRelativePath = 'ksqldb';
|
||||||
export const clusterKsqlDbQueryRelativePath = 'query';
|
export const clusterKsqlDbQueryRelativePath = 'query';
|
||||||
|
export const clusterKsqlDbTablesRelativePath = 'tables';
|
||||||
|
export const clusterKsqlDbStreamsRelativePath = 'streams';
|
||||||
|
|
||||||
export const clusterKsqlDbPath = (
|
export const clusterKsqlDbPath = (
|
||||||
clusterName: ClusterName = RouteParams.clusterName
|
clusterName: ClusterName = RouteParams.clusterName
|
||||||
) => `${clusterPath(clusterName)}/${clusterKsqlDbRelativePath}`;
|
) => `${clusterPath(clusterName)}/${clusterKsqlDbRelativePath}`;
|
||||||
export const clusterKsqlDbQueryPath = (
|
export const clusterKsqlDbQueryPath = (
|
||||||
clusterName: ClusterName = RouteParams.clusterName
|
clusterName: ClusterName = RouteParams.clusterName
|
||||||
) => `${clusterKsqlDbPath(clusterName)}/${clusterKsqlDbQueryRelativePath}`;
|
) => `${clusterKsqlDbPath(clusterName)}/${clusterKsqlDbQueryRelativePath}`;
|
||||||
|
export const clusterKsqlDbTablesPath = (
|
||||||
|
clusterName: ClusterName = RouteParams.clusterName
|
||||||
|
) => `${clusterKsqlDbPath(clusterName)}/${clusterKsqlDbTablesRelativePath}`;
|
||||||
|
export const clusterKsqlDbStreamsPath = (
|
||||||
|
clusterName: ClusterName = RouteParams.clusterName
|
||||||
|
) => `${clusterKsqlDbPath(clusterName)}/${clusterKsqlDbStreamsRelativePath}`;
|
||||||
|
|
|
@ -18,7 +18,6 @@ export interface KsqlState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface KsqlDescription {
|
export interface KsqlDescription {
|
||||||
type?: string;
|
|
||||||
name?: string;
|
name?: string;
|
||||||
topic?: string;
|
topic?: string;
|
||||||
keyFormat?: string;
|
keyFormat?: string;
|
||||||
|
|
|
@ -15,7 +15,10 @@ describe('TopicMessages selectors', () => {
|
||||||
|
|
||||||
it('Returns empty state', () => {
|
it('Returns empty state', () => {
|
||||||
expect(selectors.getKsqlDbTables(store.getState())).toEqual({
|
expect(selectors.getKsqlDbTables(store.getState())).toEqual({
|
||||||
rows: [],
|
rows: {
|
||||||
|
streams: [],
|
||||||
|
tables: [],
|
||||||
|
},
|
||||||
fetched: false,
|
fetched: false,
|
||||||
fetching: true,
|
fetching: true,
|
||||||
tablesCount: 0,
|
tablesCount: 0,
|
||||||
|
@ -34,10 +37,10 @@ describe('TopicMessages selectors', () => {
|
||||||
|
|
||||||
it('Returns tables and streams', () => {
|
it('Returns tables and streams', () => {
|
||||||
expect(selectors.getKsqlDbTables(store.getState())).toEqual({
|
expect(selectors.getKsqlDbTables(store.getState())).toEqual({
|
||||||
rows: [
|
rows: {
|
||||||
...fetchKsqlDbTablesPayload.streams,
|
streams: [...fetchKsqlDbTablesPayload.streams],
|
||||||
...fetchKsqlDbTablesPayload.tables,
|
tables: [...fetchKsqlDbTablesPayload.tables],
|
||||||
],
|
},
|
||||||
fetched: true,
|
fetched: true,
|
||||||
fetching: false,
|
fetching: false,
|
||||||
tablesCount: 2,
|
tablesCount: 2,
|
||||||
|
|
|
@ -15,7 +15,7 @@ const getKsqlExecutionStatus = createFetchingSelector('ksqlDb/executeKsql');
|
||||||
export const getKsqlDbTables = createSelector(
|
export const getKsqlDbTables = createSelector(
|
||||||
[ksqlDbState, getKsqlDbFetchTablesAndStreamsFetchingStatus],
|
[ksqlDbState, getKsqlDbFetchTablesAndStreamsFetchingStatus],
|
||||||
(state, status) => ({
|
(state, status) => ({
|
||||||
rows: [...state.streams, ...state.tables],
|
rows: { streams: [...state.streams], tables: [...state.tables] },
|
||||||
fetched: status === AsyncRequestStatus.fulfilled,
|
fetched: status === AsyncRequestStatus.fulfilled,
|
||||||
fetching: status === AsyncRequestStatus.pending,
|
fetching: status === AsyncRequestStatus.pending,
|
||||||
tablesCount: state.tables.length,
|
tablesCount: state.tables.length,
|
||||||
|
|
Loading…
Add table
Reference in a new issue