Skip to content

Commit

Permalink
replaced the key hack with ref approach
Browse files Browse the repository at this point in the history
  • Loading branch information
bobular committed Oct 31, 2024
1 parent 337348e commit 2520735
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 86 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { ReactNode, useState, useEffect, useMemo } from 'react';
import {
ReactNode,
useState,
useEffect,
useMemo,
forwardRef,
useImperativeHandle,
} from 'react';
import { Popover } from '@material-ui/core';
import SwissArmyButton from '../SwissArmyButton';
import { gray } from '../../../definitions/colors';
Expand Down Expand Up @@ -66,6 +73,11 @@ const defaultStyle: ButtonStyleSpec = {
},
};

export interface PopoverButtonHandle {
/** Closes the popover */
close: () => void;
}

export interface PopoverButtonProps {
/** Contents of the menu when opened */
children: ReactNode;
Expand All @@ -87,86 +99,96 @@ export interface PopoverButtonProps {
/**
* Renders a button that display `children` in a popover widget.
*/
export default function PopoverButton(props: PopoverButtonProps) {
const {
children,
buttonDisplayContent,
onClose,
setIsPopoverOpen,
isDisabled = false,
styleOverrides = {},
} = props;
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);

const finalStyle = useMemo(
() => merge({}, defaultStyle, styleOverrides),
[styleOverrides]
);

const onCloseHandler = () => {
setAnchorEl(null);
onClose && onClose();
};

useEffect(() => {
if (!setIsPopoverOpen) return;
if (anchorEl) {
setIsPopoverOpen(true);
} else {
setIsPopoverOpen(false);
}
}, [anchorEl, setIsPopoverOpen]);

const menu = (
<Popover
id="dropdown"
aria-expanded={!!anchorEl}
open={Boolean(anchorEl)}
onClose={onCloseHandler}
anchorEl={anchorEl}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'left',
}}
keepMounted
>
{children}
</Popover>
);

const button = (
<SwissArmyButton
text={buttonDisplayContent}
textTransform="none"
onPress={(event) => setAnchorEl(event.currentTarget)}
disabled={isDisabled}
styleSpec={finalStyle}
icon={ArrowDown}
iconPosition="right"
additionalAriaProperties={{
'aria-controls': 'dropdown',
'aria-haspopup': 'true',
}}
/>
);

return (
<div
style={{
width: 'fit-content',
...(isDisabled ? { cursor: 'not-allowed' } : {}),
}}
onClick={(event) => {
// prevent click event from propagating to ancestor nodes
event.stopPropagation();
}}
>
{button}
{menu}
</div>
);
}

const PopoverButton = forwardRef<PopoverButtonHandle, PopoverButtonProps>(
function PopoverButton(props, ref) {
const {
children,
buttonDisplayContent,
onClose,
setIsPopoverOpen,
isDisabled = false,
styleOverrides = {},
} = props;
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);

const finalStyle = useMemo(
() => merge({}, defaultStyle, styleOverrides),
[styleOverrides]
);

const onCloseHandler = () => {
setAnchorEl(null);
onClose && onClose();
};

// Expose the `close()` method to external components via ref
useImperativeHandle(ref, () => ({
close: onCloseHandler,
}));

useEffect(() => {
if (!setIsPopoverOpen) return;
if (anchorEl) {
setIsPopoverOpen(true);
} else {
setIsPopoverOpen(false);
}
}, [anchorEl, setIsPopoverOpen]);

const menu = (
<Popover
id="dropdown"
aria-expanded={!!anchorEl}
open={Boolean(anchorEl)}
onClose={onCloseHandler}
anchorEl={anchorEl}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'left',
}}
keepMounted
>
{children}
</Popover>
);

const button = (
<SwissArmyButton
text={buttonDisplayContent}
textTransform="none"
onPress={(event) => setAnchorEl(event.currentTarget)}
disabled={isDisabled}
styleSpec={finalStyle}
icon={ArrowDown}
iconPosition="right"
additionalAriaProperties={{
'aria-controls': 'dropdown',
'aria-haspopup': 'true',
}}
/>
);

return (
<div
style={{
width: 'fit-content',
...(isDisabled ? { cursor: 'not-allowed' } : {}),
}}
onClick={(event) => {
// prevent click event from propagating to ancestor nodes
event.stopPropagation();
}}
>
{button}
{menu}
</div>
);
}
);

export default PopoverButton;
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, {
useCallback,
useDeferredValue,
useMemo,
useRef,
useState,
} from 'react';
import TreeTable from '@veupathdb/components/lib/components/tidytree/TreeTable';
Expand All @@ -23,7 +24,9 @@ import { PfamDomainArchitecture } from 'ortho-client/components/pfam-domains/Pfa
import { extractPfamDomain } from 'ortho-client/records/utils';
import Banner from '@veupathdb/coreui/lib/components/banners/Banner';
import { RowCounter } from '@veupathdb/coreui/lib/components/Mesa';
import PopoverButton from '@veupathdb/coreui/lib/components/buttons/PopoverButton/PopoverButton';
import PopoverButton, {
PopoverButtonHandle,
} from '@veupathdb/coreui/lib/components/buttons/PopoverButton/PopoverButton';
import { PfamDomain } from 'ortho-client/components/pfam-domains/PfamDomain';
import {
FilledButton,
Expand Down Expand Up @@ -384,6 +387,10 @@ export function RecordTable_Sequences(
[finalNewick, treeWidth, highlightColor, highlightedNodes]
);

const proteinFilterButtonRef = useRef<PopoverButtonHandle>(null);

// None shall pass! (hooks, at least)

if (
!mesaState ||
!sortedRows ||
Expand Down Expand Up @@ -505,24 +512,26 @@ export function RecordTable_Sequences(
<OutlinedButton
text="Reset protein filter"
onPress={() => {
proteinFilterButtonRef.current?.close();
setProteinFilterIds([]);
}}
/>
);

const updateProteinFilterIds = () => {
proteinFilterButtonRef.current?.close();
setProteinFilterIds(highlightedNodes);
setHighlightedNodes([]);
};

const proteinFilter = (
<PopoverButton
ref={proteinFilterButtonRef}
buttonDisplayContent={`Proteins${
volatileProteinFilterIds.length > 0
? ` (${volatileProteinFilterIds.length})`
: ''
}${highlightedNodes.length > 0 ? '*' : ''}`}
key={volatileProteinFilterIds.join(':')}
>
<div
style={{
Expand Down

0 comments on commit 2520735

Please sign in to comment.