Skip to content

Commit

Permalink
refactor(sanity): update form and config typings to work with compone…
Browse files Browse the repository at this point in the history
…nts API
  • Loading branch information
hermanwikner authored and mariuslundgard committed Oct 6, 2022
1 parent 088612e commit e885e44
Show file tree
Hide file tree
Showing 45 changed files with 226 additions and 172 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ export default function GeneralPreviewStory() {
style={{lineHeight: 0}}
>
{createElement(
component as React.ComponentType<PreviewProps>,
previewProps as PreviewProps
component as React.ComponentType<Omit<PreviewProps, 'renderDefault'>>,
previewProps
)}
</Card>
</Container>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ const LAYOUT_OPTIONS: Record<string, PortableTextPreviewLayoutKey> = {
// }

const previewComponents: {
[TLayoutKey in PortableTextPreviewLayoutKey]: React.ComponentType<PreviewProps<TLayoutKey>>
[TLayoutKey in PortableTextPreviewLayoutKey]: React.ComponentType<
Omit<PreviewProps<TLayoutKey>, 'renderDefault'>
>
} = {
block: BlockPreview,
blockImage: BlockImagePreview,
Expand Down Expand Up @@ -100,7 +102,10 @@ export default function PortableTextPreviewStory() {
<Flex align="center" height="fill" justify="center" padding={4} sizing="border">
<Container width={1}>
<Card border padding={padding[layout]} radius={1} style={{lineHeight: 0}}>
{createElement(component as ComponentType<PreviewProps>, previewProps as PreviewProps)}
{createElement(
component as ComponentType<Omit<PreviewProps, 'renderDefault'>>,
previewProps
)}
</Card>
</Container>
</Flex>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {renderPreviewNode} from '../helpers'
import {HeaderFlex, MediaCard, RootBox} from './BlockImagePreview.styled'

/** @beta */
export type BlockImagePreviewProps = PreviewProps<'blockImage'>
export type BlockImagePreviewProps = Omit<PreviewProps<'blockImage'>, 'renderDefault'>

const DEFAULT_MEDIA_DIMENSIONS: PreviewMediaDimensions = {
...PREVIEW_MEDIA_SIZE.blockImage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const HeaderFlex = styled(Flex).attrs({align: 'center'})`
`

/** @beta */
export function BlockPreview(props: PreviewProps<'block'>) {
export function BlockPreview(props: Omit<PreviewProps<'block'>, 'renderDefault'>) {
const {
actions,
title,
Expand Down
1 change: 0 additions & 1 deletion packages/sanity/src/core/config/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export * from './types'
export * from './useMiddlewareComponents'
4 changes: 0 additions & 4 deletions packages/sanity/src/core/config/components/types.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,31 +1,39 @@
import {ComponentType, createElement, useMemo} from 'react'
/* eslint-disable @typescript-eslint/ban-types */

import {ComponentType, createElement, Fragment, useMemo} from 'react'
import {useSource} from '../../studio'
import {PluginOptions} from '../types'

interface MiddlewareProps<T> {
renderDefault: (props: T) => React.ReactElement
}
const emptyRender = () => createElement(Fragment)

function _createMiddlewareComponent<T extends {renderDefault: (props: T) => React.ReactElement}>(
function _createMiddlewareComponent<T extends {}>(
defaultComponent: ComponentType<T>,
middlewareComponents: ComponentType<T>[]
): ComponentType<T> {
return (_props: T) => {
const _defaultItem = (props: T) => createElement(middlewareComponents[0], props)

let next = _defaultItem

for (const middleware of middlewareComponents.slice(1)) {
const defaultRender = next

next = (props) => {
return createElement(middleware, {
...props,
renderDefault: defaultRender,
})
}
return (outerProps: T) => {
// This is the inner "layer" of the middleware chain
// Here we render the _default_ component (typically Sanity's component)
let next = (props: T) => createElement(defaultComponent, props)

// Since the middleware array is actually a middleware _stack_ data structure, we are
// essentially looping backwards here. This makes it possible to define the _next_ layer for
// each layer.
for (const middleware of middlewareComponents) {
// As we progress through the chain, the meaning of "renderDefault" changes.
// At a given layer in the chain, the _next_ layer is the "default".
const renderDefault = next

// Here we replace `next` so that the _previous_ layer may use this as its _next_.
next = (props) => createElement(middleware, {...props, renderDefault})
}

return next(_props)
return next({
...outerProps,
// NOTE: it's safe to pass the empty render function, since it'll be overwritten in the next step (above).
// NOTE: it's important that the default component does not use `renderDefault`, since it will
// get the `emptyRender` callback will be passed when the middleware stack is empty.
renderDefault: emptyRender,
})
}
}

Expand Down Expand Up @@ -59,18 +67,19 @@ function _pickFromPluginOptions<T>(
}

/** @internal */
export function useMiddlewareComponents<T extends MiddlewareProps<T>>(props: {
pick: (plugin: PluginOptions) => ComponentType<T> | undefined
export function useMiddlewareComponents<T extends {}>(props: {
pick: (plugin: PluginOptions) => ComponentType<T>
defaultComponent: ComponentType<T>
}): ComponentType<T> {
const {options} = useSource().__internal
const {defaultComponent, pick} = props

const middlewareComponents: ComponentType<T>[] = useMemo(() => {
const middleware = _pickFromPluginOptions(options, pick)

return [defaultComponent, ...middleware]
}, [defaultComponent, options, pick])
const middlewareComponents = useMemo(() => {
return _pickFromPluginOptions(options, pick)
}, [options, pick])

return useMemo(() => _createMiddlewareComponent(middlewareComponents), [middlewareComponents])
return useMemo(
() => _createMiddlewareComponent(defaultComponent, middlewareComponents),
[defaultComponent, middlewareComponents]
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
/**
* @internal
*/
export function useInputComponent(): ComponentType<InputProps> {
export function useInputComponent(): ComponentType<Omit<InputProps, 'renderDefault'>> {
return useMiddlewareComponents({
defaultComponent: DefaultInput,
pick: pickInputComponent,
Expand All @@ -23,7 +23,7 @@ export function useInputComponent(): ComponentType<InputProps> {
/**
* @internal
*/
export function useFieldComponent(): ComponentType<FieldProps> {
export function useFieldComponent(): ComponentType<Omit<FieldProps, 'renderDefault'>> {
return useMiddlewareComponents({
defaultComponent: DefaultField,
pick: pickFieldComponent,
Expand All @@ -33,7 +33,7 @@ export function useFieldComponent(): ComponentType<FieldProps> {
/**
* @internal
*/
export function usePreviewComponent(): ComponentType<PreviewProps> {
export function usePreviewComponent(): ComponentType<Omit<PreviewProps, 'renderDefault'>> {
return useMiddlewareComponents({
defaultComponent: DefaultPreview,
pick: pickPreviewComponent,
Expand All @@ -43,7 +43,7 @@ export function usePreviewComponent(): ComponentType<PreviewProps> {
/**
* @internal
*/
export function useItemComponent(): ComponentType<ItemProps> {
export function useItemComponent(): ComponentType<Omit<ItemProps, 'renderDefault'>> {
return useMiddlewareComponents({
defaultComponent: DefaultItem,
pick: pickItemComponent,
Expand Down
39 changes: 21 additions & 18 deletions packages/sanity/src/core/form/form-components-hooks/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,19 @@ import {
} from '../studio/inputResolver/inputResolver'
import {InputProps, FieldProps, ItemProps} from '../types'

function useResolveDefaultComponent<T>(props: {
componentProps: T & {schemaType: SchemaType}
componentResolver: (schemaType: SchemaType) => React.ComponentType<T>
function useResolveDefaultComponent<T extends {schemaType?: SchemaType}>(props: {
componentProps: Omit<T, 'renderDefault'>
componentResolver: (schemaType: SchemaType) => React.ComponentType<Omit<T, 'renderDefault'>>
}): React.ReactElement<T> {
const {componentResolver, componentProps} = props
const defaultResolvedComponent = componentResolver(
componentProps.schemaType
) as React.ComponentType<any>

// NOTE: this will not happen, but we do this to avoid updating too many places
// TODO: We need to clean up the preview machinery + types to remove this
if (!componentProps.schemaType) {
throw new Error('the `schemaType` property must be defined')
}

const defaultResolvedComponent = componentResolver(componentProps.schemaType)

const renderDefault = useCallback(
(parentTypeProps: T) => {
Expand All @@ -29,9 +34,7 @@ function useResolveDefaultComponent<T>(props: {
// in order to prevent that a component is render itself
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const {components, ...restSchemaType} = componentProps.schemaType
const parentTypeResolvedComponent = componentResolver(
restSchemaType
) as React.ComponentType<any>
const parentTypeResolvedComponent = componentResolver(restSchemaType as SchemaType)
return createElement(parentTypeResolvedComponent, parentTypeProps)
},
[componentProps.schemaType, componentResolver]
Expand All @@ -46,8 +49,8 @@ function useResolveDefaultComponent<T>(props: {
/**
* @internal
*/
export function DefaultInput(props: InputProps): React.ReactElement<InputProps> {
return useResolveDefaultComponent<InputProps>({
export function DefaultInput(props: Omit<InputProps, 'renderDefault'>): React.ReactElement {
return useResolveDefaultComponent<Omit<InputProps, 'renderDefault'>>({
componentProps: props,
componentResolver: defaultResolveInputComponent,
})
Expand All @@ -56,8 +59,8 @@ export function DefaultInput(props: InputProps): React.ReactElement<InputProps>
/**
* @internal
*/
export function DefaultField(props: FieldProps): React.ReactElement<FieldProps> {
return useResolveDefaultComponent<FieldProps>({
export function DefaultField(props: Omit<FieldProps, 'renderDefault'>): React.ReactElement {
return useResolveDefaultComponent<Omit<FieldProps, 'renderDefault'>>({
componentProps: props,
componentResolver: defaultResolveFieldComponent,
})
Expand All @@ -66,8 +69,8 @@ export function DefaultField(props: FieldProps): React.ReactElement<FieldProps>
/**
* @internal
*/
export function DefaultItem(props: ItemProps): React.ReactElement<ItemProps> {
return useResolveDefaultComponent<ItemProps>({
export function DefaultItem(props: Omit<ItemProps, 'renderDefault'>): React.ReactElement {
return useResolveDefaultComponent<Omit<ItemProps, 'renderDefault'>>({
componentProps: props,
componentResolver: defaultResolveItemComponent,
})
Expand All @@ -76,9 +79,9 @@ export function DefaultItem(props: ItemProps): React.ReactElement<ItemProps> {
/**
* @internal
*/
export function DefaultPreview(props: PreviewProps): React.ReactElement<PreviewProps> {
return useResolveDefaultComponent<PreviewProps>({
componentProps: props as any,
export function DefaultPreview(props: Omit<PreviewProps, 'renderDefault'>): React.ReactElement {
return useResolveDefaultComponent<Omit<PreviewProps, 'renderDefault'>>({
componentProps: props,
componentResolver: defaultResolvePreviewComponent,
})
}
24 changes: 16 additions & 8 deletions packages/sanity/src/core/form/form-components-hooks/picks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,26 @@ import {PreviewProps} from '../../components/previews'
import {PluginOptions} from '../../config'
import {FieldProps, InputProps, ItemProps} from '../types'

export function pickInputComponent(plugin: PluginOptions): ComponentType<InputProps> {
return plugin.form?.components?.input as ComponentType<InputProps>
export function pickInputComponent(
plugin: PluginOptions
): ComponentType<Omit<InputProps, 'renderDefault'>> {
return plugin.form?.components?.input as ComponentType<Omit<InputProps, 'renderDefault'>>
}

export function pickFieldComponent(plugin: PluginOptions): ComponentType<FieldProps> {
return plugin.form?.components?.field as ComponentType<FieldProps>
export function pickFieldComponent(
plugin: PluginOptions
): ComponentType<Omit<FieldProps, 'renderDefault'>> {
return plugin.form?.components?.field as ComponentType<Omit<FieldProps, 'renderDefault'>>
}

export function pickPreviewComponent(plugin: PluginOptions): ComponentType<PreviewProps> {
return plugin.form?.components?.preview as ComponentType<PreviewProps>
export function pickPreviewComponent(
plugin: PluginOptions
): ComponentType<Omit<PreviewProps, 'renderDefault'>> {
return plugin.form?.components?.preview as ComponentType<Omit<PreviewProps, 'renderDefault'>>
}

export function pickItemComponent(plugin: PluginOptions): ComponentType<ItemProps> {
return plugin.form?.components?.item as ComponentType<ItemProps>
export function pickItemComponent(
plugin: PluginOptions
): ComponentType<Omit<ItemProps, 'renderDefault'>> {
return plugin.form?.components?.item as ComponentType<Omit<ItemProps, 'renderDefault'>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const fieldsetsTestType = defineField({

describe('fieldset with default options', () => {
it('renders fields in a <fieldset element and includes a <legend', async () => {
function FieldWithTestId(props: FieldProps) {
function FieldWithTestId(props: Omit<FieldProps, 'renderDefault'>) {
return <div data-testid={`input-${props.name}`}>{props.children}</div>
}
const {result} = await renderObjectInput({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const DebugInput = forwardRef(function DebugInput(props: InputProps, ref) {
export function inputResolver(
input: SchemaType,
form: SanityFormConfig
): React.ComponentType<InputProps> {
): React.ComponentType<Omit<InputProps, 'renderDefault'>> {
if (!input.type) {
throw new Error('inputResolver: missing subtype')
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export function BlockObjectPreview(props: BlockObjectPreviewProps): ReactElement
if (isCustomPreviewComponent) {
return (
<Flex>
<Box flex={1}>{renderPreview(previewProps as PreviewProps)}</Box>
<Box flex={1}>{renderPreview(previewProps)}</Box>
<Box marginLeft={1}>{actions}</Box>
</Flex>
)
Expand All @@ -152,8 +152,8 @@ export function BlockObjectPreview(props: BlockObjectPreviewProps): ReactElement
layout: isImageType ? 'blockImage' : 'block',
schemaType: type,
value,
media: isImageType ? value : undefined,
} as PreviewProps)}
media: (isImageType ? value : undefined) as any, // TODO
})}
</>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ export const InlineObject = React.forwardRef(function InlineObject(
value,
}

return <PreviewSpan>{renderPreview(previewProps as PreviewProps)}</PreviewSpan>
return <PreviewSpan>{renderPreview(previewProps)}</PreviewSpan>
}, [renderPreview, type, value])

const markersToolTip = useMemo(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function PreviewReferenceValue(props: {

return (
<Flex align="center">
<Box flex={1}>{renderPreview(previewProps as PreviewProps)}</Box>
<Box flex={1}>{renderPreview(previewProps)}</Box>
<Box>
<Inline space={4}>
{showTypeLabel && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export function ReferencePreview(props: {
return (
<Flex align="center">
{availability.available ? (
<Box flex={1}>{renderPreview(previewProps as PreviewProps)}</Box>
<Box flex={1}>{renderPreview(previewProps)}</Box>
) : (
<Box flex={1}>
<Flex align="center">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ export class ArrayInput extends React.PureComponent<ArrayInputProps> {
}

/** @internal */
renderArrayItem = (itemProps: ItemProps) => {
renderArrayItem = (itemProps: Omit<ItemProps, 'renderDefault'>) => {
if (!isObjectItemProps(itemProps)) {
throw new Error('Expected item to be of object type')
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ export const CellItem = React.forwardRef(function ItemCell(
onFocus={onFocus}
__unstable_focusRing
>
{renderPreview(previewProps as PreviewProps)}
{renderPreview(previewProps)}
</PreviewCard>
) : (
<MissingTypeBox flex={1}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export const RowItem = React.forwardRef(function RegularItem(
onFocus={onFocus}
__unstable_focusRing
>
{renderPreview(previewProps as PreviewProps)}
{renderPreview(previewProps)}
</Card>
) : (
<Box flex={1}>
Expand Down

0 comments on commit e885e44

Please sign in to comment.