Fleet UI: Refactor client filtered counts for cleaner rendering (#19689)

This commit is contained in:
RachelElysia
2024-06-14 13:12:56 -04:00
committed by GitHub
parent 21207dab81
commit 35a467b7e0
25 changed files with 173 additions and 269 deletions
+1
View File
@@ -0,0 +1 @@
- Cleanup count rendering fixing clientside flashing counts
@@ -76,7 +76,6 @@ const TargetsInput = ({
columnConfigs={searchResultsTableConfig}
data={dropdownHosts}
isLoading={isTargetsLoading}
resultsTitle=""
emptyComponent={() => (
<div className="empty-search">
<div className="empty-search__inner">
@@ -107,7 +106,6 @@ const TargetsInput = ({
columnConfigs={selectedHostsTableConifg}
data={targetedHosts}
isLoading={false}
resultsTitle=""
showMarkAllPages={false}
isAllPagesSelected={false}
disableCount
@@ -43,7 +43,7 @@ interface IDataTableProps {
showMarkAllPages: boolean;
isAllPagesSelected: boolean; // TODO: make dependent on showMarkAllPages
toggleAllPagesSelected?: any; // TODO: an event type and make it dependent on showMarkAllPages
resultsTitle: string;
resultsTitle?: string;
defaultPageSize: number;
defaultPageIndex?: number;
primarySelectAction?: IActionButtonProps;
@@ -85,7 +85,7 @@ const DataTable = ({
showMarkAllPages,
isAllPagesSelected,
toggleAllPagesSelected,
resultsTitle,
resultsTitle = "results",
defaultPageSize,
defaultPageIndex,
primarySelectAction,
@@ -12,7 +12,6 @@ import Icon from "components/Icon/Icon";
import { COLORS } from "styles/var/colors";
import DataTable from "./DataTable/DataTable";
import TableContainerUtils from "./utilities/TableContainerUtils";
import { IActionButtonProps } from "./DataTable/ActionButton/ActionButton";
export interface ITableQueryData {
@@ -44,7 +43,8 @@ interface ITableContainerProps<T = any> {
inputPlaceHolder?: string;
disableActionButton?: boolean;
disableMultiRowSelect?: boolean;
resultsTitle: string;
/** resultsTitle used in DataTable for matching results text */
resultsTitle?: string;
resultsHtml?: JSX.Element;
additionalQueries?: string;
emptyComponent: React.ElementType;
@@ -64,10 +64,6 @@ interface ITableContainerProps<T = any> {
primarySelectAction?: IActionButtonProps;
/** Secondary button/s after selecting a row */
secondarySelectActions?: IActionButtonProps[]; // TODO: Combine with primarySelectAction as these are all rendered in the same spot
/**
* @deprecated please use renderCount instead
* */
filteredCount?: number;
searchToolTipText?: string;
// TODO - consolidate this functionality within `filters`
searchQueryColumn?: string;
@@ -103,7 +99,6 @@ interface ITableContainerProps<T = any> {
* bar and API call so TableContainer will reset its page state to 0 */
resetPageIndex?: boolean;
disableTableHeader?: boolean;
show0Count?: boolean;
}
const baseClass = "table-container";
@@ -140,7 +135,6 @@ const TableContainer = <T,>({
disableCount,
primarySelectAction,
secondarySelectActions,
filteredCount,
searchToolTipText,
isClientSidePagination,
onClientSidePaginationChange,
@@ -160,7 +154,6 @@ const TableContainer = <T,>({
setExportRows,
resetPageIndex,
disableTableHeader,
show0Count,
}: ITableContainerProps<T>) => {
const [searchQuery, setSearchQuery] = useState(defaultSearchQuery);
const [sortHeader, setSortHeader] = useState(defaultSortHeader || "");
@@ -252,16 +245,6 @@ const TableContainer = <T,>({
additionalQueries,
]);
// TODO: refactor existing components relying on displayCount to use renderCount pattern
const displayCount = useCallback((): any => {
if (typeof filteredCount === "number") {
return filteredCount;
} else if (typeof clientFilterCount === "number") {
return clientFilterCount;
}
return data?.length || 0;
}, [filteredCount, clientFilterCount, data]);
const renderPagination = useCallback(() => {
if (disablePagination || isClientSidePagination) {
return null;
@@ -309,37 +292,16 @@ const TableContainer = <T,>({
stackControls ? "stack-table-controls" : ""
}`}
>
<span className="results-count">
{renderCount && (
<div
className={`${baseClass}__results-count ${
stackControls ? "stack-table-controls" : ""
}`}
style={opacity}
>
{renderCount()}
</div>
)}
{!renderCount &&
!disableCount &&
(isMultiColumnFilter || displayCount() || show0Count) ? (
<div
className={`${baseClass}__results-count ${
stackControls ? "stack-table-controls" : ""
}`}
style={opacity}
>
{TableContainerUtils.generateResultsCountText(
resultsTitle,
displayCount(),
show0Count
)}
{resultsHtml}
</div>
) : (
<div />
)}
</span>
{renderCount && !disableCount && (
<div
className={`${baseClass}__results-count ${
stackControls ? "stack-table-controls" : ""
}`}
style={opacity}
>
{renderCount()}
</div>
)}
<span className="controls">
{actionButton && !actionButton.hideButton && (
<Button
@@ -0,0 +1,14 @@
import React from "react";
import { generateResultsCountText } from "../utilities/TableContainerUtils";
interface ITableCountProps {
name: string;
count?: number;
}
const TableCount = ({ name, count }: ITableCountProps): JSX.Element => {
return <span>{generateResultsCountText(name, count)}</span>;
};
export default TableCount;
@@ -0,0 +1 @@
export { default } from "./TableCount";
@@ -54,9 +54,6 @@
height: 38px; // Fixes overlap with .Select outline
}
}
.results-count {
height: 40px; // Match height of search/filters
}
&__results-count {
display: flex;
@@ -66,6 +63,7 @@
color: $core-fleet-black;
margin: 0;
height: 40px;
gap: 12px;
.count-error {
color: $ui-error;
@@ -1,11 +1,10 @@
const DEFAULT_RESULTS_NAME = "results";
const generateResultsCountText = (
export const generateResultsCountText = (
name: string = DEFAULT_RESULTS_NAME,
resultsCount: number,
show0Count = false
resultsCount?: number
): string => {
if (resultsCount === 0 && !show0Count) return `No ${name}`;
if (!resultsCount || resultsCount === 0) return `0 ${name}`;
// If there is 1 result and the last 3 letters in the result
// name are "ies," we remove the "ies" and add "y"
// to make the name singular
@@ -13,6 +13,7 @@ import CustomLink from "components/CustomLink";
import TableContainer from "components/TableContainer";
import LastUpdatedText from "components/LastUpdatedText";
import { ITableQueryData } from "components/TableContainer/TableContainer";
import TableCount from "components/TableContainer/TableCount";
import EmptySoftwareTable from "pages/SoftwarePage/components/EmptySoftwareTable";
import { IOSVersionsResponse } from "services/entities/operating_systems";
@@ -130,34 +131,19 @@ const SoftwareOSTable = ({
router.push(path);
};
const getItemsCountText = () => {
const count = data?.count;
if (!data?.os_versions || !count) return "";
return count === 1 ? `${count} item` : `${count} items`;
};
const getLastUpdatedText = () => {
if (!data?.os_versions || !data?.counts_updated_at) return "";
return (
<LastUpdatedText
lastUpdatedAt={data.counts_updated_at}
whatToRetrieve="software"
/>
);
};
const renderSoftwareCount = () => {
const itemText = getItemsCountText();
const lastUpdatedText = getLastUpdatedText();
if (!itemText) return null;
if (!data?.os_versions || !data?.count) return null;
return (
<div className={`${baseClass}__count`}>
<span>{itemText}</span>
{lastUpdatedText}
</div>
<>
<TableCount name="items" count={data?.count} />
{data?.os_versions && data?.counts_updated_at && (
<LastUpdatedText
lastUpdatedAt={data.counts_updated_at}
whatToRetrieve="vulnerabilities"
/>
)}
</>
);
};
@@ -1,10 +1,4 @@
.software-os-table {
&__count {
display: flex;
gap: 12px;
align-items: center;
}
.hosts-cell__wrapper {
display: flex;
align-items: center;
@@ -31,6 +31,7 @@ import Slider from "components/forms/fields/Slider";
import CustomLink from "components/CustomLink";
import LastUpdatedText from "components/LastUpdatedText";
import { ITableQueryData } from "components/TableContainer/TableContainer";
import TableCount from "components/TableContainer/TableCount";
import EmptySoftwareTable from "pages/SoftwarePage/components/EmptySoftwareTable";
@@ -189,23 +190,6 @@ const SoftwareTable = ({
isSoftwareEnabled &&
(!!tableData || query !== "" || softwareFilter === "vulnerableSoftware");
const getItemsCountText = () => {
const count = data?.count;
if (!tableData || !count) return "";
return count === 1 ? `${count} item` : `${count} items`;
};
const getLastUpdatedText = () => {
if (!tableData || !data?.counts_updated_at) return "";
return (
<LastUpdatedText
lastUpdatedAt={data.counts_updated_at}
whatToRetrieve="software"
/>
);
};
const handleShowVersionsToggle = () => {
const queryParams: Record<string, string | number | undefined> = {
query,
@@ -276,16 +260,18 @@ const SoftwareTable = ({
};
const renderSoftwareCount = () => {
const itemText = getItemsCountText();
const lastUpdatedText = getLastUpdatedText();
if (!itemText) return null;
if (!tableData || !data?.count) return null;
return (
<div className={`${baseClass}__count`}>
<span>{itemText}</span>
{lastUpdatedText}
</div>
<>
<TableCount name="items" count={data?.count} />
{tableData && data?.counts_updated_at && (
<LastUpdatedText
lastUpdatedAt={data.counts_updated_at}
whatToRetrieve="software"
/>
)}
</>
);
};
@@ -1,10 +1,4 @@
.software-table {
&__count {
display: flex;
gap: 12px;
align-items: center;
}
&__vuln_dropdown {
.Select-menu-outer {
width: 250px;
@@ -39,7 +33,6 @@
.table-container {
&__header {
flex-direction: column-reverse; // Search bar on top
margin-bottom: $pad-medium;
@media (min-width: $table-controls-break) {
flex-direction: row;
@@ -15,6 +15,7 @@ import CustomLink from "components/CustomLink";
import TableContainer from "components/TableContainer";
import LastUpdatedText from "components/LastUpdatedText";
import { ITableQueryData } from "components/TableContainer/TableContainer";
import TableCount from "components/TableContainer/TableCount";
import EmptySoftwareTable from "pages/SoftwarePage/components/EmptySoftwareTable";
import { IVulnerabilitiesResponse } from "services/entities/vulnerabilities";
@@ -182,34 +183,19 @@ const SoftwareVulnerabilitiesTable = ({
router.push(path);
};
const getItemsCountText = () => {
const count = data?.count;
if (!data?.vulnerabilities || !count) return "";
return count === 1 ? `${count} item` : `${count} items`;
};
const getLastUpdatedText = () => {
if (!data?.vulnerabilities || !data?.counts_updated_at) return "";
return (
<LastUpdatedText
lastUpdatedAt={data.counts_updated_at}
whatToRetrieve="vulnerabilities"
/>
);
};
const renderVulnerabilityCount = () => {
const itemText = getItemsCountText();
const lastUpdatedText = getLastUpdatedText();
if (!itemText) return null;
if (!data?.vulnerabilities || !data?.count) return null;
return (
<div className={`${baseClass}__count`}>
<span>{itemText}</span>
{lastUpdatedText}
</div>
<>
<TableCount name="items" count={data?.count} />
{data?.vulnerabilities && data?.counts_updated_at && (
<LastUpdatedText
lastUpdatedAt={data.counts_updated_at}
whatToRetrieve="vulnerabilities"
/>
)}
</>
);
};
@@ -77,6 +77,7 @@ import Dropdown from "components/forms/fields/Dropdown";
import TableContainer from "components/TableContainer";
import InfoBanner from "components/InfoBanner/InfoBanner";
import { ITableQueryData } from "components/TableContainer/TableContainer";
import TableCount from "components/TableContainer/TableCount";
import TableDataError from "components/DataError";
import { IActionButtonProps } from "components/TableContainer/DataTable/ActionButton/ActionButton";
import TeamsDropdown from "components/TeamsDropdown";
@@ -1403,18 +1404,10 @@ const ManageHostsPage = ({
};
const renderHostCount = useCallback(() => {
const count = hostsCount;
return (
<div
className={`${baseClass}__count ${
isLoadingHostsCount ? "count-loading" : ""
}`}
>
{count !== undefined && (
<span>{`${count} host${count === 1 ? "" : "s"}`}</span>
)}
{!!count && (
<>
<TableCount name="hosts" count={hostsCount} />
{!!hostsCount && (
<Button
className={`${baseClass}__export-btn`}
onClick={onExportHostsResults}
@@ -1426,7 +1419,7 @@ const ManageHostsPage = ({
</>
</Button>
)}
</div>
</>
);
}, [isLoadingHostsCount, hostsCount]);
@@ -273,14 +273,6 @@
}
&__export-btn {
margin-left: $pad-medium;
img {
width: 13px;
height: 13px;
margin-left: 8px;
position: relative;
top: -2px;
}
margin-left: $pad-xsmall;
}
}
@@ -2,6 +2,7 @@ import Button from "components/buttons/Button";
import EmptyTable from "components/EmptyTable";
import Icon from "components/Icon";
import TableContainer from "components/TableContainer";
import TableCount from "components/TableContainer/TableCount";
import React, { useCallback, useState } from "react";
import { Row } from "react-table";
import {
@@ -119,15 +120,14 @@ const HQRTable = ({
}, [lastFetched, hostName, reportClipped]);
const renderCount = useCallback(() => {
const count = filteredResults.length;
return (
<div className={`${baseClass}__results-count-and-last-fetched`}>
<span>{`${count} result${count === 1 ? "" : "s"}`}</span>
<>
<TableCount name="results" count={filteredResults.length} />
<span className="last-fetched">
Last fetched{" "}
<HumanTimeDiffWithFleetLaunchCutoff timeString={lastFetched ?? ""} />
</span>
</div>
</>
);
}, [filteredResults.length, lastFetched]);
@@ -1,30 +1,14 @@
.hqr-table {
gap: $pad-medium;
&__results-count-and-last-fetched {
display: flex;
align-items: baseline;
gap: $pad-small;
.last-fetched {
font-weight: initial;
@include grey-text;
}
.last-fetched {
font-weight: initial;
@include grey-text;
}
&__results-cta {
display: flex;
gap: $pad-medium;
.button {
height: auto;
}
}
&__export-btn {
.children-wrapper {
align-self: flex-end;
}
.icon {
display: initial;
}
}
&__query-info {
@@ -7,8 +7,10 @@ import { getNextLocationPath } from "utilities/helpers";
import TableContainer from "components/TableContainer";
import { ITableQueryData } from "components/TableContainer/TableContainer";
import { generateResultsCountText } from "components/TableContainer/utilities/TableContainerUtils";
import EmptySoftwareTable from "pages/SoftwarePage/components/EmptySoftwareTable";
import TableCount from "components/TableContainer/TableCount";
const DEFAULT_PAGE_SIZE = 20;
@@ -26,16 +28,6 @@ interface IHostSoftwareTableProps {
pagePath: string;
}
const SoftwareCount = ({ count }: { count: number }) => {
return (
<div className={`${baseClass}__count`}>
<span>
{count === 1 ? `${count} software item` : `${count} software items`}
</span>
</div>
);
};
const HostSoftwareTable = ({
tableConfig,
data,
@@ -108,7 +100,7 @@ const HostSoftwareTable = ({
const memoizedSoftwareCount = useCallback(() => {
const count = data?.count || data?.software.length || 0;
return <SoftwareCount count={count} />;
return <TableCount name="items" count={count} />;
}, [data?.count, data?.software.length]);
const memoizedEmptyComponent = useCallback(() => {
@@ -119,7 +111,6 @@ const HostSoftwareTable = ({
<div className={baseClass}>
<TableContainer
renderCount={memoizedSoftwareCount}
resultsTitle="software items"
columnConfigs={tableConfig}
data={data?.software || []}
isLoading={isLoading}
@@ -1,8 +1,9 @@
import React from "react";
import React, { useCallback } from "react";
import { IHostUser } from "interfaces/host_users";
import TableContainer from "components/TableContainer";
import { ITableQueryData } from "components/TableContainer/TableContainer";
import TableCount from "components/TableContainer/TableCount";
import EmptyTable from "components/EmptyTable";
import CustomLink from "components/CustomLink";
import Card from "components/Card";
@@ -28,6 +29,10 @@ const Users = ({
}: IUsersProps): JSX.Element => {
const tableHeaders = generateUsersTableHeaders();
const renderUsersCount = useCallback(() => {
return <TableCount name="users" count={usersState.length} />;
}, [usersState.length]);
if (!hostUsersEnabled) {
return (
<Card
@@ -72,7 +77,6 @@ const Users = ({
defaultSortDirection="asc"
inputPlaceHolder="Search users by username"
onQueryChange={onUsersTableSearchChange}
resultsTitle="users"
emptyComponent={() => (
<EmptyTable
header="No users match your search criteria"
@@ -83,7 +87,7 @@ const Users = ({
isAllPagesSelected={false}
searchable
wideSearch
filteredCount={usersState.length}
renderCount={renderUsersCount}
isClientSidePagination
/>
) : (
@@ -34,6 +34,7 @@ import teamPoliciesAPI, {
import teamsAPI, { ILoadTeamResponse } from "services/entities/teams";
import { ITableQueryData } from "components/TableContainer/TableContainer";
import TableCount from "components/TableContainer/TableCount";
import Button from "components/buttons/Button";
// @ts-ignore
import Dropdown from "components/forms/fields/Dropdown";
@@ -624,19 +625,21 @@ const ManagePolicyPage = ({
}
const renderPoliciesCount = (count?: number) => {
// Show count if there is no errors AND there are policy results or a search filter
const showCount =
count !== undefined &&
!policiesErrors &&
(policyResults || searchQuery !== "");
// Hide count if fetching count || there are errors OR there are no policy results with no a search filter
const isFetchingCount = isAnyTeamSelected
? isFetchingTeamCountMergeInherited
: isFetchingGlobalCount;
return (
<div className={`${baseClass}__count`}>
{showCount && (
<span>{`${count} polic${count === 1 ? "y" : "ies"}`}</span>
)}
</div>
);
const hideCount =
isFetchingCount ||
policiesErrors ||
(!policyResults && searchQuery === "");
if (hideCount) {
return null;
}
return <TableCount name="policies" count={count} />;
};
const renderMainTable = () => {
@@ -658,9 +661,7 @@ const ManagePolicyPage = ({
currentTeam={currentTeamSummary}
currentAutomatedPolicies={currentAutomatedPolicies}
renderPoliciesCount={() =>
(!isFetchingTeamCountMergeInherited &&
renderPoliciesCount(teamPoliciesCountMergeInherited)) ||
null
renderPoliciesCount(teamPoliciesCountMergeInherited)
}
isPremiumTier={isPremiumTier}
searchQuery={searchQuery}
@@ -683,11 +684,7 @@ const ManagePolicyPage = ({
currentTeam={currentTeamSummary}
currentAutomatedPolicies={currentAutomatedPolicies}
isPremiumTier={isPremiumTier}
renderPoliciesCount={() =>
(!isFetchingGlobalCount &&
renderPoliciesCount(globalPoliciesCount)) ||
null
}
renderPoliciesCount={() => renderPoliciesCount(globalPoliciesCount)}
searchQuery={searchQuery}
sortHeader={sortHeader}
sortDirection={sortDirection}
@@ -3,16 +3,6 @@
margin: 2rem auto 1.25rem;
}
&__export-btn {
img {
width: 13px;
height: 13px;
margin-left: 8px;
position: relative;
bottom: 2px;
}
}
.data-table__wrapper {
overflow-x: scroll;
}
@@ -43,7 +43,7 @@ interface IManageQueriesPageProps {
location: {
pathname: string;
query: {
platform?: string;
platform?: SupportedPlatform;
page?: string;
query?: string;
order_key?: string;
@@ -1,16 +1,25 @@
/* eslint-disable react/prop-types */
import React, { useContext, useCallback, useMemo } from "react";
import React, {
useContext,
useCallback,
useMemo,
useState,
useEffect,
} from "react";
import { InjectedRouter } from "react-router";
import { AppContext } from "context/app";
import { IEmptyTableProps } from "interfaces/empty_table";
import { SupportedPlatform } from "interfaces/platform";
import { IEnhancedQuery } from "interfaces/schedulable_query";
import { ITableQueryData } from "components/TableContainer/TableContainer";
import { IActionButtonProps } from "components/TableContainer/DataTable/ActionButton/ActionButton";
import PATHS from "router/paths";
import { getNextLocationPath } from "utilities/helpers";
import { checkPlatformCompatibility } from "utilities/sql_tools";
import Button from "components/buttons/Button";
import TableContainer from "components/TableContainer";
import TableCount from "components/TableContainer/TableCount";
import CustomLink from "components/CustomLink";
import EmptyTable from "components/EmptyTable";
// @ts-ignore
@@ -29,7 +38,7 @@ export interface IQueriesTableProps {
isAnyTeamObserverPlus: boolean;
router?: InjectedRouter;
queryParams?: {
platform?: string;
platform?: SupportedPlatform;
page?: string;
query?: string;
order_key?: string;
@@ -92,6 +101,38 @@ const QueriesTable = ({
}: IQueriesTableProps): JSX.Element | null => {
const { currentUser } = useContext(AppContext);
// Client side filtering bugs fixed with bypassing TableContainer filters
// queriesState tracks search filter and compatible platform filter
// to correctly show filtered queries and filtered count
// isQueryStateLoading prevents flashing of unfiltered count during clientside filtering
const [queriesState, setQueriesState] = useState<IEnhancedQuery[]>([]);
const [isQueriesStateLoading, setIsQueriesStateLoading] = useState(true);
useEffect(() => {
setIsQueriesStateLoading(true);
if (queriesList) {
setQueriesState(
queriesList.filter((query) => {
const filterSearchQuery = queryParams?.query
? query.name
.toLowerCase()
.includes(queryParams?.query.toLowerCase())
: true;
const compatiblePlatforms =
checkPlatformCompatibility(query.query).platforms || [];
const filterCompatiblePlatform = queryParams?.platform
? compatiblePlatforms.includes(queryParams?.platform)
: true;
return filterSearchQuery && filterCompatiblePlatform;
}) || []
);
}
setIsQueriesStateLoading(false);
}, [queriesList, queryParams?.query]);
// Functions to avoid race conditions
const initialSearchQuery = (() => queryParams?.query ?? "")();
const initialSortHeader = (() =>
@@ -236,6 +277,15 @@ const QueriesTable = ({
);
}, [platform, queryParams, router]);
const renderQueriesCount = useCallback(() => {
// Fixes flashing incorrect count before clientside filtering
if (isQueriesStateLoading) {
return null;
}
return <TableCount name="queries" count={queriesState?.length} />;
}, [queriesState, isQueriesStateLoading]);
const columnConfigs = useMemo(
() =>
currentUser &&
@@ -281,14 +331,15 @@ const QueriesTable = ({
} as IActionButtonProps),
[onDeleteQueryClick]
);
return columnConfigs && !isLoading ? (
<div className={`${baseClass}`}>
<TableContainer
resultsTitle="queries"
columnConfigs={columnConfigs}
data={queriesList}
filters={{ global: trimmedSearchQuery }}
isLoading={isLoading}
data={queriesState}
filters={{ name: trimmedSearchQuery }}
isLoading={isLoading || isQueriesStateLoading}
defaultSortHeader={sortHeader || DEFAULT_SORT_HEADER}
defaultSortDirection={sortDirection || DEFAULT_SORT_DIRECTION}
defaultSearchQuery={trimmedSearchQuery}
@@ -308,7 +359,7 @@ const QueriesTable = ({
primarySelectAction={deleteQueryTableActionButtonProps}
// TODO - consolidate this functionality within `filters`
selectedDropdownFilter={platform}
show0Count
renderCount={renderQueriesCount}
/>
</div>
) : (
@@ -14,6 +14,8 @@ import { IQueryReport, IQueryReportResultRow } from "interfaces/query_report";
import Button from "components/buttons/Button";
import Icon from "components/Icon/Icon";
import TableContainer from "components/TableContainer";
import TableCount from "components/TableContainer/TableCount";
import { generateResultsCountText } from "components/TableContainer/utilities/TableContainerUtils";
import TooltipWrapper from "components/TooltipWrapper";
import EmptyTable from "components/EmptyTable";
@@ -102,7 +104,7 @@ const QueryReport = ({
if (isClipped) {
return (
<div className={`${baseClass}__count `}>
<>
<TooltipWrapper
tipContent={
<>
@@ -115,16 +117,13 @@ const QueryReport = ({
</>
}
>
{`${count} result${count === 1 ? "" : "s"}`}
{generateResultsCountText("results", count)}
</TooltipWrapper>
</div>
</>
);
}
return (
<div className={`${baseClass}__count `}>
<span>{`${count} result${count === 1 ? "" : "s"}`}</span>
</div>
);
return <TableCount name="results" count={count} />;
}, [filteredResults.length, isClipped]);
const renderTable = () => {
@@ -8,21 +8,6 @@
margin-right: $pad-medium;
}
&__export-btn {
img {
width: 13px;
margin-left: 8px;
position: relative;
bottom: 2px;
}
}
&__show-query-btn {
img {
width: 13px;
margin-left: 8px;
}
}
.data-table__wrapper {
overflow-x: scroll;
}