Skip to content

Commit

Permalink
feat(form-builder): add support for resolving initial values in array…
Browse files Browse the repository at this point in the history
… input
  • Loading branch information
bjoerge committed Apr 28, 2021
1 parent 96416de commit 959bdb4
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 8 deletions.
15 changes: 14 additions & 1 deletion examples/test-studio/schemas/initialValuesTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export const initialValuesTest = {
{
type: 'object',
name: 'slowInitialValue',
description: 'the initial title value here takes 3s to load',
description: 'the initial title value here takes a bit of time to load',
fields: [
{
name: 'title',
Expand All @@ -107,6 +107,19 @@ export const initialValuesTest = {
},
],
},
{
type: 'object',
name: 'errorProneInitialValue',
description: 'the initial value here seems to fail',
fields: [
{
name: 'title',
type: 'string',
initialValue: () =>
sleep(1000).then(() => Promise.reject(new Error('Ouch, something went wrong'))),
},
],
},
],
initialValue: [
{_type: 'simpleObject', title: 'initial 1', primitiveValues: {title: 'inner title'}},
Expand Down
1 change: 1 addition & 0 deletions packages/@sanity/form-builder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@sanity/generate-help-url": "2.2.6",
"@sanity/icons": "^1.0.1",
"@sanity/imagetool": "2.9.0",
"@sanity/initial-value-templates": "2.9.1",
"@sanity/mutator": "2.2.6",
"@sanity/portable-text-editor": "0.1.25",
"@sanity/types": "2.9.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import {FormFieldPresence} from '@sanity/base/presence'
import {ArraySchemaType, isObjectSchemaType, Marker, Path, SchemaType} from '@sanity/types'
import {
ArraySchemaType,
isObjectSchemaType,
Marker,
ObjectSchemaType,
Path,
SchemaType,
} from '@sanity/types'
import {FOCUS_TERMINATOR} from '@sanity/util/paths'
import {isPlainObject} from 'lodash'
import {FormFieldSet} from '@sanity/base/components'
import {Button, Stack, Text} from '@sanity/ui'
import {FormFieldSet, ImperativeToast} from '@sanity/base/components'
import {Button, Stack, Text, Spinner, Flex, Card, Box, ToastParams} from '@sanity/ui'
import React from 'react'
import {map} from 'rxjs/operators'
import {Subscription} from 'rxjs'
Expand All @@ -21,6 +28,8 @@ import {uploadTarget} from './uploadTarget/uploadTarget'

declare const __DEV__: boolean

type Toast = {push: (params: ToastParams) => void}

const UploadTargetFieldset = uploadTarget(FormFieldSet)

function createProtoValue(type: SchemaType): ArrayMember {
Expand Down Expand Up @@ -49,17 +58,26 @@ export interface Props {
filterField: () => any
ArrayFunctionsImpl: typeof ArrayFunctions
resolveUploader?: (type: SchemaType, file: File) => Uploader | null
resolveInitialValue?: (type: ObjectSchemaType, value: any) => Promise<any>
presence: FormFieldPresence[]
}

interface State {
isResolvingInitialValue: boolean
}

export class ArrayInput extends React.Component<Props> {
static defaultProps = {
focusPath: [],
}

_focusArea: HTMLElement | null = null
toast: Toast | null = null

uploadSubscriptions: Record<string, Subscription> = {}
state: State = {
isResolvingInitialValue: false,
}

insert = (itemValue: ArrayMember, position: 'before' | 'after', atIndex: number) => {
const {onChange} = this.props
Expand All @@ -73,8 +91,31 @@ export class ArrayInput extends React.Component<Props> {
}

handleAppend = (value: ArrayMember) => {
this.insert(value, 'after', -1)
this.handleFocusItem(value)
const {resolveInitialValue} = this.props
this.setState({isResolvingInitialValue: true})
const memberType = this.getMemberTypeOfItem(value)
const resolvedInitialValue = resolveInitialValue
? resolveInitialValue(memberType as ObjectSchemaType, value)
: Promise.resolve({})
resolvedInitialValue
.then(
(initial) => {
this.insert({...value, ...initial}, 'after', -1)
this.handleFocusItem(value)
},
(error) => {
this.toast?.push({
title: `Could not resolve initial value`,
description: `Unable to resolve initial value for type: ${memberType.name}: ${error.message}.`,
status: 'error',
})
this.insert(value, 'after', -1)
this.handleFocusItem(value)
}
)
.finally(() => {
this.setState({isResolvingInitialValue: false})
})
}

handleRemoveItem = (item: ArrayMember) => {
Expand Down Expand Up @@ -187,7 +228,9 @@ export class ArrayInput extends React.Component<Props> {

onChange(PatchEvent.from(...patches))
}

setToast = (toast: any | null) => {
this.toast = toast
}
handleRemoveNonObjectValues = () => {
const {onChange, value} = this.props
const nonObjects = value
Expand Down Expand Up @@ -237,6 +280,8 @@ export class ArrayInput extends React.Component<Props> {
ArrayFunctionsImpl,
} = this.props

const {isResolvingInitialValue} = this.state

const hasNonObjectValues = (value || []).some((item) => !isPlainObject(item))

if (hasNonObjectValues) {
Expand Down Expand Up @@ -301,6 +346,7 @@ export class ArrayInput extends React.Component<Props> {
getUploadOptions={this.getUploadOptions}
onUpload={this.handleUpload}
>
<ImperativeToast ref={this.setToast} />
<Stack space={3}>
{hasMissingKeys && (
<Alert
Expand Down Expand Up @@ -337,7 +383,7 @@ export class ArrayInput extends React.Component<Props> {
)}

<Stack data-ui="ArrayInput__content" space={3}>
{value && value.length > 0 && (
{(value?.length > 0 || isResolvingInitialValue) && (
<List onSortEnd={this.handleSortEnd} isSortable={isSortable} isGrid={isGrid}>
{value.map((item, index) => {
return (
Expand Down Expand Up @@ -366,6 +412,18 @@ export class ArrayInput extends React.Component<Props> {
</Item>
)
})}
{isResolvingInitialValue && (
<Item isGrid={isGrid} index={-1}>
<Card border radius={1} padding={1}>
<Flex align="center" justify="center" padding={3}>
<Box marginX={3}>
<Spinner muted />
</Box>
<Text>Resolving initial value…</Text>
</Flex>
</Card>
</Item>
)}
</List>
)}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, {ForwardedRef, forwardRef} from 'react'
import formBuilderConfig from 'config:@sanity/form-builder'
import ArrayFunctions from 'part:@sanity/form-builder/input/array/functions'
import {resolveInitialValueForType} from '@sanity/initial-value-templates'
import resolveUploader from '../uploads/resolveUploader'
import ArrayInput, {Props} from '../../inputs/arrays/ArrayOfObjectsInput'
import {
Expand All @@ -19,6 +20,7 @@ export const SanityArrayInput = forwardRef(function SanityArrayInput(
{...props}
ref={ref}
resolveUploader={resolveUploader}
resolveInitialValue={resolveInitialValueForType}
ArrayFunctionsImpl={ArrayFunctions}
directUploads={SUPPORT_DIRECT_UPLOADS}
/>
Expand Down

0 comments on commit 959bdb4

Please sign in to comment.