@sanity/sdk-react 0.0.0-alpha.19 → 0.0.0-alpha.2

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.
Files changed (107) hide show
  1. package/README.md +67 -38
  2. package/dist/_chunks-es/useLogOut.js +44 -0
  3. package/dist/_chunks-es/useLogOut.js.map +1 -0
  4. package/dist/assets/bundle-CcAyERuZ.css +11 -0
  5. package/dist/components.d.ts +257 -0
  6. package/dist/components.js +316 -0
  7. package/dist/components.js.map +1 -0
  8. package/dist/hooks.d.ts +187 -0
  9. package/dist/hooks.js +81 -0
  10. package/dist/hooks.js.map +1 -0
  11. package/dist/index.d.ts +2 -4641
  12. package/dist/index.js +2 -960
  13. package/dist/index.js.map +1 -1
  14. package/package.json +56 -25
  15. package/src/_exports/components.ts +13 -0
  16. package/src/_exports/hooks.ts +9 -0
  17. package/src/_exports/index.ts +10 -58
  18. package/src/components/DocumentGridLayout/DocumentGridLayout.stories.tsx +113 -0
  19. package/src/components/DocumentGridLayout/DocumentGridLayout.test.tsx +42 -0
  20. package/src/components/DocumentGridLayout/DocumentGridLayout.tsx +21 -0
  21. package/src/components/DocumentListLayout/DocumentListLayout.stories.tsx +105 -0
  22. package/src/components/DocumentListLayout/DocumentListLayout.test.tsx +42 -0
  23. package/src/components/DocumentListLayout/DocumentListLayout.tsx +12 -0
  24. package/src/components/DocumentPreviewLayout/DocumentPreviewLayout.md +49 -0
  25. package/src/components/DocumentPreviewLayout/DocumentPreviewLayout.stories.tsx +39 -0
  26. package/src/components/DocumentPreviewLayout/DocumentPreviewLayout.test.tsx +30 -0
  27. package/src/components/DocumentPreviewLayout/DocumentPreviewLayout.tsx +171 -0
  28. package/src/components/Login/LoginLinks.test.tsx +12 -2
  29. package/src/components/Login/LoginLinks.tsx +29 -14
  30. package/src/components/auth/AuthBoundary.test.tsx +17 -4
  31. package/src/components/auth/AuthBoundary.tsx +4 -20
  32. package/src/components/auth/Login.test.tsx +16 -2
  33. package/src/components/auth/Login.tsx +30 -11
  34. package/src/components/auth/LoginCallback.test.tsx +17 -2
  35. package/src/components/auth/LoginCallback.tsx +10 -5
  36. package/src/components/auth/LoginError.test.tsx +17 -2
  37. package/src/components/auth/LoginError.tsx +16 -11
  38. package/src/components/auth/LoginFooter.test.tsx +16 -2
  39. package/src/components/auth/LoginFooter.tsx +24 -8
  40. package/src/components/auth/LoginLayout.test.tsx +16 -2
  41. package/src/components/auth/LoginLayout.tsx +38 -8
  42. package/src/{context → components/context}/SanityProvider.test.tsx +2 -2
  43. package/src/components/context/SanityProvider.tsx +42 -0
  44. package/src/css/css.config.js +220 -0
  45. package/src/css/paramour.css +2347 -0
  46. package/src/css/styles.css +11 -0
  47. package/src/hooks/auth/useAuthState.tsx +5 -4
  48. package/src/hooks/auth/useAuthToken.tsx +1 -1
  49. package/src/hooks/auth/useCurrentUser.tsx +4 -27
  50. package/src/hooks/auth/useHandleCallback.tsx +0 -1
  51. package/src/hooks/auth/useLogOut.tsx +1 -1
  52. package/src/hooks/auth/useLoginUrls.tsx +0 -1
  53. package/src/hooks/client/useClient.test.tsx +130 -0
  54. package/src/hooks/client/useClient.ts +30 -8
  55. package/src/hooks/context/useSanityInstance.test.tsx +2 -2
  56. package/src/hooks/context/useSanityInstance.ts +8 -24
  57. package/src/hooks/documentCollection/useDocuments.test.ts +130 -0
  58. package/src/hooks/documentCollection/useDocuments.ts +87 -0
  59. package/src/hooks/helpers/createCallbackHook.tsx +2 -3
  60. package/src/hooks/helpers/createStateSourceHook.test.tsx +0 -66
  61. package/src/hooks/helpers/createStateSourceHook.tsx +10 -29
  62. package/src/hooks/preview/usePreview.test.tsx +10 -19
  63. package/src/hooks/preview/usePreview.tsx +13 -67
  64. package/src/components/SDKProvider.test.tsx +0 -79
  65. package/src/components/SDKProvider.tsx +0 -42
  66. package/src/components/SanityApp.test.tsx +0 -156
  67. package/src/components/SanityApp.tsx +0 -90
  68. package/src/components/auth/authTestHelpers.tsx +0 -11
  69. package/src/components/utils.ts +0 -22
  70. package/src/context/SanityInstanceContext.ts +0 -4
  71. package/src/context/SanityProvider.tsx +0 -50
  72. package/src/hooks/_synchronous-groq-js.mjs +0 -4
  73. package/src/hooks/comlink/useFrameConnection.test.tsx +0 -157
  74. package/src/hooks/comlink/useFrameConnection.ts +0 -130
  75. package/src/hooks/comlink/useManageFavorite.test.ts +0 -106
  76. package/src/hooks/comlink/useManageFavorite.ts +0 -98
  77. package/src/hooks/comlink/useRecordDocumentHistoryEvent.test.ts +0 -77
  78. package/src/hooks/comlink/useRecordDocumentHistoryEvent.ts +0 -75
  79. package/src/hooks/comlink/useWindowConnection.test.ts +0 -125
  80. package/src/hooks/comlink/useWindowConnection.ts +0 -94
  81. package/src/hooks/datasets/useDatasets.ts +0 -37
  82. package/src/hooks/document/useApplyActions.test.ts +0 -25
  83. package/src/hooks/document/useApplyActions.ts +0 -74
  84. package/src/hooks/document/useDocument.test.ts +0 -81
  85. package/src/hooks/document/useDocument.ts +0 -107
  86. package/src/hooks/document/useDocumentEvent.test.ts +0 -63
  87. package/src/hooks/document/useDocumentEvent.ts +0 -54
  88. package/src/hooks/document/useDocumentSyncStatus.test.ts +0 -16
  89. package/src/hooks/document/useDocumentSyncStatus.ts +0 -30
  90. package/src/hooks/document/useEditDocument.test.ts +0 -179
  91. package/src/hooks/document/useEditDocument.ts +0 -195
  92. package/src/hooks/document/usePermissions.ts +0 -82
  93. package/src/hooks/infiniteList/useInfiniteList.test.tsx +0 -152
  94. package/src/hooks/infiniteList/useInfiniteList.ts +0 -174
  95. package/src/hooks/paginatedList/usePaginatedList.test.tsx +0 -259
  96. package/src/hooks/paginatedList/usePaginatedList.ts +0 -290
  97. package/src/hooks/projection/useProjection.test.tsx +0 -218
  98. package/src/hooks/projection/useProjection.ts +0 -135
  99. package/src/hooks/projects/useProject.ts +0 -45
  100. package/src/hooks/projects/useProjects.ts +0 -41
  101. package/src/hooks/query/useQuery.test.tsx +0 -188
  102. package/src/hooks/query/useQuery.ts +0 -103
  103. package/src/hooks/users/useUsers.test.ts +0 -163
  104. package/src/hooks/users/useUsers.ts +0 -107
  105. package/src/utils/getEnv.ts +0 -21
  106. package/src/version.ts +0 -8
  107. package/src/vite-env.d.ts +0 -10
