UI: Maintain header titles, remove "select all," "clear selection" options and selected item count from Select software table (#33301)

## For #33277 

<img width="1046" height="821" alt="Screenshot 2025-09-22 at 4 26 20 PM"
src="https://github.com/user-attachments/assets/d2e20d88-9795-42c3-91de-d3e7f98b2b6e"
/>


- [x] QA'd all new/changed functionality manually

---------

Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
This commit is contained in:
jacobshandling
2025-09-23 09:55:18 -07:00
committed by GitHub
co-authored by Jacob Shandling
parent 477f3cbaec
commit 5893b8186b
5 changed files with 54 additions and 85 deletions
@@ -80,6 +80,7 @@ interface IDataTableProps {
renderPagination?: () => JSX.Element | null;
setExportRows?: (rows: Row[]) => void;
onClearSelection?: () => void;
suppressHeaderActions?: boolean;
}
interface IHeaderGroup extends HeaderGroup {
@@ -126,6 +127,7 @@ const DataTable = ({
renderPagination,
setExportRows,
onClearSelection = noop,
suppressHeaderActions,
}: IDataTableProps): JSX.Element => {
// used to track the initial mount of the component.
const isInitialRender = useRef(true);
@@ -515,6 +517,47 @@ const DataTable = ({
"is-observer": isOnlyObserver,
});
const renderHeaderWithActions = () => (
<thead className="active-selection">
<tr {...headerGroups[0].getHeaderGroupProps()}>
<th
className="active-selection__checkbox"
{...headerGroups[0].headers[0].getHeaderProps(
headerGroups[0].headers[0].getSortByToggleProps({
title: null,
})
)}
>
{headerGroups[0].headers[0].render("Header")}
</th>
<th className="active-selection__container">
<div className="active-selection__inner">
{renderSelectedCount()}
<div className="active-selection__inner-left">
{secondarySelectActions && renderSecondarySelectActions()}
</div>
<div className="active-selection__inner-right">
{primarySelectAction && renderPrimarySelectAction()}
</div>
{toggleAllPagesSelected && renderAreAllSelected()}
{shouldRenderToggleAllPages && (
<Button
onClick={onToggleAllPagesClick}
variant="text-link"
className="light-text"
>
<>Select all matching {resultsTitle}</>
</Button>
)}
<Button onClick={onClearSelectionClick} variant="text-link">
Clear selection
</Button>
</div>
</th>
</tr>
</thead>
);
return (
<div className={baseClass}>
{isLoading && (
@@ -524,46 +567,9 @@ const DataTable = ({
)}
<div className="data-table data-table__wrapper">
<table className={tableStyles}>
{Object.keys(selectedRowIds).length !== 0 && (
<thead className="active-selection">
<tr {...headerGroups[0].getHeaderGroupProps()}>
<th
className="active-selection__checkbox"
{...headerGroups[0].headers[0].getHeaderProps(
headerGroups[0].headers[0].getSortByToggleProps({
title: null,
})
)}
>
{headerGroups[0].headers[0].render("Header")}
</th>
<th className="active-selection__container">
<div className="active-selection__inner">
{renderSelectedCount()}
<div className="active-selection__inner-left">
{secondarySelectActions && renderSecondarySelectActions()}
</div>
<div className="active-selection__inner-right">
{primarySelectAction && renderPrimarySelectAction()}
</div>
{toggleAllPagesSelected && renderAreAllSelected()}
{shouldRenderToggleAllPages && (
<Button
onClick={onToggleAllPagesClick}
variant="text-link"
className="light-text"
>
<>Select all matching {resultsTitle}</>
</Button>
)}
<Button onClick={onClearSelectionClick} variant="text-link">
Clear selection
</Button>
</div>
</th>
</tr>
</thead>
)}
{!suppressHeaderActions &&
Object.keys(selectedRowIds).length !== 0 &&
renderHeaderWithActions()}
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
@@ -123,6 +123,8 @@ interface ITableContainerProps<T = any> {
hideFooter?: boolean;
/** handler called when the `clear selection` button is called */
onClearSelection?: () => void;
/** don't show the Clear selection button and selected item count when items are selected */
suppressHeaderActions?: boolean;
}
const baseClass = "table-container";
@@ -184,6 +186,7 @@ const TableContainer = <T,>({
disableTableHeader,
persistSelectedRows,
onClearSelection = noop,
suppressHeaderActions,
}: ITableContainerProps<T>) => {
const isControlledSearchQuery = controlledSearchQuery !== undefined;
const [searchQuery, setSearchQuery] = useState(defaultSearchQuery);
@@ -574,6 +577,7 @@ const TableContainer = <T,>({
}
setExportRows={setExportRows}
onClearSelection={onClearSelection}
suppressHeaderActions={suppressHeaderActions}
persistSelectedRows={persistSelectedRows}
hideFooter={hideFooter}
/>
@@ -76,13 +76,6 @@ const SelectSoftwareModal = ({
});
}, []);
const onChangeSelectAll = useCallback(
(selectAll: boolean) => {
setSelectedSoftwareIds(selectAll ? softwareTitles.map((s) => s.id) : []);
},
[softwareTitles]
);
return (
<Modal
className={baseClass}
@@ -94,7 +87,6 @@ const SelectSoftwareModal = ({
<SelectSoftwareTable
softwareTitles={softwareTitles}
onChangeSoftwareSelect={onChangeSoftwareSelect}
onChangeSelectAll={onChangeSelectAll}
platform={platform}
/>
<div className="modal-cta-wrap">
@@ -26,23 +26,17 @@ const generateSelectedRows = (softwareTitles: ISoftwareTitle[]) => {
interface ISelectSoftwareTableProps {
softwareTitles: ISoftwareTitle[];
onChangeSoftwareSelect: (select: boolean, id: number) => void;
onChangeSelectAll: (selectAll: boolean) => void;
platform: SetupExperiencePlatform;
}
const SelectSoftwareTable = ({
softwareTitles,
onChangeSoftwareSelect,
onChangeSelectAll,
platform,
}: ISelectSoftwareTableProps) => {
const tableConfig = useMemo(() => {
return generateTableConfig(
platform,
onChangeSelectAll,
onChangeSoftwareSelect
);
}, [onChangeSelectAll, onChangeSoftwareSelect, platform]);
return generateTableConfig(platform, onChangeSoftwareSelect);
}, [onChangeSoftwareSelect, platform]);
const initialSelectedSoftwareRows = useMemo(() => {
return generateSelectedRows(softwareTitles);
@@ -78,7 +72,6 @@ const SelectSoftwareTable = ({
searchable
searchQueryColumn="name"
isClientSideFilter
onClearSelection={() => onChangeSelectAll(false)}
renderTableHelpText={() => (
<p className={`${baseClass}__help-text`}>
Software will be installed on{" "}
@@ -87,6 +80,7 @@ const SelectSoftwareTable = ({
experience.
</p>
)}
suppressHeaderActions
/>
);
};
@@ -1,7 +1,7 @@
import React from "react";
import { CellProps, Column } from "react-table";
import { IHeaderProps, IStringCellProps } from "interfaces/datatable_config";
import { IStringCellProps } from "interfaces/datatable_config";
import { ISoftwareTitle, SoftwareSource } from "interfaces/software";
import TextCell from "components/TableContainer/DataTable/TextCell";
@@ -13,7 +13,6 @@ import { SetupExperiencePlatform } from "interfaces/platform";
import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants";
type ISelectSoftwareTableConfig = Column<ISoftwareTitle>;
type ITableHeaderProps = IHeaderProps<ISoftwareTitle>;
type ITableStringCellProps = IStringCellProps<ISoftwareTitle>;
type ISelectionCellProps = CellProps<ISoftwareTitle>;
@@ -32,38 +31,12 @@ const getSetupExperienceLinuxPackageCopy = (source: SoftwareSource) => {
const generateTableConfig = (
platform: SetupExperiencePlatform,
onSelectAll: (selectAll: boolean) => void,
onSelectSoftware: (select: boolean, id: number) => void
): ISelectSoftwareTableConfig[] => {
const headerConfigs: ISelectSoftwareTableConfig[] = [
{
id: "selection",
disableSortBy: true,
Header: (cellProps: ITableHeaderProps) => {
const {
checked,
indeterminate,
} = cellProps.getToggleAllRowsSelectedProps();
const checkboxProps = {
value: checked,
indeterminate,
onChange: () => {
onSelectAll(!checked);
cellProps.toggleAllRowsSelected();
},
};
return (
<GitOpsModeTooltipWrapper
position="right"
tipOffset={6}
fixedPositionStrategy
renderChildren={(disableChildren) => (
<Checkbox disabled={disableChildren} {...checkboxProps} />
)}
/>
);
},
Cell: (cellProps: ISelectionCellProps) => {
const { checked } = cellProps.row.getToggleRowSelectedProps();
const checkboxProps = {