@sanity/ui 4.0.0-static.44 → 4.0.0-static.45

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/ui",
3
- "version": "4.0.0-static.44",
3
+ "version": "4.0.0-static.45",
4
4
  "keywords": [
5
5
  "react",
6
6
  "ui",
@@ -11,8 +11,8 @@ import {
11
11
  useFloating,
12
12
  } from '@floating-ui/react-dom'
13
13
  import {type MaxWidth, popover as popoverCss, type ResponsiveProp} from '@sanity/ui/css'
14
- import {AnimatePresence} from 'motion/react'
15
14
  import {
15
+ Activity,
16
16
  cloneElement,
17
17
  type ForwardedRef,
18
18
  type ReactElement,
@@ -30,6 +30,7 @@ import {_getResponsiveProp} from '../../helpers/props'
30
30
  import {usePrefersReducedMotion} from '../../hooks/usePrefersReducedMotion'
31
31
  import {origin} from '../../middleware/origin'
32
32
  import type {ComponentType, Placement, Props} from '../../types'
33
+ import {AnimateActivity} from '../../utils/AnimateActivity'
33
34
  import {BoundaryElementContext} from '../../utils/boundaryElement/BoundaryElementContext'
34
35
  import {getElementRef} from '../../utils/getElementRef'
35
36
  import {Portal} from '../../utils/portal/Portal'
@@ -307,7 +308,7 @@ export function Popover<E extends PopoverElementType = typeof DEFAULT_POPOVER_EL
307
308
  </>
308
309
  )
309
310
 
310
- let children = open ? node : null
311
+ let children = node
311
312
 
312
313
  if (open && portal) {
313
314
  let resolvedTone = tone as Exclude<typeof tone, 'inherit'>
@@ -329,7 +330,13 @@ export function Popover<E extends PopoverElementType = typeof DEFAULT_POPOVER_EL
329
330
  return (
330
331
  <>
331
332
  {/* the popover */}
332
- {animate ? <AnimatePresence>{children}</AnimatePresence> : children}
333
+ {animate ? (
334
+ <AnimateActivity mode={open ? 'visible' : 'hidden'} layoutMode="default">
335
+ {children}
336
+ </AnimateActivity>
337
+ ) : (
338
+ <Activity mode={open ? 'visible' : 'hidden'}>{children}</Activity>
339
+ )}
333
340
 
334
341
  {/* the referred element */}
335
342
  {child}
@@ -31,7 +31,7 @@ describe('Tooltip', () => {
31
31
  const button = screen.getByText('Hover me')
32
32
 
33
33
  // Validate tooltip content is not rendered
34
- expect(screen.queryByText('Tooltip content')).not.toBeInTheDocument()
34
+ expect(screen.queryByText('Tooltip content')).not.toBeVisible()
35
35
 
36
36
  fireEvent.mouseEnter(button)
37
37
 
@@ -40,7 +40,7 @@ describe('Tooltip', () => {
40
40
 
41
41
  fireEvent.mouseOut(button)
42
42
  // Validate tooltip content is not rendered anymore
43
- expect(screen.queryByText('Tooltip content')).not.toBeInTheDocument()
43
+ expect(screen.queryByText('Tooltip content')).not.toBeVisible()
44
44
  })
45
45
  it('should support delays to show and hide the tooltip.', () => {
46
46
  vi.useFakeTimers()
@@ -59,13 +59,13 @@ describe('Tooltip', () => {
59
59
  const button = screen.getByText('Hover me')
60
60
 
61
61
  // Validate tooltip content is not rendered
62
- expect(screen.queryByText('Tooltip content')).not.toBeInTheDocument()
62
+ expect(screen.queryByText('Tooltip content')).not.toBeVisible()
63
63
 
64
64
  fireEvent.mouseEnter(button)
65
65
 
66
66
  act(() => vi.advanceTimersByTime(delay / 2))
67
67
  // Content should not be rendered yet
68
- expect(screen.queryByText('Tooltip content')).not.toBeInTheDocument()
68
+ expect(screen.queryByText('Tooltip content')).not.toBeVisible()
69
69
  act(() => vi.advanceTimersByTime(delay / 2))
70
70
 
71
71
  // Validate tooltip content is rendered
@@ -76,7 +76,7 @@ describe('Tooltip', () => {
76
76
  screen.getByText('Tooltip content')
77
77
  act(() => vi.advanceTimersByTime(delay))
78
78
  // Validate tooltip content is not rendered anymore
79
- expect(screen.queryByText('Tooltip content')).not.toBeInTheDocument()
79
+ expect(screen.queryByText('Tooltip content')).not.toBeVisible()
80
80
  })
81
81
  it('should support different open and close delays to show and hide the tooltip.', () => {
82
82
  vi.useFakeTimers()
@@ -99,13 +99,13 @@ describe('Tooltip', () => {
99
99
  const button = screen.getByText('Hover me')
100
100
 
101
101
  // Validate tooltip content is not rendered
102
- expect(screen.queryByText('Tooltip content')).not.toBeInTheDocument()
102
+ expect(screen.queryByText('Tooltip content')).not.toBeVisible()
103
103
 
104
104
  fireEvent.mouseEnter(button)
105
105
 
106
106
  act(() => vi.advanceTimersByTime(openDelay / 2))
107
107
  // Content should not be rendered yet
108
- expect(screen.queryByText('Tooltip content')).not.toBeInTheDocument()
108
+ expect(screen.queryByText('Tooltip content')).not.toBeVisible()
109
109
  act(() => vi.advanceTimersByTime(openDelay / 2))
110
110
 
111
111
  // Validate tooltip content is rendered
@@ -116,7 +116,7 @@ describe('Tooltip', () => {
116
116
  screen.getByText('Tooltip content')
117
117
  act(() => vi.advanceTimersByTime(closeDelay))
118
118
  // Validate tooltip content is not rendered anymore
119
- expect(screen.queryByText('Tooltip content')).not.toBeInTheDocument()
119
+ expect(screen.queryByText('Tooltip content')).not.toBeVisible()
120
120
  })
121
121
  })
122
122
 
@@ -144,20 +144,20 @@ describe('Tooltip', () => {
144
144
  const button2 = screen.getByText('Button 2')
145
145
 
146
146
  // Validate tooltip content is not rendered
147
- expect(screen.queryByText('Tooltip 1')).not.toBeInTheDocument()
148
- expect(screen.queryByText('Tooltip 2')).not.toBeInTheDocument()
147
+ expect(screen.queryByText('Tooltip 1')).not.toBeVisible()
148
+ expect(screen.queryByText('Tooltip 2')).not.toBeVisible()
149
149
 
150
150
  // Hovers on first button, it should show first tooltip only
151
151
  fireEvent.mouseEnter(button1)
152
152
  act(() => vi.advanceTimersByTime(delay / 2))
153
153
  // Content should not be rendered yet, we have a delay of 150ms
154
- expect(screen.queryByText('Tooltip 1')).not.toBeInTheDocument()
155
- expect(screen.queryByText('Tooltip 2')).not.toBeInTheDocument()
154
+ expect(screen.queryByText('Tooltip 1')).not.toBeVisible()
155
+ expect(screen.queryByText('Tooltip 2')).not.toBeVisible()
156
156
  act(() => vi.advanceTimersByTime(delay / 2))
157
157
 
158
158
  // Validate Tooltip 1 is rendered
159
159
  screen.getByText('Tooltip 1')
160
- expect(screen.queryByText('Tooltip 2')).not.toBeInTheDocument()
160
+ expect(screen.queryByText('Tooltip 2')).not.toBeVisible()
161
161
 
162
162
  // Hovers on second button.
163
163
  fireEvent.mouseOut(button1)
@@ -165,13 +165,13 @@ describe('Tooltip', () => {
165
165
 
166
166
  // Validate Tooltip 1 is not rendered, now tooltip 2 is open.
167
167
  act(() => vi.advanceTimersByTime(1))
168
- expect(screen.queryByText('Tooltip 1')).not.toBeInTheDocument()
168
+ expect(screen.queryByText('Tooltip 1')).not.toBeVisible()
169
169
  screen.getByText('Tooltip 2')
170
170
 
171
171
  // Validate tooltip content is not rendered anymore
172
172
  fireEvent.mouseOut(button2)
173
173
  act(() => vi.advanceTimersByTime(delay + 1))
174
- expect(screen.queryByText('Tooltip 2')).not.toBeInTheDocument()
174
+ expect(screen.queryByText('Tooltip 2')).not.toBeVisible()
175
175
 
176
176
  // Hovering again, should trigger the tooltip to show immediately, as the group is not deactivated yet
177
177
  fireEvent.mouseEnter(button2)
@@ -181,13 +181,13 @@ describe('Tooltip', () => {
181
181
  // Validate tooltip content is not rendered anymore
182
182
  fireEvent.mouseOut(button2)
183
183
  act(() => vi.advanceTimersByTime(delay + 1))
184
- expect(screen.queryByText('Tooltip 2')).not.toBeInTheDocument()
184
+ expect(screen.queryByText('Tooltip 2')).not.toBeVisible()
185
185
 
186
186
  // Wait 200ms, the group is deactivated, hovering again should trigger the delay
187
187
  act(() => vi.advanceTimersByTime(200))
188
188
  fireEvent.mouseEnter(button2)
189
189
  act(() => vi.advanceTimersByTime(delay / 2))
190
- expect(screen.queryByText('Tooltip 2')).not.toBeInTheDocument()
190
+ expect(screen.queryByText('Tooltip 2')).not.toBeVisible()
191
191
  act(() => vi.advanceTimersByTime(delay / 2))
192
192
  screen.getByText('Tooltip 2')
193
193
  })
@@ -220,20 +220,20 @@ describe('Tooltip', () => {
220
220
  const button2 = screen.getByText('Button 2')
221
221
 
222
222
  // Validate tooltip content is not rendered
223
- expect(screen.queryByText('Tooltip 1')).not.toBeInTheDocument()
224
- expect(screen.queryByText('Tooltip 2')).not.toBeInTheDocument()
223
+ expect(screen.queryByText('Tooltip 1')).not.toBeVisible()
224
+ expect(screen.queryByText('Tooltip 2')).not.toBeVisible()
225
225
 
226
226
  // Hovers on first button, it should show first tooltip only
227
227
  fireEvent.mouseEnter(button1)
228
228
  act(() => vi.advanceTimersByTime(openDelay / 2))
229
229
  // Content should not be rendered yet, we have a delay of2150ms
230
- expect(screen.queryByText('Tooltip 1')).not.toBeInTheDocument()
231
- expect(screen.queryByText('Tooltip 2')).not.toBeInTheDocument()
230
+ expect(screen.queryByText('Tooltip 1')).not.toBeVisible()
231
+ expect(screen.queryByText('Tooltip 2')).not.toBeVisible()
232
232
  act(() => vi.advanceTimersByTime(openDelay / 2))
233
233
 
234
234
  // Validate Tooltip 1 is rendered
235
235
  screen.getByText('Tooltip 1')
236
- expect(screen.queryByText('Tooltip 2')).not.toBeInTheDocument()
236
+ expect(screen.queryByText('Tooltip 2')).not.toBeVisible()
237
237
 
238
238
  // Hovers on second button.
239
239
  fireEvent.mouseOut(button1)
@@ -241,13 +241,13 @@ describe('Tooltip', () => {
241
241
 
242
242
  // Validate Tooltip 1 is not rendered, now tooltip 2 is open.
243
243
  act(() => vi.advanceTimersByTime(1))
244
- expect(screen.queryByText('Tooltip 1')).not.toBeInTheDocument()
244
+ expect(screen.queryByText('Tooltip 1')).not.toBeVisible()
245
245
  screen.getByText('Tooltip 2')
246
246
 
247
247
  // Validate tooltip content is not rendered anymore
248
248
  fireEvent.mouseOut(button2)
249
249
  act(() => vi.advanceTimersByTime(closeDelay + 1))
250
- expect(screen.queryByText('Tooltip 2')).not.toBeInTheDocument()
250
+ expect(screen.queryByText('Tooltip 2')).not.toBeVisible()
251
251
 
252
252
  // Hovering again, should trigger the tooltip to show immediately, as the group is not deactivated yet
253
253
  fireEvent.mouseEnter(button2)
@@ -257,13 +257,13 @@ describe('Tooltip', () => {
257
257
  // Validate tooltip content is not rendered anymore
258
258
  fireEvent.mouseOut(button2)
259
259
  act(() => vi.advanceTimersByTime(closeDelay + 1))
260
- expect(screen.queryByText('Tooltip 2')).not.toBeInTheDocument()
260
+ expect(screen.queryByText('Tooltip 2')).not.toBeVisible()
261
261
 
262
262
  // Wait 200ms, the group is deactivated, hovering again should trigger the delay
263
263
  act(() => vi.advanceTimersByTime(200))
264
264
  fireEvent.mouseEnter(button2)
265
265
  act(() => vi.advanceTimersByTime(openDelay / 2))
266
- expect(screen.queryByText('Tooltip 2')).not.toBeInTheDocument()
266
+ expect(screen.queryByText('Tooltip 2')).not.toBeVisible()
267
267
  act(() => vi.advanceTimersByTime(openDelay / 2))
268
268
  screen.getByText('Tooltip 2')
269
269
  })
@@ -288,7 +288,7 @@ describe('Tooltip', () => {
288
288
  const button = screen.getByText('Hover me')
289
289
 
290
290
  // Validate tooltip content is not rendered
291
- expect(screen.queryByText('Tooltip content')).not.toBeInTheDocument()
291
+ expect(screen.queryByText('Tooltip content')).not.toBeVisible()
292
292
  act(() => fireEvent.focus(button))
293
293
  act(() => vi.advanceTimersByTime(delay))
294
294
 
@@ -299,7 +299,7 @@ describe('Tooltip', () => {
299
299
  fireEvent.keyDown(button, {key: 'Escape', code: 'Escape'})
300
300
  })
301
301
  // Validate tooltip content is not rendered anymore
302
- expect(screen.queryByText('Tooltip content')).not.toBeInTheDocument()
302
+ expect(screen.queryByText('Tooltip content')).not.toBeVisible()
303
303
  })
304
304
  it('With <TooltipDelayGroupProvider /> closes immediately with Escape key', () => {
305
305
  const delay = 150
@@ -320,8 +320,8 @@ describe('Tooltip', () => {
320
320
 
321
321
  const button = screen.getByText('Hover me')
322
322
 
323
- // Validate tooltip content is not rendered
324
- expect(screen.queryByText('Tooltip content')).not.toBeInTheDocument()
323
+ // Validate tooltip content is not visible
324
+ expect(screen.queryByText('Tooltip content')).not.toBeVisible()
325
325
  act(() => fireEvent.focus(button))
326
326
 
327
327
  act(() => vi.advanceTimersByTime(delay))
@@ -332,8 +332,8 @@ describe('Tooltip', () => {
332
332
  act(() => {
333
333
  fireEvent.keyDown(button, {key: 'Escape', code: 'Escape'})
334
334
  })
335
- // Validate tooltip content is not rendered anymore
336
- expect(screen.queryByText('Tooltip content')).not.toBeInTheDocument()
335
+ // Validate tooltip content is not visible anymore
336
+ expect(screen.queryByText('Tooltip content')).not.toBeVisible()
337
337
  })
338
338
  })
339
339
 
@@ -349,19 +349,19 @@ describe('Tooltip', () => {
349
349
 
350
350
  const button = screen.getByText('Hover me')
351
351
 
352
- // Assertion: tooltip does not exist in the document
353
- expect(screen.queryByText('Tooltip content')).not.toBeInTheDocument()
352
+ // Assertion: the tooltip is not visible
353
+ expect(screen.queryByText('Tooltip content')).not.toBeVisible()
354
354
  act(() => fireEvent.focus(button))
355
355
 
356
356
  act(() => vi.advanceTimersByTime(delay))
357
357
 
358
- // Assertion: the tooltip is not visible
358
+ // Assertion: the tooltip is visible
359
359
  expect(screen.queryByText('Tooltip content')).toBeVisible()
360
360
 
361
361
  act(() => fireEvent.click(button))
362
362
 
363
- // Assertion: tooltip does not exist in the document
364
- expect(screen.queryByText('Tooltip content')).not.toBeInTheDocument()
363
+ // Assertion: the tooltip is not visible
364
+ expect(screen.queryByText('Tooltip content')).not.toBeVisible()
365
365
  })
366
366
 
367
367
  it('Should close the tooltip when the context menu is opened (right click)', () => {
@@ -375,19 +375,19 @@ describe('Tooltip', () => {
375
375
 
376
376
  const button = screen.getByText('Hover me')
377
377
 
378
- // Assertion: tooltip does not exist in the document
379
- expect(screen.queryByText('Tooltip content')).not.toBeInTheDocument()
378
+ // Assertion: the tooltip is not visible
379
+ expect(screen.queryByText('Tooltip content')).not.toBeVisible()
380
380
  act(() => fireEvent.focus(button))
381
381
 
382
382
  act(() => vi.advanceTimersByTime(delay))
383
383
 
384
- // Assertion: the tooltip is not visible
384
+ // Assertion: the tooltip is visible
385
385
  expect(screen.queryByText('Tooltip content')).toBeVisible()
386
386
 
387
387
  act(() => fireEvent.contextMenu(button))
388
388
 
389
- // Assertion: tooltip does not exist in the document
390
- expect(screen.queryByText('Tooltip content')).not.toBeInTheDocument()
389
+ // Assertion: the tooltip is not visible
390
+ expect(screen.queryByText('Tooltip content')).not.toBeVisible()
391
391
  })
392
392
  })
393
393
 
@@ -10,8 +10,8 @@ import {
10
10
  } from '@floating-ui/react-dom'
11
11
  import {type RadiusStyleProps, type ShadowStyleProps, tooltip} from '@sanity/ui/css'
12
12
  import type {CardTone, ColorScheme} from '@sanity/ui/theme'
13
- import {AnimatePresence} from 'motion/react'
14
13
  import {
14
+ Activity,
15
15
  cloneElement,
16
16
  type FocusEvent,
17
17
  type HTMLAttributes,
@@ -36,6 +36,7 @@ import {useDelayedState} from '../../hooks/useDelayedState'
36
36
  import {usePrefersReducedMotion} from '../../hooks/usePrefersReducedMotion'
37
37
  import {origin} from '../../middleware/origin'
38
38
  import type {ComponentType, Delay, Placement, Props} from '../../types'
39
+ import {AnimateActivity} from '../../utils/AnimateActivity'
39
40
  import {BoundaryElementContext} from '../../utils/boundaryElement/BoundaryElementContext'
40
41
  import {getElementRef} from '../../utils/getElementRef'
41
42
  import {Portal} from '../../utils/portal/Portal'
@@ -388,7 +389,7 @@ export function Tooltip<E extends TooltipElementType = typeof DEFAULT_TOOLTIP_EL
388
389
  </TooltipLayer>
389
390
  )
390
391
 
391
- let children = showTooltip ? node : null
392
+ let children = node
392
393
 
393
394
  if (showTooltip && portalProp) {
394
395
  let resolvedTone = tone as Exclude<typeof tone, 'inherit'>
@@ -410,7 +411,13 @@ export function Tooltip<E extends TooltipElementType = typeof DEFAULT_TOOLTIP_EL
410
411
  return (
411
412
  <>
412
413
  {/* the tooltip */}
413
- {animate ? <AnimatePresence>{children}</AnimatePresence> : children}
414
+ {animate ? (
415
+ <AnimateActivity mode={showTooltip ? 'visible' : 'hidden'} layoutMode="default">
416
+ {children}
417
+ </AnimateActivity>
418
+ ) : (
419
+ <Activity mode={showTooltip ? 'visible' : 'hidden'}>{children}</Activity>
420
+ )}
414
421
 
415
422
  {/* the referred element */}
416
423
  {child}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * MIT License
3
+ * Copyright (c) 2024 Motion One
4
+
5
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ * of this software and associated documentation files (the "Software"), to deal
7
+ * in the Software without restriction, including without limitation the rights
8
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ * copies of the Software, and to permit persons to whom the Software is
10
+ * furnished to do so, subject to the following conditions:
11
+
12
+ * The above copyright notice and this permission notice shall be included in all
13
+ * copies or substantial portions of the Software.
14
+
15
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ * SOFTWARE.
22
+ */
23
+
24
+ import {invariant, PresenceChild} from 'motion/react'
25
+ import {Activity, useState} from 'react'
26
+
27
+ export interface AnimateActivityProps {
28
+ mode: 'visible' | 'hidden'
29
+ layoutMode: 'default' | 'pop'
30
+ children: React.ReactNode
31
+ }
32
+
33
+ /**
34
+ * This component will become available on `motion/react` in the future, this "fork" is temporary
35
+ */
36
+ export function AnimateActivity({mode: modeFromProps, layoutMode, children}: AnimateActivityProps) {
37
+ /**
38
+ * This is the mode that we'll render.
39
+ */
40
+ const [mode, setMode] = useState(modeFromProps)
41
+
42
+ /**
43
+ * This is the goal state as defined by props.
44
+ */
45
+ const isPresent = modeFromProps === 'visible'
46
+
47
+ /**
48
+ * Immediately switch to mode="visible" when the user
49
+ * changes the mode prop.
50
+ */
51
+ if (isPresent && mode !== 'visible') {
52
+ setMode('visible')
53
+ return null
54
+ }
55
+
56
+ /**
57
+ * Set mode to "hidden" only when the exit animation is complete.
58
+ */
59
+ const onExitComplete = () => setMode('hidden')
60
+
61
+ invariant(Boolean(Activity), 'Activity component not found - upgrade to React 19.2.0 or higher')
62
+
63
+ return (
64
+ <Activity mode={mode}>
65
+ <PresenceChild
66
+ isPresent={isPresent}
67
+ onExitComplete={!isPresent ? onExitComplete : undefined}
68
+ presenceAffectsLayout={false}
69
+ mode={layoutMode === 'pop' ? 'popLayout' : 'sync'}
70
+ >
71
+ {
72
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
+ children as any
74
+ }
75
+ </PresenceChild>
76
+ </Activity>
77
+ )
78
+ }