diff --git a/packages/design-system/src/components/table/components/tableBody/bodyCell.tsx b/packages/design-system/src/components/table/components/tableBody/bodyCell.tsx index c0da64279..1d3877378 100644 --- a/packages/design-system/src/components/table/components/tableBody/bodyCell.tsx +++ b/packages/design-system/src/components/table/components/tableBody/bodyCell.tsx @@ -27,8 +27,6 @@ import type { TableRow } from '../../useTable'; interface BodyCellProps { cell?: () => React.JSX.Element; width: number; - isHighlighted?: boolean; - isRowFocused: boolean; row: TableRow; hasIcon?: boolean; showIcon?: boolean | null; @@ -42,12 +40,10 @@ interface BodyCellProps { const BodyCell = ({ cell, width, - isRowFocused, row, hasIcon = false, showIcon = false, icon, - isHighlighted = false, rowHeightClass, }: BodyCellProps) => { const IconElement = icon?.Element; @@ -70,13 +66,9 @@ const BodyCell = ({ e.stopPropagation(); } }} - className={`flex box-border outline-0 px-1 py-px text-xs ${ - isHighlighted - ? `${ - isRowFocused ? 'text-white' : 'dark:text-dirty-red text-dirty-red' - }` - : 'dark:text-bright-gray' - } cursor-default flex-1 ${rowHeightClass ?? 'h-5'}`} + className={`flex box-border outline-0 px-1 py-px text-xs cursor-default flex-1 ${ + rowHeightClass ?? 'h-5' + }`} > {hasIcon && (
diff --git a/packages/design-system/src/components/table/components/tableBody/bodyRow.tsx b/packages/design-system/src/components/table/components/tableBody/bodyRow.tsx index ff4ebe5d6..22ff17541 100644 --- a/packages/design-system/src/components/table/components/tableBody/bodyRow.tsx +++ b/packages/design-system/src/components/table/components/tableBody/bodyRow.tsx @@ -105,7 +105,7 @@ const BodyRow = ({ style={{ backgroundColor: verticalBarColorHash, }} - className="absolute block top-0 bottom-0 left-0 border-l-2 border-emerald-600 dark:border-leaf-green-dark" + className="absolute block top-0 bottom-0 left-0 w-1 h-full" /> )} {columns.map( @@ -124,8 +124,6 @@ const BodyRow = ({ onRowClick={onRowClick} cell={row[accessorKey]?.value} width={width || 0} - isHighlighted={isHighlighted} - isRowFocused={rowKey === selectedKey} row={row} hasIcon={enableBodyCellPrefixIcon} showIcon={ diff --git a/packages/design-system/src/components/table/useTable/provider.tsx b/packages/design-system/src/components/table/useTable/provider.tsx index 36e3e9fe4..ffdc4f8b0 100644 --- a/packages/design-system/src/components/table/useTable/provider.tsx +++ b/packages/design-system/src/components/table/useTable/provider.tsx @@ -42,6 +42,7 @@ export const TableProvider = ({ conditionalTableRowClassesHandler, exportTableData, hasVerticalBar, + getVerticalBarColorHash, isRowSelected, children, }: PropsWithChildren) => { @@ -167,6 +168,7 @@ export const TableProvider = ({ conditionalTableRowClassesHandler, exportTableData, hasVerticalBar, + getVerticalBarColorHash, }, }} > diff --git a/packages/explorable-explanations/src/protectedAudience/app.js b/packages/explorable-explanations/src/protectedAudience/app.js index 5d3b4b544..fe045b860 100644 --- a/packages/explorable-explanations/src/protectedAudience/app.js +++ b/packages/explorable-explanations/src/protectedAudience/app.js @@ -64,6 +64,7 @@ const app = { visitedIndexOrderTracker: -1, isRevisitingNodeInInteractiveMode: false, setCurrentSite: () => null, + setHighlightedInterestGroup: () => null, setPlayState: () => null, usedNextOrPrev: false, promiseQueue: null, diff --git a/packages/explorable-explanations/src/protectedAudience/index.js b/packages/explorable-explanations/src/protectedAudience/index.js index 96cf7ddac..78b48eaeb 100644 --- a/packages/explorable-explanations/src/protectedAudience/index.js +++ b/packages/explorable-explanations/src/protectedAudience/index.js @@ -638,6 +638,10 @@ export const interestGroupSketch = (p) => { app.setCurrentSite = props.setCurrentSite; } + if (props.setHighlightedInterestGroup) { + app.setHighlightedInterestGroup = props.setHighlightedInterestGroup; + } + if (app.setPlayState) { app.setPlayState = props.setPlayState; } diff --git a/packages/explorable-explanations/src/protectedAudience/modules/bubbles.js b/packages/explorable-explanations/src/protectedAudience/modules/bubbles.js index dcba4d6f4..59bb3e0ac 100644 --- a/packages/explorable-explanations/src/protectedAudience/modules/bubbles.js +++ b/packages/explorable-explanations/src/protectedAudience/modules/bubbles.js @@ -374,6 +374,7 @@ bubbles.showExpandedBubbles = () => { app.openButton.style.display = 'none'; app.minifiedBubbleContainer.classList.toggle('expanded', true); }; + bubbles.showMinifiedBubbles = () => { app.bubbles.minifiedSVG = bubbles.bubbleChart(app.bubbles.positions, { label: (d) => @@ -495,9 +496,13 @@ bubbles.bubbleChart = ( .attr('style', `${!app.bubbles.isExpanded ? 'pointer-events: none;' : ''}`) .attr('transform', (d) => `translate(${d.x},${d.y})`); - const eventHandler = (event) => { + const eventHandler = (event, d) => { // eslint-disable-next-line no-console console.log(event); + app.setHighlightedInterestGroup({ + interestGroupName: titles[d.data].split('\n')[0], + color: app.color(groups[d.data]), + }); event.stopPropagation(); }; @@ -518,7 +523,10 @@ bubbles.bubbleChart = ( ? 'none' : fill ) - .on('click', !app.bubbles.isExpanded ? null : eventHandler) + .on( + 'click', + !app.bubbles.isExpanded ? null : (event, d) => eventHandler(event, d) + ) .attr('fill-opacity', fillOpacity) .attr('r', (d) => { return d.r; diff --git a/packages/extension/src/view/devtools/components/privateAdvertising/protectedAudience/explorableExplanation/index.tsx b/packages/extension/src/view/devtools/components/privateAdvertising/protectedAudience/explorableExplanation/index.tsx index 92a3e4797..ac7ea9e82 100644 --- a/packages/extension/src/view/devtools/components/privateAdvertising/protectedAudience/explorableExplanation/index.tsx +++ b/packages/extension/src/view/devtools/components/privateAdvertising/protectedAudience/explorableExplanation/index.tsx @@ -202,6 +202,25 @@ const ExplorableExplanation = () => { return interestGroupsRef.current; }, [currentSiteData]); + const [highlightedInterestGroup, setHighlightedInterestGroup] = useState<{ + interestGroupName: string; + color: string; + } | null>(null); + + const timeoutRef = useRef | null>(null); + + useEffect(() => { + timeoutRef.current = setTimeout(() => { + setHighlightedInterestGroup(null); + }, 1500); + + return () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + }; + }, [highlightedInterestGroup]); + const tabItems = useMemo( () => [ { @@ -210,6 +229,7 @@ const ExplorableExplanation = () => { Element: IGTable, props: { interestGroupDetails: [...(interestGroupData as InterestGroups[])], + highlightedInterestGroup, }, }, }, @@ -251,7 +271,12 @@ const ExplorableExplanation = () => { }, }, ], - [auctionsData, customAdsAndBidders, interestGroupData] + [ + auctionsData, + customAdsAndBidders, + highlightedInterestGroup, + interestGroupData, + ] ); return ( @@ -259,6 +284,7 @@ const ExplorableExplanation = () => { ); diff --git a/packages/extension/src/view/devtools/components/privateAdvertising/protectedAudience/explorableExplanation/panel.tsx b/packages/extension/src/view/devtools/components/privateAdvertising/protectedAudience/explorableExplanation/panel.tsx index 5b113ef46..fc520cda9 100644 --- a/packages/extension/src/view/devtools/components/privateAdvertising/protectedAudience/explorableExplanation/panel.tsx +++ b/packages/extension/src/view/devtools/components/privateAdvertising/protectedAudience/explorableExplanation/panel.tsx @@ -48,9 +48,19 @@ declare module 'react' { interface PanelProps { setCurrentSite: React.Dispatch>; currentSiteData: CurrentSiteData | null; + setHighlightedInterestGroup: React.Dispatch< + React.SetStateAction<{ + interestGroupName: string; + color: string; + } | null> + >; } -const Panel = ({ currentSiteData, setCurrentSite }: PanelProps) => { +const Panel = ({ + currentSiteData, + setCurrentSite, + setHighlightedInterestGroup, +}: PanelProps) => { const [play, setPlay] = useState(true); const [sliderStep, setSliderStep] = useState(1); const [interactiveMode, _setInteractiveMode] = useState(false); @@ -279,6 +289,7 @@ const Panel = ({ currentSiteData, setCurrentSite }: PanelProps) => { speedMultiplier={2 * sliderStep} setCurrentSite={setCurrentSite} setPlayState={setPlay} + setHighlightedInterestGroup={setHighlightedInterestGroup} /> diff --git a/packages/extension/src/view/devtools/components/privateAdvertising/protectedAudience/interestGroups/table.tsx b/packages/extension/src/view/devtools/components/privateAdvertising/protectedAudience/interestGroups/table.tsx index 32b36c80d..014139dd3 100644 --- a/packages/extension/src/view/devtools/components/privateAdvertising/protectedAudience/interestGroups/table.tsx +++ b/packages/extension/src/view/devtools/components/privateAdvertising/protectedAudience/interestGroups/table.tsx @@ -36,9 +36,16 @@ import { prettyPrintJson } from 'pretty-print-json'; interface InterestGroupsProps { interestGroupDetails: InterestGroupsType[]; + highlightedInterestGroup?: { + interestGroupName: string; + color: string; + } | null; } -const IGTable = ({ interestGroupDetails }: InterestGroupsProps) => { +const IGTable = ({ + interestGroupDetails, + highlightedInterestGroup, +}: InterestGroupsProps) => { const [selectedRow, setSelectedRow] = useState(null); const [filterData, setFilterData] = useState(false); @@ -110,6 +117,49 @@ const IGTable = ({ interestGroupDetails }: InterestGroupsProps) => { [] ); + const modifiedInterestGroupDetails = useMemo(() => { + if (!highlightedInterestGroup) { + return interestGroupDetails; + } + + return interestGroupDetails.map((interestGroup) => { + const isHighlighted = + interestGroup.name === highlightedInterestGroup.interestGroupName; + + return { + ...interestGroup, + highlighted: isHighlighted, + }; + }); + }, [interestGroupDetails, highlightedInterestGroup]); + + const hasVerticalBar = useCallback((row: TableRow) => { + return Boolean(row.originalData.highlighted); + }, []); + + const getVerticalBarColorHash = useCallback( + (row: TableRow) => { + return row.originalData.highlighted + ? highlightedInterestGroup?.color ?? '' + : ''; + }, + [highlightedInterestGroup?.color] + ); + + const conditionalTableRowClassesHandler = useCallback( + (row: TableRow, isRowFocused: boolean) => { + const isHighlighted = row?.originalData?.highlighted; + const tableRowClassName = isHighlighted + ? isRowFocused + ? 'bg-selection-yellow-dark dark:bg-selection-yellow-light text-black transition-colors' + : 'bg-royal-blue text-white dark:bg-medium-persian-blue dark:text-chinese-silver' + : ''; + + return tableRowClassName; + }, + [] + ); + const handleChange = useCallback( (event: React.ChangeEvent) => { setFilterData(event.target.checked); @@ -153,8 +203,8 @@ const IGTable = ({ interestGroupDetails }: InterestGroupsProps) => { event.type === 'leave' || event.type === 'join' ) } @@ -162,6 +212,9 @@ const IGTable = ({ interestGroupDetails }: InterestGroupsProps) => { tableFilterData={tableFilters} tableSearchKeys={undefined} tablePersistentSettingsKey="interestGroupsTable" + conditionalTableRowClassesHandler={conditionalTableRowClassesHandler} + getVerticalBarColorHash={getVerticalBarColorHash} + hasVerticalBar={hasVerticalBar} onRowClick={(row) => { setSelectedRow(row as InterestGroupsType); }}