Skip to content
This repository was archived by the owner on Sep 20, 2024. It is now read-only.

Commit b027c4b

Browse files
committed
fix(aria-hidden): undo aria-hidden on unmount modal
1 parent 662da3a commit b027c4b

File tree

11 files changed

+229
-22
lines changed

11 files changed

+229
-22
lines changed

packages/c-modal/src/use-modal.ts

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
import { useIds } from '@chakra-ui/vue-composables'
1515
import { FocusLockProps, useFocusLock } from '@chakra-ui/c-focus-lock'
1616
import { MaybeElementRef, useRef, getSelector } from '@chakra-ui/vue-utils'
17-
import { hideOthers, Undo } from 'aria-hidden'
17+
import { hideOthers, Undo } from '@chakra-ui/vue-a11y'
1818
import { FocusTarget } from 'focus-trap'
1919
import { focus, FocusableElement } from '@chakra-ui/utils'
2020
import { useBodyScrollLock } from '@chakra-ui/c-scroll-lock'
@@ -120,11 +120,6 @@ export function useModal(options: UseModalOptions) {
120120
`chakra-modal--body`
121121
)
122122

123-
/**
124-
* `aria-hidden` attributes handling
125-
*/
126-
const shouldHide = computed(() => isOpen.value && useInert?.value)
127-
useAriaHidden(dialogRefEl, shouldHide)
128123
const { lastFocusedSelector } = useReturnFocusSelector(isOpen)
129124

130125
const hasHeader = ref(false)
@@ -245,6 +240,11 @@ export function useModal(options: UseModalOptions) {
245240
},
246241
})
247242
)
243+
/**
244+
* `aria-hidden` attributes handling
245+
*/
246+
const shouldHide = computed(() => isOpen.value && useInert?.value)
247+
useAriaHidden(dialogRefEl, shouldHide)
248248

