sanity-plugin-media 4.3.1 → 4.3.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 (145) hide show
  1. package/LICENSE +4 -4
  2. package/README.md +12 -12
  3. package/dist/index.d.mts +263 -195
  4. package/dist/index.d.ts +263 -195
  5. package/dist/index.js +83 -203
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +84 -203
  8. package/dist/index.mjs.map +1 -1
  9. package/package.json +41 -63
  10. package/src/__tests__/fixtures/createEpicTestStore.ts +5 -4
  11. package/src/__tests__/fixtures/mockSanityClient.ts +8 -8
  12. package/src/__tests__/fixtures/renderWithProviders.tsx +8 -7
  13. package/src/__tests__/fixtures/rootState.ts +4 -4
  14. package/src/components/AssetGridVirtualized/index.tsx +8 -7
  15. package/src/components/AssetMetadata/index.tsx +6 -5
  16. package/src/components/AssetTableVirtualized/index.tsx +7 -6
  17. package/src/components/AutoTagInputWrapper/index.tsx +9 -4
  18. package/src/components/Browser/Browser.test.tsx +9 -8
  19. package/src/components/Browser/index.tsx +2 -1
  20. package/src/components/Browser/useBrowserInit.ts +9 -9
  21. package/src/components/ButtonAssetCopy/index.tsx +1 -0
  22. package/src/components/ButtonViewGroup/index.tsx +4 -3
  23. package/src/components/CardAsset/CardAsset.test.tsx +53 -52
  24. package/src/components/CardAsset/index.tsx +52 -49
  25. package/src/components/CardUpload/index.tsx +7 -6
  26. package/src/components/Controls/index.tsx +7 -6
  27. package/src/components/DebugControls/index.tsx +5 -4
  28. package/src/components/DialogAssetEdit/Details.tsx +3 -2
  29. package/src/components/DialogAssetEdit/DialogAssetEdit.test.tsx +28 -27
  30. package/src/components/DialogAssetEdit/index.tsx +37 -37
  31. package/src/components/DialogConfirm/index.tsx +2 -1
  32. package/src/components/DialogSearchFacets/index.tsx +3 -2
  33. package/src/components/DialogTagCreate/DialogTagCreate.test.tsx +16 -15
  34. package/src/components/DialogTagCreate/index.tsx +11 -10
  35. package/src/components/DialogTagEdit/DialogTagEdit.test.tsx +28 -27
  36. package/src/components/DialogTagEdit/index.tsx +17 -16
  37. package/src/components/DialogTags/index.tsx +4 -3
  38. package/src/components/Dialogs/index.tsx +2 -3
  39. package/src/components/DocumentList/index.tsx +2 -3
  40. package/src/components/FileAssetPreview/index.tsx +2 -2
  41. package/src/components/FormBuilderTool/FormBuilderTool.test.tsx +12 -11
  42. package/src/components/FormBuilderTool/index.tsx +2 -1
  43. package/src/components/FormFieldInputLabel/index.tsx +1 -2
  44. package/src/components/FormFieldInputTags/index.tsx +4 -3
  45. package/src/components/FormSubmitButton/index.tsx +1 -1
  46. package/src/components/Header/index.tsx +3 -3
  47. package/src/components/Image/index.tsx +10 -4
  48. package/src/components/Items/index.tsx +5 -4
  49. package/src/components/Notifications/index.tsx +3 -2
  50. package/src/components/OrderSelect/index.tsx +4 -3
  51. package/src/components/PickedBar/index.tsx +2 -1
  52. package/src/components/Progress/index.tsx +3 -3
  53. package/src/components/ReduxProvider/index.tsx +15 -12
  54. package/src/components/SearchFacet/index.tsx +3 -2
  55. package/src/components/SearchFacetNumber/index.tsx +8 -8
  56. package/src/components/SearchFacetSelect/index.tsx +7 -8
  57. package/src/components/SearchFacetString/index.tsx +1 -1
  58. package/src/components/SearchFacetTags/index.tsx +13 -12
  59. package/src/components/SearchFacets/index.tsx +2 -3
  60. package/src/components/SearchFacetsControl/index.tsx +13 -12
  61. package/src/components/TableHeader/index.tsx +18 -17
  62. package/src/components/TableHeaderItem/index.tsx +4 -4
  63. package/src/components/TableRowAsset/index.tsx +37 -36
  64. package/src/components/TableRowUpload/index.tsx +7 -6
  65. package/src/components/Tag/index.tsx +8 -7
  66. package/src/components/TagView/index.tsx +2 -2
  67. package/src/components/TagViewHeader/index.tsx +5 -4
  68. package/src/components/TagsPanel/index.tsx +3 -3
  69. package/src/components/TagsVirtualized/index.tsx +25 -24
  70. package/src/components/TextInputSearch/index.tsx +3 -2
  71. package/src/components/UploadDropzone/UploadDropzone.test.tsx +8 -7
  72. package/src/components/UploadDropzone/index.tsx +14 -13
  73. package/src/config/orders.ts +6 -6
  74. package/src/config/searchFacets.ts +56 -55
  75. package/src/constants.ts +15 -14
  76. package/src/contexts/AssetSourceDispatchContext.tsx +1 -1
  77. package/src/contexts/ToolOptionsContext.tsx +6 -5
  78. package/src/formSchema/index.test.ts +6 -5
  79. package/src/formSchema/index.ts +5 -5
  80. package/src/hooks/useBreakpointIndex.ts +6 -6
  81. package/src/hooks/useKeyPress.ts +2 -2
  82. package/src/hooks/usePortalPopoverProps.ts +1 -1
  83. package/src/modules/assets/actions.ts +8 -7
  84. package/src/modules/assets/deleteAndUpdateEpics.test.ts +18 -17
  85. package/src/modules/assets/fetchEpic.test.ts +12 -11
  86. package/src/modules/assets/index.ts +134 -133
  87. package/src/modules/assets/reducer.test.ts +9 -8
  88. package/src/modules/assets/tagsAndListenerEpics.test.ts +36 -35
  89. package/src/modules/debug/index.ts +3 -3
  90. package/src/modules/dialog/actions.ts +2 -2
  91. package/src/modules/dialog/epics.test.ts +29 -28
  92. package/src/modules/dialog/index.ts +36 -35
  93. package/src/modules/dialog/reducer.test.ts +31 -30
  94. package/src/modules/index.ts +9 -9
  95. package/src/modules/notifications/epics.test.ts +71 -70
  96. package/src/modules/notifications/index.ts +50 -49
  97. package/src/modules/notifications/reducer.test.ts +8 -7
  98. package/src/modules/search/index.test.ts +2 -1
  99. package/src/modules/search/index.ts +22 -22
  100. package/src/modules/selected/index.ts +2 -2
  101. package/src/modules/selectors.test.ts +4 -3
  102. package/src/modules/selectors.ts +5 -5
  103. package/src/modules/tags/epics.test.ts +16 -15
  104. package/src/modules/tags/index.test.ts +2 -1
  105. package/src/modules/tags/index.ts +82 -81
  106. package/src/modules/uploads/actions.ts +3 -3
  107. package/src/modules/uploads/epics.test.ts +13 -12
  108. package/src/modules/uploads/index.test.ts +8 -7
  109. package/src/modules/uploads/index.ts +48 -47
  110. package/src/operators/checkTagName.test.ts +7 -6
  111. package/src/operators/checkTagName.ts +6 -5
  112. package/src/operators/debugThrottle.ts +4 -4
  113. package/src/plugin.tsx +18 -18
  114. package/src/schemas/tag.ts +7 -7
  115. package/src/styled/react-select/creatable.tsx +40 -39
  116. package/src/styled/react-select/single.tsx +39 -38
  117. package/src/types/index.ts +4 -3
  118. package/src/utils/applyMediaTags.ts +11 -10
  119. package/src/utils/blocksToText.test.ts +5 -4
  120. package/src/utils/blocksToText.ts +2 -2
  121. package/src/utils/constructFilter.test.ts +15 -14
  122. package/src/utils/constructFilter.ts +7 -7
  123. package/src/utils/generatePreviewBlobUrl.test.ts +6 -5
  124. package/src/utils/generatePreviewBlobUrl.ts +2 -2
  125. package/src/utils/getAssetResolution.test.ts +3 -2
  126. package/src/utils/getDocumentAssetIds.test.ts +7 -6
  127. package/src/utils/getDocumentAssetIds.ts +2 -2
  128. package/src/utils/getSchemeColor.test.ts +1 -0
  129. package/src/utils/getSchemeColor.ts +9 -9
  130. package/src/utils/getTagSelectOptions.test.ts +6 -5
  131. package/src/utils/getTagSelectOptions.ts +1 -1
  132. package/src/utils/getUniqueDocuments.test.ts +4 -3
  133. package/src/utils/getUniqueDocuments.ts +2 -2
  134. package/src/utils/imageDprUrl.test.ts +4 -3
  135. package/src/utils/imageDprUrl.ts +1 -1
  136. package/src/utils/isSupportedAssetType.test.ts +1 -0
  137. package/src/utils/mediaField.ts +4 -3
  138. package/src/utils/sanitizeFormData.test.ts +14 -13
  139. package/src/utils/typeGuards.test.ts +2 -1
  140. package/src/utils/uploadSanityAsset.test.ts +5 -4
  141. package/src/utils/uploadSanityAsset.ts +17 -16
  142. package/src/utils/withMaxConcurrency.test.ts +5 -4
  143. package/src/utils/withMaxConcurrency.ts +4 -4
  144. package/src/utils/zodFormResolver.ts +17 -0
  145. package/v2-incompatible.js +2 -2
