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 14f07c141b..aa6c3b94cc 100644 --- a/src/components/__tests__/Portal.test.tsx +++ b/src/components/__tests__/Portal.test.tsx @@ -3,6 +3,8 @@ import { Text } from 'react-native'; import { expect, it, jest } from '@jest/globals'; import { render, screen } from '../../test-utils'; +import Dialog from '../Dialog/Dialog'; +import Modal from '../Modal'; import Portal from '../Portal/Portal'; jest.useRealTimers(); @@ -21,3 +23,49 @@ it('renders portal with siblings', async () => { expect(toJSON()).toMatchSnapshot(); }); + +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'); +});