Skip to content

Commit

Permalink
fix(core): add opt out option to studioAnnouncements (#7820)
Browse files Browse the repository at this point in the history
  • Loading branch information
pedrobonamin authored Nov 15, 2024
1 parent 4dab0b9 commit 3efc59d
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 11 deletions.
3 changes: 3 additions & 0 deletions dev/studio-e2e-testing/sanity.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,7 @@ export default defineConfig({
enabled: true,
},
},
announcements: {
enabled: false,
},
})
23 changes: 23 additions & 0 deletions packages/sanity/src/core/config/configPropertyReducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -497,3 +497,26 @@ export const createFallbackOriginReducer = (config: PluginOptions): string | und

return result
}

export const announcementsEnabledReducer = (opts: {
config: PluginOptions
initialValue: boolean
}): boolean => {
const {config, initialValue} = opts
const flattenedConfig = flattenConfig(config, [])

const result = flattenedConfig.reduce((acc, {config: innerConfig}) => {
const resolver = innerConfig.announcements?.enabled

if (!resolver && typeof resolver !== 'boolean') return acc
if (typeof resolver === 'boolean') return resolver

throw new Error(
`Expected \`announcements.enabled\` to be a boolean, but received ${getPrintableType(
resolver,
)}`,
)
}, initialValue)

return result
}
5 changes: 5 additions & 0 deletions packages/sanity/src/core/config/prepareConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {operatorDefinitions} from '../studio/components/navbar/search/definition
import {type InitialValueTemplateItem, type Template, type TemplateItem} from '../templates'
import {EMPTY_ARRAY, isNonNullable} from '../util'
import {
announcementsEnabledReducer,
createFallbackOriginReducer,
documentActionsReducer,
documentBadgesReducer,
Expand Down Expand Up @@ -663,6 +664,10 @@ function resolveSource({
fallbackStudioOrigin: createFallbackOriginReducer(config),
},
},

announcements: {
enabled: announcementsEnabledReducer({config, initialValue: true}),
},
}

return source
Expand Down
14 changes: 14 additions & 0 deletions packages/sanity/src/core/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,13 @@ export interface PluginOptions {
* @beta
*/
onUncaughtError?: (error: Error, errorInfo: ErrorInfo) => void
/**
* @hidden
* @internal
*/
announcements?: {
enabled: boolean
}
}

/** @internal */
Expand Down Expand Up @@ -810,6 +817,13 @@ export interface Source {
* @beta
*/
onUncaughtError?: (error: Error, errorInfo: ErrorInfo) => void
/**
* @hidden
* @internal
*/
announcements?: {
enabled: boolean
}
}

/** @internal */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {catchError, combineLatest, map, type Observable, startWith} from 'rxjs'
import {StudioAnnouncementContext} from 'sanity/_singletons'

import {useClient} from '../../hooks/useClient'
import {useSource} from '../../studio/source'
import {useWorkspace} from '../../studio/workspace'
import {SANITY_VERSION} from '../../version'
import {
Expand All @@ -28,11 +29,7 @@ interface StudioAnnouncementsProviderProps {
}
const CLIENT_OPTIONS = {apiVersion: 'v2024-09-19'}

/**
* @internal
* @hidden
*/
export function StudioAnnouncementsProvider({children}: StudioAnnouncementsProviderProps) {
function StudioAnnouncementsProviderInner({children}: StudioAnnouncementsProviderProps) {
const telemetry = useTelemetry()
const [dialogMode, setDialogMode] = useState<DialogMode | null>(null)
const [isCardDismissed, setIsCardDismissed] = useState(false)
Expand Down Expand Up @@ -159,3 +156,16 @@ export function StudioAnnouncementsProvider({children}: StudioAnnouncementsProvi
</StudioAnnouncementContext.Provider>
)
}

/**
* @internal
* @hidden
*/
export function StudioAnnouncementsProvider(props: StudioAnnouncementsProviderProps) {
const source = useSource()

if (source.announcements?.enabled) {
return <StudioAnnouncementsProviderInner {...props} />
}
return props.children
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import {fireEvent, render, renderHook, waitFor} from '@testing-library/react'
import {type ReactNode} from 'react'
import {of} from 'rxjs'
import {defineConfig} from 'sanity'
import {type Config, defineConfig} from 'sanity'
import {beforeAll, beforeEach, describe, expect, test, vi} from 'vitest'

import {createTestProvider} from '../../../../../test/testUtils/TestProvider'
Expand Down Expand Up @@ -36,10 +36,11 @@ vi.mock('@sanity/client', () => ({
vi.mock('../../../hooks/useClient')
const useClientMock = useClient as ReturnType<typeof vi.fn>

const mockObservableRequest = vi.fn((announcements) => of(announcements))
const mockClient = (announcements: StudioAnnouncementDocument[]) => {
useClientMock.mockReturnValue({
observable: {
request: () => of(announcements),
request: () => mockObservableRequest(announcements),
},
})
}
Expand All @@ -55,9 +56,12 @@ const config = defineConfig({
projectId: 'test',
dataset: 'test',
})
async function createAnnouncementWrapper() {
async function createAnnouncementWrapper(configOverride: Partial<Config> = {}) {
const wrapper = await createTestProvider({
config,
config: {
...config,
...configOverride,
},
resources: [],
})

Expand Down Expand Up @@ -114,7 +118,8 @@ describe('StudioAnnouncementsProvider', () => {
const {result} = renderHook(() => useStudioAnnouncements(), {
wrapper,
})

expect(seenAnnouncementsMock).toBeCalled()
expect(mockObservableRequest).toBeCalled()
expect(result.current.unseenAnnouncements).toEqual([])
expect(result.current.studioAnnouncements).toEqual(mockAnnouncements)
})
Expand Down Expand Up @@ -641,3 +646,24 @@ describe('StudioAnnouncementsProvider', () => {
})
})
})

describe('StudioAnnouncementsProvider-Disabled', () => {
let wrapper = ({children}: {children: ReactNode}) => children
beforeAll(async () => {
// Reset all mocks
vi.clearAllMocks()
wrapper = await createAnnouncementWrapper({
announcements: {enabled: false},
})
})
test('if the feature is disabled, the client should not be called', () => {
const {result} = renderHook(() => useStudioAnnouncements(), {
wrapper,
})

expect(result.current.unseenAnnouncements).toEqual([])
expect(result.current.studioAnnouncements).toEqual([])
expect(seenAnnouncementsMock).not.toBeCalled()
expect(mockObservableRequest).not.toBeCalled()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ import {type StudioAnnouncementsContextValue} from './types'

export function useStudioAnnouncements(): StudioAnnouncementsContextValue {
const context = useContext(StudioAnnouncementContext)

if (!context) {
throw new Error('useStudioAnnouncements: missing context value')
return {
studioAnnouncements: [],
unseenAnnouncements: [],
onDialogOpen: () => {},
}
}

return context
Expand Down

0 comments on commit 3efc59d

Please sign in to comment.