@@ -1,7 +1,8 @@
1
- import userEvent from '@testing-library/user-event'
2
1
  import {fireEvent, screen, waitFor} from '@testing-library/react'
3
- import {describe, expect, it, vi} from 'vitest'
2
+ import userEvent from '@testing-library/user-event'
4
3
  import {Subject} from 'rxjs'
4
+ import {describe, expect, it, vi} from 'vitest'
5
+
5
6
  import DialogAssetEdit from './index'
6
7
 
7
8
  vi.mock('../Image', () => ({default: () => null}))
@@ -12,8 +13,8 @@ import {createMockSanityClient} from '../../__tests__/fixtures/mockSanityClient'
12
13
  import {renderWithProviders} from '../../__tests__/fixtures/renderWithProviders'
13
14
  import {createTestRootState} from '../../__tests__/fixtures/rootState'
14
15
  import {inputByName, withinDialog} from '../../__tests__/fixtures/withinDialog'
15
- import type {RootReducerState} from '../../modules/types'
16
16
  import {assetsActions, initialState as assetsInitialState} from '../../modules/assets'
17
+ import type {RootReducerState} from '../../modules/types'
17
18
  import type {AssetType, ImageAsset, MediaToolOptions} from '../../types'
18
19
 
19
20
  const asset = {
@@ -26,7 +27,7 @@ const asset = {
26
27
  size: 1,
27
28
  mimeType: 'image/png',
28
29
  url: 'https://example.com/x.png',
29
- metadata: {dimensions: {width: 100, height: 100}, isOpaque: true}
30
+ metadata: {dimensions: {width: 100, height: 100}, isOpaque: true},
30
31
  } as ImageAsset
31
32
 
32
33
  const assetsPreloaded = {
@@ -34,25 +35,25 @@ const assetsPreloaded = {
34
35
  assetTypes: ['image'] as AssetType[],
35
36
  allIds: ['a1'],
36
37
  byIds: {
37
- a1: {_type: 'asset' as const, asset, picked: false, updating: false}
38
- }
38
+ a1: {_type: 'asset' as const, asset, picked: false, updating: false},
39
+ },
39
40
  }
40
41
 
41
- vi.mock('sanity', async importOriginal => {
42
+ vi.mock('sanity', async (importOriginal) => {
42
43
  const actual = await importOriginal<typeof import('sanity')>()
43
44
  return {
44
45
  ...actual,
45
46
  WithReferringDocuments: ({children}: {children: (args: unknown) => unknown}) =>
46
47
  children({isLoading: false, referringDocuments: []}),
47
- useDocumentStore: () => ({})
48
+ useDocumentStore: () => ({}),
48
49
  }
49
50
  })
50
51
 
51
52
  vi.mock('../../hooks/useVersionedClient', () => ({
52
53
  default: () =>
53
54
  createMockSanityClient({
54
- listen: vi.fn(() => new Subject())
55
- })
55
+ listen: vi.fn(() => new Subject()),
56
+ }),
56
57
  }))
57
58
 
58
59
  function renderAssetDialog(
@@ -60,7 +61,7 @@ function renderAssetDialog(
60
61
  opts: {
61
62
  preloaded?: Partial<RootReducerState>
62
63
  toolOptions?: Partial<MediaToolOptions>
63
- } = {}
64
+ } = {},
64
65
  ) {
65
66
  const {preloaded: extraPreloaded, toolOptions} = opts
66
67
  return renderWithProviders(
@@ -70,10 +71,10 @@ function renderAssetDialog(
70
71
  {
71
72
  preloaded: {
72
73
  assets: assetsPreloaded,
73
- ...extraPreloaded
74
+ ...extraPreloaded,
74
75
  },
75
- toolOptions: {creditLine: {enabled: true}, ...toolOptions}
76
- }
76
+ toolOptions: {creditLine: {enabled: true}, ...toolOptions},
77
+ },
77
78
  )
78
79
  }
79
80
 
@@ -82,7 +83,7 @@ describe('DialogAssetEdit', () => {
82
83
  renderAssetDialog({
83
84
  id: 'dlg-1',
84
85
  type: 'assetEdit',
85
- assetId: 'a1'
86
+ assetId: 'a1',
86
87
  })
87
88
 
88
89
  const dlg = withinDialog(/asset details/i, screen)
@@ -94,7 +95,7 @@ describe('DialogAssetEdit', () => {
94
95
  renderAssetDialog({
95
96
  id: 'dlg-1',
96
97
  type: 'assetEdit',
97
- assetId: 'a1'
98
+ assetId: 'a1',
98
99
  })
99
100
 
100
101
  const dlg = withinDialog(/asset details/i, screen)
@@ -106,7 +107,7 @@ describe('DialogAssetEdit', () => {
106
107
  const {store} = renderAssetDialog({
107
108
  id: 'dlg-1',
108
109
  type: 'assetEdit',
109
- assetId: 'a1'
110
+ assetId: 'a1',
110
111
  })
111
112
  const dispatchSpy = vi.spyOn(store, 'dispatch')
112
113
  const dlg = withinDialog(/asset details/i, screen)
@@ -131,8 +132,8 @@ describe('DialogAssetEdit', () => {
131
132
  closeDialogId: 'a1',
132
133
  formData: expect.objectContaining({
133
134
  title: 'Hero image',
134
- originalFilename: 'x.png'
135
- })
135
+ originalFilename: 'x.png',
136
+ }),
136
137
  })
137
138
  })
138
139
  })
@@ -143,10 +144,10 @@ describe('DialogAssetEdit', () => {
143
144
  dialog: {
144
145
  items: [
145
146
  {id: 'dlg-1', type: 'assetEdit', assetId: 'a1'},
146
- {id: 'tags', type: 'tags'}
147
- ]
147
+ {id: 'tags', type: 'tags'},
148
+ ],
148
149
  },
149
- assets: assetsPreloaded
150
+ assets: assetsPreloaded,
150
151
  })
