From 7df96323f0a98930e2e9d79c91ea74a3799fa936 Mon Sep 17 00:00:00 2001 From: Andrew Azores Date: Thu, 25 Jul 2024 16:58:48 -0400 Subject: [PATCH] chore(pf5): upgrade TargetContextSelector (#1304) --- .../CreateRecording/CustomRecordingForm.tsx | 3 +- .../Charts/mbean/MBeanMetricsChartCard.tsx | 1 + src/app/Rules/CreateRule.tsx | 7 +- src/app/TargetView/TargetContextSelector.tsx | 189 +++++++++--------- 4 files changed, 98 insertions(+), 102 deletions(-) diff --git a/src/app/CreateRecording/CustomRecordingForm.tsx b/src/app/CreateRecording/CustomRecordingForm.tsx index afb6b58563..bb9e7c0101 100644 --- a/src/app/CreateRecording/CustomRecordingForm.tsx +++ b/src/app/CreateRecording/CustomRecordingForm.tsx @@ -559,7 +559,8 @@ export const CustomRecordingForm: React.FC = () => { - Write contents of buffer onto disk. If disabled, the buffer acts as circular buffer only keeping the most recent Recording information + Write contents of buffer onto disk. If disabled, the buffer acts as circular buffer only keeping the + most recent Recording information diff --git a/src/app/Dashboard/Charts/mbean/MBeanMetricsChartCard.tsx b/src/app/Dashboard/Charts/mbean/MBeanMetricsChartCard.tsx index eace6bedcd..f48f0216de 100644 --- a/src/app/Dashboard/Charts/mbean/MBeanMetricsChartCard.tsx +++ b/src/app/Dashboard/Charts/mbean/MBeanMetricsChartCard.tsx @@ -399,6 +399,7 @@ export const MBeanMetricsChartCard: DashboardCardFC React.useEffect(() => { resizeObserver.current = getResizeObserver(containerRef.current, handleResize); + handleResize(); return resizeObserver.current; }, [resizeObserver, containerRef, handleResize]); diff --git a/src/app/Rules/CreateRule.tsx b/src/app/Rules/CreateRule.tsx index be0d0868a9..aa6c906a33 100644 --- a/src/app/Rules/CreateRule.tsx +++ b/src/app/Rules/CreateRule.tsx @@ -595,8 +595,8 @@ export const CreateRuleForm: React.FC = (_props) => { - Initial delay before archiving starts. The first archived copy will be made this long after the Recording is started. - The second archived copy will occur one Archival period later. + Initial delay before archiving starts. The first archived copy will be made this long after the Recording + is started. The second archived copy will occur one Archival period later. @@ -631,7 +631,8 @@ export const CreateRuleForm: React.FC = (_props) => { - The number of Archived Recording copies to preserve in archives for each target application affected by this rule. + The number of Archived Recording copies to preserve in archives for each target application affected by + this rule. diff --git a/src/app/TargetView/TargetContextSelector.tsx b/src/app/TargetView/TargetContextSelector.tsx index 236cca5ec4..68a76d2374 100644 --- a/src/app/TargetView/TargetContextSelector.tsx +++ b/src/app/TargetView/TargetContextSelector.tsx @@ -28,13 +28,14 @@ import { SearchInput, Dropdown, DropdownGroup, + DropdownItem, + DropdownList, MenuToggle, MenuSearch, MenuSearchInput, - SelectOption, - SelectGroup, + Split, + SplitItem, } from '@patternfly/react-core'; -import { SearchIcon } from '@patternfly/react-icons'; import * as React from 'react'; import { Link } from 'react-router-dom'; @@ -49,20 +50,23 @@ export const TargetContextSelector: React.FC = ({ cl const [targets, setTargets] = React.useState([]); const [selectedTarget, setSelectedTarget] = React.useState(); const [favorites, setFavorites] = React.useState(getFromLocalStorage('TARGET_FAVORITES', [])); + const [searchTerm, setSearchTerm] = React.useState(''); const [isTargetOpen, setIsTargetOpen] = React.useState(false); const [isLoading, setLoading] = React.useState(false); - const handleSelectToggle = React.useCallback(() => setIsTargetOpen((old) => !old), [setIsTargetOpen]); + const onToggleClick = React.useCallback(() => { + setIsTargetOpen((v) => !v); + }, [setIsTargetOpen]); - const handleTargetSelect = React.useCallback( - (_, { target }, isPlaceholder) => { + const onSelect = React.useCallback( + (_, target) => { setIsTargetOpen(false); - const toSelect: Target = isPlaceholder ? undefined : target; - if (!isEqualTarget(toSelect, selectedTarget)) { - context.target.setTarget(toSelect); + if (!isEqualTarget(target, selectedTarget)) { + setSelectedTarget(target); + context.target.setTarget(target); } }, - [setIsTargetOpen, selectedTarget, context.target], + [selectedTarget, setSelectedTarget, setIsTargetOpen, context.target], ); React.useEffect(() => { @@ -119,91 +123,67 @@ export const TargetContextSelector: React.FC = ({ cl const selectOptions = React.useMemo(() => { if (noOptions) { return [ - + No target found - , + , ]; } - const favSet = new Set(favorites); + const matchExp = new RegExp(searchTerm, 'i'); + const filteredTargets = targets.filter((t) => + [t.alias, t.connectUrl, getAnnotation(t.annotations.cryostat, 'REALM') ?? ''].some((v) => matchExp.test(v)), + ); const groupNames = new Set(); - targets.forEach((t) => groupNames.add(getAnnotation(t.annotations.cryostat, 'REALM') || 'Others')); + filteredTargets.forEach((t) => groupNames.add(getAnnotation(t.annotations.cryostat, 'REALM') || 'Others')); const options = Array.from(groupNames) .map((name) => ( - - {targets + + {filteredTargets .filter((t) => getAnnotation(t.annotations.cryostat, 'REALM') === name) .map((t: Target) => ( - getTargetRepresentation(t), - compareTo: (other) => other.target.connectUrl === t.connectUrl, - ...{ target: t }, // Bypassing type checks - }} - /> + description={t.connectUrl} + > + {t.alias} + ))} - + )) .sort((a, b) => `${a.props['label']}`.localeCompare(`${b.props['label']}`)); - const favGroup = favorites.length - ? [ - - {favorites - .map((f) => targets.find((t) => t.connectUrl === f)) - .filter((t) => t !== undefined) - .map((t: Target) => ( - getTargetRepresentation(t), - compareTo: (other) => other.target.connectUrl === t.connectUrl, - ...{ target: t }, - }} - /> - ))} - , - , - ] - : []; + const favGroup = + !searchTerm && favorites.length + ? [ + + {favorites + .map((f) => targets.find((t) => t.connectUrl === f)) + .filter((t) => t !== undefined) + .map((t: Target) => ( + + {t.alias} + + ))} + , + , + ] + : []; return favGroup.concat(options); - }, [targets, noOptions, favorites]); + }, [targets, noOptions, favorites, searchTerm]); - const handleTargetFilter = React.useCallback( - (_, value: string) => { - if (!value || noOptions) { - // In case of empty options, placeholder is returned. - return selectOptions; + const onFavoriteClick = React.useCallback( + (_, item: Target, actionId: string) => { + if (!actionId) { + return; } - - const matchExp = new RegExp(value, 'i'); - return selectOptions - .filter((grp) => grp.props.children) - .map((grp) => - React.cloneElement(grp, { - children: grp.props.children.filter((option) => { - const { target } = option.props.value; - return matchExp.test(target.connectUrl) || matchExp.test(target.alias); - }), - }), - ) - .filter((grp) => grp.props.children.length > 0); - }, - [selectOptions, noOptions], - ); - - const handleFavorite = React.useCallback( - (itemId: string, isFavorite: boolean) => { setFavorites((old) => { - const toUpdate = !isFavorite ? [...old, itemId] : old.filter((f) => f !== itemId); + const prevFav = old.includes(item.connectUrl); + const toUpdate = prevFav ? old.filter((f) => f !== item.connectUrl) : [...old, item.connectUrl]; saveToLocalStorage('TARGET_FAVORITES', toUpdate); return toUpdate; }); @@ -211,6 +191,13 @@ export const TargetContextSelector: React.FC = ({ cl [setFavorites], ); + const onClearSelection = React.useCallback(() => { + setIsTargetOpen(false); + removeFromLocalStorage('TARGET'); + setSelectedTarget(undefined); + context.target.setTarget(undefined); + }, [setSelectedTarget, setIsTargetOpen, context.target]); + const selectionPrefix = React.useMemo( () => (!selectedTarget ? undefined : Target:), [selectedTarget], @@ -218,11 +205,20 @@ export const TargetContextSelector: React.FC = ({ cl const selectFooter = React.useMemo( () => ( - - - + + + + + + + + ), - [], + [onClearSelection], ); return ( @@ -233,44 +229,41 @@ export const TargetContextSelector: React.FC = ({ cl ) : ( setIsTargetOpen(isOpen)} + onOpenChangeKeys={['Escape']} + onSelect={onSelect} + onActionClick={onFavoriteClick} toggle={(toggleRef) => ( handleTargetSelect(undefined, { target: selectedTarget }, undefined)} - variant="plain" + onClick={onToggleClick} + isExpanded={isTargetOpen} + variant="plainText" icon={selectionPrefix} > - {!selectedTarget - ? undefined - : { - toString: () => getTargetRepresentation(selectedTarget), - compareTo: (other) => other.target.connectUrl === selectedTarget.connectUrl, - ...{ target: selectedTarget }, - }} + {!selectedTarget ? 'Select a Target' : getTargetRepresentation(selectedTarget)} )} popperProps={{ enableFlip: true, appendTo: portalRoot, - //maxHeight: '30em', }} > - - + setSearchTerm(v)} + /> - {favorites} - {handleFavorite} - {selectOptions} + + {selectOptions} {selectFooter} )}