@sanity/ui 1.4.0 → 1.5.0

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": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "sideEffects": false,
5
5
  "types": "./dist/index.d.ts",
6
6
  "source": "./src/index.ts",
@@ -50,45 +50,45 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@floating-ui/react-dom": "1.1.1",
53
- "@sanity/color": "^2.2.3",
54
- "@sanity/icons": "^2.2.2",
55
- "csstype": "^3.1.1",
56
- "framer-motion": "^10.0.0",
53
+ "@sanity/color": "^2.2.5",
54
+ "@sanity/icons": "^2.3.1",
55
+ "csstype": "^3.1.2",
56
+ "framer-motion": "^10.12.16",
57
57
  "react-refractor": "^2.1.7"
58
58
  },
59
59
  "devDependencies": {
60
- "@babel/core": "^7.20.12",
61
- "@babel/preset-env": "^7.20.2",
62
- "@babel/preset-react": "^7.18.6",
63
- "@babel/preset-typescript": "^7.18.6",
64
- "@commitlint/cli": "^17.4.4",
65
- "@commitlint/config-conventional": "^17.4.4",
60
+ "@babel/core": "^7.22.1",
61
+ "@babel/preset-env": "^7.22.4",
62
+ "@babel/preset-react": "^7.22.3",
63
+ "@babel/preset-typescript": "^7.21.5",
64
+ "@commitlint/cli": "^17.6.5",
65
+ "@commitlint/config-conventional": "^17.6.5",
66
66
  "@juggle/resize-observer": "^3.4.0",
67
- "@sanity/pkg-utils": "^2.2.13",
68
- "@sanity/semantic-release-preset": "^4.1.0",
67
+ "@sanity/pkg-utils": "^2.3.2",
68
+ "@sanity/semantic-release-preset": "^4.1.1",
69
69
  "@sanity/ui-workshop": "^1.2.1",
70
- "@testing-library/dom": "^9.0.0",
70
+ "@testing-library/dom": "^9.3.0",
71
71
  "@testing-library/jest-dom": "^5.16.5",
72
72
  "@testing-library/react": "^14.0.0",
73
- "@types/jest": "^29.4.0",
73
+ "@types/jest": "^29.5.2",
74
74
  "@types/jest-axe": "^3.5.5",
75
- "@types/node": "^18.14.0",
76
- "@types/react": "^18.0.28",
77
- "@types/react-dom": "^18.0.11",
78
- "@types/react-is": "^18.0.0",
75
+ "@types/node": "^18.16.16",
76
+ "@types/react": "^18.2.9",
77
+ "@types/react-dom": "^18.2.4",
78
+ "@types/react-is": "^18.2.0",
79
79
  "@types/refractor": "^3.0.2",
80
80
  "@types/styled-components": "^5.1.26",
81
- "@types/testing-library__jest-dom": "^5.14.5",
82
- "@typescript-eslint/eslint-plugin": "^5.52.0",
83
- "@typescript-eslint/parser": "^5.52.0",
81
+ "@types/testing-library__jest-dom": "^5.14.6",
82
+ "@typescript-eslint/eslint-plugin": "^5.59.9",
83
+ "@typescript-eslint/parser": "^5.59.9",
84
84
  "commitizen": "^4.3.0",
85
- "cypress": "^12.8.1",
86
- "cypress-real-events": "^1.7.6",
85
+ "cypress": "^12.14.0",
86
+ "cypress-real-events": "^1.8.1",
87
87
  "cz-conventional-changelog": "^3.3.0",
88
- "esbuild": "^0.17.12",
88
+ "esbuild": "^0.17.19",
89
89
  "esbuild-register": "^3.4.2",
90
- "eslint": "^8.34.0",
91
- "eslint-config-prettier": "^8.6.0",
90
+ "eslint": "^8.42.0",
91
+ "eslint-config-prettier": "^8.8.0",
92
92
  "eslint-plugin-import": "^2.27.5",
93
93
  "eslint-plugin-jsx-a11y": "^6.7.1",
94
94
  "eslint-plugin-prettier": "^4.2.1",
@@ -96,22 +96,22 @@
96
96
  "eslint-plugin-react-hooks": "^4.6.0",
97
97
  "http-server": "^14.1.1",
98
98
  "husky": "^8.0.3",
99
- "jest": "^29.4.3",
100
- "jest-axe": "^7.0.0",
101
- "jest-environment-jsdom": "^29.4.3",
102
- "lint-staged": "^13.1.2",
103
- "module-alias": "^2.2.2",
99
+ "jest": "^29.5.0",
100
+ "jest-axe": "^7.0.1",
101
+ "jest-environment-jsdom": "^29.5.0",
102
+ "lint-staged": "^13.2.2",
103
+ "module-alias": "^2.2.3",
104
104
  "npm-run-all": "^4.1.5",
105
- "prettier": "^2.8.4",
105
+ "prettier": "^2.8.8",
106
106
  "react": "^18.2.0",
107
107
  "react-dom": "^18.2.0",
108
108
  "react-is": "^18.2.0",
109
- "rimraf": "^5.0.0",
110
- "semantic-release": "^21.0.0",
109
+ "rimraf": "^5.0.1",
110
+ "semantic-release": "^21.0.3",
111
111
  "start-server-and-test": "^2.0.0",
112
- "styled-components": "^5.3.9",
113
- "tsconfig-paths": "^4.1.2",
114
- "typescript": "^5.0.0"
112
+ "styled-components": "^5.3.11",
113
+ "tsconfig-paths": "^4.2.0",
114
+ "typescript": "^5.1.3"
115
115
  },
