Skip to content

Commit

Permalink
feat: add Popover component (#247)
Browse files Browse the repository at this point in the history
  • Loading branch information
web-padawan authored May 28, 2024
1 parent 4cc6080 commit 3f86d0b
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 0 deletions.
14 changes: 14 additions & 0 deletions dev/pages/Popover.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Button } from '../../packages/react-components/src/Button.js';
import { Popover } from '../../packages/react-components/src/Popover.js';

export default function () {
return (
<>
<Popover for="button">
<Button>Click me</Button>
</Popover>

<Button id="button">Toggle popover</Button>
</>
);
}
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions packages/react-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"@vaadin/number-field": "24.5.0-alpha1",
"@vaadin/overlay": "24.5.0-alpha1",
"@vaadin/password-field": "24.5.0-alpha1",
"@vaadin/popover": "24.5.0-alpha1",
"@vaadin/progress-bar": "24.5.0-alpha1",
"@vaadin/radio-group": "24.5.0-alpha1",
"@vaadin/scroller": "24.5.0-alpha1",
Expand Down Expand Up @@ -318,6 +319,10 @@
"types": "./PasswordField.d.ts",
"default": "./PasswordField.js"
},
"./Popover.js": {
"types": "./Popover.d.ts",
"default": "./Popover.js"
},
"./ProgressBar.js": {
"types": "./ProgressBar.d.ts",
"default": "./ProgressBar.js"
Expand Down Expand Up @@ -440,6 +445,7 @@
"./Notification": "./Notification.js",
"./NumberField": "./NumberField.js",
"./PasswordField": "./PasswordField.js",
"./Popover": "./Popover.js",
"./ProgressBar": "./ProgressBar.js",
"./RadioButton": "./RadioButton.js",
"./RadioGroup": "./RadioGroup.js",
Expand Down
45 changes: 45 additions & 0 deletions packages/react-components/src/Popover.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {
type ComponentType,
type ForwardedRef,
type HTMLAttributes,
forwardRef,
type ReactElement,
type ReactNode,
} from 'react';
import { Popover as _Popover, type PopoverElement, type PopoverProps as _PopoverProps } from './generated/Popover.js';
import { useSimpleOrChildrenRenderer } from './renderers/useSimpleOrChildrenRenderer.js';
import type { ReactSimpleRendererProps } from './renderers/useSimpleRenderer.js';

export * from './generated/Popover.js';

export type PopoverReactRendererProps = ReactSimpleRendererProps<PopoverElement>;

type OmittedPopoverHTMLAttributes = Omit<
HTMLAttributes<PopoverElement>,
'id' | 'className' | 'dangerouslySetInnerHTML' | 'slot'
>;

export type PopoverProps = Partial<
Omit<_PopoverProps, 'children' | 'renderer' | keyof OmittedPopoverHTMLAttributes>
> &
Readonly<{
children?: ReactNode | ComponentType<PopoverReactRendererProps>;
renderer?: ComponentType<PopoverReactRendererProps> | null;
}>;

function Popover(
{ children, ...props }: PopoverProps,
ref: ForwardedRef<PopoverElement>,
): ReactElement | null {
const [portals, renderer] = useSimpleOrChildrenRenderer(props.renderer, children);

return (
<_Popover {...props} ref={ref} renderer={renderer}>
{portals}
</_Popover>
);
}

const ForwardedPopover = forwardRef(Popover);

export { ForwardedPopover as Popover };
56 changes: 56 additions & 0 deletions test/Popover.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { expect, use as useChaiPlugin } from '@esm-bundle/chai';
import { cleanup, render } from '@testing-library/react/pure.js';
import chaiDom from 'chai-dom';
import { Popover, type PopoverElement } from '../packages/react-components/src/Popover.js';
import createOverlayCloseCatcher from './utils/createOverlayCloseCatcher.js';

useChaiPlugin(chaiDom);

describe('Popover', () => {
const overlayTag = 'vaadin-popover-overlay';

const [ref, catcher] = createOverlayCloseCatcher<PopoverElement>(overlayTag, (ref) => {
ref.opened = false;
});

async function assert() {
await new Promise((r) => requestAnimationFrame(r));

const overlay = document.querySelector(overlayTag);
expect(overlay).to.exist;

const childNodes = Array.from(overlay!.childNodes);

const body = childNodes.find((node) => node.nodeType === Node.TEXT_NODE);
expect(body).to.have.text('FooBar');
}

afterEach(cleanup);
afterEach(catcher);

it('should use children if no renderer property set', async () => {
render(
<Popover ref={ref} opened>
FooBar
</Popover>,
);

await assert();
});

it('should use renderer prop if it is set', async () => {
render(<Popover ref={ref} opened renderer={() => <>FooBar</>}></Popover>);

await assert();
});

it('should use children as renderer prop', async () => {
render(
<Popover ref={ref} opened>
{() => <>FooBar</>}
</Popover>,
);

await assert();
});
});
8 changes: 8 additions & 0 deletions test/typings/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import {
type MenuBarItemSelectedEvent,
} from '../../packages/react-components/src/MenuBar.js';
import type { SubMenuItem } from '../../packages/react-components/src/MenuBar.js';
import { Popover, PopoverElement } from '../../packages/react-components/src/Popover.js';
import { TabSheet, TabSheetElement, TabSheetTab } from '../../packages/react-components/src/TabSheet.js';
import type { TabElement } from '../../packages/react-components/src/Tab.js';

Expand Down Expand Up @@ -241,6 +242,13 @@ const menuBarOnItemSelected: typeof menuBarProps.onItemSelected = (event) => {
};
assertType<typeof menuBarProps.onItemSelected>(menuBarOnItemSelected);

const popoverProps = React.createElement(Popover, {}).props;
type PopoverProps = typeof popoverProps;

assertOmitted<HTMLAttributes<PopoverElement>, PopoverProps>('style');
assertOmitted<HTMLAttributes<PopoverElement>, PopoverProps>('contentEditable');
assertOmitted<HTMLAttributes<PopoverElement>, PopoverProps>('onClick');

const tabSheetProps = React.createElement(TabSheet, {}).props;
type TabSheetProps = typeof tabSheetProps;
assertType<number | null | undefined>(tabSheetProps.selected);
Expand Down

0 comments on commit 3f86d0b

Please sign in to comment.