diff --git a/src/app/Dashboard/AutomatedAnalysis/ClickableAutomatedAnalysisLabel.tsx b/src/app/Dashboard/AutomatedAnalysis/ClickableAutomatedAnalysisLabel.tsx index 719edfa221..e55dce124f 100644 --- a/src/app/Dashboard/AutomatedAnalysis/ClickableAutomatedAnalysisLabel.tsx +++ b/src/app/Dashboard/AutomatedAnalysis/ClickableAutomatedAnalysisLabel.tsx @@ -82,7 +82,7 @@ export const ClickableAutomatedAnalysisLabel: React.FC{result.name}} alertSeverityVariant={alertPopoverVariant} diff --git a/src/app/Events/EventTemplates.tsx b/src/app/Events/EventTemplates.tsx index 24a52905df..1c129153ea 100644 --- a/src/app/Events/EventTemplates.tsx +++ b/src/app/Events/EventTemplates.tsx @@ -357,7 +357,7 @@ export const EventTemplates: React.FC = (_) => { type="search" placeholder="Filter..." aria-label="Event template filter" - onChange={setFilterText} + onChange={(_, value: string) => setFilterText(value)} value={filterText} isDisabled={errorMessage != ''} /> diff --git a/src/app/Events/EventTypes.tsx b/src/app/Events/EventTypes.tsx index 076376542c..35a7719545 100644 --- a/src/app/Events/EventTypes.tsx +++ b/src/app/Events/EventTypes.tsx @@ -213,7 +213,7 @@ export const EventTypes: React.FC = (_) => { ); const onFilterTextChange = React.useCallback( - (filterText: string) => { + (_, filterText: string) => { setFilterText(filterText); setCurrentPage(1); }, diff --git a/src/app/Login/BasicAuthForm.tsx b/src/app/Login/BasicAuthForm.tsx index 0bfec71429..6a8e036926 100644 --- a/src/app/Login/BasicAuthForm.tsx +++ b/src/app/Login/BasicAuthForm.tsx @@ -16,7 +16,19 @@ import { AuthMethod } from '@app/Shared/Services/service.types'; import { ServiceContext } from '@app/Shared/Services/Services'; -import { ActionGroup, Button, Checkbox, Form, FormGroup, Text, TextInput, TextVariants } from '@patternfly/react-core'; +import { + ActionGroup, + Button, + Checkbox, + Form, + FormGroup, + FormHelperText, + HelperText, + HelperTextItem, + Text, + TextInput, + TextVariants, +} from '@patternfly/react-core'; import { Base64 } from 'js-base64'; import * as React from 'react'; import { map } from 'rxjs/operators'; @@ -85,7 +97,12 @@ export const BasicAuthForm: React.FC = ({ onSubmit }) => { return (
- + + + + Please provide your username + + = ({ onSubmit }) => { onKeyDown={handleKeyDown} /> - + + + + Please provide your password + + = ({ ); const onInnerClose = React.useCallback( - (ev?: React.MouseEvent) => { + (ev?: KeyboardEvent | React.MouseEvent) => { ev && ev.stopPropagation(); onClose(); }, diff --git a/src/app/RecordingMetadata/BulkEditLabels.tsx b/src/app/RecordingMetadata/BulkEditLabels.tsx index e47f92a3fa..353bf3232f 100644 --- a/src/app/RecordingMetadata/BulkEditLabels.tsx +++ b/src/app/RecordingMetadata/BulkEditLabels.tsx @@ -28,7 +28,17 @@ import { import { ServiceContext } from '@app/Shared/Services/Services'; import { useSubscriptions } from '@app/utils/hooks/useSubscriptions'; import { hashCode, portalRoot } from '@app/utils/utils'; -import { Button, Split, SplitItem, Stack, StackItem, Text, Tooltip, ValidatedOptions } from '@patternfly/react-core'; +import { + Button, + Icon, + Split, + SplitItem, + Stack, + StackItem, + Text, + Tooltip, + ValidatedOptions, +} from '@patternfly/react-core'; import { HelpIcon } from '@patternfly/react-icons'; import * as React from 'react'; import { combineLatest, concatMap, filter, first, forkJoin, map, Observable, of } from 'rxjs'; @@ -291,7 +301,9 @@ export const BulkEditLabels: React.FC = ({ } appendTo={portalRoot} > - + + + diff --git a/src/app/RecordingMetadata/RecordingLabelFields.tsx b/src/app/RecordingMetadata/RecordingLabelFields.tsx index a35210ea83..fbdbd62b41 100644 --- a/src/app/RecordingMetadata/RecordingLabelFields.tsx +++ b/src/app/RecordingMetadata/RecordingLabelFields.tsx @@ -21,6 +21,7 @@ import { FormHelperText, HelperText, HelperTextItem, + Icon, List, ListItem, Popover, @@ -261,7 +262,11 @@ export const RecordingLabelFields: React.FC = ({ variant="link" aria-label="Remove Label" isDisabled={isDisabled} - icon={} + icon={ + + {' '} + + } /> diff --git a/src/app/Recordings/ActiveRecordingsTable.tsx b/src/app/Recordings/ActiveRecordingsTable.tsx index ee731ee486..2cecf8f77e 100644 --- a/src/app/Recordings/ActiveRecordingsTable.tsx +++ b/src/app/Recordings/ActiveRecordingsTable.tsx @@ -867,7 +867,7 @@ export const ActiveRecordingRow: React.FC = ({ const handleToggle = React.useCallback(() => toggleExpanded(expandedRowId), [expandedRowId, toggleExpanded]); const handleCheck = React.useCallback( - (checked: boolean) => { + (_, checked: boolean) => { handleRowCheck(checked, index); }, [index, handleRowCheck], diff --git a/src/app/Recordings/ArchivedRecordingsTable.tsx b/src/app/Recordings/ArchivedRecordingsTable.tsx index 35cb9cec29..e4301f2cfe 100644 --- a/src/app/Recordings/ArchivedRecordingsTable.tsx +++ b/src/app/Recordings/ArchivedRecordingsTable.tsx @@ -786,7 +786,7 @@ export const ArchivedRecordingRow: React.FC = ({ }, [expandedRowId, expandedRows]); const handleCheck = React.useCallback( - (checked: boolean) => { + (_, checked: boolean) => { handleRowCheck(checked, index); }, [index, handleRowCheck], diff --git a/src/app/Recordings/Filters/DatetimeFilter.tsx b/src/app/Recordings/Filters/DatetimeFilter.tsx index 98f09a30ff..91b0e30234 100644 --- a/src/app/Recordings/Filters/DatetimeFilter.tsx +++ b/src/app/Recordings/Filters/DatetimeFilter.tsx @@ -82,7 +82,7 @@ export const DateTimeFilter: React.FC = ({ onSubmit }) => { ); const handleTextInput = React.useCallback( - (value: string) => { + (_, value: string) => { setDatetimeInput((_) => { if (value === '') { return _emptyDatetimeInput; diff --git a/src/app/Recordings/Filters/DurationFilter.tsx b/src/app/Recordings/Filters/DurationFilter.tsx index e9a78d4078..b88be56ee8 100644 --- a/src/app/Recordings/Filters/DurationFilter.tsx +++ b/src/app/Recordings/Filters/DurationFilter.tsx @@ -32,7 +32,7 @@ export const DurationFilter: React.FC = ({ const isContinuous = React.useMemo(() => durations && durations.includes('continuous'), [durations]); const handleContinuousCheckBoxChange = React.useCallback( - (checked) => { + (_, checked: boolean) => { onContinuousDurationSelect(checked); }, [onContinuousDurationSelect], @@ -56,7 +56,7 @@ export const DurationFilter: React.FC = ({ value={duration} id="duration-input" aria-label="duration filter" - onChange={(e) => setDuration(Number(e))} + onChange={(e, value) => setDuration(Number(value))} min="0" onKeyDown={handleEnterKey} /> diff --git a/src/app/Recordings/Filters/LabelFilter.tsx b/src/app/Recordings/Filters/LabelFilter.tsx index a8e7d536ee..4e39450bb5 100644 --- a/src/app/Recordings/Filters/LabelFilter.tsx +++ b/src/app/Recordings/Filters/LabelFilter.tsx @@ -16,8 +16,20 @@ import { parseLabels, getLabelDisplay } from '@app/RecordingMetadata/utils'; import { Recording } from '@app/Shared/Services/api.types'; -import { Label } from '@patternfly/react-core'; -import { Select, SelectOption, SelectVariant } from '@patternfly/react-core/deprecated'; +import { + Button, + Label, + MenuToggle, + MenuToggleElement, + Select, + SelectList, + SelectOption, + SelectOptionProps, + TextInputGroup, + TextInputGroupMain, + TextInputGroupUtilities, +} from '@patternfly/react-core'; +import { TimesIcon } from '@patternfly/react-icons'; import * as React from 'react'; export interface LabelFilterProps { @@ -28,10 +40,11 @@ export interface LabelFilterProps { export const LabelFilter: React.FC = ({ recordings, filteredLabels, onSubmit }) => { const [isExpanded, setIsExpanded] = React.useState(false); + const [filterValue, setFilterValue] = React.useState(''); const onSelect = React.useCallback( - (_, selection, isPlaceholder) => { - if (!isPlaceholder) { + (_, selection: string) => { + if (selection) { setIsExpanded(false); onSubmit(selection); } @@ -39,7 +52,11 @@ export const LabelFilter: React.FC = ({ recordings, filteredLa [onSubmit, setIsExpanded], ); - const labels = React.useMemo(() => { + const onToggle = React.useCallback(() => setIsExpanded((isExpanded) => !isExpanded), [setIsExpanded]); + + const onInputChange = React.useCallback((_, inputVal: string) => setFilterValue(inputVal), [setFilterValue]); + + const labelOptions = React.useMemo(() => { const labels = new Set(); recordings.forEach((r) => { if (!r || !r.metadata || !r.metadata.labels) return; @@ -50,24 +67,62 @@ export const LabelFilter: React.FC = ({ recordings, filteredLa .sort(); }, [recordings, filteredLabels]); + const filteredLabelOptions = React.useMemo(() => { + return !filterValue ? labelOptions : labelOptions.filter((l) => l.includes(filterValue.toLowerCase())); + }, [filterValue, labelOptions]); + + const selectOptionProps: SelectOptionProps[] = React.useMemo(() => { + if (!filteredLabelOptions.length) { + return [{ isDisabled: true, children: `No results found for "${filterValue}"`, value: undefined }]; + } + return filteredLabelOptions.map((l) => ({ children: l, value: l })); + }, [filteredLabelOptions]); + + const toggle = React.useCallback( + (toggleRef: React.Ref) => ( + + + + + {filterValue ? ( + + ) : null} + + + + ), + [onToggle, isExpanded, filterValue, onInputChange, setFilterValue], + ); + return ( - + + {selectOptionProps.map(({ value, children }, index) => ( + + + + ))} + ); }; diff --git a/src/app/Recordings/Filters/NameFilter.tsx b/src/app/Recordings/Filters/NameFilter.tsx index d25963e94f..18233f8161 100644 --- a/src/app/Recordings/Filters/NameFilter.tsx +++ b/src/app/Recordings/Filters/NameFilter.tsx @@ -15,7 +15,20 @@ */ import { Recording } from '@app/Shared/Services/api.types'; -import { Select, SelectOption, SelectVariant } from '@patternfly/react-core/deprecated'; +import { + Button, + MenuToggle, + MenuToggleElement, + Select, + SelectList, + SelectOption, + SelectOptionProps, + TextInputGroup, + TextInputGroupMain, + TextInputGroupUtilities, +} from '@patternfly/react-core'; +import { SelectVariant } from '@patternfly/react-core/deprecated'; +import { TimesIcon } from '@patternfly/react-icons'; import * as React from 'react'; export interface NameFilterProps { @@ -26,10 +39,11 @@ export interface NameFilterProps { export const NameFilter: React.FC = ({ recordings, filteredNames, onSubmit }) => { const [isExpanded, setIsExpanded] = React.useState(false); + const [filterValue, setFilterValue] = React.useState(''); const onSelect = React.useCallback( - (_, selection, isPlaceholder) => { - if (!isPlaceholder) { + (_, selection: string) => { + if (selection) { setIsExpanded(false); onSubmit(selection); } @@ -37,25 +51,68 @@ export const NameFilter: React.FC = ({ recordings, filteredName [onSubmit, setIsExpanded], ); + const onToggle = React.useCallback(() => setIsExpanded((isExpanded) => !isExpanded), [setIsExpanded]); + + const onInputChange = React.useCallback((_, inputVal: string) => setFilterValue(inputVal), [setFilterValue]); + const nameOptions = React.useMemo(() => { - return recordings - .map((r) => r.name) - .filter((n) => !filteredNames.includes(n)) - .map((option, index) => ); + return recordings.map((r) => r.name).filter((n) => !filteredNames.includes(n)); }, [recordings, filteredNames]); + const filteredNameOptions = React.useMemo(() => { + return !filterValue ? nameOptions : nameOptions.filter((n) => n.includes(filterValue.toLowerCase())); + }, [filterValue, nameOptions]); + + const selectOptionProps: SelectOptionProps[] = React.useMemo(() => { + if (!filteredNameOptions.length) { + return [{ isDisabled: true, children: `No results found for "${filterValue}"`, value: undefined }]; + } + return filteredNameOptions.map((n) => ({ children: n, value: n })); + }, [filteredNameOptions]); + + const toggle = React.useCallback( + (toggleRef: React.Ref) => ( + + + + + {filterValue ? ( + + ) : null} + + + + ), + [onToggle, isExpanded, filterValue, onInputChange, setFilterValue], + ); + return ( - + + {selectOptionProps.map(({ value, children }, index) => ( + + {children} + + ))} + ); }; diff --git a/src/app/Recordings/Filters/RecordingStateFilter.tsx b/src/app/Recordings/Filters/RecordingStateFilter.tsx index f956b04f17..38843957aa 100644 --- a/src/app/Recordings/Filters/RecordingStateFilter.tsx +++ b/src/app/Recordings/Filters/RecordingStateFilter.tsx @@ -15,7 +15,7 @@ */ import { RecordingState } from '@app/Shared/Services/api.types'; -import { Select, SelectOption, SelectVariant } from '@patternfly/react-core/deprecated'; +import { Badge, MenuToggle, MenuToggleElement, Select, SelectOption } from '@patternfly/react-core'; import * as React from 'react'; export interface RecordingStateFilterProps { @@ -27,25 +27,29 @@ export const RecordingStateFilter: React.FC = ({ filt const [isOpen, setIsOpen] = React.useState(false); const onSelect = React.useCallback( - (_, selection) => { + (_, selection: RecordingState) => { setIsOpen(false); onSelectToggle(selection); }, [setIsOpen, onSelectToggle], ); + const toggle = React.useCallback( + (toggleRef: React.Ref) => ( + setIsOpen((isOpen) => !isOpen)} isExpanded={isOpen}> + Filter by state + {filteredStates?.length ? {filteredStates.length} : null} + + ), + [filteredStates, setIsOpen], + ); + return ( - {Object.values(RecordingState).map((rs) => ( - + + {rs} + ))} ); diff --git a/src/app/Recordings/RecordingActions.tsx b/src/app/Recordings/RecordingActions.tsx index 30633557dc..ca5940398c 100644 --- a/src/app/Recordings/RecordingActions.tsx +++ b/src/app/Recordings/RecordingActions.tsx @@ -17,7 +17,8 @@ import { Recording, Target } from '@app/Shared/Services/api.types'; import { NotificationsContext } from '@app/Shared/Services/Notifications.service'; import { ServiceContext } from '@app/Shared/Services/Services'; import { useSubscriptions } from '@app/utils/hooks/useSubscriptions'; -import { Dropdown, DropdownItem, KebabToggle } from '@patternfly/react-core/deprecated'; +import { Dropdown, DropdownItem, DropdownList, MenuToggle, MenuToggleElement } from '@patternfly/react-core'; +import { EllipsisVIcon } from '@patternfly/react-icons'; import { Td } from '@patternfly/react-table'; import * as React from 'react'; import { Observable } from 'rxjs'; @@ -101,22 +102,41 @@ export const RecordingActions: React.FC = (props) => { [setIsOpen], ); + const toggle = React.useCallback( + (toggleRef: React.Ref) => ( + setIsOpen((isOpen) => !isOpen)} + isExpanded={isOpen} + data-quickstart-id="recording-kebab" + > + + + ), + [setIsOpen, isOpen], + ); + return ( } isPlain isOpen={isOpen} - dropdownItems={actionItems.map((action) => ( - onSelect(action)} data-quickstart-id={action.key}> - {action.title} - - ))} - /> + > + + {actionItems.map((action) => ( + onSelect(action)} data-quickstart-id={action.key}> + {action.title} + + ))} + + ); };