fleet/frontend/components/TargetsInput/TargetsInput.tsx
RachelElysia c9e66b221e
Frontend: Lint warning cleanup part 1 (#43411)
## Issue
- First batch of @iansltx 's work of cleaning up lint warnings #43387 

## Description
- Quick PR review and grabbed as many confirmed low-risk quick wins as I
could `git checkout lint-cleanup <file/path/1> <file/path/2>`

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Release Notes

This release contains internal code improvements with one minor UI
tweak:

* **Style**
* Dropdown menu background color adjusted for clearer contrast in action
lists
* **Refactor**
* Improved type safety across the codebase with stricter TypeScript
annotations
  * Removed unused imports and constants to reduce code clutter
* Enhanced React hook dependency arrays for more consistent component
behavior
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Rachel Perkins <rachel@Rachels-MacBook-Pro.local>
Co-authored-by: Ian Littman <iansltx@gmail.com>
2026-04-10 19:49:52 -05:00

150 lines
4.6 KiB
TypeScript

import React, { useRef, useEffect, useState } from "react";
import { Row } from "react-table";
import { isEmpty, pullAllBy } from "lodash";
import { IHost } from "interfaces/host";
import { HOSTS_SEARCH_BOX_PLACEHOLDER } from "utilities/constants";
import DataError from "components/DataError";
// @ts-ignore
import InputFieldWithIcon from "components/forms/fields/InputFieldWithIcon/InputFieldWithIcon";
import TableContainer from "components/TableContainer";
import { ITargestInputHostTableConfig } from "./TargetsInputHostsTableConfig";
interface ITargetsInputProps {
searchText: string;
searchResults: IHost[];
isTargetsLoading: boolean;
hasFetchError: boolean;
targetedHosts: IHost[];
searchResultsTableConfig: ITargestInputHostTableConfig[];
selectedHostsTableConifg: ITargestInputHostTableConfig[];
/** disabled pagination for the results table. The pagination is currently
* client side pagination. Defaults to `false` */
disablePagination?: boolean;
label?: string;
placeholder?: string;
autofocus?: boolean;
setSearchText: (value: string) => void;
handleRowSelect: (value: Row<IHost>) => void;
}
const baseClass = "targets-input";
const DEFAULT_LABEL = "Target specific hosts";
const TargetsInput = ({
searchText,
searchResults,
isTargetsLoading,
hasFetchError,
targetedHosts,
searchResultsTableConfig,
selectedHostsTableConifg,
disablePagination = false,
label = DEFAULT_LABEL,
placeholder = HOSTS_SEARCH_BOX_PLACEHOLDER,
autofocus = false,
handleRowSelect,
setSearchText,
}: ITargetsInputProps): JSX.Element => {
const dropdownRef = useRef<HTMLDivElement | null>(null);
const dropdownHosts =
searchResults && pullAllBy(searchResults, targetedHosts, "id");
const [isActiveSearch, setIsActiveSearch] = useState(false);
const isSearchError = !isEmpty(searchText) && hasFetchError;
// Closes target search results when clicking outside of results
// But not during API loading state as it will reopen on API return
useEffect(() => {
if (!isTargetsLoading) {
const handleClickOutside = (event: MouseEvent) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node)
) {
setIsActiveSearch(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}
return undefined;
}, [isTargetsLoading]);
useEffect(() => {
setIsActiveSearch(
!isEmpty(searchText) && (!hasFetchError || isTargetsLoading)
);
}, [searchText, hasFetchError, isTargetsLoading]);
return (
<div>
<div className={baseClass}>
<InputFieldWithIcon
autofocus={autofocus}
type="search"
iconSvg="search"
value={searchText}
label={label}
placeholder={placeholder}
onChange={setSearchText}
/>
{isActiveSearch && (
<div
className={`${baseClass}__hosts-search-dropdown`}
ref={dropdownRef}
>
<TableContainer<Row<IHost>>
columnConfigs={searchResultsTableConfig}
data={dropdownHosts}
isLoading={isTargetsLoading}
emptyComponent={() => (
<div className="empty-search">
<div className="empty-search__inner">
<h4>No matching hosts.</h4>
<p>
Expecting to see hosts? Try again in a few seconds as the
system catches up.
</p>
</div>
</div>
)}
showMarkAllPages={false}
isAllPagesSelected={false}
disableCount
disablePagination
disableMultiRowSelect
onClickRow={handleRowSelect}
keyboardSelectableRows
/>
</div>
)}
{isSearchError && (
<div className={`${baseClass}__hosts-search-dropdown`}>
<DataError />
</div>
)}
<div className={`${baseClass}__hosts-selected-table`}>
<TableContainer
columnConfigs={selectedHostsTableConifg}
data={targetedHosts}
isLoading={false}
showMarkAllPages={false}
isAllPagesSelected={false}
disableCount
disablePagination={disablePagination}
isClientSidePagination={!disablePagination}
emptyComponent={() => <></>}
/>
</div>
</div>
</div>
);
};
export default TargetsInput;