@sanity/ui 4.0.0-static.33 → 4.0.0-static.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +6 -20
- package/dist/index.js +284 -230
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/components/dialog/Dialog.tsx +16 -7
- package/src/core/components/dialog/DialogCard.tsx +4 -4
- package/src/core/components/dialog/DialogProvider.tsx +1 -1
- package/src/core/components/menu/Menu.test.tsx +6 -6
- package/src/core/components/menu/Menu.tsx +2 -2
- package/src/core/components/toast/Toast.test.tsx +6 -6
- package/src/core/components/toast/ToastProvider.tsx +2 -2
- package/src/core/components/tree/Tree.tsx +2 -2
- package/src/core/components/tree/TreeItem.tsx +4 -4
- package/src/core/helpers/props.ts +0 -9
- package/src/core/hooks/useResponsiveProp.ts +4 -17
- package/src/core/index.ts +4 -5
- package/src/core/primitives/avatar/Avatar.tsx +2 -2
- package/src/core/primitives/card/CardProvider.tsx +2 -11
- package/src/core/primitives/card/useCard.ts +8 -3
- package/src/core/primitives/layer/Layer.test.tsx +6 -6
- package/src/core/primitives/layer/LayerProvider.tsx +3 -3
- package/src/core/primitives/popover/Popover.tsx +22 -17
- package/src/core/primitives/textInput/TextInput.tsx +73 -53
- package/src/core/primitives/tooltip/Tooltip.tsx +30 -20
- package/src/core/primitives/tooltip/tooltipDelayGroup/TooltipDelayGroupProvider.tsx +1 -3
- package/src/core/utils/boundaryElement/BoundaryElementContext.ts +2 -5
- package/src/core/utils/boundaryElement/BoundaryElementProvider.tsx +2 -4
- package/src/core/utils/boundaryElement/types.ts +1 -4
- package/src/core/utils/boundaryElement/useBoundaryElement.ts +6 -18
- package/src/core/utils/portal/Portal.tsx +6 -3
- package/src/core/utils/portal/usePortal.ts +9 -2
- package/src/core/utils/boundaryElement/BoundaryElement.test.tsx +0 -102
|
@@ -23,8 +23,8 @@ import {
|
|
|
23
23
|
import {isValidElementType} from 'react-is'
|
|
24
24
|
|
|
25
25
|
import {EMPTY_RECORD} from '../../constants'
|
|
26
|
+
import {_getResponsiveProp} from '../../helpers/props'
|
|
26
27
|
import {useCustomValidity} from '../../hooks/useCustomValidity'
|
|
27
|
-
import {useResponsiveProp} from '../../hooks/useResponsiveProp'
|
|
28
28
|
import {isRecord} from '../../lib/isRecord'
|
|
29
29
|
import type {ComponentType, Props} from '../../types'
|
|
30
30
|
import {Box} from '../box/Box'
|
|
@@ -107,7 +107,7 @@ export function TextInput<E extends TextInputElementType = typeof DEFAULT_TEXT_I
|
|
|
107
107
|
|
|
108
108
|
const ref = useRef<HTMLInputElement | null>(null)
|
|
109
109
|
|
|
110
|
-
const responsivePadding =
|
|
110
|
+
const responsivePadding = useMemo(() => _getResponsiveProp(padding), [padding])
|
|
111
111
|
|
|
112
112
|
const withClearButton = Boolean(clearButton)
|
|
113
113
|
|
|
@@ -137,35 +137,6 @@ export function TextInput<E extends TextInputElementType = typeof DEFAULT_TEXT_I
|
|
|
137
137
|
[onClear, ref],
|
|
138
138
|
)
|
|
139
139
|
|
|
140
|
-
const clearButtonBoxPadding = useMemo(() => {
|
|
141
|
-
return Object.fromEntries(
|
|
142
|
-
Object.entries(responsivePadding).map(([key, value]) => {
|
|
143
|
-
if (value === 0) return [key, 0]
|
|
144
|
-
if (value === 1) return [key, 1]
|
|
145
|
-
if (value === 2) return [key, 1]
|
|
146
|
-
|
|
147
|
-
return [key, typeof value === 'number' ? value - 2 : 0]
|
|
148
|
-
}),
|
|
149
|
-
) as ResponsiveProp<Space>
|
|
150
|
-
}, [responsivePadding])
|
|
151
|
-
|
|
152
|
-
const clearButtonPadding = useMemo(() => {
|
|
153
|
-
return Object.fromEntries(
|
|
154
|
-
Object.entries(responsivePadding).map(([key, value]) => {
|
|
155
|
-
if (value === 0) return [key, 0]
|
|
156
|
-
if (value === 1) return [key, 0]
|
|
157
|
-
if (value === 2) return [key, 1]
|
|
158
|
-
|
|
159
|
-
return [key, typeof value === 'number' ? value - 1 : 0]
|
|
160
|
-
}),
|
|
161
|
-
) as ResponsiveProp<Space>
|
|
162
|
-
}, [responsivePadding])
|
|
163
|
-
|
|
164
|
-
const clearButtonProps: TextInputClearButtonProps = useMemo(
|
|
165
|
-
() => (isRecord(clearButton) ? clearButton : EMPTY_RECORD),
|
|
166
|
-
[clearButton],
|
|
167
|
-
)
|
|
168
|
-
|
|
169
140
|
return (
|
|
170
141
|
<span
|
|
171
142
|
className={textInput({
|
|
@@ -224,28 +195,14 @@ export function TextInput<E extends TextInputElementType = typeof DEFAULT_TEXT_I
|
|
|
224
195
|
</span>
|
|
225
196
|
|
|
226
197
|
{!disabled && !readOnly && clearButton && (
|
|
227
|
-
<
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
<Button
|
|
236
|
-
aria-label="Clear"
|
|
237
|
-
data-qa="clear-button"
|
|
238
|
-
display="block"
|
|
239
|
-
fontSize={fontSize}
|
|
240
|
-
icon={CloseIcon}
|
|
241
|
-
mode="bleed"
|
|
242
|
-
padding={clearButtonPadding}
|
|
243
|
-
radius={radius}
|
|
244
|
-
{...clearButtonProps}
|
|
245
|
-
onClick={handleClearClick}
|
|
246
|
-
onMouseDown={handleClearMouseDown}
|
|
247
|
-
/>
|
|
248
|
-
</Box>
|
|
198
|
+
<ClearButton
|
|
199
|
+
fontSize={fontSize}
|
|
200
|
+
radius={radius}
|
|
201
|
+
clearButtonProps={isRecord(clearButton) ? clearButton : EMPTY_RECORD}
|
|
202
|
+
handleClearClick={handleClearClick}
|
|
203
|
+
handleClearMouseDown={handleClearMouseDown}
|
|
204
|
+
padding={responsivePadding}
|
|
205
|
+
/>
|
|
249
206
|
)}
|
|
250
207
|
</span>
|
|
251
208
|
|
|
@@ -257,3 +214,66 @@ export function TextInput<E extends TextInputElementType = typeof DEFAULT_TEXT_I
|
|
|
257
214
|
</span>
|
|
258
215
|
)
|
|
259
216
|
}
|
|
217
|
+
|
|
218
|
+
function ClearButton({
|
|
219
|
+
fontSize,
|
|
220
|
+
radius,
|
|
221
|
+
clearButtonProps,
|
|
222
|
+
handleClearClick,
|
|
223
|
+
handleClearMouseDown,
|
|
224
|
+
padding,
|
|
225
|
+
}: {
|
|
226
|
+
clearButtonProps: TextInputClearButtonProps
|
|
227
|
+
handleClearClick: (event: MouseEvent<HTMLButtonElement>) => void
|
|
228
|
+
handleClearMouseDown: (event: MouseEvent<HTMLButtonElement>) => void
|
|
229
|
+
padding: ResponsiveProp<Space>
|
|
230
|
+
} & Pick<TextInputOwnProps, 'fontSize' | 'radius'>) {
|
|
231
|
+
const clearButtonBoxPadding = useMemo(() => {
|
|
232
|
+
return Object.fromEntries(
|
|
233
|
+
Object.entries(padding).map(([key, value]) => {
|
|
234
|
+
if (value === 0) return [key, 0]
|
|
235
|
+
if (value === 1) return [key, 1]
|
|
236
|
+
if (value === 2) return [key, 1]
|
|
237
|
+
|
|
238
|
+
return [key, typeof value === 'number' ? value - 2 : 0]
|
|
239
|
+
}),
|
|
240
|
+
) as ResponsiveProp<Space>
|
|
241
|
+
}, [padding])
|
|
242
|
+
|
|
243
|
+
const clearButtonPadding = useMemo(() => {
|
|
244
|
+
return Object.fromEntries(
|
|
245
|
+
Object.entries(padding).map(([key, value]) => {
|
|
246
|
+
if (value === 0) return [key, 0]
|
|
247
|
+
if (value === 1) return [key, 0]
|
|
248
|
+
if (value === 2) return [key, 1]
|
|
249
|
+
|
|
250
|
+
return [key, typeof value === 'number' ? value - 1 : 0]
|
|
251
|
+
}),
|
|
252
|
+
) as ResponsiveProp<Space>
|
|
253
|
+
}, [padding])
|
|
254
|
+
|
|
255
|
+
return (
|
|
256
|
+
<Box
|
|
257
|
+
as="span"
|
|
258
|
+
insetTop={0}
|
|
259
|
+
insetRight={0}
|
|
260
|
+
padding={clearButtonBoxPadding}
|
|
261
|
+
position="absolute"
|
|
262
|
+
style={{zIndex: 2}}
|
|
263
|
+
>
|
|
264
|
+
<Button
|
|
265
|
+
aria-label="Clear"
|
|
266
|
+
data-qa="clear-button"
|
|
267
|
+
display="block"
|
|
268
|
+
fontSize={fontSize}
|
|
269
|
+
icon={CloseIcon}
|
|
270
|
+
mode="bleed"
|
|
271
|
+
padding={clearButtonPadding}
|
|
272
|
+
radius={radius}
|
|
273
|
+
{...clearButtonProps}
|
|
274
|
+
onClick={handleClearClick}
|
|
275
|
+
onMouseDown={handleClearMouseDown}
|
|
276
|
+
/>
|
|
277
|
+
</Box>
|
|
278
|
+
)
|
|
279
|
+
}
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
type ReactElement,
|
|
20
20
|
type ReactNode,
|
|
21
21
|
type RefAttributes,
|
|
22
|
+
use,
|
|
22
23
|
useCallback,
|
|
23
24
|
useEffect,
|
|
24
25
|
useId,
|
|
@@ -35,20 +36,21 @@ import {useDelayedState} from '../../hooks/useDelayedState'
|
|
|
35
36
|
import {usePrefersReducedMotion} from '../../hooks/usePrefersReducedMotion'
|
|
36
37
|
import {origin} from '../../middleware/origin'
|
|
37
38
|
import type {ComponentType, Delay, Placement, Props} from '../../types'
|
|
38
|
-
import {
|
|
39
|
+
import {BoundaryElementContext} from '../../utils/boundaryElement/BoundaryElementContext'
|
|
39
40
|
import {getElementRef} from '../../utils/getElementRef'
|
|
40
41
|
import {Portal} from '../../utils/portal/Portal'
|
|
41
|
-
import {
|
|
42
|
+
import {PortalContext} from '../../utils/portal/PortalContext'
|
|
43
|
+
import {assertPortalContext} from '../../utils/portal/usePortal'
|
|
44
|
+
import {CardContext} from '../card/CardContext'
|
|
42
45
|
import {CardProvider} from '../card/CardProvider'
|
|
43
|
-
import {
|
|
46
|
+
import {assertCardContext} from '../card/useCard'
|
|
44
47
|
import type {LayerOwnProps} from '../layer/Layer'
|
|
45
48
|
import {
|
|
46
49
|
DEFAULT_FALLBACK_PLACEMENTS,
|
|
47
50
|
DEFAULT_TOOLTIP_DISTANCE,
|
|
48
51
|
DEFAULT_TOOLTIP_PADDING,
|
|
49
52
|
} from './constants'
|
|
50
|
-
import {
|
|
51
|
-
// import {useTooltipDelayGroup} from './TooltipDelayGroup'
|
|
53
|
+
import {TooltipDelayGroupContext} from './tooltipDelayGroup/TooltipDelayGroupContext'
|
|
52
54
|
import {TooltipLayer} from './TooltipLayer'
|
|
53
55
|
|
|
54
56
|
/** @public */
|
|
@@ -106,8 +108,6 @@ export type TooltipProps<E extends TooltipElementType = TooltipElementType> = Pr
|
|
|
106
108
|
export function Tooltip<E extends TooltipElementType = typeof DEFAULT_TOOLTIP_ELEMENT>(
|
|
107
109
|
props: TooltipProps<E>,
|
|
108
110
|
): React.JSX.Element {
|
|
109
|
-
const boundaryElementContext = useBoundaryElement()
|
|
110
|
-
|
|
111
111
|
const {
|
|
112
112
|
animate: _animate = false,
|
|
113
113
|
arrow: arrowProp = false,
|
|
@@ -130,11 +130,10 @@ export function Tooltip<E extends TooltipElementType = typeof DEFAULT_TOOLTIP_EL
|
|
|
130
130
|
tone = 'inherit',
|
|
131
131
|
...rest
|
|
132
132
|
} = props as TooltipProps<typeof DEFAULT_TOOLTIP_ELEMENT>
|
|
133
|
-
const boundaryElement = _boundaryElement ??
|
|
133
|
+
const boundaryElement = _boundaryElement ?? use(BoundaryElementContext)
|
|
134
134
|
const fallbackPlacements =
|
|
135
135
|
_fallbackPlacements ?? DEFAULT_FALLBACK_PLACEMENTS[props.placement ?? 'bottom']
|
|
136
136
|
|
|
137
|
-
const card = useCard()
|
|
138
137
|
const prefersReducedMotion = usePrefersReducedMotion()
|
|
139
138
|
const animate = prefersReducedMotion ? false : _animate
|
|
140
139
|
const ref = useRef<HTMLDivElement | null>(null)
|
|
@@ -145,9 +144,13 @@ export function Tooltip<E extends TooltipElementType = typeof DEFAULT_TOOLTIP_EL
|
|
|
145
144
|
|
|
146
145
|
useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(forwardedRef, () => ref.current)
|
|
147
146
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
147
|
+
let portalElement: HTMLElement | null = null
|
|
148
|
+
if (portalProp) {
|
|
149
|
+
const portal = use(PortalContext)
|
|
150
|
+
assertPortalContext(portal)
|
|
151
|
+
portalElement =
|
|
152
|
+
typeof portalProp === 'string' ? portal.elements?.[portalProp] || null : portal.element
|
|
153
|
+
}
|
|
151
154
|
|
|
152
155
|
const middleware = useMiddleware({
|
|
153
156
|
animate,
|
|
@@ -174,7 +177,7 @@ export function Tooltip<E extends TooltipElementType = typeof DEFAULT_TOOLTIP_EL
|
|
|
174
177
|
|
|
175
178
|
const tooltipId = useId()
|
|
176
179
|
const [isOpen, setIsOpen] = useDelayedState(false)
|
|
177
|
-
const delayGroupContext =
|
|
180
|
+
const delayGroupContext = use(TooltipDelayGroupContext)
|
|
178
181
|
const {setIsGroupActive, setOpenTooltipId} = delayGroupContext || {}
|
|
179
182
|
const showTooltip = isOpen || delayGroupContext?.openTooltipId === tooltipId
|
|
180
183
|
|
|
@@ -385,17 +388,24 @@ export function Tooltip<E extends TooltipElementType = typeof DEFAULT_TOOLTIP_EL
|
|
|
385
388
|
</TooltipLayer>
|
|
386
389
|
)
|
|
387
390
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
+
let children = showTooltip ? node : null
|
|
392
|
+
|
|
393
|
+
if (showTooltip && portalProp) {
|
|
394
|
+
let resolvedTone = tone as Exclude<typeof tone, 'inherit'>
|
|
395
|
+
if (tone === 'inherit') {
|
|
396
|
+
const card = use(CardContext)
|
|
397
|
+
assertCardContext(card)
|
|
398
|
+
resolvedTone = card.tone
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
children = (
|
|
391
402
|
<Portal __unstable_name={typeof portalProp === 'string' ? portalProp : undefined}>
|
|
392
|
-
<CardProvider tone={
|
|
403
|
+
<CardProvider tone={resolvedTone} scheme={scheme ?? 'light'}>
|
|
393
404
|
{node}
|
|
394
405
|
</CardProvider>
|
|
395
406
|
</Portal>
|
|
396
|
-
)
|
|
397
|
-
|
|
398
|
-
))
|
|
407
|
+
)
|
|
408
|
+
}
|
|
399
409
|
|
|
400
410
|
return (
|
|
401
411
|
<>
|
|
@@ -49,7 +49,5 @@ export function TooltipDelayGroupProvider(
|
|
|
49
49
|
[closeDelay, isGroupActive, openDelay, openTooltipId, setIsGroupActive, setOpenTooltipId],
|
|
50
50
|
)
|
|
51
51
|
|
|
52
|
-
return
|
|
53
|
-
<TooltipDelayGroupContext.Provider value={value}>{children}</TooltipDelayGroupContext.Provider>
|
|
54
|
-
)
|
|
52
|
+
return <TooltipDelayGroupContext value={value}>{children}</TooltipDelayGroupContext>
|
|
55
53
|
}
|
|
@@ -3,8 +3,5 @@ import type {Context} from 'react'
|
|
|
3
3
|
import {createGlobalScopedContext} from '../../lib/createGlobalScopedContext'
|
|
4
4
|
import type {BoundaryElementContextValue} from './types'
|
|
5
5
|
|
|
6
|
-
export const BoundaryElementContext: Context<BoundaryElementContextValue
|
|
7
|
-
createGlobalScopedContext<BoundaryElementContextValue
|
|
8
|
-
'@sanity/ui/v4/boundaryElement',
|
|
9
|
-
null,
|
|
10
|
-
)
|
|
6
|
+
export const BoundaryElementContext: Context<BoundaryElementContextValue> =
|
|
7
|
+
createGlobalScopedContext<BoundaryElementContextValue>('@sanity/ui/v4/boundaryElement', null)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {type ReactNode
|
|
1
|
+
import {type ReactNode} from 'react'
|
|
2
2
|
|
|
3
3
|
import {BoundaryElementContext} from './BoundaryElementContext'
|
|
4
|
-
import type {BoundaryElementContextValue} from './types'
|
|
5
4
|
|
|
6
5
|
/** @public */
|
|
7
6
|
export interface BoundaryElementProviderProps {
|
|
@@ -12,7 +11,6 @@ export interface BoundaryElementProviderProps {
|
|
|
12
11
|
/** @public */
|
|
13
12
|
export function BoundaryElementProvider(props: BoundaryElementProviderProps): React.JSX.Element {
|
|
14
13
|
const {children, element} = props
|
|
15
|
-
const value: BoundaryElementContextValue = useMemo(() => ({version: 0.0, element}), [element])
|
|
16
14
|
|
|
17
|
-
return <BoundaryElementContext
|
|
15
|
+
return <BoundaryElementContext value={element}>{children}</BoundaryElementContext>
|
|
18
16
|
}
|
|
@@ -1,24 +1,12 @@
|
|
|
1
|
-
import {use} from 'react'
|
|
1
|
+
import {use, useMemo} from 'react'
|
|
2
2
|
|
|
3
|
-
import {isRecord} from '../../lib/isRecord'
|
|
4
3
|
import {BoundaryElementContext} from './BoundaryElementContext'
|
|
5
4
|
import type {BoundaryElementContextValue} from './types'
|
|
6
5
|
|
|
7
|
-
const DEFAULT_VALUE: BoundaryElementContextValue = {
|
|
8
|
-
version: 0.0,
|
|
9
|
-
element: null,
|
|
10
|
-
}
|
|
11
|
-
|
|
12
6
|
/** @public */
|
|
13
|
-
export function useBoundaryElement(): BoundaryElementContextValue {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
// - If the value is an object, but doesn’t have `version: 0.0`, it’s not compatible with the current version
|
|
19
|
-
if (value && (!isRecord(value) || value.version !== 0.0)) {
|
|
20
|
-
throw new Error('useBoundaryElement(): the context value is not compatible')
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return value || DEFAULT_VALUE
|
|
7
|
+
export function useBoundaryElement(): {version: 0.0; element: BoundaryElementContextValue} {
|
|
8
|
+
// eslint-disable-next-line no-warning-comments
|
|
9
|
+
// @TODO this wrapping is for BC and should be removed
|
|
10
|
+
const element = use(BoundaryElementContext)
|
|
11
|
+
return useMemo(() => ({version: 0.0, element}), [element])
|
|
24
12
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import {type ReactNode, type ReactPortal, use} from 'react'
|
|
2
2
|
import {createPortal} from 'react-dom'
|
|
3
3
|
|
|
4
|
+
import {CardContext} from '../../primitives/card/CardContext'
|
|
4
5
|
import {CardProvider} from '../../primitives/card/CardProvider'
|
|
5
|
-
import {
|
|
6
|
+
import {assertCardContext} from '../../primitives/card/useCard'
|
|
6
7
|
import {usePortal} from './usePortal'
|
|
7
8
|
|
|
8
9
|
/** @public */
|
|
@@ -17,7 +18,6 @@ export interface PortalProps {
|
|
|
17
18
|
/** @public */
|
|
18
19
|
export function Portal(props: PortalProps): ReactPortal | null {
|
|
19
20
|
const {children, __unstable_name: name} = props
|
|
20
|
-
const card = useCard()
|
|
21
21
|
const portal = usePortal()
|
|
22
22
|
const portalElement =
|
|
23
23
|
(name ? portal.elements && portal.elements[name] : portal.element) ||
|
|
@@ -27,6 +27,9 @@ export function Portal(props: PortalProps): ReactPortal | null {
|
|
|
27
27
|
return null
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
const card = use(CardContext)
|
|
31
|
+
assertCardContext(card)
|
|
32
|
+
|
|
30
33
|
return createPortal(
|
|
31
34
|
<CardProvider tone="transparent" scheme={card.scheme}>
|
|
32
35
|
{children}
|
|
@@ -8,6 +8,15 @@ import type {PortalContextValue} from './types'
|
|
|
8
8
|
export function usePortal(): PortalContextValue {
|
|
9
9
|
const value = use(PortalContext)
|
|
10
10
|
|
|
11
|
+
assertPortalContext(value)
|
|
12
|
+
|
|
13
|
+
return value
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** @internal */
|
|
17
|
+
export function assertPortalContext(
|
|
18
|
+
value: PortalContextValue | null,
|
|
19
|
+
): asserts value is PortalContextValue {
|
|
11
20
|
if (!value) {
|
|
12
21
|
throw new Error('usePortal(): missing context value')
|
|
13
22
|
}
|
|
@@ -18,6 +27,4 @@ export function usePortal(): PortalContextValue {
|
|
|
18
27
|
if (!isRecord(value) || value.version !== 0.0) {
|
|
19
28
|
throw new Error('usePortal(): the context value is not compatible')
|
|
20
29
|
}
|
|
21
|
-
|
|
22
|
-
return value
|
|
23
30
|
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import {describe, expect, it, vi} from 'vitest'
|
|
2
|
-
|
|
3
|
-
import {render} from '$test/utils'
|
|
4
|
-
|
|
5
|
-
import {BoundaryElementContext} from './BoundaryElementContext'
|
|
6
|
-
import type {BoundaryElementContextValue} from './types'
|
|
7
|
-
import {useBoundaryElement} from './useBoundaryElement'
|
|
8
|
-
|
|
9
|
-
describe('utils/boundaryElement', () => {
|
|
10
|
-
describe('useBoundaryElement', () => {
|
|
11
|
-
it('should get context value', async () => {
|
|
12
|
-
const log = vi.fn()
|
|
13
|
-
|
|
14
|
-
function Debug() {
|
|
15
|
-
const rootBoundaryElement = useBoundaryElement()
|
|
16
|
-
|
|
17
|
-
log(rootBoundaryElement)
|
|
18
|
-
|
|
19
|
-
return <>debug</>
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function Root() {
|
|
23
|
-
const value: BoundaryElementContextValue = {
|
|
24
|
-
version: 0.0,
|
|
25
|
-
element: null,
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<BoundaryElementContext.Provider value={value}>
|
|
30
|
-
<Debug />
|
|
31
|
-
</BoundaryElementContext.Provider>
|
|
32
|
-
)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
render(<Root />)
|
|
36
|
-
|
|
37
|
-
expect(log.mock.calls[0][0].version).toBe(0.0)
|
|
38
|
-
expect(log.mock.calls[0][0].element).toBe(null)
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('should provide default value when no context value is provided', async () => {
|
|
42
|
-
const log = vi.fn()
|
|
43
|
-
|
|
44
|
-
function Debug() {
|
|
45
|
-
const rootBoundaryElement = useBoundaryElement()
|
|
46
|
-
|
|
47
|
-
log(rootBoundaryElement)
|
|
48
|
-
|
|
49
|
-
return null
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function Root() {
|
|
53
|
-
return (
|
|
54
|
-
<BoundaryElementContext.Provider
|
|
55
|
-
// @ts-expect-error - we want to test the error case
|
|
56
|
-
value={undefined}
|
|
57
|
-
>
|
|
58
|
-
<Debug />
|
|
59
|
-
</BoundaryElementContext.Provider>
|
|
60
|
-
)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
render(<Root />)
|
|
64
|
-
|
|
65
|
-
expect(log.mock.calls[0][0].version).toBe(0.0)
|
|
66
|
-
expect(log.mock.calls[0][0].element).toBe(null)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it('should fail when context value is not compatible', async () => {
|
|
70
|
-
const log = vi.fn()
|
|
71
|
-
|
|
72
|
-
function Debug() {
|
|
73
|
-
try {
|
|
74
|
-
useBoundaryElement()
|
|
75
|
-
} catch (err) {
|
|
76
|
-
log(err)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return null
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function Root() {
|
|
83
|
-
// NOTE: we’re testing this because the context value may be a function in the future
|
|
84
|
-
|
|
85
|
-
return (
|
|
86
|
-
<BoundaryElementContext.Provider
|
|
87
|
-
// @ts-expect-error - we want to test the error case
|
|
88
|
-
value={() => ({version: 1})}
|
|
89
|
-
>
|
|
90
|
-
<Debug />
|
|
91
|
-
</BoundaryElementContext.Provider>
|
|
92
|
-
)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
render(<Root />)
|
|
96
|
-
|
|
97
|
-
expect(log.mock.calls[0][0].message).toEqual(
|
|
98
|
-
'useBoundaryElement(): the context value is not compatible',
|
|
99
|
-
)
|
|
100
|
-
})
|
|
101
|
-
})
|
|
102
|
-
})
|