@@ -1,3 +1,4 @@
1
+ import {Button, Card, Container, Flex, Heading, Stack} from '@sanity/ui'
1
2
  import {type ReactElement} from 'react'
2
3
 
3
4
  import {useAuthState} from '../../hooks/auth/useAuthState'
@@ -29,30 +30,44 @@ export const LoginLinks = (): ReactElement => {
29
30
  useHandleCallback()
30
31
 
31
32
  if (authState.type === 'logging-in') {
32
- return <div className="sc-login-links__logging-in">Logging in...</div>
33
+ return <div>Logging in...</div>
33
34
  }
34
35
 
35
36
  // Show success state after authentication
36
37
  if (authState.type === 'logged-in') {
37
- return <div className="sc-login-links__logged-in">You are logged in</div>
38
+ return <div>You are logged in</div>
38
39
  }
39
40
 
40
41
  /**
41
42
  * Render provider selection UI
43
+ * Uses Sanity UI components for consistent styling
42
44
  */
43
45
  return (
44
- <div className="sc-login-links">
45
- <h2 className="sc-login-links__title">Choose login provider</h2>
46
+ <Card height="fill" overflow="auto" paddingX={4}>
47
+ <Flex height="fill" direction="column" align="center" justify="center" paddingTop={4}>
48
+ <Container width={0}>
49
+ <Stack space={4}>
50
+ <Heading align="center" size={1}>
51
+ Choose login provider
52
+ </Heading>
46
53
 
47
- <ul className="sc-login-links__list">
48
- {loginUrls.map((provider, index) => (
49
- <li key={`${provider.url}_${index}`} className="sc-login-links__item">
50
- <a href={provider.url} className="sc-login-links__link">
51
- {provider.title}
52
- </a>
53
- </li>
54
- ))}
55
- </ul>
56
- </div>
54
+ <Stack space={2}>
55
+ {loginUrls.map((provider, index) => (
56
+ <Button
57
+ key={`${provider.url}_${index}`}
58
+ as="a"
59
+ href={provider.url}
60
+ mode="ghost"
61
+ tone="default"
62
+ space={3}
63
+ padding={3}
64
+ text={provider.title}
65
+ />
66
+ ))}
67
+ </Stack>
68
+ </Stack>
69
+ </Container>
70
+ </Flex>
71
+ </Card>
57
72
  )
58
73
  }