249249
return {
250250
isOpen,
@@ -276,26 +276,22 @@ export type UseModalReturn = Omit<
276276
*/
277277
export function useAriaHidden(
278278
node: Ref<HTMLElement | undefined | null>,
279-
shouldHide: Ref<boolean | undefined>
279+
shouldHide: Ref<boolean>
280280
) {
281281
let undo: Undo | null = null
282282

283-
watchEffect((onInvalidate) => {
284-
if (!node.value) return
285-
286-
console.log({
287-
hasNode: !!node.value,
288-
shouldHide: shouldHide.value,
289-
})
290-
291-
if (shouldHide.value && node.value) {
292-
undo = hideOthers(node.value)
283+
watchEffect(
284+
() => {
285+
if (shouldHide.value && node.value) {
286+
undo = hideOthers(node.value)
287+
} else {
288+
undo?.()
289+
}
290+
},
291+
{
292+
flush: 'post',
293293
}
294-
295-
onInvalidate(() => {
296-
undo?.()
297-
})
298-
})
294+
)
299295
}
300296

301297
/** Tracks last focused element selector before Modal/dialog is opened */

packages/core/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
"@chakra-ui/c-visually-hidden": "1.0.0-alpha.2",
5050
"@chakra-ui/styled-system": "^1.10.0",
5151
"@chakra-ui/utils": "^1.5.0",
52+
"@chakra-ui/vue-a11y": "0.1.0-alpha.1",
53+
"@chakra-ui/vue-composables": "0.1.0-alpha.1",
5254
"@chakra-ui/vue-layout": "0.1.0-alpha.3",
5355
"@chakra-ui/vue-system": "0.1.0-alpha.2",
5456
"@chakra-ui/vue-theme": "0.1.0-alpha.2",

packages/core/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ export * from '@chakra-ui/c-theme-provider'
123123
// V
124124
export * from '@chakra-ui/c-visually-hidden'
125125

126+
// OTHERS
127+
export * from '@chakra-ui/vue-composables'
128+
export * from '@chakra-ui/vue-a11y'
129+
126130
/**
127131
*
128132
* Directives exports

packages/vue-a11y/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# @chakra-ui/vue-a11y
2+
3+
Accessibility composables for chakra ui vue
4+
5+
## Installation
6+
7+
```sh
8+
yarn add @chakra-ui/vue-a11y
9+
# or
10+
npm i @chakra-ui/vue-a11y
11+
```
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<template>
2+
<vue-a11y> HELLO VueA11y </vue-a11y>
3+
</template>

packages/vue-a11y/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './src'

packages/vue-a11y/package.json

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"name": "@chakra-ui/vue-a11y",
3+
"description": "Chakra UI Vue | Accessibility composables for chakra ui vue component",
4+
"version": "0.1.0-alpha.1",
5+
"main": "dist/cjs/index.js",
6+
"module": "dist/esm/index.js",
7+
"types": "dist/types/index.d.ts",
8+
"typings": "dist/types/index.d.ts",
9+
"author": "Jonathan Bakebwa <[email protected]>",
10+
"homepage": "https://github.com/chakra-ui/chakra-ui-vue-next#readme",
11+
"license": "MIT",
12+
"files": [
13+
"dist"
14+
],
15+
"exports": {
16+
".": {
17+
"require": "./dist/cjs/index.js",
18+
"default": "./dist/esm/index.js"
19+
}
20+
},
21+
"publishConfig": {
22+
"access": "public"
23+
},
24+
"repository": "https://github.com/chakra-ui/chakra-ui-vue-next/tree/master/packages/vue-a11y",
25+
"bugs": {
26+
"url": "https://github.com/chakra-ui/chakra-ui-vue-next/issues"
27+
},
28+
"sideEffects": false,
29+
"scripts": {
30+
"build": "rimraf ./dist && concurrently yarn:build:*",
31+
"build:esm": "cross-env BABEL_ENV=esm babel src --root-mode upward --extensions .ts,.tsx -d dist/esm --source-maps",
32+
"build:cjs": "cross-env BABEL_ENV=cjs babel src --root-mode upward --extensions .ts,.tsx -d dist/cjs --source-maps",
33+
"build:types": "cross-env tsc --emitDeclarationOnly --declaration --declarationDir dist/types",
34+
"watch": "concurrently yarn:watch:*",
35+
"watch:esm": "cross-env BABEL_ENV=esm babel src --root-mode upward --extensions .ts,.tsx -d dist/esm --source-maps --watch",
36+
"watch:cjs": "cross-env BABEL_ENV=cjs babel src --root-mode upward --extensions .ts,.tsx -d dist/cjs --source-maps --watch",
37+
"watch:types": "cross-env tsc --emitDeclarationOnly --declaration --declarationDir dist/types --watch --incremental"
38+
},
39+
"dependencies": {
40+
"@chakra-ui/styled-system": "^1.10.0",
41+
"@chakra-ui/vue-system": "0.1.0-alpha.2",
42+
"@chakra-ui/vue-utils": "0.1.0-alpha.2"
43+
},
44+
"peerDependencies": {
45+
"vue": ">=3.0.5"
46+
},
47+
"devDependencies": {
48+
"vue": ">=3.0.5"
49+
}
50+
}

packages/vue-a11y/src/aria-hidden.ts

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/**
2+
* Hey! Welcome to @chakra-ui/vue-next VueA11y
3+
*
4+
* Accessibility composables for chakra ui vue
5+
*
6+
* @see Docs https://next.vue.chakra-ui.com/vue-a11y
7+
* @see Source https://github.com/chakra-ui/chakra-ui-vue-next/blob/master/packages/vue-a11y/src/vue-a11y/vue-a11y.ts
8+
* @see WAI-ARIA https://www.w3.org/TR/wai-aria-practices-1.2
9+
*/
10+
11+
import { getSelector } from '@chakra-ui/vue-utils'
12+
13+
export type Undo = () => void
14+
15+
const getDefaultParent = (originalTarget: Element | Element[]) => {
16+
if (typeof document === 'undefined') {
17+
return null
18+
}
19+
20+
const sampleTarget = Array.isArray(originalTarget)
21+
? originalTarget[0]
22+
: originalTarget
23+
return sampleTarget.ownerDocument.body
24+
}
25+
26+
let counterMap = new WeakMap<Element, number>()
27+
let uncontrolledNodes = new WeakMap<Element, boolean>()
28+
let markerMap: Record<string, WeakMap<Element, number>> = {}
29+
let lockCount = 0
30+
31+
export const hideOthers = (
32+
originalTarget: Element | Element[],
33+
parentNode = getDefaultParent(originalTarget),
34+
markerName = 'chakra-aria-hidden'
35+
): Undo => {
36+
const targets = Array.isArray(originalTarget)
37+
? originalTarget
38+
: [originalTarget]
39+
40+
if (!markerMap[markerName]) {
41+
markerMap[markerName] = new WeakMap()
42+
}
43+
const markerCounter = markerMap[markerName]
44+
const hiddenNodes: Element[] = []
45+
46+
const deep = (parent: Element | null) => {
47+
if (!parent || targets.indexOf(parent) >= 0) {
48+
return
49+
}
50+
51+
Array.prototype.forEach.call(parent.children, (node: Element) => {
52+
if (
53+
targets.some((target) => 'contains' in node && node.contains(target))
54+
) {
55+
deep(node)
56+
} else {
57+
const attr = node.getAttribute('aria-hidden')
58+
const alreadyHidden = attr !== null && attr !== 'false'
59+
const counterValue = (counterMap.get(node) || 0) + 1
60+
const markerValue = (markerCounter.get(node) || 0) + 1
61+
62+
counterMap.set(node, counterValue)
63+
markerCounter.set(node, markerValue)
64+
hiddenNodes.push(node)
65+
66+
if (counterValue === 1 && alreadyHidden) {
67+
uncontrolledNodes.set(node, true)
68+
}
69+
70+
if (markerValue === 1) {
71+
node.setAttribute(markerName, 'true')
72+
}
73+
74+
if (!alreadyHidden) {
75+
node.setAttribute('aria-hidden', 'true')
76+
}
77+
}
78+
})
79+
}
80+
81+
deep(parentNode)
82+
83+
lockCount++
84+
85+
return () => {
86+
// const ariaHiddenNodes = hiddenNodes
87+
// .map((target) => getSelector(target as HTMLElement))
88+
// .map((selector) => document.querySelector(selector) as Element)
89+
90+
hiddenNodes.forEach((node) => {
91+
const counterValue = (counterMap.get(node) as number) - 1
92+
const markerValue = (markerCounter.get(node) as number) - 1
93+
94+
counterMap.set(node, counterValue)
95+
markerCounter.set(node, markerValue)
96+
97+
if (!counterValue) {
98+
if (!uncontrolledNodes.has(node)) {
99+
node.removeAttribute('aria-hidden')
100+
}
101+
uncontrolledNodes.delete(node)
102+
}
103+
104+
if (!markerValue) {
105+
node.removeAttribute(markerName)
106+
}
107+
})
108+
109+
lockCount--
110+
if (!lockCount) {
111+
// clear
112+
counterMap = new WeakMap()
113+
counterMap = new WeakMap()
114+
uncontrolledNodes = new WeakMap()
115+
markerMap = {}
116+
}
117+
}
118+
}

packages/vue-a11y/src/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Hey! Welcome to @chakra-ui/vue-next Vue Accessibility
3+
*
4+
* Accessibility composables for chakra ui vue
5+
*
6+
* @see Docs https://next.vue.chakra-ui.com/vue-a11y
7+
* @see Source https://github.com/chakra-ui/chakra-ui-vue-next/blob/master/packages/vue-a11y/src/vue-a11y/vue-a11y.ts
8+
* @see WAI-ARIA https://www.w3.org/TR/wai-aria-practices-1.2
9+
*/
10+
11+
export * from './aria-hidden'
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { render } from '../../test-utils/src'
2+
import { VueA11y } from '../src'
3+
4+
it('should render properly', () => {
5+
const { asFragment } = render(VueA11y)
6+
expect(asFragment()).toMatchSnapshot()
7+
})

0 commit comments

Comments
 (0)