-
Notifications
You must be signed in to change notification settings - Fork 450
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f7f92bf
commit c5407b9
Showing
74 changed files
with
1,736 additions
and
1,100 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ | |
/cli.js | ||
/desk.js | ||
/router.js | ||
/document.js | ||
|
||
# Playwright-ct artifacts | ||
/playwright-ct/report | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from '../src/core/document' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/** | ||
* Represents an error thrown when the document context value is missing or not | ||
* found.This error typically indicates that a component is not wrapped in a | ||
* `<DocumentProvider />`. | ||
*/ | ||
export class DocumentContextError extends Error { | ||
constructor() { | ||
super('Could not find context value. Did you wrap this component in a <DocumentProvider />?') | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
packages/sanity/src/core/document/DocumentIdAndTypeContext.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import {createContext} from 'react' | ||
|
||
/** | ||
* The context value type associated with the `DocumentIdAndTypeProvider`. | ||
* @internal | ||
*/ | ||
export interface DocumentIdAndTypeContextValue { | ||
/** | ||
* The published ID of the document. | ||
*/ | ||
documentId: string | ||
/** | ||
* The resolved document type. If a document type was not given it will be | ||
* resolved async using the `resolveTypeForDocument` method from the document | ||
* store. | ||
*/ | ||
documentType: string | ||
} | ||
|
||
/** | ||
* The react context associated with the `DocumentIdAndTypeProvider`. | ||
* @internal | ||
*/ | ||
export const DocumentIdAndTypeContext = createContext<DocumentIdAndTypeContextValue | null>(null) |
96 changes: 96 additions & 0 deletions
96
packages/sanity/src/core/document/DocumentIdAndTypeProvider.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import React, {useEffect, useMemo, useState} from 'react' | ||
import {first} from 'rxjs' | ||
import {getPublishedId} from '../util' | ||
import {useTemplates} from '../hooks' | ||
import {useDocumentStore} from '../store' | ||
import {Template} from '../templates' | ||
import {DocumentIdAndTypeContext, DocumentIdAndTypeContextValue} from './DocumentIdAndTypeContext' | ||
|
||
export interface DocumentIdAndTypeProviderProps { | ||
documentId: string | ||
documentType: string | undefined | ||
templateName: string | undefined | ||
children: React.ReactNode | ||
fallback: React.ReactNode | ||
} | ||
|
||
export function DocumentIdAndTypeProvider({ | ||
fallback, | ||
children, | ||
documentType: typeFromProps, | ||
documentId: idFromProps, | ||
templateName, | ||
}: DocumentIdAndTypeProviderProps) { | ||
const documentId = getPublishedId(idFromProps) | ||
const templates = useTemplates() | ||
const documentStore = useDocumentStore() | ||
|
||
const [error, setError] = useState<Error | null>(null) | ||
if (error) throw error | ||
|
||
// generate a lookup object for templates by their IDs | ||
const templatesById = useMemo(() => { | ||
return templates.reduce<Record<string, Template>>((acc, t) => { | ||
acc[t.id] = t | ||
return acc | ||
}, {}) | ||
}, [templates]) | ||
|
||
// determines the initial `documentType` based on the following priority: | ||
// 1. the value provided in `typeFromProps`, unless it's a legacy value. | ||
// 2. the schema type associated with the template name in `templatesById`. | ||
// 3. defaults to `null` if neither source provides a valid value. | ||
|
||
// mote: The legacy value `' * '` was used historically to denote an unspecified | ||
// document type. We want to ensure it's not used to initialize `documentType`. | ||
const [documentType, setDocumentType] = useState<string | null>(() => { | ||
// check for a valid `typeFromProps` (excluding the legacy value). | ||
if (typeFromProps && typeFromProps !== '*') return typeFromProps | ||
|
||
// determine the document type from the template's schema type. | ||
const typeFromTemplates = templateName && templatesById[templateName]?.schemaType | ||
if (typeFromTemplates) return typeFromTemplates | ||
|
||
// default to `null` if no valid type is determined. | ||
return null | ||
}) | ||
|
||
useEffect(() => { | ||
// exit early if document type is already determined. | ||
if (documentType) return undefined | ||
|
||
// fetch and set the document type from the document store | ||
const subscription = documentStore | ||
.resolveTypeForDocument(documentId) | ||
// note: this operation is only done once to maintain consistency with | ||
// other non-reactive code paths. | ||
.pipe(first()) | ||
.subscribe({ | ||
next: (documentTypeFromContentLake) => { | ||
if (documentTypeFromContentLake) { | ||
setDocumentType(documentTypeFromContentLake) | ||
} else { | ||
setError( | ||
new Error(`Could not resolve document type for document with ID ${documentId}`), | ||
) | ||
} | ||
}, | ||
error: setError, | ||
}) | ||
|
||
return () => subscription.unsubscribe() | ||
}, [documentId, documentStore, documentType]) | ||
|
||
if (!documentType) return <>{fallback}</> | ||
|
||
const contextValue: DocumentIdAndTypeContextValue = { | ||
documentId, | ||
documentType, | ||
} | ||
|
||
return ( | ||
<DocumentIdAndTypeContext.Provider value={contextValue}> | ||
{children} | ||
</DocumentIdAndTypeContext.Provider> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import React from 'react' | ||
import {noop} from 'lodash' | ||
import {InitialValueProvider, InitialValueProviderProps} from './initialValue' | ||
import { | ||
DocumentIdAndTypeProvider, | ||
DocumentIdAndTypeProviderProps, | ||
} from './DocumentIdAndTypeProvider' | ||
import {TimelineProvider, TimelineProviderProps} from './timeline' | ||
import {FormStateProvider, FormStateProviderProps} from './formState' | ||
import { | ||
ReferenceInputOptionsProvider, | ||
ReferenceInputOptionsProviderProps, | ||
} from './referenceInputOptions' | ||
|
||
/** @internal */ | ||
export interface DocumentProviderProps | ||
extends DocumentIdAndTypeProviderProps, | ||
TimelineProviderProps, | ||
InitialValueProviderProps, | ||
FormStateProviderProps, | ||
ReferenceInputOptionsProviderProps {} | ||
|
||
/** @internal */ | ||
export function DocumentProvider({ | ||
documentId, | ||
documentType, | ||
templateName, | ||
templateParams, | ||
timelineRange = {}, | ||
onTimelineRangeChange = noop, | ||
initialFocusPath, | ||
isHistoryInspectorOpen, | ||
fallback, | ||
children, | ||
EditReferenceLinkComponent, | ||
onEditReference, | ||
activePath, | ||
}: DocumentProviderProps) { | ||
return ( | ||
<DocumentIdAndTypeProvider | ||
documentId={documentId} | ||
documentType={documentType} | ||
templateName={templateName} | ||
fallback={fallback} | ||
> | ||
<TimelineProvider timelineRange={timelineRange} onTimelineRangeChange={onTimelineRangeChange}> | ||
<InitialValueProvider | ||
templateName={templateName} | ||
templateParams={templateParams} | ||
fallback={fallback} | ||
> | ||
<ReferenceInputOptionsProvider | ||
EditReferenceLinkComponent={EditReferenceLinkComponent} | ||
onEditReference={onEditReference} | ||
activePath={activePath} | ||
fallback={fallback} | ||
> | ||
<FormStateProvider | ||
initialFocusPath={initialFocusPath} | ||
isHistoryInspectorOpen={isHistoryInspectorOpen} | ||
> | ||
{children} | ||
</FormStateProvider> | ||
</ReferenceInputOptionsProvider> | ||
</InitialValueProvider> | ||
</TimelineProvider> | ||
</DocumentIdAndTypeProvider> | ||
) | ||
} |
83 changes: 83 additions & 0 deletions
83
packages/sanity/src/core/document/formState/FormStateContext.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import {SanityDocumentLike, ObjectSchemaType, ValidationMarker, Path} from '@sanity/types' | ||
import {createContext} from 'react' | ||
import {PatchEvent, DocumentFormNode, StateTree} from '../../form' | ||
import {ConnectionState} from '../../hooks' | ||
import {EditStateFor, PermissionCheckResult} from '../../store' | ||
|
||
export interface FormStateContextValue<TDocument extends SanityDocumentLike> { | ||
documentId: string | ||
documentType: string | ||
|
||
/** | ||
* the current value of the form. note that this supersedes both the `value` | ||
* and the `displayed` prop from the previous `DocumentPaneContextValue` | ||
* interface | ||
*/ | ||
value: TDocument | ||
|
||
editState: EditStateFor | ||
|
||
/** | ||
* if the value originates from the current value from context lake, then this | ||
* will be `current-value`. if the value is from a historical revision then | ||
* it will be `historical-value`, if the value is from an initial value | ||
* template then it will be `initial-value` | ||
*/ | ||
valueOrigin: 'draft-value' | 'published-value' | 'initial-value' | 'historical-value' | undefined | ||
|
||
schemaType: ObjectSchemaType | ||
|
||
/** | ||
* The value that is used to compare changes since a particular time. This is | ||
* used to show change indicators. For example, this value is typically the | ||
* published version of the document so that while you're editing the draft, | ||
* the published version can be compared to the current draft version. | ||
*/ | ||
compareValue: TDocument | null | ||
|
||
/** | ||
* Signals when the document is ready for editing. Considers the connection | ||
* state, edit states, and whether or not the timeline is ready. | ||
*/ | ||
ready: boolean | ||
|
||
/** | ||
* Propagates changes described by a patch event message to the form value. | ||
*/ | ||
patchValue: (event: PatchEvent) => void | ||
|
||
/** | ||
* Contains the prepared root form node state. This is the result of | ||
* `prepareFormState`. | ||
*/ | ||
formState: DocumentFormNode | null | ||
|
||
focusPath: Path | ||
setFocusPath: (path: Path) => void | ||
|
||
openPath: Path | ||
setOpenPath: (path: Path) => void | ||
|
||
collapsedFieldsets: StateTree<boolean> | ||
setFieldsetCollapsed: (path: Path, collapsed: boolean) => void | ||
|
||
collapsedPaths: StateTree<boolean> | ||
setPathCollapsed: (path: Path, collapsed: boolean) => void | ||
|
||
activeFieldGroups: StateTree<string> | ||
setActiveFieldGroup: (path: Path, groupName: string) => void | ||
|
||
validation: ValidationMarker[] | ||
permissions: PermissionCheckResult | undefined | ||
isPermissionsLoading: boolean | ||
|
||
connectionState: ConnectionState | ||
|
||
delete: () => void | ||
isDeleting: boolean | ||
isDeleted: boolean | ||
} | ||
|
||
export const FormStateContext = createContext<FormStateContextValue<SanityDocumentLike> | null>( | ||
null, | ||
) |
Oops, something went wrong.