Merge pull request #30983 from overleaf/cd-close-modal-select-menus-on-click

Ensure select menus in Modals close on modal click

GitOrigin-RevId: 4a07f16146ac99e6fb1fdf09d222e6832cc93079
This commit is contained in:
Chris Dryden
2026-01-29 10:50:17 +00:00
committed by Copybot
parent fd5a8159cc
commit a054938414
3 changed files with 45 additions and 7 deletions

View File

@@ -25,7 +25,9 @@ export default function FocusTrap({
fallbackFocus: () => containerRef.current as HTMLElement,
}}
>
<div ref={containerRef}>{children}</div>
<div ref={containerRef} tabIndex={-1}>
{children}
</div>
</FocusTrapReact>
)
}

View File

@@ -6,10 +6,22 @@ import {
OLModalFooter,
OLModalTitle,
} from '@/shared/components/ol/ol-modal'
import { Select } from '@/shared/components/select'
type TestItem = {
key: string
label: string
}
function Modal({ backdrop }: { backdrop?: boolean | 'static' } = {}) {
const [isModalOpen, setIsModalOpen] = useState(false)
const testItems: TestItem[] = [
{ key: 'item1', label: 'First Item' },
{ key: 'item2', label: 'Second Item' },
{ key: 'item3', label: 'Third Item' },
]
return (
<div>
<button onClick={() => setIsModalOpen(true)}>Open modal</button>
@@ -26,6 +38,13 @@ function Modal({ backdrop }: { backdrop?: boolean | 'static' } = {}) {
<p>This is for testing the modal behaviour</p>
<label htmlFor="modal-input">Enter text:&nbsp;</label>
<input id="modal-input" />
<Select
items={testItems}
itemToKey={item => item.key}
itemToString={item => item?.label || ''}
dataTestId="test-select"
defaultText="Select an item"
/>
</OLModalBody>
<OLModalFooter>
<button onClick={() => setIsModalOpen(false)}>Close the modal</button>
@@ -74,6 +93,8 @@ describe('<OLModal />', function () {
cy.focused().tab()
cy.findByLabelText(/enter text/i).should('be.focused')
cy.focused().tab()
cy.get('[data-testid="test-select"]').should('be.focused')
cy.focused().tab()
cy.findByRole('button', { name: 'Close the modal' }).should('be.focused')
cy.focused().tab()
cy.findByRole('button', { name: 'Close dialog' }).should('be.focused')
@@ -81,6 +102,21 @@ describe('<OLModal />', function () {
cy.findByRole('button', { name: 'Close the modal' }).should('be.focused')
})
it('closes select menus when the modal container is clicked on', function () {
cy.mount(<Modal />)
cy.findByRole('button', { name: 'Open modal' }).click()
cy.get('[data-testid="test-select"]').click()
cy.contains('First Item').should('be.visible')
cy.contains('Second Item').should('be.visible')
cy.contains('Third Item').should('be.visible')
cy.findByText('This is for testing the modal behaviour').click()
cy.contains('First Item').should('not.exist')
cy.contains('Second Item').should('not.exist')
cy.contains('Third Item').should('not.exist')
})
it('restores focus to trigger button after modal closes', function () {
cy.mount(<Modal />)
cy.findByRole('button', { name: 'Open modal' }).click()

View File

@@ -912,17 +912,17 @@ describe('<ShareProjectModal/>', function () {
// Pressing Tab should add the entered item
fireEvent.keyDown(inputElement, { key: 'Tab', code: 'Tab' })
await waitFor(() => {
expect(screen.getAllByRole('button', { name: /Remove/ })).to.have.length(
1
)
const collaborators = await waitFor(() => {
return screen.getAllByRole('button', { name: /Remove/ })
})
expect(collaborators).to.not.be.null
// Blurring the input should not add another contact
fireEvent.blur(inputElement)
await waitFor(() => {
expect(screen.getAllByRole('button', { name: /Remove/ })).to.have.length(
1
expect(screen.getAllByRole('button', { name: /Remove/ }).length).to.eql(
collaborators.length
)
})
})