116
116
  "peerDependencies": {
117
117
  "react": "^18",
@@ -66,6 +66,8 @@ export default function MenuButtonStory() {
66
66
  />
67
67
  </Menu>
68
68
  }
69
+ onClose={useAction('onClose')}
70
+ onOpen={useAction('onOpen')}
69
71
  popover={popover}
70
72
  />
71
73
  </LayerProvider>
@@ -21,6 +21,7 @@ export interface MenuButtonProps {
21
21
  id: string
22
22
  menu?: React.ReactElement
23
23
  onClose?: () => void
24
+ onOpen?: () => void
24
25
  /**
25
26
  * @deprecated Use `popover={{placement: 'top'}}` instead.
26
27
  */
@@ -59,6 +60,7 @@ export const MenuButton = forwardRef(function MenuButton(
59
60
  id,
60
61
  menu: menuProp,
61
62
  onClose,
63
+ onOpen,
62
64
  placement: deprecated_placement,
63
65
  popoverScheme: deprecated_popoverScheme,
64
66
  portal: deprecated_portal = true,
@@ -72,6 +74,14 @@ export const MenuButton = forwardRef(function MenuButton(
72
74
  const [menuElements, setChildMenuElements] = useState<HTMLElement[]>([])
73
75
  const openRef = useRef<boolean>(open)
74
76
 
77
+ // Notify consumers when the menu opens
78
+ useEffect(() => {
79
+ if (onOpen && open && !openRef.current) {
80
+ onOpen()
81
+ }
82
+ }, [onOpen, open])
83
+
84
+ // Notify consumers when the menu closes
75
85
  useEffect(() => {
76
86
  if (onClose && !open && openRef.current) {
77
87
  onClose()
@@ -40,10 +40,5 @@ export default defineScope({
40
40
  title: 'Open on mount',
41
41
  component: lazy(() => import('./OpenOnMountStory')),
42
42
  },
43
- {
44
- name: 'positionStory',
45
- title: 'Position',
46
- component: lazy(() => import('./PositionStory')),
47
- },
48
43
  ],
49
44
  })
@@ -1,4 +1 @@
1
1
  export * from './popover'
2
- export * from './popoverProvider'
3
- export * from './usePopover'
4
- export * from './types'
@@ -23,7 +23,6 @@ import {
23
23
  } from './constants'
24
24
  import {size} from './floating-ui/size'
25
25
  import {PopoverCard} from './popoverCard'
26
- import {useOnForcedUpdate} from './useOnForcedUpdate'
27
26
 
28
27
  /**
29
28
  * @public
@@ -201,15 +200,10 @@ export const Popover = memo(
201
200
  placement,
202
201
  reference: referenceRef,
203
202
  floating: floatingRef,
204
- update: floatingUpdate,
205
203
  middlewareData,
206
204
  strategy,
207
205
  } = useFloating(floatingProps)
208
206
 
209
- const handleForcedUpdate = useCallback(() => {
210
- floatingUpdate()
211
- }, [floatingUpdate])
212
-
213
207
  const referenceHidden = middlewareData.hide?.referenceHidden
214
208
 
215
209
  const arrowX = middlewareData.arrow?.x
@@ -253,8 +247,6 @@ export const Popover = memo(
253
247
  referenceRef(referenceElement || null)
254
248
  }, [referenceRef, referenceElement])
255
249
 
256
- useOnForcedUpdate(handleForcedUpdate)
257
-
258
250
  if (disabled) {
259
251
  return childProp || <></>
260
252
  }
@@ -1,103 +0,0 @@
1
- import {useBoolean} from '@sanity/ui-workshop'
2
- import {useCallback, useMemo, useState} from 'react'
3
- import {BoundaryElementProvider} from '../../../utils'
4
- import {Button} from '../../button'
5
- import {Card} from '../../card'
6
- import {Flex} from '../../flex'
7
- import {Text} from '../../text'
8
- import {Popover, PopoverProps} from '../popover'
9
- import {PopoverProvider} from '../popoverProvider'
10
- import {usePopover} from '../usePopover'
11
-
12
- function PopoverComponent({
13
- referenceElement,
14
- boundaryElement,
15
- }: {
16
- referenceElement: PopoverProps['referenceElement']
17
- boundaryElement: PopoverProps['boundaryElement']
18
- }) {
19
- return (
20
- <Popover
21
- open
22
- content={
23
- <Card padding={5} radius={3}>
24
- <Text weight="semibold" muted>
25
- I should follow my reference element <br />
26
- when the parent container is resized.
27
- </Text>
28
- </Card>
29
- }
30
- referenceElement={referenceElement}
31
- boundaryElement={boundaryElement}
32
- />
33
- )
34
- }
35
-
36
- export default function PositionStory() {
37
- return (
38
- <PopoverProvider>
39
- <InnerStory />
40
- </PopoverProvider>
41
- )
42
- }
43
-
44
- function InnerStory() {
45
- const [size, setSize] = useState(0.5)
46
- const [referenceElement, setReferenceElement] = useState<PopoverProps['referenceElement']>(null)
47
- const [boundaryElement, setBoundaryElement] = useState<PopoverProps['boundaryElement']>(null)
48
- const updatePositionOnClick = useBoolean('Force popover update on size change', false, 'Props')
49
- const {forceUpdate} = usePopover()
50
-
51
- // Random number between 0.2 and 5
52
- const handleSetSize = useCallback(() => {
53
- const s = Math.random() * (0.5 - 0.2) + 0.2
54
-
55
- setSize(s)
56
-
57
- if (updatePositionOnClick) {
58
- forceUpdate()
59
- }
60
- }, [updatePositionOnClick])
61
-
62
- const element = useMemo(() => {
63
- return (
64
- <Card
65
- as="span"
66
- border
67
- radius={2}
68
- style={{display: 'inline', padding: '0.4, 1'}}
69
- ref={setReferenceElement}
70
- tone="primary"
71
- >
72
- nunc
73
- </Card>
74
- )
75
- }, [])
76
-
77
- return (
78
- <>
79
- <PopoverComponent referenceElement={referenceElement} boundaryElement={boundaryElement} />
80
-
81
- <BoundaryElementProvider element={boundaryElement as HTMLDivElement}>
82
- <Flex height="fill">
83
- <Card borderRight flex={size} tone="transparent">
84
- <Flex align="center" justify="center" height="fill">
85
- <Button text="Change size" onClick={handleSetSize} />
86
- </Flex>
87
- </Card>
88
-
89
- <Card flex={1} ref={setBoundaryElement}>
90
- <Flex height="fill" padding={5}>
91
- <Text>
92
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam ultricies, nisl ut
93
- aliquet ultricies, nunc sapien ultricies tortor, nec tincidunt elit risus ac nunc.
94
- Nulla facilisi. Sed aliquam, {element} eget aliquam ultricies, diam diam aliquam
95
- augue, eget ultricies nisl nunc eget nunc. Nulla facilisi. Sed aliquam,
96
- </Text>
97
- </Flex>
98
- </Card>
99
- </Flex>
100
- </BoundaryElementProvider>
101
- </>
102
- )
103
- }
@@ -1,164 +0,0 @@
1
- /** @jest-environment jsdom */
2
-
3
- import {fireEvent, waitFor, screen} from '@testing-library/react'
4
- import {startTransition, useCallback, useEffect, useMemo, useState} from 'react'
5
- import {render} from '../../../test'
6
- import {PopoverContext} from './popoverContext'
7
- import {PopoverContextValue} from './types'
8
- import {useOnForcedUpdate} from './useOnForcedUpdate'
9
- import {usePopover} from './usePopover'
10
-
11
- describe('components/toast', () => {
12
- describe('usePopover', () => {
13
- it('should get context value', async () => {
14
- const log = jest.fn()
15
-
16
- function Debug() {
17
- const popover = usePopover()
18
-
19
- log(popover)
20
-
21
- return <>debug</>
22
- }
23
-
24
- function Root() {
25
- const value: PopoverContextValue = {
26
- version: 0.0,
27
- // eslint-disable-next-line @typescript-eslint/no-empty-function
28
- forceUpdate: () => {},
29
- lastUpdateId: null,
30
- }
31
-
32
- return (
33
- <PopoverContext.Provider value={value}>
34
- <Debug />
35
- </PopoverContext.Provider>
36
- )
37
- }
38
-
39
- render(<Root />)
40
-
41
- expect(log.mock.calls[0][0].version).toBe(0.0)
42
- })
43
-
44
- it('should fail when no context value is provided', async () => {
45
- const log = jest.fn()
46
-
47
- function Debug() {
48
- try {
49
- usePopover()
50
- } catch (err) {
51
- log(err)
52
- }
53
-
54
- return null
55
- }
56
-
57
- function Root() {
58
- const value = undefined
59
-
60
- return (
61
- <PopoverContext.Provider value={value as any}>
62
- <Debug />
63
- </PopoverContext.Provider>
64
- )
65
- }
66
-
67
- render(<Root />)
68
-
69
- expect(log.mock.calls[0][0].message).toEqual('usePopover(): missing context value')
70
- })
71
-
72
- it('should fail when context value is not compatible', async () => {
73
- const log = jest.fn()
74
-
75
- function Debug() {
76
- try {
77
- usePopover()
78
- } catch (err) {
79
- log(err)
80
- }
81
-
82
- return null
83
- }
84
-
85
- function Root() {
86
- // NOTE: we’re testing this because the context value may be a function in the future
87
- const value = () => {
88
- return {version: 1}
89
- }
90
-
91
- return (
92
- <PopoverContext.Provider value={value as any}>
93
- <Debug />
94
- </PopoverContext.Provider>
95
- )
96
- }
97
-
98
- render(<Root />)
99
-
100
- expect(log.mock.calls[0][0].message).toEqual(
101
- 'usePopover(): the context value is not compatible'
102
- )
103
- })
104
-
105
- it('should trigger hook when forceUpdate is called', async () => {
106
- const log = jest.fn()
107
- const forcedCallback = jest.fn()
108
-
109
- function Popover() {
110
- useOnForcedUpdate(forcedCallback)
111
-
112
- return null
113
- }
114
-
115
- function Debug() {
116
- const popover = usePopover()
117
-
118
- useEffect(() => {
119
- log(popover)
120
- }, [popover])
121
-
122
- const update = useCallback(() => {
123
- popover.forceUpdate()
124
- }, [popover])
125
-
126
- return <button onClick={update}>Force position update</button>
127
- }
128
-
129
- function Root() {
130
- const [lastUpdateId, setLastUpdateId] = useState<string | null>(null)
131
- const forceUpdate = useCallback(() => {
132
- startTransition(() => {
133
- setLastUpdateId('newid')
134
- })
135
- }, [])
136
- const value: PopoverContextValue = useMemo(() => {
137
- return {
138
- version: 0.0,
139
- forceUpdate,
140
- lastUpdateId,
141
- }
142
- }, [forceUpdate, lastUpdateId])
143
-
144
- return (
145
- <PopoverContext.Provider value={value}>
146
- <Debug />
147
- <Popover />
148
- </PopoverContext.Provider>
149
- )
150
- }
151
-
152
- render(<Root />)
153
-
154
- fireEvent.click(screen.getByText('Force position update'))
155
-
156
- await waitFor(() => {
157
- expect(log).toHaveBeenCalledTimes(3)
158
- expect(log.mock.calls[0][0].lastUpdateId).toBeNull()
159
- expect(log.mock.calls[2][0].lastUpdateId).not.toBeNull()
160
- expect(forcedCallback).toHaveBeenCalledTimes(1)
161
- })
162
- })
163
- })
164
- })
@@ -1,9 +0,0 @@
1
- import {createContext} from 'react'
2
- import {globalScope} from '../../lib/globalScope'
3
- import type {PopoverContextValue} from './types'
4
-
5
- const key = Symbol.for('@sanity/ui/context/popover')
6
-
7
- globalScope[key] = globalScope[key] || createContext<PopoverContextValue | null>(null)
8
-
9
- export const PopoverContext: React.Context<PopoverContextValue | null> = globalScope[key]
@@ -1,29 +0,0 @@
1
- import React, {useState, useCallback, useMemo} from 'react'
2
- import {PopoverContext} from './popoverContext'
3
-
4
- /**
5
- * @public
6
- */
7
- export interface PopoverProviderProps {
8
- children?: React.ReactNode
9
- }
10
-
11
- /**
12
- * @public
13
- */
14
- export const PopoverProvider = ({children}: PopoverProviderProps): React.ReactElement => {
15
- const [lastUpdateId, setUpdateId] = useState<string | null>(null)
16
-
17
- const forceUpdate = useCallback(() => {
18
- // Generate a new random ID
19
- const newId = Math.random().toString(36).substr(2, 9)
20
- setUpdateId(newId)
21
- }, [])
22
-
23
- const value = useMemo(
24
- () => ({version: 0.0, lastUpdateId, forceUpdate}),
25
- [lastUpdateId, forceUpdate]
26
- )
27
-
28
- return <PopoverContext.Provider value={value}>{children}</PopoverContext.Provider>
29
- }
@@ -1,8 +0,0 @@
1
- /**
2
- * @public
3
- */
4
- export interface PopoverContextValue {
5
- version: number
6
- forceUpdate: () => void
7
- lastUpdateId: string | null
8
- }
@@ -1,15 +0,0 @@
1
- import {useContext, useEffect} from 'react'
2
- import {PopoverContext} from './popoverContext'
3
-
4
- /**
5
- * @public
6
- */
7
- export const useOnForcedUpdate = (eventHandler: () => void): void => {
8
- const contextValue = useContext(PopoverContext)
9
-
10
- useEffect(() => {
11
- if (contextValue?.lastUpdateId !== null) {
12
- eventHandler?.()
13
- }
14
- }, [contextValue?.lastUpdateId])
15
- }
@@ -1,24 +0,0 @@
1
- import {useContext} from 'react'
2
- import {isRecord} from '../../lib/isRecord'
3
- import {PopoverContext} from './popoverContext'
4
- import type {PopoverContextValue} from './types'
5
-
6
- /**
7
- * @public
8
- */
9
- export function usePopover(): PopoverContextValue {
10
- const value = useContext(PopoverContext)
11
-
12
- if (!value) {
13
- throw new Error('usePopover(): missing context value')
14
- }
15
-
16
- // NOTE: This check is for future-compatiblity
17
- // - If the value is not an object, it’s not compatible with the current version
18
- // - If the value is an object, but doesn’t have `version: 0.0`, it’s not compatible with the current version
19
- if (!isRecord(value) || value.version !== 0.0) {
20
- throw new Error('usePopover(): the context value is not compatible')
21
- }
22
-
23
- return value
24
- }