From e417e6f9607f4144035ac61b12118388067cdb9d Mon Sep 17 00:00:00 2001 From: Ash Date: Wed, 5 Feb 2025 20:59:30 +0000 Subject: [PATCH 1/7] test(sanity): add `toMatchEmissions` matcher --- packages/sanity/package.json | 1 + .../sanity/test/matchers/toMatchEmissions.ts | 49 +++ packages/sanity/test/setup/environment.ts | 8 +- pnpm-lock.yaml | 287 +++++++++++++----- 4 files changed, 271 insertions(+), 74 deletions(-) create mode 100644 packages/sanity/test/matchers/toMatchEmissions.ts diff --git a/packages/sanity/package.json b/packages/sanity/package.json index e5a815e4034..ed7568bcfe5 100644 --- a/packages/sanity/package.json +++ b/packages/sanity/package.json @@ -305,6 +305,7 @@ "@types/semver": "^6.2.3", "@types/tar-fs": "^2.0.1", "@vitejs/plugin-react": "^4.3.4", + "@vitest/expect": "^3.0.5", "@vvo/tzdb": "6.137.0", "babel-plugin-react-compiler": "19.0.0-beta-714736e-20250131", "blob-polyfill": "^9.0.20240710", diff --git a/packages/sanity/test/matchers/toMatchEmissions.ts b/packages/sanity/test/matchers/toMatchEmissions.ts new file mode 100644 index 00000000000..269cc67e16a --- /dev/null +++ b/packages/sanity/test/matchers/toMatchEmissions.ts @@ -0,0 +1,49 @@ +import {type AsyncExpectationResult, type MatcherState} from '@vitest/expect' +import {firstValueFrom, type OperatorFunction, Subject, toArray} from 'rxjs' + +export const NO_EMISSION = Symbol('NO_EMISSION') + +type Snapshot = [A, B | typeof NO_EMISSION] + +interface OperatorFunctionMatchers { + /** + * Ensure each entry in the provided array results in the expected value being emitted when piped + * to the observable. + */ + toMatchEmissions: Type extends () => OperatorFunction + ? (snapshots: Snapshot[]) => Promise + : never +} + +declare module 'vitest' { + interface Assertion extends OperatorFunctionMatchers {} + interface AsymmetricMatchersContaining extends OperatorFunctionMatchers {} +} + +export async function toMatchEmissions( + this: MatcherState, + createOperator: () => OperatorFunction, + snapshots: [A: unknown, B: unknown][], +): AsyncExpectationResult { + const {equals} = this + const input$ = new Subject() + + const expectedEmissions = snapshots + .filter(([, expectedEmission]) => expectedEmission !== NO_EMISSION) + .map(([, expectedEmission]) => expectedEmission) + + const emissions$ = input$.pipe(createOperator(), toArray()) + const emissions = firstValueFrom(emissions$) + + snapshots.forEach(([value]) => input$.next(value)) + input$.complete() + + const actualEmissions = await emissions + + return { + pass: equals(actualEmissions, expectedEmissions), + message: () => 'Observable emissions did not match', + actual: actualEmissions, + expected: expectedEmissions, + } +} diff --git a/packages/sanity/test/setup/environment.ts b/packages/sanity/test/setup/environment.ts index 9370c9aac06..ecea544b331 100644 --- a/packages/sanity/test/setup/environment.ts +++ b/packages/sanity/test/setup/environment.ts @@ -6,7 +6,13 @@ import './clipboardItemPolyfill' import '@testing-library/jest-dom/vitest' import {cleanup} from '@testing-library/react' -import {afterEach, beforeEach, vi} from 'vitest' +import {afterEach, beforeEach, expect, vi} from 'vitest' + +import {toMatchEmissions} from '../matchers/toMatchEmissions' + +expect.extend({ + toMatchEmissions, +}) afterEach(() => cleanup()) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9c12f677c95..35530c942b0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -60,7 +60,7 @@ importers: version: 0.0.1-alpha.1 '@sanity/tsdoc': specifier: 1.0.169 - version: 1.0.169(@emotion/is-prop-valid@1.3.1)(@types/babel__core@7.20.5)(@types/node@22.10.2)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(terser@5.37.0)(yaml@2.6.1) + version: 1.0.169(@emotion/is-prop-valid@1.3.1)(@types/babel__core@7.20.5)(@types/node@22.10.2)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(terser@5.37.0)(yaml@2.6.1) '@sanity/ui': specifier: ^2.11.8 version: 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) @@ -242,7 +242,7 @@ importers: version: 3.5.7(react@18.3.1) '@sanity/ui': specifier: ^2.11.8 - version: 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react-is@19.0.0-rc.1)(react@18.3.1)(styled-components@6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + version: 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react-is@19.0.0-rc.1)(react@18.3.1)(styled-components@6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) react: specifier: ^18.3.1 version: 18.3.1 @@ -254,13 +254,13 @@ importers: version: link:../../packages/sanity styled-components: specifier: ^6.1.0 - version: 6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1) dev/embedded-studio: dependencies: '@sanity/ui': specifier: ^2.11.8 - version: 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react-is@19.0.0-rc.1)(react@18.3.1)(styled-components@6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + version: 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react-is@19.0.0-rc.1)(react@18.3.1)(styled-components@6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) react: specifier: ^18.3.1 version: 18.3.1 @@ -272,7 +272,7 @@ importers: version: link:../../packages/sanity styled-components: specifier: ^6.1.0 - version: 6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1) devDependencies: '@types/react': specifier: ^19.0.8 @@ -315,7 +315,7 @@ importers: version: link:../../packages/sanity styled-components: specifier: ^6.1.8 - version: 6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1) dev/starter-studio: dependencies: @@ -336,7 +336,7 @@ importers: version: link:../../packages/sanity styled-components: specifier: ^6.1.0 - version: 6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1) dev/strict-studio: dependencies: @@ -351,22 +351,22 @@ importers: version: link:../../packages/sanity styled-components: specifier: ^6.1.0 - version: 6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1) dev/studio-e2e-testing: dependencies: '@sanity/color-input': specifier: ^4.0.1 - version: 4.0.3(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + version: 4.0.3(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) '@sanity/google-maps-input': specifier: ^4.0.0 - version: 4.1.0(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + version: 4.1.0(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) '@sanity/icons': specifier: ^3.5.7 version: 3.5.7(react@19.0.0) '@sanity/ui': specifier: ^2.11.8 - version: 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + version: 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) '@sanity/vision': specifier: 3.74.1 version: link:../../packages/@sanity/vision @@ -384,25 +384,25 @@ importers: version: link:../../packages/sanity sanity-plugin-markdown: specifier: ^5.0.0 - version: 5.0.0(@emotion/is-prop-valid@1.3.1)(easymde@2.18.0)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + version: 5.0.0(@emotion/is-prop-valid@1.3.1)(easymde@2.18.0)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) sanity-plugin-media: specifier: ^2.3.1 - version: 2.3.2(@sanity/ui@2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + version: 2.3.2(@sanity/ui@2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) sanity-plugin-mux-input: specifier: ^2.2.1 - version: 2.4.0(@emotion/is-prop-valid@1.3.1)(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + version: 2.4.0(@emotion/is-prop-valid@1.3.1)(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) sanity-test-studio: specifier: workspace:* version: link:../test-studio styled-components: specifier: ^6.1.0 - version: 6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0) dev/test-create-integration-studio: dependencies: '@sanity/code-input': specifier: ^5.0.0 - version: 5.1.2(@babel/runtime@7.26.7)(@codemirror/theme-one-dark@6.1.2)(@emotion/is-prop-valid@1.3.1)(codemirror@6.0.1)(react-dom@18.3.1(react@18.3.1))(react-is@19.0.0-rc.1)(react@18.3.1)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + version: 5.1.2(@babel/runtime@7.26.7)(@codemirror/theme-one-dark@6.1.2)(@emotion/is-prop-valid@1.3.1)(codemirror@6.0.1)(react-dom@18.3.1(react@18.3.1))(react-is@19.0.0-rc.1)(react@18.3.1)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) react: specifier: ^18.3.1 version: 18.3.1 @@ -414,13 +414,13 @@ importers: version: link:../../packages/sanity styled-components: specifier: ^6.1.0 - version: 6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1) dev/test-studio: dependencies: '@portabletext/block-tools': specifier: ^1.1.4 - version: 1.1.5(@sanity/types@packages+@sanity+types)(@types/react@19.0.8) + version: 1.1.4(@sanity/types@packages+@sanity+types)(@types/react@19.0.8) '@portabletext/editor': specifier: ^1.30.4 version: 1.30.4(@sanity/schema@packages+@sanity+schema)(@sanity/types@packages+@sanity+types)(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rxjs@7.8.1) @@ -429,7 +429,7 @@ importers: version: 3.2.0(react@19.0.0) '@sanity/assist': specifier: ^3.1.0 - version: 3.1.0(@emotion/is-prop-valid@1.3.1)(@sanity/mutator@packages+@sanity+mutator)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + version: 3.1.0(@emotion/is-prop-valid@1.3.1)(@sanity/mutator@packages+@sanity+mutator)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) '@sanity/client': specifier: ^6.27.2 version: 6.27.2(debug@4.4.0) @@ -438,10 +438,10 @@ importers: version: 3.0.6 '@sanity/color-input': specifier: ^4.0.1 - version: 4.0.3(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + version: 4.0.3(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) '@sanity/google-maps-input': specifier: ^4.0.0 - version: 4.1.0(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + version: 4.1.0(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) '@sanity/icons': specifier: ^3.5.7 version: 3.5.7(react@19.0.0) @@ -477,16 +477,16 @@ importers: version: 1.10.41(@sanity/types@packages+@sanity+types)(react@19.0.0) '@sanity/tsdoc': specifier: 1.0.169 - version: 1.0.169(@emotion/is-prop-valid@1.3.1)(@types/babel__core@7.20.5)(@types/node@22.10.2)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(terser@5.37.0)(yaml@2.6.1) + version: 1.0.169(@emotion/is-prop-valid@1.3.1)(@types/babel__core@7.20.5)(@types/node@22.10.2)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(terser@5.37.0)(yaml@2.6.1) '@sanity/types': specifier: workspace:* version: link:../../packages/@sanity/types '@sanity/ui': specifier: ^2.11.8 - version: 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + version: 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) '@sanity/ui-workshop': specifier: ^1.0.0 - version: 1.2.11(@sanity/icons@3.5.7(react@19.0.0))(@sanity/ui@2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)))(@types/node@22.10.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(terser@5.37.0)(yaml@2.6.1) + version: 1.2.11(@sanity/icons@3.5.7(react@19.0.0))(@sanity/ui@2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)))(@types/node@22.10.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(terser@5.37.0)(yaml@2.6.1) '@sanity/util': specifier: workspace:* version: link:../../packages/@sanity/util @@ -543,19 +543,19 @@ importers: version: link:../../packages/sanity sanity-plugin-hotspot-array: specifier: ^2.0.0 - version: 2.1.2(@emotion/is-prop-valid@1.3.1)(@sanity/ui@2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + version: 2.1.2(@emotion/is-prop-valid@1.3.1)(@sanity/ui@2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) sanity-plugin-markdown: specifier: ^5.0.0 - version: 5.0.0(@emotion/is-prop-valid@1.3.1)(easymde@2.18.0)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + version: 5.0.0(@emotion/is-prop-valid@1.3.1)(easymde@2.18.0)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) sanity-plugin-media: specifier: ^2.3.1 - version: 2.3.2(@sanity/ui@2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + version: 2.3.2(@sanity/ui@2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) sanity-plugin-mux-input: specifier: ^2.2.1 - version: 2.4.0(@emotion/is-prop-valid@1.3.1)(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + version: 2.4.0(@emotion/is-prop-valid@1.3.1)(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) styled-components: specifier: ^6.1.11 - version: 6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0) devDependencies: '@million/lint': specifier: 1.0.14 @@ -580,7 +580,7 @@ importers: version: link:../../packages/sanity styled-components: specifier: ^6.1.0 - version: 6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1) examples/clean-studio: dependencies: @@ -595,7 +595,7 @@ importers: version: link:../../packages/sanity styled-components: specifier: ^6.1.0 - version: 6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1) examples/ecommerce-studio: dependencies: @@ -604,7 +604,7 @@ importers: version: link:../../packages/@sanity/cli '@sanity/ui': specifier: ^2.11.8 - version: 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react-is@19.0.0-rc.1)(react@18.3.1)(styled-components@6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + version: 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react-is@19.0.0-rc.1)(react@18.3.1)(styled-components@6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) react: specifier: ^18.3.1 version: 18.3.1 @@ -619,13 +619,13 @@ importers: version: link:../../packages/sanity styled-components: specifier: ^6.1.0 - version: 6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1) examples/movies-studio: dependencies: '@sanity/google-maps-input': specifier: ^4.0.0 - version: 4.1.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react-is@19.0.0-rc.1)(react@18.3.1)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + version: 4.1.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react-is@19.0.0-rc.1)(react@18.3.1)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) react: specifier: ^18.3.1 version: 18.3.1 @@ -637,7 +637,7 @@ importers: version: link:../../packages/sanity styled-components: specifier: ^6.1.0 - version: 6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1) packages/@repo/dev-aliases: {} @@ -1348,7 +1348,7 @@ importers: version: 3.4.0 '@portabletext/block-tools': specifier: ^1.1.4 - version: 1.1.5(@sanity/types@packages+@sanity+types)(@types/react@19.0.8) + version: 1.1.4(@sanity/types@packages+@sanity+types)(@types/react@19.0.8) '@portabletext/editor': specifier: ^1.30.4 version: 1.30.4(@sanity/schema@packages+@sanity+schema)(@sanity/types@packages+@sanity+types)(@types/react@19.0.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1) @@ -1794,6 +1794,9 @@ importers: '@types/tar-fs': specifier: ^2.0.1 version: 2.0.4 + '@vitest/expect': + specifier: ^3.0.5 + version: 3.0.5 '@vvo/tzdb': specifier: 6.137.0 version: 6.137.0 @@ -1835,7 +1838,7 @@ importers: version: 18.3.1(react@18.3.1) styled-components: specifier: ^6.1.0 - version: 6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1) packages/sanity/fixtures/examples/prj-with-react-19: dependencies: @@ -1847,7 +1850,7 @@ importers: version: 19.0.0(react@19.0.0) styled-components: specifier: ^6.1.0 - version: 6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0) packages/sanity/fixtures/examples/prj-with-styled-components-5: dependencies: @@ -1946,7 +1949,7 @@ importers: version: link:../../packages/sanity styled-components: specifier: ^6.1.0 - version: 6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0) perf/tests: dependencies: @@ -4187,6 +4190,12 @@ packages: resolution: {integrity: sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==} engines: {node: '>=12'} + '@portabletext/block-tools@1.1.4': + resolution: {integrity: sha512-cVY3s6vCaKPDZaxkeCsMK0K04gNW/ppBHAPN8qH2OOnBZHwcrhFTIIzDf5syWEt1RO6jFVY9BU737YBQVxJtjA==} + peerDependencies: + '@sanity/types': ^3.72.1 + '@types/react': 18 || 19 + '@portabletext/block-tools@1.1.5': resolution: {integrity: sha512-8feoCEzIKKWCahoBZn3d4MXLt8hJGUPjvj2lgVn1vzFCHM6CEb7KeM+dxEM7ViCV1TBqR5wapxpRdg3xAHWyQw==} peerDependencies: @@ -6830,6 +6839,9 @@ packages: resolution: {integrity: sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q==} engines: {node: '>= 0.4'} + es-module-lexer@1.5.4: + resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} + es-module-lexer@1.6.0: resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} @@ -9800,6 +9812,10 @@ packages: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} + postcss@8.4.38: + resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + engines: {node: ^10 || ^12 || >=14} + postcss@8.4.49: resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} engines: {node: ^10 || ^12 || >=14} @@ -11053,6 +11069,13 @@ packages: react-dom: '>= 16.8.0' react-is: '>= 16.8.0' + styled-components@6.1.14: + resolution: {integrity: sha512-KtfwhU5jw7UoxdM0g6XU9VZQFV4do+KrM8idiVCH5h4v49W+3p3yMe0icYwJgZQZepa5DbH04Qv8P0/RdcLcgg==} + engines: {node: '>= 16'} + peerDependencies: + react: '>= 16.8.0' + react-dom: '>= 16.8.0' + styled-components@6.1.15: resolution: {integrity: sha512-PpOTEztW87Ua2xbmLa7yssjNyUF9vE7wdldRfn1I2E6RTkqknkBYpj771OxM/xrvRGinLy2oysa7GOd7NcZZIA==} engines: {node: '>= 16'} @@ -14497,6 +14520,13 @@ snapshots: '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 + '@portabletext/block-tools@1.1.4(@sanity/types@packages+@sanity+types)(@types/react@19.0.8)': + dependencies: + '@sanity/types': link:packages/@sanity/types + '@types/react': 19.0.8 + get-random-values-esm: 1.0.2 + lodash: 4.17.21 + '@portabletext/block-tools@1.1.5(@sanity/types@packages+@sanity+types)(@types/react@19.0.8)': dependencies: '@sanity/types': link:packages/@sanity/types @@ -14791,12 +14821,12 @@ snapshots: '@sanity/asset-utils@2.2.1': {} - '@sanity/assist@3.1.0(@emotion/is-prop-valid@1.3.1)(@sanity/mutator@packages+@sanity+mutator)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0))': + '@sanity/assist@3.1.0(@emotion/is-prop-valid@1.3.1)(@sanity/mutator@packages+@sanity+mutator)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0))': dependencies: '@sanity/icons': 3.5.7(react@19.0.0) '@sanity/incompatible-plugin': 1.0.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@sanity/mutator': link:packages/@sanity/mutator - '@sanity/ui': 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + '@sanity/ui': 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) date-fns: 3.6.0 lodash: 4.17.21 lodash-es: 4.17.21 @@ -14805,7 +14835,7 @@ snapshots: rxjs: 7.8.1 rxjs-exhaustmap-with-trailing: 2.1.1(rxjs@7.8.1) sanity: link:packages/sanity - styled-components: 6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + styled-components: 6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0) transitivePeerDependencies: - '@emotion/is-prop-valid' - react-dom @@ -14826,7 +14856,7 @@ snapshots: transitivePeerDependencies: - debug - '@sanity/code-input@5.1.2(@babel/runtime@7.26.7)(@codemirror/theme-one-dark@6.1.2)(@emotion/is-prop-valid@1.3.1)(codemirror@6.0.1)(react-dom@18.3.1(react@18.3.1))(react-is@19.0.0-rc.1)(react@18.3.1)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': + '@sanity/code-input@5.1.2(@babel/runtime@7.26.7)(@codemirror/theme-one-dark@6.1.2)(@emotion/is-prop-valid@1.3.1)(codemirror@6.0.1)(react-dom@18.3.1(react@18.3.1))(react-is@19.0.0-rc.1)(react@18.3.1)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': dependencies: '@codemirror/autocomplete': 6.18.4 '@codemirror/commands': 6.8.0 @@ -14846,13 +14876,13 @@ snapshots: '@lezer/highlight': 1.2.1 '@sanity/icons': 3.5.7(react@18.3.1) '@sanity/incompatible-plugin': 1.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@sanity/ui': 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react-is@19.0.0-rc.1)(react@18.3.1)(styled-components@6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + '@sanity/ui': 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react-is@19.0.0-rc.1)(react@18.3.1)(styled-components@6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) '@uiw/codemirror-themes': 4.23.7(@codemirror/language@6.10.8)(@codemirror/state@6.5.2)(@codemirror/view@6.36.2) '@uiw/react-codemirror': 4.23.7(@babel/runtime@7.26.7)(@codemirror/autocomplete@6.18.4)(@codemirror/language@6.10.8)(@codemirror/search@6.5.8)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.2)(codemirror@6.0.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) sanity: link:packages/sanity - styled-components: 6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + styled-components: 6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1) transitivePeerDependencies: - '@babel/runtime' - '@codemirror/lint' @@ -14861,15 +14891,15 @@ snapshots: - codemirror - react-is - '@sanity/color-input@4.0.3(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0))': + '@sanity/color-input@4.0.3(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0))': dependencies: '@sanity/icons': 3.5.7(react@19.0.0) '@sanity/incompatible-plugin': 1.0.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@sanity/ui': 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + '@sanity/ui': 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) react: 19.0.0 react-color: 2.19.3(react@19.0.0) sanity: link:packages/sanity - styled-components: 6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + styled-components: 6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0) use-effect-event: 1.0.2(react@19.0.0) transitivePeerDependencies: - '@emotion/is-prop-valid' @@ -14959,27 +14989,27 @@ snapshots: '@sanity/generate-help-url@3.0.0': {} - '@sanity/google-maps-input@4.1.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react-is@19.0.0-rc.1)(react@18.3.1)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': + '@sanity/google-maps-input@4.1.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react-is@19.0.0-rc.1)(react@18.3.1)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': dependencies: '@sanity/icons': 3.5.7(react@18.3.1) '@sanity/incompatible-plugin': 1.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@sanity/ui': 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react-is@19.0.0-rc.1)(react@18.3.1)(styled-components@6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + '@sanity/ui': 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react-is@19.0.0-rc.1)(react@18.3.1)(styled-components@6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) react: 18.3.1 sanity: link:packages/sanity - styled-components: 6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + styled-components: 6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1) transitivePeerDependencies: - '@emotion/is-prop-valid' - react-dom - react-is - '@sanity/google-maps-input@4.1.0(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0))': + '@sanity/google-maps-input@4.1.0(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0))': dependencies: '@sanity/icons': 3.5.7(react@19.0.0) '@sanity/incompatible-plugin': 1.0.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@sanity/ui': 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + '@sanity/ui': 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) react: 19.0.0 sanity: link:packages/sanity - styled-components: 6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + styled-components: 6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0) transitivePeerDependencies: - '@emotion/is-prop-valid' - react-dom @@ -15341,7 +15371,65 @@ snapshots: - tsx - yaml - '@sanity/tsdoc@1.0.169(@emotion/is-prop-valid@1.3.1)(@types/babel__core@7.20.5)(@types/node@22.10.2)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(terser@5.37.0)(yaml@2.6.1)': + '@sanity/tsdoc@1.0.169(@emotion/is-prop-valid@1.3.1)(@types/babel__core@7.20.5)(@types/node@22.10.2)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(terser@5.37.0)(yaml@2.6.1)': + dependencies: + '@microsoft/api-extractor': 7.49.0(@types/node@22.10.2) + '@microsoft/api-extractor-model': 7.30.1(@types/node@22.10.2) + '@microsoft/tsdoc': 0.15.1 + '@microsoft/tsdoc-config': 0.17.1 + '@portabletext/react': 3.2.0(react@19.0.0) + '@portabletext/toolkit': 2.0.16 + '@sanity/client': 6.27.2(debug@4.4.0) + '@sanity/color': 3.0.6 + '@sanity/icons': 3.5.7(react@19.0.0) + '@sanity/pkg-utils': 6.13.4(@types/babel__core@7.20.5)(@types/node@22.10.2)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(typescript@5.7.3) + '@sanity/ui': 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + '@types/cpx': 1.5.5 + '@vitejs/plugin-react': 4.3.4(vite@6.0.11(@types/node@22.10.2)(terser@5.37.0)(yaml@2.6.1)) + cac: 6.7.14 + chalk: 4.1.2 + chokidar: 4.0.3 + cors: 2.8.5 + dotenv-flow: 3.3.0 + esbuild: 0.24.2 + esbuild-register: 3.6.0(esbuild@0.24.2) + express: 4.21.2 + globby: 11.1.0 + groq: link:packages/groq + groq-js: 1.15.0 + history: 5.3.0 + jsonc-parser: 3.3.1 + mkdirp: 1.0.4 + pkg-up: 3.1.0 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + react-refractor: 2.2.0(react@19.0.0) + sanity: link:packages/sanity + slugify: 1.6.6 + styled-components: 6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + tmp: 0.2.3 + typescript: 5.7.3 + vite: 6.0.11(@types/node@22.10.2)(terser@5.37.0)(yaml@2.6.1) + transitivePeerDependencies: + - '@emotion/is-prop-valid' + - '@types/babel__core' + - '@types/node' + - babel-plugin-react-compiler + - debug + - jiti + - less + - lightningcss + - react-is + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + '@sanity/tsdoc@1.0.169(@emotion/is-prop-valid@1.3.1)(@types/babel__core@7.20.5)(@types/node@22.10.2)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(terser@5.37.0)(yaml@2.6.1)': dependencies: '@microsoft/api-extractor': 7.49.0(@types/node@22.10.2) '@microsoft/api-extractor-model': 7.30.1(@types/node@22.10.2) @@ -15441,10 +15529,10 @@ snapshots: - tsx - yaml - '@sanity/ui-workshop@1.2.11(@sanity/icons@3.5.7(react@19.0.0))(@sanity/ui@2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)))(@types/node@22.10.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(terser@5.37.0)(yaml@2.6.1)': + '@sanity/ui-workshop@1.2.11(@sanity/icons@3.5.7(react@19.0.0))(@sanity/ui@2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)))(@types/node@22.10.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(terser@5.37.0)(yaml@2.6.1)': dependencies: '@sanity/icons': 3.5.7(react@19.0.0) - '@sanity/ui': 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + '@sanity/ui': 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) '@vitejs/plugin-react': 4.3.4(vite@6.0.11(@types/node@22.10.2)(terser@5.37.0)(yaml@2.6.1)) axe-core: 4.10.2 cac: 6.7.14 @@ -15460,7 +15548,7 @@ snapshots: react: 19.0.0 react-dom: 19.0.0(react@19.0.0) segmented-property: 3.0.3 - styled-components: 6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + styled-components: 6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0) vite: 6.0.11(@types/node@22.10.2)(terser@5.37.0)(yaml@2.6.1) transitivePeerDependencies: - '@types/node' @@ -15493,7 +15581,7 @@ snapshots: transitivePeerDependencies: - '@emotion/is-prop-valid' - '@sanity/ui@2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react-is@19.0.0-rc.1)(react@18.3.1)(styled-components@6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': + '@sanity/ui@2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react-is@19.0.0-rc.1)(react@18.3.1)(styled-components@6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': dependencies: '@floating-ui/react-dom': 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@sanity/color': 3.0.6 @@ -15505,7 +15593,7 @@ snapshots: react-dom: 18.3.1(react@18.3.1) react-is: 19.0.0-rc.1 react-refractor: 2.2.0(react@18.3.1) - styled-components: 6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + styled-components: 6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1) use-effect-event: 1.0.2(react@18.3.1) transitivePeerDependencies: - '@emotion/is-prop-valid' @@ -15527,6 +15615,23 @@ snapshots: transitivePeerDependencies: - '@emotion/is-prop-valid' + '@sanity/ui@2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0))': + dependencies: + '@floating-ui/react-dom': 2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@sanity/color': 3.0.6 + '@sanity/icons': 3.5.7(react@19.0.0) + csstype: 3.1.3 + framer-motion: 12.3.1(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: 19.0.0 + react-compiler-runtime: 19.0.0-beta-714736e-20250131(react@19.0.0) + react-dom: 19.0.0(react@19.0.0) + react-is: 19.0.0-rc.1 + react-refractor: 2.2.0(react@19.0.0) + styled-components: 6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + use-effect-event: 1.0.2(react@19.0.0) + transitivePeerDependencies: + - '@emotion/is-prop-valid' + '@sanity/ui@2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0))': dependencies: '@floating-ui/react-dom': 2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -17970,6 +18075,8 @@ snapshots: iterator.prototype: 1.1.4 safe-array-concat: 1.1.3 + es-module-lexer@1.5.4: {} + es-module-lexer@1.6.0: {} es-object-atoms@1.1.1: @@ -21459,6 +21566,12 @@ snapshots: source-map-js: 1.2.1 optional: true + postcss@8.4.38: + dependencies: + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 + postcss@8.4.49: dependencies: nanoid: 3.3.8 @@ -22164,7 +22277,7 @@ snapshots: dependencies: '@rollup/pluginutils': 5.1.4(rollup@4.32.1) debug: 4.4.0(supports-color@9.4.0) - es-module-lexer: 1.6.0 + es-module-lexer: 1.5.4 esbuild: 0.24.2 get-tsconfig: 4.8.1 rollup: 4.32.1 @@ -22285,43 +22398,43 @@ snapshots: safer-buffer@2.1.2: {} - sanity-plugin-hotspot-array@2.1.2(@emotion/is-prop-valid@1.3.1)(@sanity/ui@2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)): + sanity-plugin-hotspot-array@2.1.2(@emotion/is-prop-valid@1.3.1)(@sanity/ui@2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)): dependencies: '@sanity/asset-utils': 2.2.1 '@sanity/image-url': 1.1.0 '@sanity/incompatible-plugin': 1.0.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@sanity/ui': 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + '@sanity/ui': 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) '@sanity/util': link:packages/@sanity/util '@types/lodash-es': 4.17.12 framer-motion: 11.18.2(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) lodash-es: 4.17.21 react: 19.0.0 sanity: link:packages/sanity - styled-components: 6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + styled-components: 6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0) transitivePeerDependencies: - '@emotion/is-prop-valid' - react-dom - sanity-plugin-markdown@5.0.0(@emotion/is-prop-valid@1.3.1)(easymde@2.18.0)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)): + sanity-plugin-markdown@5.0.0(@emotion/is-prop-valid@1.3.1)(easymde@2.18.0)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)): dependencies: '@sanity/incompatible-plugin': 1.0.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@sanity/ui': 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + '@sanity/ui': 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) easymde: 2.18.0 react: 19.0.0 react-simplemde-editor: 5.2.0(easymde@2.18.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) sanity: link:packages/sanity - styled-components: 6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + styled-components: 6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0) transitivePeerDependencies: - '@emotion/is-prop-valid' - react-dom - react-is - sanity-plugin-media@2.3.2(@sanity/ui@2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)): + sanity-plugin-media@2.3.2(@sanity/ui@2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)): dependencies: '@hookform/resolvers': 3.9.1(react-hook-form@7.54.1(react@19.0.0)) '@reduxjs/toolkit': 1.9.7(react-redux@7.2.9(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) '@sanity/incompatible-plugin': 1.0.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@sanity/ui': 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + '@sanity/ui': 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) '@sanity/uuid': 3.0.2 '@tanem/react-nprogress': 5.0.53(react-dom@19.0.0(react@19.0.0))(react@19.0.0) copy-to-clipboard: 3.3.3 @@ -22344,20 +22457,20 @@ snapshots: redux-observable: 2.0.0(redux@4.2.1) rxjs: 7.8.1 sanity: link:packages/sanity - styled-components: 6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + styled-components: 6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0) zod: 3.24.1 transitivePeerDependencies: - '@types/react' - react-native - supports-color - sanity-plugin-mux-input@2.4.0(@emotion/is-prop-valid@1.3.1)(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)): + sanity-plugin-mux-input@2.4.0(@emotion/is-prop-valid@1.3.1)(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(sanity@packages+sanity)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)): dependencies: '@mux/mux-player-react': 2.9.1(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@mux/upchunk': 3.4.0 '@sanity/icons': 3.5.7(react@19.0.0) '@sanity/incompatible-plugin': 1.0.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@sanity/ui': 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + '@sanity/ui': 2.11.8(@emotion/is-prop-valid@1.3.1)(react-dom@19.0.0(react@19.0.0))(react-is@19.0.0-rc.1)(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) '@sanity/uuid': 3.0.2 iso-639-1: 3.1.3 jsonwebtoken-esm: 1.0.5 @@ -22368,7 +22481,7 @@ snapshots: rxjs: 7.8.1 sanity: link:packages/sanity scroll-into-view-if-needed: 3.1.0 - styled-components: 6.1.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + styled-components: 6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0) suspend-react: 0.1.3(react@19.0.0) swr: 2.2.5(react@19.0.0) type-fest: 4.30.2 @@ -23023,6 +23136,34 @@ snapshots: transitivePeerDependencies: - '@babel/core' + styled-components@6.1.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@emotion/is-prop-valid': 1.2.2 + '@emotion/unitless': 0.8.1 + '@types/stylis': 4.2.5 + css-to-react-native: 3.2.0 + csstype: 3.1.3 + postcss: 8.4.38 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + shallowequal: 1.1.0 + stylis: 4.3.2 + tslib: 2.6.2 + + styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + '@emotion/is-prop-valid': 1.2.2 + '@emotion/unitless': 0.8.1 + '@types/stylis': 4.2.5 + css-to-react-native: 3.2.0 + csstype: 3.1.3 + postcss: 8.4.38 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + shallowequal: 1.1.0 + stylis: 4.3.2 + tslib: 2.6.2 + styled-components@6.1.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@emotion/is-prop-valid': 1.2.2 From c6c419fd9fb56c741b17c536644bc1bba2249977 Mon Sep 17 00:00:00 2001 From: Ash Date: Thu, 6 Feb 2025 09:42:12 +0000 Subject: [PATCH 2/7] test(sanity): add `activeUndecidedErrorRelease` fixture --- .../releases/__fixtures__/release.fixture.ts | 17 ++++++++++++ .../__tests__/ReleasesOverview.test.tsx | 27 +++++++++++++------ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/packages/sanity/src/core/releases/__fixtures__/release.fixture.ts b/packages/sanity/src/core/releases/__fixtures__/release.fixture.ts index 74f64fd6559..b939335fc54 100644 --- a/packages/sanity/src/core/releases/__fixtures__/release.fixture.ts +++ b/packages/sanity/src/core/releases/__fixtures__/release.fixture.ts @@ -87,3 +87,20 @@ export const activeUndecidedRelease: ReleaseDocument = { description: 'undecided Release description', }, } + +export const activeUndecidedErrorRelease: ReleaseDocument = { + _rev: 'undecidedErrorRev', + _id: '_.releases.rUndecidedError', + _type: 'system.release', + _createdAt: '2023-10-10T08:00:00Z', + _updatedAt: '2023-10-10T09:00:00Z', + state: 'active', + metadata: { + title: 'undecided Error Release', + releaseType: 'undecided', + description: 'undecided Error Release description', + }, + error: { + message: 'An unexpected error occurred during publication.', + }, +} diff --git a/packages/sanity/src/core/releases/tool/overview/__tests__/ReleasesOverview.test.tsx b/packages/sanity/src/core/releases/tool/overview/__tests__/ReleasesOverview.test.tsx index ada83decc5a..e962932905a 100644 --- a/packages/sanity/src/core/releases/tool/overview/__tests__/ReleasesOverview.test.tsx +++ b/packages/sanity/src/core/releases/tool/overview/__tests__/ReleasesOverview.test.tsx @@ -20,6 +20,7 @@ import { import { activeASAPRelease, activeScheduledRelease, + activeUndecidedErrorRelease, activeUndecidedRelease, archivedScheduledRelease, publishedASAPRelease, @@ -214,6 +215,7 @@ describe('ReleasesOverview', () => { activeASAPRelease, activeUndecidedRelease, scheduledRelease, + activeUndecidedErrorRelease, ] let activeRender: ReturnType @@ -262,7 +264,7 @@ describe('ReleasesOverview', () => { it('shows each open release', () => { const releaseRows = screen.getAllByTestId('table-row') - expect(releaseRows).toHaveLength(4) + expect(releaseRows).toHaveLength(5) const [unsortedFirstRelease, unsortedSecondRelease, unsortedThirdRelease] = releaseRows @@ -371,7 +373,7 @@ describe('ReleasesOverview', () => { fireEvent.click(todayTile!) await waitFor(() => { - expect(screen.getAllByTestId('table-row')).toHaveLength(4) + expect(screen.getAllByTestId('table-row')).toHaveLength(5) }) }) @@ -379,7 +381,7 @@ describe('ReleasesOverview', () => { fireEvent.click(screen.getByTestId('selected-date-filter')) await waitFor(() => { - expect(screen.getAllByTestId('table-row')).toHaveLength(4) + expect(screen.getAllByTestId('table-row')).toHaveLength(5) }) }) }) @@ -466,23 +468,32 @@ describe('ReleasesOverview', () => { }) it('sorts the list of releases', () => { - const [unsortedFirstRelease, unsortedSecondRelease, unsortedThirdRelease] = - screen.getAllByTestId('table-row') + const [ + unsortedFirstRelease, + unsortedSecondRelease, + unsortedThirdRelease, + unsortedFourthRelease, + unsortedFifthRelease, + ] = screen.getAllByTestId('table-row') // default sort asap, then scheduled by publish asc within(unsortedFirstRelease).getByText(activeASAPRelease.metadata.title) within(unsortedSecondRelease).getByText(scheduledRelease.metadata.title) within(unsortedThirdRelease).getByText(activeScheduledRelease.metadata.title) + within(unsortedFourthRelease).getByText(activeUndecidedRelease.metadata.title) + within(unsortedFifthRelease).getByText(activeUndecidedErrorRelease.metadata.title) // sort by asc publish at fireEvent.click(screen.getByText('Time')) const [ - // first release is undecided - _, descPublishSortedFirstRelease, descPublishSortedSecondRelease, descPublishSortedThirdRelease, - ] = screen.getAllByTestId('table-row') + ] = screen + .getAllByTestId('table-row') + // first two releases are undecided + .slice(2) + within(descPublishSortedFirstRelease).getByText(activeScheduledRelease.metadata.title) within(descPublishSortedSecondRelease).getByText(scheduledRelease.metadata.title) within(descPublishSortedThirdRelease).getByText(activeASAPRelease.metadata.title) From 56671b48fa5017001ac64ee106f5b8d84c91fa81 Mon Sep 17 00:00:00 2001 From: Ash Date: Mon, 3 Feb 2025 15:33:02 +0000 Subject: [PATCH 3/7] feat(sanity): load release document `error` field --- .../sanity/src/core/releases/store/createReleaseStore.ts | 6 ++++++ packages/sanity/src/core/releases/store/types.ts | 3 +++ 2 files changed, 9 insertions(+) diff --git a/packages/sanity/src/core/releases/store/createReleaseStore.ts b/packages/sanity/src/core/releases/store/createReleaseStore.ts index 250d09afa32..c7c40ceecfc 100644 --- a/packages/sanity/src/core/releases/store/createReleaseStore.ts +++ b/packages/sanity/src/core/releases/store/createReleaseStore.ts @@ -45,6 +45,12 @@ const QUERY_PROJECTION = `{ "title": "", "releaseType": "${DEFAULT_RELEASE_TYPE}", }), + // Content Lake initially encoded non-error states as {error: {message: ""}}. This projection + // ensures the error field only appears if the document has a non-empty error message. + ...select( + length(error.message) > 0 => { error }, + {} + ), }` // Newest releases first diff --git a/packages/sanity/src/core/releases/store/types.ts b/packages/sanity/src/core/releases/store/types.ts index 36dcdc21e1c..5ea5a49e737 100644 --- a/packages/sanity/src/core/releases/store/types.ts +++ b/packages/sanity/src/core/releases/store/types.ts @@ -38,6 +38,9 @@ export interface ReleaseDocument extends SanityDocument { _updatedAt: string _rev: string state: ReleaseState + error?: { + message: string + } finalDocumentStates?: ReleaseFinalDocumentState[] /** * If defined, it takes precedence over the intendedPublishAt, the state should be 'scheduled' From b35bc247b7881fc75848138b1a4e05afb3a71994 Mon Sep 17 00:00:00 2001 From: Ash Date: Mon, 3 Feb 2025 15:50:43 +0000 Subject: [PATCH 4/7] feat(sanity): add error count to releases store --- .../__tests__/createsReleaseStore.test.ts | 75 +++++++++++++++++++ .../core/releases/store/createReleaseStore.ts | 25 ++++++- .../sanity/src/core/releases/store/types.ts | 5 ++ 3 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 packages/sanity/src/core/releases/store/__tests__/createsReleaseStore.test.ts diff --git a/packages/sanity/src/core/releases/store/__tests__/createsReleaseStore.test.ts b/packages/sanity/src/core/releases/store/__tests__/createsReleaseStore.test.ts new file mode 100644 index 00000000000..bcfe1de3b88 --- /dev/null +++ b/packages/sanity/src/core/releases/store/__tests__/createsReleaseStore.test.ts @@ -0,0 +1,75 @@ +import {describe, expect, it} from 'vitest' + +import {NO_EMISSION} from '../../../../../test/matchers/toMatchEmissions' +import { + activeUndecidedErrorRelease, + activeUndecidedRelease, + archivedScheduledRelease, + publishedASAPRelease, + scheduledRelease, +} from '../../__fixtures__/release.fixture' +import {releaseStoreErrorCount} from '../createReleaseStore' + +describe('releaseStoreErrorCount', () => { + it('emits the count of releases in an error state when the count changes', async () => { + await expect(releaseStoreErrorCount).toMatchEmissions([ + [ + { + releases: new Map([['a', activeUndecidedRelease]]), + state: 'loaded', + }, + 0, + ], + [ + { + releases: new Map([ + ['a', activeUndecidedErrorRelease], + ['b', activeUndecidedErrorRelease], + ]), + state: 'loaded', + }, + 2, + ], + [ + { + releases: new Map([ + ['a', activeUndecidedErrorRelease], + ['b', activeUndecidedErrorRelease], + ['c', archivedScheduledRelease], + ['d', publishedASAPRelease], + ]), + state: 'loaded', + }, + NO_EMISSION, + ], + [ + { + releases: new Map([ + ['a', activeUndecidedErrorRelease], + ['b', activeUndecidedErrorRelease], + ['c', publishedASAPRelease], + ['d', activeUndecidedErrorRelease], + ['e', activeUndecidedErrorRelease], + ['f', scheduledRelease], + ]), + state: 'loaded', + }, + 4, + ], + [ + { + releases: new Map(), + state: 'loaded', + }, + 0, + ], + [ + { + releases: new Map(), + state: 'loaded', + }, + NO_EMISSION, + ], + ]) + }) +}) diff --git a/packages/sanity/src/core/releases/store/createReleaseStore.ts b/packages/sanity/src/core/releases/store/createReleaseStore.ts index c7c40ceecfc..ebbd1fd2777 100644 --- a/packages/sanity/src/core/releases/store/createReleaseStore.ts +++ b/packages/sanity/src/core/releases/store/createReleaseStore.ts @@ -4,17 +4,21 @@ import { catchError, concat, concatWith, + count, filter, + from, merge, type Observable, of, + type OperatorFunction, + pipe, scan, shareReplay, Subject, switchMap, tap, } from 'rxjs' -import {map, startWith} from 'rxjs/operators' +import {distinctUntilChanged, map, startWith} from 'rxjs/operators' import {type DocumentPreviewStore} from '../../preview' import {listenQuery} from '../../store/_legacy' @@ -141,11 +145,30 @@ export function createReleaseStore(context: { shareReplay(1), ) + const errorCount$ = state$.pipe(releaseStoreErrorCount(), shareReplay(1)) + const getMetadataStateForSlugs$ = createReleaseMetadataAggregator(client) return { state$, + errorCount$, getMetadataStateForSlugs$, dispatch, } } + +/** + * @internal + */ +export function releaseStoreErrorCount(): OperatorFunction { + return pipe( + switchMap(({releases}) => + from(releases.values()).pipe( + filter((release) => release.state === 'active'), + filter((release) => typeof release.error !== 'undefined'), + count(), + ), + ), + distinctUntilChanged(), + ) +} diff --git a/packages/sanity/src/core/releases/store/types.ts b/packages/sanity/src/core/releases/store/types.ts index 5ea5a49e737..b44b9ca0d0f 100644 --- a/packages/sanity/src/core/releases/store/types.ts +++ b/packages/sanity/src/core/releases/store/types.ts @@ -81,6 +81,11 @@ export function isReleaseDocument(doc: unknown): doc is ReleaseDocument { */ export interface ReleaseStore { state$: Observable + /** + * Counts all loaded release documents that are in an active state and have an error recorded. + * This is determined by the presence of the `error` field in the release document. + */ + errorCount$: Observable getMetadataStateForSlugs$: (slugs: string[]) => Observable dispatch: Dispatch } From 05971f62083457d1557cecb4a02caae1e2794b47 Mon Sep 17 00:00:00 2001 From: Ash Date: Mon, 3 Feb 2025 16:16:49 +0000 Subject: [PATCH 5/7] feat(sanity): add release tool error indicator --- .../src/core/perspective/ReleasesToolLink.tsx | 41 +++++++++++++++++-- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/packages/sanity/src/core/perspective/ReleasesToolLink.tsx b/packages/sanity/src/core/perspective/ReleasesToolLink.tsx index b4f5a213631..5e53dc3f935 100644 --- a/packages/sanity/src/core/perspective/ReleasesToolLink.tsx +++ b/packages/sanity/src/core/perspective/ReleasesToolLink.tsx @@ -1,20 +1,44 @@ import {CalendarIcon} from '@sanity/icons' // eslint-disable-next-line no-restricted-imports -- Bundle Button requires more fine-grained styling than studio button -import {Box, Button} from '@sanity/ui' +import {Button} from '@sanity/ui' import {useCallback} from 'react' import {useTranslation} from 'react-i18next' +import {useObservable} from 'react-rx' import {useRouterState} from 'sanity/router' +import {styled} from 'styled-components' import {Tooltip} from '../../ui-components/tooltip/Tooltip' import {RELEASES_TOOL_NAME} from '../releases/plugin' +import {useReleasesStore} from '../releases/store/useReleasesStore' import {ToolLink} from '../studio/components/navbar/tools/ToolLink' +const Dot = styled.div({ + width: 4, + height: 4, + borderRadius: 3, + boxShadow: '0 0 0 1px var(--card-bg-color)', +}) + +const Container = styled.div` + flex: none; + + // The children in button is rendered inside a span, we need to absolutely position it. + span:has(> [data-ui='status-icon']) { + position: absolute; + top: 6px; + right: 6px; + padding: 0; + } +` /** * represents the calendar icon for the releases tool. * It will be hidden if users have turned off releases. */ export function ReleasesToolLink(): React.JSX.Element { const {t} = useTranslation() + const {errorCount$} = useReleasesStore() + const errorCount = useObservable(errorCount$) + const hasError = errorCount !== 0 const activeToolName = useRouterState( useCallback( @@ -24,7 +48,7 @@ export function ReleasesToolLink(): React.JSX.Element { ) return ( - + - + ) } From 14ccade373508e7bd9621d52f58f290f49a81c1a Mon Sep 17 00:00:00 2001 From: Ash Date: Tue, 4 Feb 2025 16:02:28 +0000 Subject: [PATCH 6/7] feat(sanity): add release error indicators to `ReleasesOverview` --- .../src/core/releases/i18n/resources.ts | 3 ++ .../overview/ReleasesOverviewColumnDefs.tsx | 31 +++++++++++++++++-- .../__tests__/ReleasesOverview.test.tsx | 5 +++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/packages/sanity/src/core/releases/i18n/resources.ts b/packages/sanity/src/core/releases/i18n/resources.ts index 3bfc94962be..bc3bb1916b2 100644 --- a/packages/sanity/src/core/releases/i18n/resources.ts +++ b/packages/sanity/src/core/releases/i18n/resources.ts @@ -143,6 +143,9 @@ const releasesLocaleStrings = { /** Title text when error during release update */ 'failed-edit-title': 'Failed to save changes', + /** Title text displayed for releases that failed to publish */ + 'failed-publish-title': 'Failed to publish', + /**The text that will be shown in the footer to indicate the time the release was archived */ 'footer.status.archived': 'Archived', /**The text that will be shown in the footer to indicate the time the release was created */ diff --git a/packages/sanity/src/core/releases/tool/overview/ReleasesOverviewColumnDefs.tsx b/packages/sanity/src/core/releases/tool/overview/ReleasesOverviewColumnDefs.tsx index 0818fb9aa3f..1bb53eb1e2c 100644 --- a/packages/sanity/src/core/releases/tool/overview/ReleasesOverviewColumnDefs.tsx +++ b/packages/sanity/src/core/releases/tool/overview/ReleasesOverviewColumnDefs.tsx @@ -1,7 +1,10 @@ -import {LockIcon} from '@sanity/icons' +import {ErrorOutlineIcon, LockIcon} from '@sanity/icons' import {Flex, Text} from '@sanity/ui' import {type TFunction} from 'i18next' +import {Fragment} from 'react' +import {ToneIcon} from '../../../../ui-components/toneIcon/ToneIcon' +import {Tooltip} from '../../../../ui-components/tooltip/Tooltip' import {RelativeTime} from '../../../components' import {getPublishDateFromRelease, isReleaseScheduledOrScheduling} from '../../util/util' import {Headers} from '../components/Table/TableHeader' @@ -88,10 +91,34 @@ export const releasesOverviewColumnDefs: ( ) }, }, + { + id: 'error', + sorting: false, + width: 40, + header: () => , + cell: ({datum: {error, state}, cellProps}) => ( + + {typeof error !== 'undefined' && state === 'active' && ( + {t('failed-publish-title')}} portal> + + + + + )} + + ), + }, { id: 'documentCount', sorting: false, - width: 100, + width: 120, header: ({headerProps}) => ( diff --git a/packages/sanity/src/core/releases/tool/overview/__tests__/ReleasesOverview.test.tsx b/packages/sanity/src/core/releases/tool/overview/__tests__/ReleasesOverview.test.tsx index e962932905a..6e287589be4 100644 --- a/packages/sanity/src/core/releases/tool/overview/__tests__/ReleasesOverview.test.tsx +++ b/packages/sanity/src/core/releases/tool/overview/__tests__/ReleasesOverview.test.tsx @@ -305,6 +305,11 @@ describe('ReleasesOverview', () => { within(scheduledReleaseRow).getByTestId('release-lock-icon') }) + it('shows error indicator next to active releases in error state', () => { + const row = screen.getAllByTestId('table-row')[4] + within(row).getByTestId('error-indicator') + }) + it('allows for switching between history modes', () => { expect(screen.getByText('Open').closest('button')).not.toBeDisabled() expect(screen.getByText('Archived').closest('button')).not.toBeDisabled() From 05cf15cb2f7ba0304d439172b7a01765a95248b1 Mon Sep 17 00:00:00 2001 From: Ash Date: Tue, 4 Feb 2025 16:16:46 +0000 Subject: [PATCH 7/7] feat(sanity): add release error details to `ReleaseDashboardDetails` --- .../src/core/releases/i18n/resources.ts | 2 + .../tool/detail/ReleaseDashboardDetails.tsx | 38 +++++++++++++++++-- .../detail/__tests__/ReleaseDetail.test.tsx | 27 ++++++++++++- 3 files changed, 63 insertions(+), 4 deletions(-) diff --git a/packages/sanity/src/core/releases/i18n/resources.ts b/packages/sanity/src/core/releases/i18n/resources.ts index bc3bb1916b2..db8f00b04d0 100644 --- a/packages/sanity/src/core/releases/i18n/resources.ts +++ b/packages/sanity/src/core/releases/i18n/resources.ts @@ -141,6 +141,8 @@ const releasesLocaleStrings = { /** Label when a release has been deleted by a different user */ 'deleted-release': "The '{{title}}' release has been deleted", + /** Title text displayed for technical error details */ + 'error-details-title': 'Error details', /** Title text when error during release update */ 'failed-edit-title': 'Failed to save changes', /** Title text displayed for releases that failed to publish */ diff --git a/packages/sanity/src/core/releases/tool/detail/ReleaseDashboardDetails.tsx b/packages/sanity/src/core/releases/tool/detail/ReleaseDashboardDetails.tsx index 443b942f2b5..e58c68f729d 100644 --- a/packages/sanity/src/core/releases/tool/detail/ReleaseDashboardDetails.tsx +++ b/packages/sanity/src/core/releases/tool/detail/ReleaseDashboardDetails.tsx @@ -1,15 +1,20 @@ -import {PinFilledIcon, PinIcon} from '@sanity/icons' +import {ErrorOutlineIcon, PinFilledIcon, PinIcon} from '@sanity/icons' import { Box, // Custom button with full radius used here // eslint-disable-next-line no-restricted-imports Button, + Card, Container, Flex, Stack, + Text, } from '@sanity/ui' import {useCallback} from 'react' +import {ToneIcon} from '../../../../ui-components/toneIcon/ToneIcon' +import {TextWithTone} from '../../../components/textWithTone/TextWithTone' +import {Details} from '../../../form/components/Details' import {useTranslation} from '../../../i18n' import {usePerspective} from '../../../perspective/usePerspective' import {useSetPerspective} from '../../../perspective/useSetPerspective' @@ -28,6 +33,7 @@ export function ReleaseDashboardDetails({release}: {release: ReleaseDocument}) { const {selectedReleaseId} = usePerspective() const setPerspective = useSetPerspective() const isSelected = releaseId === selectedReleaseId + const shouldDisplayError = release.state === 'active' && typeof release.error !== 'undefined' const handlePinRelease = useCallback(() => { if (isSelected) { @@ -40,7 +46,7 @@ export function ReleaseDashboardDetails({release}: {release: ReleaseDocument}) { return ( - +