@@ -1,10 +1,13 @@
1
- import {AuthStateType} from '@sanity/sdk'
2
- import {useAuthState} from '@sanity/sdk-react'
3
- import {screen, waitFor} from '@testing-library/react'
1
+ import {AuthStateType, createSanityInstance} from '@sanity/sdk'
2
+ import {ThemeProvider} from '@sanity/ui'
3
+ import {buildTheme} from '@sanity/ui/theme'
4
+ import {render, screen, waitFor} from '@testing-library/react'
5
+ import React from 'react'
4
6
  import {beforeEach, describe, expect, it, type MockInstance, vi} from 'vitest'
5
7
 
8
+ import {useAuthState} from '../../hooks/auth/useAuthState'
9
+ import {SanityProvider} from '../context/SanityProvider'
6
10
  import {AuthBoundary} from './AuthBoundary'
7
- import {renderWithWrappers} from './authTestHelpers'
8
11
 
9
12
  // Mock hooks
10
13
  vi.mock('../../hooks/auth/useAuthState', () => ({
@@ -35,6 +38,16 @@ vi.mock('./AuthError', async (importOriginal) => {
35
38
  }
36
39
  })
37
40
 
41
+ const theme = buildTheme({})
42
+ const sanityInstance = createSanityInstance({projectId: 'test-project-id', dataset: 'production'})
43
+ const renderWithWrappers = (ui: React.ReactElement) => {
44
+ return render(
45
+ <ThemeProvider theme={theme}>
46
+ <SanityProvider sanityInstance={sanityInstance}>{ui}</SanityProvider>
47
+ </ThemeProvider>,
48
+ )
49
+ }
50
+
38
51
  describe('AuthBoundary', () => {
39
52
  let consoleErrorSpy: MockInstance
40
53
  beforeEach(() => {
@@ -3,32 +3,16 @@ import {useMemo} from 'react'
3
3
  import {ErrorBoundary, type FallbackProps} from 'react-error-boundary'
4
4
 
5
5
  import {useAuthState} from '../../hooks/auth/useAuthState'
6
- import {isInIframe} from '../utils'
7
6
  import {AuthError} from './AuthError'
8
7
  import {Login} from './Login'
9
8
  import {LoginCallback} from './LoginCallback'
10
9
  import {LoginError, type LoginErrorProps} from './LoginError'
11
- import {type LoginLayoutProps} from './LoginLayout'
12
-
13
- // Only import bridge if we're in an iframe. This assumes that the app is
14
- // running within SanityOS if it is in an iframe.
15
- if (isInIframe()) {
16
- const parsedUrl = new URL(window.location.href)
17
- const mode = new URLSearchParams(parsedUrl.hash.slice(1)).get('mode')
18
- const script = document.createElement('script')
19
- script.src =
20
- mode === 'core-ui--staging'
21
- ? 'https://core.sanity-cdn.work/bridge.js'
22
- : 'https://core.sanity-cdn.com/bridge.js'
23
- script.type = 'module'
24
- script.async = true
25
- document.head.appendChild(script)
26
- }
10
+ import type {LoginLayoutProps} from './LoginLayout'
27
11
 
28
12
  /**
29
- * @internal
13
+ * @alpha
30
14
  */
31
- interface AuthBoundaryProps extends LoginLayoutProps {
15
+ export interface AuthBoundaryProps extends LoginLayoutProps {
32
16
  /**
33
17
  * Custom component to render the login screen.
34
18
  * Receives all login layout props. Defaults to {@link Login}.
@@ -68,7 +52,7 @@ interface AuthBoundaryProps extends LoginLayoutProps {
68
52
  * }
69
53
  * ```
70
54
  *
71
- * @internal
55
+ * @alpha
72
56
  */
73
57
  export function AuthBoundary({
74
58
  LoginErrorComponent = LoginError,
@@ -1,7 +1,11 @@
1
- import {screen} from '@testing-library/react'
1
+ import {createSanityInstance} from '@sanity/sdk'
2
+ import {ThemeProvider} from '@sanity/ui'
3
+ import {buildTheme} from '@sanity/ui/theme'
4
+ import {render, screen} from '@testing-library/react'
5
+ import React from 'react'
2
6
  import {describe, expect, it, vi} from 'vitest'
3
7
 
4
- import {renderWithWrappers} from './authTestHelpers'
8
+ import {SanityProvider} from '../context/SanityProvider'
5
9
  import {Login} from './Login'
6
10
 
7
11
  vi.mock('../../hooks/auth/useLoginUrls', () => ({
@@ -11,6 +15,16 @@ vi.mock('../../hooks/auth/useLoginUrls', () => ({
11
15
  ]),
12
16
  }))
13
17
 
18
+ const theme = buildTheme({})
19
+ const sanityInstance = createSanityInstance({projectId: 'test-project-id', dataset: 'production'})
20
+ const renderWithWrappers = (ui: React.ReactElement) => {
21
+ return render(
22
+ <ThemeProvider theme={theme}>
23
+ <SanityProvider sanityInstance={sanityInstance}>{ui}</SanityProvider>
24
+ </ThemeProvider>,
25
+ )
26
+ }
27
+
14
28
  describe('Login', () => {
15
29
  it('renders login providers', () => {
16
30
  renderWithWrappers(<Login />)
@@ -1,25 +1,46 @@
1
- import {type JSX, Suspense} from 'react'
1
+ import {Button, Flex, Heading, Spinner} from '@sanity/ui'
2
+ import {Suspense} from 'react'
3
+ import styled from 'styled-components'
2
4
 
3
5
  import {useLoginUrls} from '../../hooks/auth/useLoginUrls'
4
6
  import {LoginLayout, type LoginLayoutProps} from './LoginLayout'
5
7
 
8
+ /**
9
+ * @alpha
10
+ */
11
+ export interface LoginProps {
12
+ header?: React.ReactNode
13
+ footer?: React.ReactNode
14
+ }
15
+
16
+ const FallbackRoot = styled(Flex)`
17
+ height: 123px;
18
+ `
19
+
6
20
  /**
7
21
  * Login component that displays available authentication providers.
8
22
  * Renders a list of login options with a loading fallback while providers load.
9
23
  *
10
24
  * @alpha
11
- * @internal
12
25
  */
13
26
  export function Login({header, footer}: LoginLayoutProps): JSX.Element {
14
27
  return (
15
28
  <LoginLayout header={header} footer={footer}>
16
- <div className="sc-login">
17
- <h1 className="sc-login__title">Choose login provider</h1>
29
+ <Flex direction="column" gap={4}>
30
+ <Heading as="h1" size={1} align="center">
31
+ Choose login provider
32
+ </Heading>
18
33
 
19
- <Suspense fallback={<div className="sc-login__loading">Loading…</div>}>
34
+ <Suspense
35
+ fallback={
36
+ <FallbackRoot align="center" justify="center">
37
+ <Spinner />
38
+ </FallbackRoot>
39
+ }
40
+ >
20
41
  <Providers />
21
42
  </Suspense>
22
- </div>
43
+ </Flex>
23
44
  </LoginLayout>
24
45
  )
25
46
  }
@@ -28,12 +49,10 @@ function Providers() {
28
49
  const loginUrls = useLoginUrls()
29
50
 
30
51
  return (
31
- <div className="sc-login-providers">
52
+ <Flex direction="column" gap={3}>
32
53
  {loginUrls.map(({title, url}) => (
33
- <a key={url} href={url}>
34
- {title}
35
- </a>
54
+ <Button key={url} text={title} as="a" href={url} mode="ghost" />
36
55
  ))}
37
- </div>
56
+ </Flex>
38
57
  )
39
58
  }
@@ -1,7 +1,22 @@
1
- import {screen, waitFor} from '@testing-library/react'
1
+ import {createSanityInstance} from '@sanity/sdk'
2
+ import {ThemeProvider} from '@sanity/ui'
3
+ import {buildTheme} from '@sanity/ui/theme'
4
+ import {render, screen, waitFor} from '@testing-library/react'
5
+ import React from 'react'
2
6
  import {afterAll, beforeAll, beforeEach, describe, expect, it, vi} from 'vitest'
3
7
 
4
- import {renderWithWrappers} from './authTestHelpers'
8
+ import {SanityProvider} from '../context/SanityProvider'
9
+
10
+ const theme = buildTheme({})
11
+ const sanityInstance = createSanityInstance({projectId: 'test-project-id', dataset: 'production'})
12
+
13
+ const renderWithWrappers = (ui: React.ReactElement) => {
14
+ return render(
15
+ <ThemeProvider theme={theme}>
16
+ <SanityProvider sanityInstance={sanityInstance}>{ui}</SanityProvider>
17
+ </ThemeProvider>,
18
+ )
19
+ }
5
20
 
6
21
  // Mock `useHandleCallback`
7
22
  vi.mock('../../hooks/auth/useHandleCallback', () => ({
@@ -1,9 +1,14 @@
1
+ import {Flex, Spinner, Text} from '@sanity/ui'
1
2
  import {useEffect} from 'react'
3
+ import styled from 'styled-components'
2
4
 
3
5
  import {useHandleCallback} from '../../hooks/auth/useHandleCallback'
4
6
  import {LoginLayout, type LoginLayoutProps} from './LoginLayout'
5
7
 
6
- /**
8
+ const StyledFlex = styled(Flex)`
9
+ margin: auto;
10
+ `
11
+
7
12
  /**
8
13
  * Component shown during auth callback processing that handles login completion.
9
14
  * Automatically processes the auth callback when mounted and updates the URL
@@ -27,10 +32,10 @@ export function LoginCallback({header, footer}: LoginLayoutProps): React.ReactNo
27
32
 
28
33
  return (
29
34
  <LoginLayout header={header} footer={footer}>
30
- <div className="sc-login-callback">
31
- <h1 className="sc-login-callback__title">Logging you in…</h1>
32
- <div className="sc-login-callback__loading">Loading…</div>
33
- </div>
35
+ <StyledFlex direction="column" justify="center" align="center" gap={4}>
36
+ <Text size={1}>Logging you in…</Text>
37
+ <Spinner size={4} />
38
+ </StyledFlex>
34
39
  </LoginLayout>
35
40
  )
36
41
  }
@@ -1,14 +1,29 @@
1
- import {fireEvent, screen, waitFor} from '@testing-library/react'
1
+ import {createSanityInstance} from '@sanity/sdk'
2
+ import {ThemeProvider} from '@sanity/ui'
3
+ import {buildTheme} from '@sanity/ui/theme'
4
+ import {fireEvent, render, screen, waitFor} from '@testing-library/react'
5
+ import React from 'react'
2
6
  import {describe, expect, it, vi} from 'vitest'
3
7
 
8
+ import {SanityProvider} from '../context/SanityProvider'
4
9
  import {AuthError} from './AuthError'
5
- import {renderWithWrappers} from './authTestHelpers'
6
10
  import {LoginError} from './LoginError'
7
11
 
8
12
  vi.mock('../../hooks/auth/useLogOut', () => ({
9
13
  useLogOut: vi.fn(() => async () => {}),
10
14
  }))
11
15
 
16
+ const theme = buildTheme({})
17
+ const sanityInstance = createSanityInstance({projectId: 'test-project-id', dataset: 'production'})
18
+
19
+ const renderWithWrappers = (ui: React.ReactElement) => {
20
+ return render(
21
+ <ThemeProvider theme={theme}>
22
+ <SanityProvider sanityInstance={sanityInstance}>{ui}</SanityProvider>
23
+ </ThemeProvider>,
24
+ )
25
+ }
26
+
12
27
  describe('LoginError', () => {
13
28
  it('shows authentication error and retry button', async () => {
14
29
  const mockReset = vi.fn()
@@ -1,5 +1,7 @@
1
+ import {Button, Flex, Text} from '@sanity/ui'
1
2
  import {useCallback} from 'react'
2
3
  import {type FallbackProps} from 'react-error-boundary'
4
+ import styled from 'styled-components'
3
5
 
4
6
  import {useLogOut} from '../../hooks/auth/useLogOut'
5
7
  import {AuthError} from './AuthError'
@@ -10,6 +12,10 @@ import {LoginLayout, type LoginLayoutProps} from './LoginLayout'
10
12
  */
11
13
  export type LoginErrorProps = FallbackProps & LoginLayoutProps
12
14
 
15
+ const StyledFlex = styled(Flex)`
16
+ margin: auto;
17
+ `
18
+
13
19
  /**
14
20
  * Displays authentication error details and provides retry functionality.
15
21
  * Only handles {@link AuthError} instances - rethrows other error types.
@@ -32,18 +38,17 @@ export function LoginError({
32
38
 
33
39
  return (
34
40
  <LoginLayout header={header} footer={footer}>
35
- <div className="sc-login-error">
36
- <div className="sc-login-error__content">
37
- <h2 className="sc-login-error__title">Authentication Error</h2>
38
- <p className="sc-login-error__description">
41
+ <StyledFlex direction="column" gap={4}>
42
+ <Flex direction="column" gap={3}>
43
+ <Text as="h2" align="center" weight="bold" size={3}>
44
+ Authentication Error
45
+ </Text>
46
+ <Text size={1} align="center">
39
47
  Please try again or contact support if the problem persists.
40
- </p>
41
- </div>
42
-
43
- <button className="sc-login-error__button" onClick={handleRetry}>
44
- Retry
45
- </button>
46
- </div>
48
+ </Text>
49
+ </Flex>
50
+ <Button text="Retry" tone="primary" onClick={handleRetry} />
51
+ </StyledFlex>
47
52
  </LoginLayout>
48
53
  )
49
54
  }
@@ -1,9 +1,23 @@
1
- import {screen} from '@testing-library/react'
1
+ import {createSanityInstance} from '@sanity/sdk'
2
+ import {ThemeProvider} from '@sanity/ui'
3
+ import {buildTheme} from '@sanity/ui/theme'
4
+ import {render, screen} from '@testing-library/react'
5
+ import React from 'react'
2
6
  import {describe, expect, it} from 'vitest'
3
7
 
4
- import {renderWithWrappers} from './authTestHelpers'
8
+ import {SanityProvider} from '../context/SanityProvider'
5
9
  import {LoginFooter} from './LoginFooter'
6
10
 
11
+ const theme = buildTheme({})
12
+ const sanityInstance = createSanityInstance({projectId: 'test-project-id', dataset: 'production'})
13
+ const renderWithWrappers = (ui: React.ReactElement) => {
14
+ return render(
15
+ <ThemeProvider theme={theme}>
16
+ <SanityProvider sanityInstance={sanityInstance}>{ui}</SanityProvider>
17
+ </ThemeProvider>,
18
+ )
19
+ }
20
+
7
21
  describe('LoginFooter', () => {
8
22
  it('renders footer links', () => {
9
23
  renderWithWrappers(<LoginFooter />)
@@ -1,5 +1,7 @@
1
1
  import {SanityLogo} from '@sanity/logos'
2
+ import {Flex, Text} from '@sanity/ui'
2
3
  import {Fragment} from 'react'
4
+ import styled from 'styled-components'
3
5
 
4
6
  const LINKS = [
5
7
  {
@@ -24,6 +26,12 @@ const LINKS = [
24
26
  },
25
27
  ]
26
28
 
29
+ const StyledText = styled(Text)`
30
+ a {
31
+ color: inherit;
32
+ }
33
+ `
34
+
27
35
  /**
28
36
  * Default footer component for login screens showing Sanity branding and legal
29
37
  * links.
@@ -32,20 +40,28 @@ const LINKS = [
32
40
  */
33
41
  export function LoginFooter(): React.ReactNode {
34
42
  return (
35
- <div className="sc-login-footer">
36
- <SanityLogo className="sc-login-footer__logo" />
43
+ <Flex direction="column" gap={4} justify="center" align="center" paddingTop={2}>
44
+ <Text size={3}>
45
+ <SanityLogo />
46
+ </Text>
37
47
 
38
- <ul className="sc-login-footer__links">
39
- {LINKS.map((link) => (
48
+ <Flex align="center" gap={2}>
49
+ {LINKS.map((link, index) => (
40
50
  <Fragment key={link.title}>
41
- <li className="sc-login-footer__link">
51
+ <StyledText muted size={1}>
42
52
  <a href={link.url} target="_blank" rel="noopener noreferrer">
43
53
  {link.title}
44
54
  </a>
45
- </li>
55
+ </StyledText>
56
+
57
+ {index < LINKS.length - 1 && (
58
+ <Text size={1} muted>
59
+
60
+ </Text>
61
+ )}
46
62
  </Fragment>
47
63
  ))}
48
- </ul>
49
- </div>
64
+ </Flex>
65
+ </Flex>
50
66
  )
51
67
  }
@@ -1,9 +1,23 @@
1
- import {screen} from '@testing-library/react'
1
+ import {createSanityInstance} from '@sanity/sdk'
2
+ import {ThemeProvider} from '@sanity/ui'
3
+ import {buildTheme} from '@sanity/ui/theme'
4
+ import {render, screen} from '@testing-library/react'
5
+ import React from 'react'
2
6
  import {describe, expect, it} from 'vitest'
3
7
 
4
- import {renderWithWrappers} from './authTestHelpers'
8
+ import {SanityProvider} from '../context/SanityProvider'
5
9
  import {LoginLayout} from './LoginLayout'
6
10
 
11
+ const theme = buildTheme({})
12
+ const sanityInstance = createSanityInstance({projectId: 'test-project-id', dataset: 'production'})
13
+ const renderWithWrappers = (ui: React.ReactElement) => {
14
+ return render(
15
+ <ThemeProvider theme={theme}>
16
+ <SanityProvider sanityInstance={sanityInstance}>{ui}</SanityProvider>
17
+ </ThemeProvider>,
18
+ )
19
+ }
20
+
7
21
  describe('LoginLayout', () => {
8
22
  it('renders header, children, and footer', () => {
9
23
  renderWithWrappers(
@@ -1,3 +1,6 @@
1
+ import {Card, Flex} from '@sanity/ui'
2
+ import styled from 'styled-components'
3
+
1
4
  import {LoginFooter} from './LoginFooter'
2
5
 
3
6
  /**
@@ -14,6 +17,23 @@ export interface LoginLayoutProps {
14
17
  children?: React.ReactNode
15
18
  }
16
19
 
20
+ const Root = styled.div`
21
+ width: 100%;
22
+ display: flex;
23
+ `
24
+
25
+ const Container = styled(Flex)`
26
+ width: 320px;
27
+ margin: auto;
28
+ display: flex;
29
+ `
30
+
31
+ const StyledCard = styled(Card)``
32
+
33
+ const ChildrenFlex = styled(Flex)`
34
+ min-height: 154px;
35
+ `
36
+
17
37
  /**
18
38
  * Layout component for login-related screens providing consistent styling and structure.
19
39
  * Renders content in a centered card with optional header and footer sections.
@@ -54,16 +74,26 @@ export function LoginLayout({
54
74
  header,
55
75
  }: LoginLayoutProps): React.ReactNode {
56
76
  return (
57
- <div className="sc-login-layout">
58
- <div className="sc-login-layout__container">
59
- <div className="sc-login-layout__card">
60
- {header && <div className="sc-login-layout__card-header">{header}</div>}
77
+ <Root>
78
+ <Container direction="column" gap={4}>
79
+ <StyledCard border radius={2} paddingY={4}>
80
+ <Flex direction="column" gap={4}>
81
+ {header && (
82
+ <Card borderBottom paddingX={4} paddingBottom={3}>
83
+ {header}
84
+ </Card>
85
+ )}
61
86
 
62
- {children && <div className="sc-login-layout__card-body">{children}</div>}
63
- </div>
87
+ {children && (
88
+ <ChildrenFlex paddingX={4} direction="column">
89
+ {children}
90
+ </ChildrenFlex>
91
+ )}
92
+ </Flex>
93
+ </StyledCard>
64
94
 
65
95
  {footer}
66
- </div>
67
- </div>
96
+ </Container>
97
+ </Root>
68
98
  )
69
99
  }
@@ -2,7 +2,7 @@ import {createSanityInstance} from '@sanity/sdk'
2
2
  import {render} from '@testing-library/react'
3
3
  import {describe, expect, it} from 'vitest'
4
4
 
5
- import {useSanityInstance} from '../hooks/context/useSanityInstance'
5
+ import {useSanityInstance} from '../../hooks/context/useSanityInstance'
6
6
  import {SanityProvider} from './SanityProvider'
7
7
 
8
8
  describe('SanityProvider', () => {
@@ -15,7 +15,7 @@ describe('SanityProvider', () => {
15
15
  }
16
16
 
17
17
  const {getByTestId} = render(
18
- <SanityProvider sanityInstances={[sanityInstance]}>
18
+ <SanityProvider sanityInstance={sanityInstance}>
19
19
  <TestComponent />
20
20
  </SanityProvider>,
21
21
  )
@@ -0,0 +1,42 @@
1
+ import {type SanityInstance} from '@sanity/sdk'
2
+ import {createContext, type ReactElement} from 'react'
3
+
4
+ /**
5
+ * @public
6
+ */
7
+ export interface SanityProviderProps {
8
+ children: React.ReactNode
9
+ sanityInstance: SanityInstance
10
+ }
11
+
12
+ export const SanityInstanceContext = createContext<SanityInstance | null>(null)
13
+
14
+ /**
15
+ * Top-level context provider that provides a Sanity configuration instance.
16
+ * This must wrap any Sanity SDK React component.
17
+ * @public
18
+ * @param props - Sanity project and dataset configuration
19
+ * @returns Rendered component
20
+ * @example
21
+ * ```tsx
22
+ * import {createSanityInstance} from '@sanity/sdk'
23
+ * import {ExampleComponent, SanityProvider} from '@sanity/sdk-react'
24
+ *
25
+ * const sanityInstance = createSanityInstance({projectId: 'your-project-id', dataset: 'production'})
26
+ *
27
+ * export default function MyApp() {
28
+ * return (
29
+ * <SanityProvider sanityInstance={sanityInstance}>
30
+ * <ExampleComponent />
31
+ * </SanityProvider>
32
+ * )
33
+ * }
34
+ * ```
35
+ */
36
+ export const SanityProvider = ({children, sanityInstance}: SanityProviderProps): ReactElement => {
37
+ return (
38
+ <SanityInstanceContext.Provider value={sanityInstance}>
39
+ {children}
40
+ </SanityInstanceContext.Provider>
41
+ )
42
+ }