Skip to content

Commit

Permalink
improvement: popover open on click
Browse files Browse the repository at this point in the history
The popover is also used to open on click in projects, but now the
target has a role button with a button inside. The tag should be a
button and the content should only be textual so there's no nested
buttons.

Added support for openOnClick to Popover.
  • Loading branch information
Gido Manders committed Dec 12, 2024
1 parent 9b1fd0c commit a117041
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 68 deletions.
25 changes: 24 additions & 1 deletion src/core/Popover/Popover.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { fireEvent, render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';

import { Popover } from './Popover';
Expand Down Expand Up @@ -70,4 +70,27 @@ describe('Component: Popover', () => {
expect(container).toMatchSnapshot();
});
});

describe('events', () => {
it('should call onClick when tag is clicked', () => {
const onClickSpy = jest.fn();
render(
<Popover onClick={onClickSpy} target={<>Click this</>} tag="button">
Popover content
</Popover>
);
fireEvent.click(screen.getByText('Click this'));
expect(onClickSpy).toBeCalledTimes(1);
});

it('should open on click when openOnClick is true', () => {
render(
<Popover openOnClick={true} target={<>Click this</>} tag="button">
Popover content
</Popover>
);
fireEvent.click(screen.getByText('Click this'));
expect(screen.queryByText('Popover content')).toBeInTheDocument();
});
});
});
41 changes: 38 additions & 3 deletions src/core/Popover/Popover.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { CSSProperties } from 'react';
import React, { CSSProperties, useEffect, useState } from 'react';
import Tippy from '@tippyjs/react';
import { TippyPlacement } from '../types';

Expand All @@ -12,6 +12,19 @@ type Props = {
*/
isOpen?: boolean;

/**
* Optionally whether the popover should open on click instead of hover.
*/
openOnClick?: boolean;

/**
* Optional callback that gets triggered when the tag is clicked.
* When openOnClick is true, this component will take care of its visibility
* state, unless onClick is defined.
* This should be used when wanting to take complete control over the popover.
*/
onClick?: () => void;

/**
* Optionally callback that gets triggered when clicked outside the popover.
* Is useful for when wanting to take complete control over the popover.
Expand Down Expand Up @@ -83,15 +96,31 @@ export function Popover({
tag = 'span',
className,
isOpen,
openOnClick,
onClick,
onClickOutside,
style,
maxWidth
}: Props) {
const Tag = tag;

const [visible, setVisible] = useState(openOnClick ? !!isOpen : undefined);

useEffect(() => {
setVisible(openOnClick ? !!isOpen : undefined);
}, [openOnClick, isOpen]);

function tagClicked() {
if (onClick) {
onClick();
} else if (openOnClick) {
setVisible(!visible);
}
}

return (
<Tippy
visible={isOpen}
visible={visible}
onClickOutside={onClickOutside}
className="border-0 tippy-popover"
content={children}
Expand All @@ -101,7 +130,13 @@ export function Popover({
zIndex={1049} // One level below bootstrap's modal
maxWidth={maxWidth}
>
<Tag className={className} style={style} tabIndex={0} role="button">
<Tag
className={className}
style={style}
tabIndex={0}
role="button"
onClick={tagClicked}
>
{target}
</Tag>
</Tippy>
Expand Down
66 changes: 2 additions & 64 deletions src/core/Popover/__snapshots__/Popover.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -17,52 +17,21 @@ exports[`Component: Popover ui default 1`] = `
exports[`Component: Popover ui open 1`] = `
<div>
<span
aria-expanded="true"
aria-expanded="false"
role="button"
tabindex="0"
>
<div>
The popover should be wrapped around this div, in a span
</div>
</span>
<div
data-tippy-root=""
id="tippy-2"
style="z-index: 1049; visibility: visible; transition: none; position: absolute; left: 0px; top: 0px; margin: 0px; bottom: 0px; transform: translate(0px, -7px);"
>
<div
class="tippy-box border-0 tippy-popover"
data-animation="fade"
data-escaped=""
data-placement="top"
data-reference-hidden=""
data-state="hidden"
role="tooltip"
style="max-width: 350px; transition-duration: 0ms;"
tabindex="-1"
>
<div
class="tippy-content"
data-state="hidden"
style="transition-duration: 0ms;"
>
<div>
Popover content
</div>
</div>
<div
class="tippy-arrow"
style="position: absolute; left: 0px; transform: translate(3px, 0px);"
/>
</div>
</div>
</div>
`;

exports[`Component: Popover ui with optional properties 1`] = `
<div>
<span
aria-expanded="true"
aria-expanded="false"
role="button"
style="margin-top: 5px; padding: 10px;"
tabindex="0"
Expand All @@ -71,36 +40,5 @@ exports[`Component: Popover ui with optional properties 1`] = `
The popover should be wrapped around this div, in a div
</div>
</span>
<div
data-tippy-root=""
id="tippy-5"
style="z-index: 1049; visibility: visible; transition: none; position: absolute; left: 0px; top: 0px; margin: 0px; transform: translate(0px, 10px);"
>
<div
class="tippy-box border-0 tippy-popover"
data-animation="fade"
data-escaped=""
data-placement="bottom"
data-reference-hidden=""
data-state="hidden"
role="tooltip"
style="max-width: 350px; transition-duration: 0ms;"
tabindex="-1"
>
<div
class="tippy-content"
data-state="hidden"
style="transition-duration: 0ms;"
>
<div>
Popover content
</div>
</div>
<div
class="tippy-arrow"
style="position: absolute; left: 0px; transform: translate(3px, 0px);"
/>
</div>
</div>
</div>
`;

0 comments on commit a117041

Please sign in to comment.