Skip to content

Commit 907e161

Browse files
committed
fix: focus on disclosure on close
1 parent f8c27bf commit 907e161

File tree

4 files changed

+18
-15
lines changed

4 files changed

+18
-15
lines changed

.changeset/fancy-mice-start.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@ultraviolet/ui": patch
3+
---
4+
5+
`Modal`: returns focus to disclosure element when closing the modal

packages/ui/src/components/Modal/components/Disclosure.tsx

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
11
'use client'
22

33
import type { PropsWithRef } from 'react'
4-
import {
5-
cloneElement,
6-
createRef,
7-
isValidElement,
8-
useEffect,
9-
useMemo,
10-
} from 'react'
4+
import { cloneElement, isValidElement, useEffect, useMemo } from 'react'
115
import type { DisclosureProps } from '../types'
126

137
export const Disclosure = ({
@@ -17,17 +11,16 @@ export const Disclosure = ({
1711
handleClose,
1812
toggle,
1913
id,
14+
ref,
2015
}: DisclosureProps) => {
21-
const disclosureRef = createRef<HTMLElement>()
22-
2316
useEffect(() => {
24-
const element = disclosureRef.current
17+
const element = ref.current
2518
element?.addEventListener('click', handleOpen)
2619

2720
return () => {
2821
element?.removeEventListener('click', handleOpen)
2922
}
30-
}, [handleOpen, disclosureRef])
23+
}, [handleOpen, ref])
3124

3225
const finalDisclosure = useMemo(() => {
3326
if (typeof disclosure === 'function') {
@@ -51,7 +44,7 @@ export const Disclosure = ({
5144
}
5245

5346
return cloneElement(finalDisclosure, {
54-
ref: disclosureRef,
47+
ref,
5548
'aria-controls': id,
5649
'aria-haspopup': 'dialog',
5750
} as unknown as PropsWithRef<typeof disclosure>)

packages/ui/src/components/Modal/index.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import type { ReactElement, ReactNode } from 'react'
44
import type React from 'react'
5-
import { useCallback, useContext, useId, useState } from 'react'
5+
import { useCallback, useContext, useId, useRef, useState } from 'react'
66
import { ModalContent } from './ModalContent'
77
import { ModalContext, ModalProvider } from './ModalProvider'
88
import { Disclosure } from './components/Disclosure'
@@ -76,12 +76,14 @@ export const Modal = ({
7676
// Used for disclosure usage only
7777
const [visible, setVisible] = useState(false)
7878
const controlId = useId()
79-
79+
const disclosureRef = useRef<HTMLDivElement>(null)
8080
const handleOpen = useCallback(() => {
8181
setVisible(true)
8282
}, [])
8383

8484
const handleClose = useCallback(() => {
85+
disclosureRef.current?.focus()
86+
8587
if (onClose) {
8688
onClose()
8789
} else {
@@ -94,6 +96,7 @@ export const Modal = ({
9496
}, [onBeforeClose, onClose])
9597

9698
const handleToggle = useCallback(() => {
99+
disclosureRef.current?.focus()
97100
setVisible(current => !current)
98101
}, [])
99102

@@ -114,6 +117,7 @@ export const Modal = ({
114117
handleClose={handleClose}
115118
visible={visible}
116119
toggle={handleToggle}
120+
ref={disclosureRef}
117121
/>
118122
) : null}
119123
{!context ? (

packages/ui/src/components/Modal/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { ReactElement, ReactNode } from 'react'
1+
import type { ReactElement, ReactNode, RefObject } from 'react'
22
import type React from 'react'
33

44
export type ModalSize = 'large' | 'medium' | 'small' | 'xsmall' | 'xxsmall'
@@ -41,6 +41,7 @@ export type DisclosureProps = {
4141
visible: ModalState['visible']
4242
toggle: ModalState['toggle']
4343
id: string
44+
ref: RefObject<HTMLDivElement | null>
4445
}
4546

4647
export type DialogProps = {

0 commit comments

Comments
 (0)