151
152
 
152
153
  const {store} = renderWithProviders(
@@ -154,15 +155,15 @@ describe('DialogAssetEdit', () => {
154
155
  dialog={{
155
156
  id: 'dlg-1',
156
157
  type: 'assetEdit',
157
- assetId: 'a1'
158
+ assetId: 'a1',
158
159
  }}
159
160
  >
160
161
  <span />
161
162
  </DialogAssetEdit>,
162
163
  {
163
164
  preloaded: base,
164
- toolOptions: {creditLine: {enabled: true}}
165
- }
165
+ toolOptions: {creditLine: {enabled: true}},
166
+ },
166
167
  )
167
168
 
168
169
  const dlg = withinDialog(/asset details/i, screen)
@@ -175,7 +176,7 @@ describe('DialogAssetEdit', () => {
175
176
  const {store} = renderAssetDialog({
176
177
  id: 'dlg-1',
177
178
  type: 'assetEdit',
178
- assetId: 'a1'
179
+ assetId: 'a1',
179
180
  })
180
181
 
181
182
  const dlg = withinDialog(/asset details/i, screen)
@@ -200,7 +201,7 @@ describe('DialogAssetEdit', () => {
200
201
  renderAssetDialog({
201
202
  id: 'dlg-1',
202
203
  type: 'assetEdit',
203
- assetId: 'a1'
204
+ assetId: 'a1',
204
205
  })
205
206
 
206
207
  const dlg = withinDialog(/asset details/i, screen)
@@ -1,30 +1,31 @@
1
- import {zodResolver} from '@hookform/resolvers/zod'
2
1
  import type {MutationEvent} from '@sanity/client'
3
2
  import {Box, Button, Card, Flex, Stack, Tab, TabList, TabPanel, Text} from '@sanity/ui'
4
- import type {Asset, AssetFormData, DialogAssetEditProps, TagSelectOption} from '../../types'
5
3
  import groq from 'groq'
6
4
  import {type ReactNode, useCallback, useEffect, useMemo, useRef, useState} from 'react'
7
5
  import {type SubmitHandler, useForm} from 'react-hook-form'
8
6
  import {useDispatch} from 'react-redux'
9
7
  import {WithReferringDocuments, useColorSchemeValue, useDocumentStore} from 'sanity'
8
+
9
+ import {useToolOptions} from '../../contexts/ToolOptionsContext'
10
10
  import {getAssetFormSchema} from '../../formSchema'
11
11
  import useTypedSelector from '../../hooks/useTypedSelector'
12
12
  import useVersionedClient from '../../hooks/useVersionedClient'
13
13
  import {assetsActions, selectAssetById} from '../../modules/assets'
14
14
  import {dialogActions} from '../../modules/dialog'
15
15
  import {selectTags, selectTagSelectOptions, tagsActions} from '../../modules/tags'
16
+ import type {Asset, AssetFormData, DialogAssetEditProps, TagSelectOption} from '../../types'
16
17
  import getTagSelectOptions from '../../utils/getTagSelectOptions'
17
18
  import {getUniqueDocuments} from '../../utils/getUniqueDocuments'
18
19
  import imageDprUrl from '../../utils/imageDprUrl'
19
20
  import sanitizeFormData from '../../utils/sanitizeFormData'
20
21
  import {isFileAsset, isImageAsset} from '../../utils/typeGuards'
22
+ import zodFormResolver from '../../utils/zodFormResolver'
21
23
  import AssetMetadata from '../AssetMetadata'
22
24
  import Dialog from '../Dialog'
23
25
  import DocumentList from '../DocumentList'
24
26
  import FileAssetPreview from '../FileAssetPreview'
25
27
  import FormSubmitButton from '../FormSubmitButton'
26
28
  import Image from '../Image'
27
- import {useToolOptions} from '../../contexts/ToolOptionsContext'
28
29
  import Details, {type DetailsProps} from './Details'
29
30
 
30
31
  function renderDefaultDetails(props: DetailsProps) {
@@ -39,7 +40,7 @@ type Props = {
39
40
  const DialogAssetEdit = (props: Props) => {
40
41
  const {
41
42
  children,
42
- dialog: {assetId, id, lastCreatedTag, lastRemovedTagIds}
43
+ dialog: {assetId, id, lastCreatedTag, lastRemovedTagIds},
43
44
  } = props
44
45
 
45
46
  const client = useVersionedClient()
@@ -48,7 +49,7 @@ const DialogAssetEdit = (props: Props) => {
48
49
  const documentStore = useDocumentStore()
49
50
 
50
51
  const dispatch = useDispatch()
51
- const assetItem = useTypedSelector(state => selectAssetById(state, String(assetId))) // TODO: check casting
52
+ const assetItem = useTypedSelector((state) => selectAssetById(state, String(assetId))) // TODO: check casting
52
53
  const tags = useTypedSelector(selectTags)
53
54
 
54
55
  const assetUpdatedPrev = useRef<string | undefined>(undefined)
@@ -90,7 +91,7 @@ const DialogAssetEdit = (props: Props) => {
90
91
  description: makeLocaleObj(asset?.description),
91
92
  originalFilename: asset?.originalFilename || '',
92
93
  opt: {media: {tags: assetTagOptions}},
93
- title: makeLocaleObj(asset?.title)
94
+ title: makeLocaleObj(asset?.title),
94
95
  }
95
96
  }
96
97
  // Normalize: if a field is a localized object but locales are disabled, pick first non-empty value
@@ -98,7 +99,7 @@ const DialogAssetEdit = (props: Props) => {
98
99
  if (typeof field === 'string') return field
99
100
  if (typeof field === 'object' && field !== null) {
100
101
  const values = Object.values(field as Record<string, string>)
101
- return values.find(v => v) || ''
102
+ return values.find((v) => v) || ''
102
103
  }
103
104
  return ''
104
105
  }
@@ -108,10 +109,10 @@ const DialogAssetEdit = (props: Props) => {
108
109
  description: flattenField(asset?.description),
109
110
  originalFilename: asset?.originalFilename || '',
110
111
  opt: {media: {tags: assetTagOptions}},
111
- title: flattenField(asset?.title)
112
+ title: flattenField(asset?.title),
112
113
  }
113
114
  },
114
- [assetTagOptions, locales]
115
+ [assetTagOptions, locales],
115
116
  )
116
117
 
117
118
  const {
@@ -122,11 +123,11 @@ const DialogAssetEdit = (props: Props) => {
122
123
  handleSubmit,
123
124
  register,
124
125
  reset,
125
- setValue
126
+ setValue,
126
127
  } = useForm<AssetFormData>({
127
128
  defaultValues: generateDefaultValues(assetItem?.asset),
128
129
  mode: 'onChange',
129
- resolver: zodResolver(getAssetFormSchema(locales))
130
+ resolver: zodFormResolver<AssetFormData>(getAssetFormSchema(locales)),
130
131
  })
131
132
 
132
133
  const formUpdating = !assetItem || assetItem?.updating
@@ -143,8 +144,8 @@ const DialogAssetEdit = (props: Props) => {
143
144
  dispatch(
144
145
  dialogActions.showConfirmDeleteAssets({
145
146
  assets: [assetItem],
146
- closeDialogId: assetItem?.asset._id
147
- })
147
+ closeDialogId: assetItem?.asset._id,
148
+ }),
148
149
  )
149
150
  }, [assetItem, dispatch])
150
151
 
@@ -162,31 +163,30 @@ const DialogAssetEdit = (props: Props) => {
162
163
  dispatch(
163
164
  tagsActions.createRequest({
164
165
  assetId: currentAsset?._id,
165
- name: tagName
166
- })
166
+ name: tagName,
167
+ }),
167
168
  )
168
169
  },
169
- [currentAsset?._id, dispatch]
170
+ [currentAsset?._id, dispatch],
170
171
  )
171
172
 
172
173
  // Detect if asset has localized fields (objects) with keys not in the configured locales
173
174
  const hasOrphanedLocales = useMemo(() => {
174
175
  if (!currentAsset) return false
175
- const isLocaleObj = (v: unknown) =>
176
- typeof v === 'object' && v !== null && !Array.isArray(v)
176
+ const isLocaleObj = (v: unknown) => typeof v === 'object' && v !== null && !Array.isArray(v)
177
177
  const fields = [
178
178
  currentAsset.title,
179
179
  currentAsset.altText,
180
180
  currentAsset.description,
181
- ...(currentAsset._type === 'sanity.imageAsset' ? [currentAsset.creditLine] : [])
181
+ ...(currentAsset._type === 'sanity.imageAsset' ? [currentAsset.creditLine] : []),
182
182
  ]
183
- const anyLocalized = fields.some(f => isLocaleObj(f))
183
+ const anyLocalized = fields.some((f) => isLocaleObj(f))
184
184
  if (!anyLocalized) return false
185
185
  if (!locales || locales.length === 0) return true
186
- const configuredIds = new Set(locales.map(l => l.id))
187
- return fields.some(f => {
186
+ const configuredIds = new Set(locales.map((l) => l.id))
187
+ return fields.some((f) => {
188
188
  if (!isLocaleObj(f)) return false
189
- return Object.keys(f as object).some(k => !configuredIds.has(k))
189
+ return Object.keys(f as object).some((k) => !configuredIds.has(k))
190
190
  })
191
191
  }, [currentAsset, locales])
192
192
 
@@ -199,9 +199,9 @@ const DialogAssetEdit = (props: Props) => {
199
199
  if (!locales || locales.length === 0) {
200
200
  // Pick the first non-empty value sorted by key for determinism
201
201
  const sorted = Object.keys(obj).sort()
202
- return sorted.map(k => obj[k]).find(v => v) || ''
202
+ return sorted.map((k) => obj[k]).find((v) => v) || ''
203
203
  }
204
- const configuredIds = new Set(locales.map(l => l.id))
204
+ const configuredIds = new Set(locales.map((l) => l.id))
205
205
  const cleaned: Record<string, string> = {}
206
206
  for (const [key, val] of Object.entries(obj)) {
207
207
  if (configuredIds.has(key)) cleaned[key] = val
@@ -216,15 +216,15 @@ const DialogAssetEdit = (props: Props) => {
216
216
  altText: cleanField(currentAsset.altText),
217
217
  description: cleanField(currentAsset.description),
218
218
  ...(currentAsset._type === 'sanity.imageAsset' && {
219
- creditLine: cleanField(currentAsset.creditLine)
220
- })
219
+ creditLine: cleanField(currentAsset.creditLine),
220
+ }),
221
221
  })
222
222
  .commit()
223
223
  }, [client, currentAsset, locales])
