Skip to content

Commit

Permalink
fix(tippy): wip related to blur and focusBackOnTrigger
Browse files Browse the repository at this point in the history
  • Loading branch information
rstachof authored and robstax committed Sep 11, 2024
1 parent 3600b26 commit accd567
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 40 deletions.
1 change: 1 addition & 0 deletions src/components/Popover/LazyTippy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Tippy, { TippyProps } from '@tippyjs/react';
export type LazyTippyProps = TippyProps & {
setInstance?: (instance?: TippyInstance) => void;
continuePropagationOnTrigger?: boolean;
hasRelatedTarget?: boolean;
};

/**
Expand Down
75 changes: 40 additions & 35 deletions src/components/Popover/Popover.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import MeetingListItem from '../MeetingListItem';
import SearchInput from '../SearchInput';
import List from '../List';
import AriaToolbarItem from '../AriaToolbarItem';
import ListItemBase from '../ListItemBase';

export default {
title: 'Momentum UI/Popover',
Expand Down Expand Up @@ -490,41 +491,46 @@ Common.parameters = {

const WithMeetingListItemWithAvatarWithPopover = Template<PopoverProps>((args) => {
return (
<Popover
{...args}
triggerComponent={
<MeetingListItem style={{ margin: '10rem auto', display: 'flex' }}>
<Popover
{...args}
triggerComponent={
<Avatar
// eslint-disable-next-line
onPress={() => {}}
initials="AB"
>
Hover or click me!
</Avatar>
}
>
<div>
<ButtonPill>test 1</ButtonPill>
<ButtonPill>test 2</ButtonPill>
<ButtonPill>test 3</ButtonPill>
</div>
</Popover>
test
</MeetingListItem>
}
trigger="click"
interactive
>
<div>
<ButtonPill>test 4</ButtonPill>
<ButtonPill>test 5</ButtonPill>
<ButtonPill>test 6</ButtonPill>
<>
<div id="outer">
<Popover
{...args}
triggerComponent={
<ButtonPill>test 1</ButtonPill>
}
trigger="click"
interactive
hideOnBlur
disableFocusLock
>
<List listSize={3}>
<ListItemBase itemIndex={0}>test 4</ListItemBase>
<ListItemBase itemIndex={1}>test 5</ListItemBase>
<ListItemBase itemIndex={2}>test 6</ListItemBase>
</List>
</Popover>
</div>
</Popover>
<div id="other">
<Popover
{...args}
triggerComponent={
<ButtonPill>test 2</ButtonPill>
}
trigger="click"
interactive
hideOnBlur
disableFocusLock
>
<List listSize={3}>
<ListItemBase itemIndex={0}>test 4</ListItemBase>
<ListItemBase itemIndex={1}>test 5</ListItemBase>
<ListItemBase itemIndex={2}>test 6</ListItemBase>
</List>
</Popover>
</div>
</>
);

}).bind({});

WithMeetingListItemWithAvatarWithPopover.argTypes = { ...argTypes };
Expand All @@ -534,7 +540,6 @@ WithMeetingListItemWithAvatarWithPopover.args = {
placement: PLACEMENTS.TOP,
showArrow: true,
interactive: true,
appendTo: () => document.querySelector('#theme-provider'),
};

export {
Expand All @@ -553,4 +558,4 @@ export {
WithSearchInput,
WithMeetingListItemWithButtonsWithPopoverInList,
WithMeetingListItemWithAvatarWithPopover,
};
};
11 changes: 7 additions & 4 deletions src/components/Popover/Popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { addTippyPlugins } from './Popover.utils';
import { v4 as uuidV4 } from 'uuid';
import { PopoverInstance } from '.';

type CustomInstance = PopoverInstance & { props: Props } & {hasRelatedTarget?: boolean;};

/**
* The Popover component allows adding a Popover to whatever provided
* `triggerComponent`. It will show the Popover after a specific event, which is
Expand Down Expand Up @@ -84,7 +86,7 @@ const Popover = forwardRef((props: Props, ref: ForwardedRef<HTMLElement>) => {
? DEFAULTS.FOCUS_BACK_ON_TRIGGER_COMPONENT_INTERACTIVE
: DEFAULTS.FOCUS_BACK_ON_TRIGGER_COMPONENT_NON_INTERACTIVE);

const popoverInstance = React.useRef<PopoverInstance>(undefined);
const popoverInstance = React.useRef<CustomInstance>(undefined);

const generatedTriggerIdRef = useRef(uuidV4());
const generatedTriggerId = generatedTriggerIdRef.current;
Expand All @@ -105,7 +107,7 @@ const Popover = forwardRef((props: Props, ref: ForwardedRef<HTMLElement>) => {
const arrowId = React.useMemo(() => `${ARROW_ID}${uuidV4()}`, []);

const popoverSetInstance = useCallback(
(instance?: PopoverInstance) => {
(instance?: CustomInstance) => {
popoverInstance.current = instance;
setInstance?.(instance);
},
Expand All @@ -119,12 +121,12 @@ const Popover = forwardRef((props: Props, ref: ForwardedRef<HTMLElement>) => {
// needs special handling since FocusScope doesn't work with the Popover from Tippy
// needs to focus back to the reference item when the popover is completely hidden
const handleOnPopoverHidden = useCallback(() => {
if (focusBackOnTrigger) {
if (focusBackOnTrigger && !popoverInstance?.current?.hasRelatedTarget) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
popoverInstance.current?.reference?.focus();
}
}, [focusBackOnTrigger]);
}, [focusBackOnTrigger, popoverInstance?.current?.hasRelatedTarget]);

useEffect(() => {
firstFocusElement?.focus();
Expand Down Expand Up @@ -249,6 +251,7 @@ const Popover = forwardRef((props: Props, ref: ForwardedRef<HTMLElement>) => {
}}
setInstance={popoverSetInstance}
zIndex={zIndex}
hasRelatedTarget={false}
>
{clonedTriggerComponent}
</LazyTippy>
Expand Down
2 changes: 2 additions & 0 deletions src/components/Popover/Popover.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,4 +242,6 @@ export interface Props extends PopoverCommonStyleProps, Partial<LifecycleHooks>
* @default `false`
*/
disableFocusLock?: boolean;

hasRelatedTarget?: boolean;
}
8 changes: 7 additions & 1 deletion src/components/Popover/tippy-plugins/hideOnBlurPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@ export interface PopperBlurPluginProps extends Props {
isChildPopoverOpen?: boolean;
}

type CustomInstance = PopoverInstance & { props: PopperBlurPluginProps } & {
hasRelatedTarget?: boolean;
};

export const hideOnBlurPlugin: Plugin = {
name: 'isChildPopoverOpen',
defaultValue: false,
fn(instance: PopoverInstance & { props: PopperBlurPluginProps }) {
fn(instance: CustomInstance) {
const focusOutHandler = (event) => {
instance.hasRelatedTarget = !!event.relatedTarget;

if (
!instance.props.isChildPopoverOpen &&
event.relatedTarget &&
Expand Down
1 change: 1 addition & 0 deletions src/components/Popover/tippy-plugins/hideOnEscPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const openedTippyInstances: TippyInstance[] = [];
// hide the last opened popover when Escape key is pressed
function onKeyDown(event: KeyboardEvent) {
if (event.key === 'Escape' && openedTippyInstances.length !== 0) {
// RS_TODO: i think the better thing to do would be set custom attri on instance here
const lastIdx = openedTippyInstances.length - 1;
openedTippyInstances[lastIdx].hide();
}
Expand Down

0 comments on commit accd567

Please sign in to comment.