modal.spec.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import { useState } from 'react'
  2. import {
  3. OLModal,
  4. OLModalHeader,
  5. OLModalBody,
  6. OLModalFooter,
  7. OLModalTitle,
  8. } from '@/shared/components/ol/ol-modal'
  9. import { Select } from '@/shared/components/select'
  10. type TestItem = {
  11. key: string
  12. label: string
  13. }
  14. function Modal({ backdrop }: { backdrop?: boolean | 'static' } = {}) {
  15. const [isModalOpen, setIsModalOpen] = useState(false)
  16. const testItems: TestItem[] = [
  17. { key: 'item1', label: 'First Item' },
  18. { key: 'item2', label: 'Second Item' },
  19. { key: 'item3', label: 'Third Item' },
  20. ]
  21. return (
  22. <div>
  23. <button onClick={() => setIsModalOpen(true)}>Open modal</button>
  24. <OLModal
  25. show={isModalOpen}
  26. onHide={() => setIsModalOpen(false)}
  27. backdrop={backdrop}
  28. >
  29. <OLModalHeader>
  30. <OLModalTitle>This is a focus trap modal</OLModalTitle>
  31. </OLModalHeader>
  32. <OLModalBody>
  33. <p>This is for testing the modal behaviour</p>
  34. <label htmlFor="modal-input">Enter text:&nbsp;</label>
  35. <input id="modal-input" />
  36. <Select
  37. items={testItems}
  38. itemToKey={item => item.key}
  39. itemToString={item => item?.label || ''}
  40. dataTestId="test-select"
  41. defaultText="Select an item"
  42. />
  43. </OLModalBody>
  44. <OLModalFooter>
  45. <button onClick={() => setIsModalOpen(false)}>Close the modal</button>
  46. </OLModalFooter>
  47. </OLModal>
  48. </div>
  49. )
  50. }
  51. describe('<OLModal />', function () {
  52. it('dismisses on single Escape key press', function () {
  53. cy.mount(<Modal />)
  54. cy.findByRole('button', { name: 'Open modal' }).should('be.visible')
  55. cy.findByRole('dialog').should('not.exist')
  56. cy.findByRole('button', { name: 'Open modal' }).click()
  57. cy.findByRole('dialog').should('be.visible')
  58. cy.findByLabelText(/enter text/i).should('be.visible')
  59. cy.findByRole('button', { name: 'Close dialog' }).click()
  60. // Modal should hide with single escape (escapeDeactivates: false means FocusTrap doesn't handle it)
  61. cy.findByRole('dialog').should('not.exist')
  62. cy.findByRole('button', { name: 'Open modal' }).should('be.visible')
  63. })
  64. it('dismisses when clicking outside of the modal (backdrop: true)', function () {
  65. cy.mount(<Modal />)
  66. cy.findByRole('button', { name: 'Open modal' }).click()
  67. cy.findByRole('dialog').should('be.visible')
  68. cy.get('body').click(0, 0)
  69. cy.findByRole('dialog').should('not.exist')
  70. })
  71. it('does not dismiss when clicking outside of the modal (backdrop: "static")', function () {
  72. cy.mount(<Modal backdrop="static" />)
  73. cy.findByRole('button', { name: 'Open modal' }).click()
  74. cy.findByRole('dialog').should('be.visible')
  75. cy.get('body').click(0, 0)
  76. cy.findByRole('dialog').should('be.visible')
  77. })
  78. it('traps focus within modal', function () {
  79. cy.mount(<Modal />)
  80. cy.findByRole('button', { name: 'Open modal' }).click()
  81. cy.findByRole('dialog').should('be.visible')
  82. cy.findByRole('button', { name: 'Close dialog' }).should('be.focused')
  83. cy.focused().tab()
  84. cy.findByLabelText(/enter text/i).should('be.focused')
  85. cy.focused().tab()
  86. cy.get('[data-testid="test-select"]').should('be.focused')
  87. cy.focused().tab()
  88. cy.findByRole('button', { name: 'Close the modal' }).should('be.focused')
  89. cy.focused().tab()
  90. cy.findByRole('button', { name: 'Close dialog' }).should('be.focused')
  91. cy.focused().tab({ shift: true })
  92. cy.findByRole('button', { name: 'Close the modal' }).should('be.focused')
  93. })
  94. it('closes select menus when the modal container is clicked on', function () {
  95. cy.mount(<Modal />)
  96. cy.findByRole('button', { name: 'Open modal' }).click()
  97. cy.get('[data-testid="test-select"]').click()
  98. cy.contains('First Item').should('be.visible')
  99. cy.contains('Second Item').should('be.visible')
  100. cy.contains('Third Item').should('be.visible')
  101. cy.findByText('This is for testing the modal behaviour').click()
  102. cy.contains('First Item').should('not.exist')
  103. cy.contains('Second Item').should('not.exist')
  104. cy.contains('Third Item').should('not.exist')
  105. })
  106. it('restores focus to trigger button after modal closes', function () {
  107. cy.mount(<Modal />)
  108. cy.findByRole('button', { name: 'Open modal' }).click()
  109. cy.findByRole('dialog').should('be.visible')
  110. cy.findByRole('button', { name: 'Close the modal' }).click()
  111. cy.findByRole('dialog').should('not.exist')
  112. cy.findByRole('button', { name: 'Open modal' }).should('be.focused')
  113. cy.focused().should('not.match', 'body')
  114. })
  115. it('closes modal when clicking close button (X)', function () {
  116. cy.mount(<Modal />)
  117. cy.findByRole('button', { name: 'Open modal' }).click()
  118. cy.findByRole('dialog').should('be.visible')
  119. cy.findByRole('button', { name: 'Close dialog' }).click()
  120. cy.findByRole('dialog').should('not.exist')
  121. })
  122. it('dismisses on Escape key with backdrop="static"', function () {
  123. cy.mount(<Modal backdrop="static" />)
  124. cy.findByRole('button', { name: 'Open modal' }).click()
  125. cy.findByRole('dialog').should('be.visible')
  126. cy.findByRole('button', { name: 'Close dialog' }).click()
  127. cy.findByRole('dialog').should('not.exist')
  128. })
  129. })