diff --git a/src/components/Portal/PortalHost.tsx b/src/components/Portal/PortalHost.tsx index d8193007b8..ecc20b8a72 100644 --- a/src/components/Portal/PortalHost.tsx +++ b/src/components/Portal/PortalHost.tsx @@ -51,7 +51,9 @@ export default class PortalHost extends React.Component { const queue = this.queue; while (queue.length && manager) { - const action = queue.pop(); + // Replay in the order the operations were recorded, otherwise portals + // that mounted in the same commit end up stacked in reverse. + const action = queue.shift(); if (action) { switch (action.type) { case 'mount': diff --git a/src/components/__tests__/Portal.test.tsx b/src/components/__tests__/Portal.test.tsx index 855d74b6ab..6865d4d159 100644 --- a/src/components/__tests__/Portal.test.tsx +++ b/src/components/__tests__/Portal.test.tsx @@ -5,6 +5,8 @@ import { expect, it, jest } from '@jest/globals'; import { LocaleProvider, useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; import { render, screen } from '../../test-utils'; +import Dialog from '../Dialog/Dialog'; +import Modal from '../Modal'; import Portal from '../Portal/Portal'; jest.useRealTimers(); @@ -57,3 +59,49 @@ it('passes local theme overrides and locale to portal content and updates them', expect(screen.getByText('3 ltr')).toBeOnTheScreen(); expect(screen.queryByText('2 rtl')).not.toBeOnTheScreen(); }); + +it('renders portals in source order when mounted in the same commit', async () => { + await render( + + + first + + + second + + + third + + + ); + + const portals = await screen.findAllByTestId('portal-content'); + + expect(portals).toHaveLength(3); + expect(portals[0]).toHaveTextContent('first'); + expect(portals[1]).toHaveTextContent('second'); + expect(portals[2]).toHaveTextContent('third'); +}); + +it('stacks components mounted in the same commit in source order', async () => { + await render( + + + {}}> + modal + + + + {}}> + dialog + + + + ); + + const layers = await screen.findAllByTestId('layer'); + + expect(layers).toHaveLength(2); + expect(layers[0]).toHaveTextContent('modal'); + expect(layers[1]).toHaveTextContent('dialog'); +});