Skip to content

Commit

Permalink
[test] Extract common popup tests (#1358)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaldudak authored Feb 21, 2025
1 parent 568fc1c commit fb1597e
Show file tree
Hide file tree
Showing 23 changed files with 412 additions and 701 deletions.
7 changes: 2 additions & 5 deletions packages/react/src/accordion/root/AccordionRoot.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as React from 'react';
import { expect } from 'chai';
import { spy } from 'sinon';
import { flushMicrotasks } from '@mui/internal-test-utils';
import { DirectionProvider } from '@base-ui-components/react/direction-provider';
import { Accordion } from '@base-ui-components/react/accordion';
import { createRenderer, describeConformance, isJSDOM } from '#test-utils';
Expand Down Expand Up @@ -149,17 +148,15 @@ describe('<Accordion.Root />', () => {
expect(trigger).to.have.attribute('aria-expanded', 'false');
expect(queryByText(PANEL_CONTENT_1)).to.equal(null);

setProps({ value: [0] });
await flushMicrotasks();
await setProps({ value: [0] });

expect(trigger).to.have.attribute('aria-expanded', 'true');
expect(trigger).to.have.attribute('data-panel-open');
expect(queryByText(PANEL_CONTENT_1)).to.not.equal(null);
expect(queryByText(PANEL_CONTENT_1)).toBeVisible();
expect(queryByText(PANEL_CONTENT_1)).to.have.attribute('data-open');

setProps({ value: [] });
await flushMicrotasks();
await setProps({ value: [] });

expect(trigger).to.have.attribute('aria-expanded', 'false');
expect(queryByText(PANEL_CONTENT_1)).to.equal(null);
Expand Down
16 changes: 15 additions & 1 deletion packages/react/src/alert-dialog/root/AlertDialogRoot.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react';
import { expect } from 'chai';
import { act, screen, waitFor } from '@mui/internal-test-utils';
import { AlertDialog } from '@base-ui-components/react/alert-dialog';
import { createRenderer, isJSDOM } from '#test-utils';
import { createRenderer, isJSDOM, popupConformanceTests } from '#test-utils';
import { spy } from 'sinon';

describe('<AlertDialog.Root />', () => {
Expand All @@ -12,6 +12,20 @@ describe('<AlertDialog.Root />', () => {
globalThis.BASE_UI_ANIMATIONS_DISABLED = true;
});

popupConformanceTests({
createComponent: (props) => (
<AlertDialog.Root {...props.root}>
<AlertDialog.Trigger {...props.trigger}>Open dialog</AlertDialog.Trigger>
<AlertDialog.Portal {...props.portal}>
<AlertDialog.Popup {...props.popup}>Dialog</AlertDialog.Popup>
</AlertDialog.Portal>
</AlertDialog.Root>
),
render,
triggerMouseAction: 'click',
expectedPopupRole: 'alertdialog',
});

it('ARIA attributes', async () => {
const { queryByRole, getByText } = await render(
<AlertDialog.Root open>
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/checkbox/root/CheckboxRoot.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ describe('<Checkbox.Root />', () => {
expect(indicator).to.have.attribute('data-readonly', '');
expect(indicator).to.have.attribute('data-required', '');

setProps({ disabled: false, readOnly: false });
await setProps({ disabled: false, readOnly: false });
fireEvent.click(checkbox);

expect(checkbox).to.have.attribute('data-unchecked', '');
Expand Down
7 changes: 2 additions & 5 deletions packages/react/src/collapsible/root/CollapsibleRoot.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use client';
import * as React from 'react';
import { expect } from 'chai';
import { flushMicrotasks } from '@mui/internal-test-utils';
import { Collapsible } from '@base-ui-components/react/collapsible';
import { createRenderer, describeConformance, isJSDOM } from '#test-utils';

Expand Down Expand Up @@ -48,8 +47,7 @@ describe('<Collapsible.Root />', () => {
expect(trigger).to.have.attribute('aria-expanded', 'false');
expect(queryByText(PANEL_CONTENT)).to.equal(null);

setProps({ open: true });
await flushMicrotasks();
await setProps({ open: true });

expect(trigger).to.have.attribute('aria-expanded', 'true');

Expand All @@ -58,8 +56,7 @@ describe('<Collapsible.Root />', () => {
expect(queryByText(PANEL_CONTENT)).to.have.attribute('data-open');
expect(trigger).to.have.attribute('data-panel-open');

setProps({ open: false });
await flushMicrotasks();
await setProps({ open: false });

expect(trigger).to.not.have.attribute('aria-controls');
expect(trigger).to.have.attribute('aria-expanded', 'false');
Expand Down
165 changes: 26 additions & 139 deletions packages/react/src/dialog/root/DialogRoot.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,30 @@ import { expect } from 'chai';
import { spy } from 'sinon';
import { act, fireEvent, screen, waitFor } from '@mui/internal-test-utils';
import { Dialog } from '@base-ui-components/react/dialog';
import { createRenderer, isJSDOM } from '#test-utils';
import { createRenderer, isJSDOM, popupConformanceTests } from '#test-utils';
import { Menu } from '@base-ui-components/react/menu';
import { Select } from '@base-ui-components/react/select';

describe('<Dialog.Root />', () => {
const { render } = createRenderer();

beforeEach(() => {
globalThis.BASE_UI_ANIMATIONS_DISABLED = true;
});

const { render } = createRenderer();
popupConformanceTests({
createComponent: (props) => (
<Dialog.Root {...props.root}>
<Dialog.Trigger {...props.trigger}>Open dialog</Dialog.Trigger>
<Dialog.Portal {...props.portal}>
<Dialog.Popup {...props.popup}>Dialog</Dialog.Popup>
</Dialog.Portal>
</Dialog.Root>
),
render,
triggerMouseAction: 'click',
expectedPopupRole: 'dialog',
});

it('ARIA attributes', async () => {
const { queryByRole, getByText } = await render(
Expand Down Expand Up @@ -40,139 +54,6 @@ describe('<Dialog.Root />', () => {
);
});

describe('uncontrolled mode', () => {
it('should open the dialog with the trigger', async () => {
const { queryByRole, getByRole } = await render(
<Dialog.Root modal={false}>
<Dialog.Trigger />
<Dialog.Portal>
<Dialog.Popup />
</Dialog.Portal>
</Dialog.Root>,
);

const button = getByRole('button');
expect(queryByRole('dialog')).to.equal(null);

await act(async () => {
button.click();
});

expect(queryByRole('dialog')).not.to.equal(null);
});
});

describe('controlled mode', () => {
it('should open and close the dialog with the `open` prop', async () => {
const { queryByRole, setProps } = await render(
<Dialog.Root open={false} modal={false}>
<Dialog.Portal>
<Dialog.Popup />
</Dialog.Portal>
</Dialog.Root>,
);

expect(queryByRole('dialog')).to.equal(null);

setProps({ open: true });
expect(queryByRole('dialog')).not.to.equal(null);

setProps({ open: false });
expect(queryByRole('dialog')).to.equal(null);
});

it('should remove the popup when there is no exit animation defined', async ({ skip }) => {
if (isJSDOM) {
skip();
}

function Test() {
const [open, setOpen] = React.useState(true);

return (
<div>
<button onClick={() => setOpen(false)}>Close</button>
<Dialog.Root open={open}>
<Dialog.Portal>
<Dialog.Popup />
</Dialog.Portal>
</Dialog.Root>
</div>
);
}

const { user } = await render(<Test />);

const closeButton = screen.getByText('Close');
await user.click(closeButton);

await waitFor(() => {
expect(screen.queryByRole('dialog')).to.equal(null);
});
});

it('should remove the popup when the animation finishes', async ({ skip }) => {
if (isJSDOM) {
skip();
}

globalThis.BASE_UI_ANIMATIONS_DISABLED = false;

let animationFinished = false;
const notifyAnimationFinished = () => {
animationFinished = true;
};

function Test() {
const style = `
@keyframes test-anim {
to {
opacity: 0;
}
}
.animation-test-popup[data-open] {
opacity: 1;
}
.animation-test-popup[data-ending-style] {
animation: test-anim 1ms;
}
`;

const [open, setOpen] = React.useState(true);

return (
<div>
{/* eslint-disable-next-line react/no-danger */}
<style dangerouslySetInnerHTML={{ __html: style }} />
<button onClick={() => setOpen(false)}>Close</button>
<Dialog.Root open={open}>
<Dialog.Portal keepMounted>
<Dialog.Popup
className="animation-test-popup"
data-testid="popup"
onAnimationEnd={notifyAnimationFinished}
/>
</Dialog.Portal>
</Dialog.Root>
</div>
);
}

const { user } = await render(<Test />);

const closeButton = screen.getByText('Close');
await user.click(closeButton);

await waitFor(() => {
expect(screen.getByTestId('popup')).to.have.attribute('hidden');
});

expect(animationFinished).to.equal(true);
});
});

describe('prop: onOpenChange', () => {
it('calls onOpenChange with the new open state', async () => {
const handleOpenChange = spy();
Expand Down Expand Up @@ -415,7 +296,7 @@ describe('<Dialog.Root />', () => {
</Dialog.Root>,
);

setProps({ open: false });
await setProps({ open: false });
expect(queryByRole('dialog')).not.to.equal(null);

await waitFor(() => {
Expand Down Expand Up @@ -579,15 +460,21 @@ describe('<Dialog.Root />', () => {
const backdrops = Array.from(document.querySelectorAll('[role="presentation"]'));
await user.click(backdrops[backdrops.length - 1]);

expect(screen.queryByTestId('level-3')).to.equal(null);
await waitFor(() => {
expect(screen.queryByTestId('level-3')).to.equal(null);
});

await user.click(backdrops[backdrops.length - 2]);

expect(screen.queryByTestId('level-2')).to.equal(null);
await waitFor(() => {
expect(screen.queryByTestId('level-2')).to.equal(null);
});

await user.click(backdrops[backdrops.length - 3]);

expect(screen.queryByTestId('level-1')).to.equal(null);
await waitFor(() => {
expect(screen.queryByTestId('level-1')).to.equal(null);
});
});

describe.skipIf(isJSDOM)('nested popups', () => {
Expand Down
7 changes: 6 additions & 1 deletion packages/react/src/dialog/root/useDialogRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,12 @@ export function useDialogRoot(params: useDialogRoot.Parameters): useDialogRoot.R
mounted,
transitionStatus,
getTriggerProps: (externalProps?: React.HTMLProps<Element>) =>
getReferenceProps(mergeReactProps(externalProps, triggerProps)),
getReferenceProps(
mergeReactProps(
mergeReactProps(externalProps, { 'aria-controls': popupElementId }),
triggerProps,
),
),
getPopupProps: getFloatingProps,
setTriggerElement,
setPopupElement,
Expand Down
Loading

0 comments on commit fb1597e

Please sign in to comment.