224
224
 
225
225
  // Submit react-hook-form
226
226
  const onSubmit: SubmitHandler<AssetFormData> = useCallback(
227
- formData => {
227
+ (formData) => {
228
228
  if (!assetItem?.asset) {
229
229
  return
230
230
  }
@@ -245,15 +245,15 @@ const DialogAssetEdit = (props: Props) => {
245
245
  sanitizedFormData.opt.media.tags?.map((tag: TagSelectOption) => ({
246
246
  _ref: tag.value,
247
247
  _type: 'reference',
248
- _weak: true
249
- })) || null
250
- }
251
- }
252
- }
253
- })
248
+ _weak: true,
249
+ })) || null,
250
+ },
251
+ },
252
+ },
253
+ }),
254
254
  )
255
255
  },
256
- [assetItem?.asset, dispatch]
256
+ [assetItem?.asset, dispatch],
257
257
  )
258
258
 
259
259
  // Listen for asset mutations and update snapshot
@@ -285,7 +285,7 @@ const DialogAssetEdit = (props: Props) => {
285
285
  useEffect(() => {
286
286
  if (lastRemovedTagIds) {
287
287
  const existingTags = (getValues('opt.media.tags') as TagSelectOption[]) || []
288
- const updatedTags = existingTags.filter(tag => {
288
+ const updatedTags = existingTags.filter((tag) => {
289
289
  return !lastRemovedTagIds.includes(tag.value)
290
290
  })
291
291
 
@@ -359,7 +359,7 @@ const DialogAssetEdit = (props: Props) => {
359
359
  handleCreateTag,
360
360
  currentAsset,
361
361
  creditLine,
362
- locales
362
+ locales,
363
363
  }
364
364
 
365
365
  return (
@@ -1,9 +1,10 @@
1
1
  import {WarningOutlineIcon} from '@sanity/icons'
2
2
  import {Box, Button, Flex, Stack, Text} from '@sanity/ui'
3
- import type {DialogConfirmProps} from '../../types'
4
3
  import {type ReactNode} from 'react'
5
4
  import {useDispatch} from 'react-redux'
5
+
6
6
  import {dialogActions} from '../../modules/dialog'
7
+ import type {DialogConfirmProps} from '../../types'
7
8
  import Dialog from '../Dialog'
8
9
 
9
10
  type Props = {
@@ -1,8 +1,9 @@
1
1
  import {Box} from '@sanity/ui'
2
- import type {DialogSearchFacetsProps} from '../../types'
3
2
  import {type ReactNode, useCallback} from 'react'
4
3
  import {useDispatch} from 'react-redux'
4
+
5
5
  import {dialogActions} from '../../modules/dialog'
6
+ import type {DialogSearchFacetsProps} from '../../types'
6
7
  import Dialog from '../Dialog'
7
8
  import SearchFacets from '../SearchFacets'
8
9
  import SearchFacetsControl from '../SearchFacetsControl'
@@ -15,7 +16,7 @@ type Props = {
15
16
  const DialogSearchFacets = (props: Props) => {
16
17
  const {
17
18
  children,
18
- dialog: {id}
19
+ dialog: {id},
19
20
  } = props
20
21
 
21
22
  // Redux
@@ -1,11 +1,12 @@
1
- import userEvent from '@testing-library/user-event'
2
1
  import {screen, waitFor} from '@testing-library/react'
2
+ import userEvent from '@testing-library/user-event'
3
3
  import {describe, expect, it, vi} from 'vitest'
4
- import DialogTagCreate from './index'
4
+
5
5
  import {renderWithProviders} from '../../__tests__/fixtures/renderWithProviders'
6
6
  import {createTestRootState} from '../../__tests__/fixtures/rootState'
7
7
  import {getDialogRoot, inputByName, withinDialog} from '../../__tests__/fixtures/withinDialog'
8
8
  import {tagsActions} from '../../modules/tags'
9
+ import DialogTagCreate from './index'
9
10
 
10
11
  describe('DialogTagCreate', () => {
11
12
  it('dispatches tag create flow when form is valid', async () => {
@@ -13,7 +14,7 @@ describe('DialogTagCreate', () => {
13
14
  const {store} = renderWithProviders(
14
15
  <DialogTagCreate dialog={{id: 'dlg-1', type: 'tagCreate'}}>
15
16
  <span />
16
- </DialogTagCreate>
17
+ </DialogTagCreate>,
17
18
  )
18
19
 
19
20
  const dlg = withinDialog(/create tag/i, screen)
@@ -28,7 +29,7 @@ describe('DialogTagCreate', () => {
28
29
  const {store} = renderWithProviders(
29
30
  <DialogTagCreate dialog={{id: 'dlg-1', type: 'tagCreate'}}>
30
31
  <span />
31
- </DialogTagCreate>
32
+ </DialogTagCreate>,
32
33
  )
33
34
  const dispatchSpy = vi.spyOn(store, 'dispatch')
34
35
  const dlg = withinDialog(/create tag/i, screen)
@@ -55,11 +56,11 @@ describe('DialogTagCreate', () => {
55
56
  renderWithProviders(
56
57
  <DialogTagCreate dialog={{id: 'dlg-1', type: 'tagCreate'}}>
57
58
  <span />
58
- </DialogTagCreate>
59
+ </DialogTagCreate>,
59
60
  )
60
61
 
61
62
  expect(
62
- withinDialog(/create tag/i, screen).getByRole('button', {name: /save and close/i})
63
+ withinDialog(/create tag/i, screen).getByRole('button', {name: /save and close/i}),
63
64
  ).toBeDisabled()
64
65
 
65
66
  const nameInput = inputByName(/create tag/i, screen, 'name')
@@ -67,7 +68,7 @@ describe('DialogTagCreate', () => {
67
68
  await user.tab()
68
69
  await waitFor(() => {
69
70
  expect(
70
- withinDialog(/create tag/i, screen).getByRole('button', {name: /save and close/i})
71
+ withinDialog(/create tag/i, screen).getByRole('button', {name: /save and close/i}),
71
72
  ).not.toBeDisabled()
72
73
  })
73
74
  })
@@ -78,16 +79,16 @@ describe('DialogTagCreate', () => {
78
79
  dialog: {
79
80
  items: [
80
81
  {id: 'dlg-1', type: 'tagCreate'},
81
- {id: 'tags', type: 'tags'}
82
- ]
83
- }
82
+ {id: 'tags', type: 'tags'},
83
+ ],
84
+ },
84
85
  })
85
86
 
86
87
  const {store} = renderWithProviders(
87
88
  <DialogTagCreate dialog={{id: 'dlg-1', type: 'tagCreate'}}>
88
89
  <span />
89
90
  </DialogTagCreate>,
90
- {preloaded: base}
91
+ {preloaded: base},
91
92
  )
92
93
 
93
94
  const dlg = withinDialog(/create tag/i, screen)
@@ -100,20 +101,20 @@ describe('DialogTagCreate', () => {
100
101
  const base = createTestRootState({
101
102
  tags: {
102
103
  ...createTestRootState().tags,
103
- creatingError: {message: 'Tag already exists', statusCode: 409}
104
- }
104
+ creatingError: {message: 'Tag already exists', statusCode: 409},
105
+ },
105
106
  })
106
107
 
107
108
  renderWithProviders(
108
109
  <DialogTagCreate dialog={{id: 'dlg-1', type: 'tagCreate'}}>
109
110
  <span />
110
111
  </DialogTagCreate>,
111
- {preloaded: base}
112
+ {preloaded: base},
112
113
  )
113
114
 
114
115
  await waitFor(() => {
115
116
  expect(
116
- getDialogRoot(/create tag/i, screen).querySelector('[data-sanity-icon="error-outline"]')
117
+ getDialogRoot(/create tag/i, screen).querySelector('[data-sanity-icon="error-outline"]'),
117
118
  ).toBeTruthy()
118
119
  })
119
120
  })
@@ -1,14 +1,15 @@
1
- import {zodResolver} from '@hookform/resolvers/zod'
2
1
  import {Box, Flex} from '@sanity/ui'
3
- import type {DialogTagCreateProps, TagFormData} from '../../types'
4
2
  import {type ReactNode, useEffect} from 'react'
5
3
  import {type SubmitHandler, useForm} from 'react-hook-form'
6
4
  import {useDispatch} from 'react-redux'
5
+
7
6
  import {tagFormSchema} from '../../formSchema'
8
7
  import useTypedSelector from '../../hooks/useTypedSelector'
9
8
  import {dialogActions} from '../../modules/dialog'
10
9
  import {tagsActions} from '../../modules/tags'
10
+ import type {DialogTagCreateProps, TagFormData} from '../../types'
11
11
  import sanitizeFormData from '../../utils/sanitizeFormData'
12
+ import zodFormResolver from '../../utils/zodFormResolver'
12
13
  import Dialog from '../Dialog'
13
14
  import FormFieldInputText from '../FormFieldInputText'
14
15
  import FormSubmitButton from '../FormSubmitButton'
@@ -21,26 +22,26 @@ type Props = {
21
22
  const DialogTagCreate = (props: Props) => {
22
23
  const {
23
24
  children,
24
- dialog: {id}
25
+ dialog: {id},
25
26
  } = props
26
27
 
27
28
  const dispatch = useDispatch()
28
29
 
29
- const creating = useTypedSelector(state => state.tags.creating)
30
- const creatingError = useTypedSelector(state => state.tags.creatingError)
30
+ const creating = useTypedSelector((state) => state.tags.creating)
31
+ const creatingError = useTypedSelector((state) => state.tags.creatingError)
31
32
 
32
33
  const {
33
34
  // Read the formState before render to subscribe the form state through Proxy
34
35
  formState: {errors, isDirty, isValid},
35
36
  handleSubmit,
36
37
  register,
37
- setError
38
+ setError,
38
39
  } = useForm<TagFormData>({
39
40
  defaultValues: {
40
- name: ''
41
+ name: '',
41
42
  },
42
43
  mode: 'onChange',
43
- resolver: zodResolver(tagFormSchema)
44
+ resolver: zodFormResolver<TagFormData>(tagFormSchema),
44
45
  })
45
46
 
46
47
  const formUpdating = creating
@@ -50,7 +51,7 @@ const DialogTagCreate = (props: Props) => {
50
51
  }
51
52
 
52
53
  // - submit react-hook-form
53
- const onSubmit: SubmitHandler<TagFormData> = formData => {
54
+ const onSubmit: SubmitHandler<TagFormData> = (formData) => {
54
55
  const sanitizedFormData = sanitizeFormData(formData)
55
56
 
56
57
  dispatch(tagsActions.createRequest({name: sanitizedFormData.name}))
@@ -59,7 +60,7 @@ const DialogTagCreate = (props: Props) => {
59
60
  useEffect(() => {
60
61
  if (creatingError) {
61
62
  setError('name', {
62
- message: creatingError?.message
63
+ message: creatingError?.message,
63
64
  })
64
65
  }
65
66
  }, [creatingError, setError])