Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Array Fields in component blocks (breaking) #7428

Merged
merged 137 commits into from
May 25, 2022
Merged
Show file tree
Hide file tree
Changes from 134 commits
Commits
Show all changes
137 commits
Select commit Hold shift + click to select a range
d995cc6
WIP
emmatown Feb 7, 2022
9718214
WIP
emmatown Feb 7, 2022
8feb75b
array fields, sorta
emmatown Feb 7, 2022
5d4f543
Improve conditional field types
emmatown Feb 10, 2022
3c96c1b
GraphQL types
emmatown Feb 11, 2022
34d3c0d
Fix things
emmatown Feb 11, 2022
970ec8f
Improve array field UI
emmatown Feb 11, 2022
d6c6c55
Merge branch 'main' into component-field-type
emmatown Feb 15, 2022
8a648a6
Merge branch 'main' into component-field-type
emmatown Feb 15, 2022
605dde4
UI performance improvements
emmatown Feb 15, 2022
ca285c2
Allow recursive props
emmatown Feb 16, 2022
5e01bfc
Check that recursive stuff is fine
emmatown Feb 16, 2022
e5c7c8f
Array field in document field cleanup
emmatown Feb 18, 2022
6abf6f4
Better array behaviour in the document field
emmatown Feb 21, 2022
adefb98
Fix more child field in array field stuff
emmatown Feb 22, 2022
96e0321
Progress
emmatown Feb 28, 2022
3bf3a28
Merge branch 'main' into component-field-type
emmatown Feb 28, 2022
038c804
Fix some TS errors
emmatown Feb 28, 2022
50cfa6e
Remove error
emmatown Feb 28, 2022
7d4076a
Progress
emmatown Feb 28, 2022
550c8bd
Merge branch 'main' into component-field-type
emmatown Mar 7, 2022
9c5dbcb
Rename some things
emmatown Mar 7, 2022
20265a9
Relationships
emmatown Mar 9, 2022
27441d9
Update preview props API
emmatown Mar 10, 2022
46d4483
Progress on always using preview props to render the form
emmatown Mar 10, 2022
2b07376
Merge branch 'main' into component-field-type
emmatown Mar 10, 2022
40657a8
Add memo to FormValueContentFromPreview
emmatown Mar 10, 2022
7bf9f7e
Design updates
emmatown Mar 16, 2022
25f3390
Preview props memoization
emmatown Mar 16, 2022
576aabb
Improvements to array fields in document editor
emmatown Mar 17, 2022
0535c2c
More child field in array field fixes
emmatown Mar 17, 2022
dd64e7a
More child field in array field improvements
emmatown Mar 17, 2022
751c50b
Cleanup
emmatown Mar 20, 2022
6fcf879
Fix child field things
emmatown Mar 21, 2022
88986be
Update a test
emmatown Mar 21, 2022
d25716c
Update tests
emmatown Mar 21, 2022
31b4869
Fix things
emmatown Mar 23, 2022
f657188
Fix website build
emmatown Mar 29, 2022
2ef4c21
Experimenting
emmatown Mar 31, 2022
c4c5e4b
Merge branch 'main' into component-field-type
emmatown Apr 1, 2022
675bd6b
Merge branch 'main' into component-field-type
emmatown Apr 4, 2022
8024f92
Remove some things
emmatown Apr 4, 2022
a85d1a4
manypkg fix
emmatown Apr 4, 2022
a206f35
Remove onInsert, onMove and onRemove from array field preview props
emmatown Apr 4, 2022
550074c
Remove more things
emmatown Apr 4, 2022
7bf3ae4
Revert more changes
emmatown Apr 4, 2022
b41add3
Fix a test
emmatown Apr 4, 2022
ebd627c
Fix some things
emmatown Apr 7, 2022
acbebbd
Some cleanup
emmatown Apr 8, 2022
3ec05f4
Generic field tests for new field
emmatown Apr 11, 2022
f7287a3
Merge branch 'main' into component-field-type
emmatown Apr 11, 2022
ad77c27
Fix a thing
emmatown Apr 11, 2022
b4e8672
Fixed Reset to Defaults Button
Achisingh Apr 11, 2022
032e2eb
changeset
Achisingh Apr 11, 2022
ff71467
Update packages/core/src/___internal-do-not-use-will-break-in-patch/a…
Achisingh Apr 12, 2022
a280b2d
Merge pull request #7456 from keystonejs/7364-pagination
Achisingh Apr 12, 2022
d503142
fix(deps): update dependency cookie to ^0.5.0 (#7462)
renovate[bot] Apr 13, 2022
bcafb07
refactor: replace deprecated String.prototype.substr() (#7402)
CommanderRoot Apr 14, 2022
96119c7
Add Codesandbox configs to allow launching online sandboxes for examp…
MurzNN Apr 14, 2022
649712c
Fixed z-index bug for editor #7395
Achisingh Apr 14, 2022
1176215
changeset update
Achisingh Apr 14, 2022
a8de009
Merge pull request #7464 from keystonejs/#7395-Document-z-index-bug
Achisingh Apr 14, 2022
242b87f
Updated issue creation template (#7466)
raveling Apr 14, 2022
23332da
typo (#7468)
noor-codes Apr 18, 2022
fdb6a7b
Error for an invalid defaultValue in a select field
emmatown Apr 19, 2022
cc96020
Type cleanup
emmatown Apr 19, 2022
775dc8d
fix(deps): update dependency @types/cookie to ^0.5.0 (#7470)
renovate[bot] Apr 19, 2022
02a8d65
Update example documentation (#7451)
dcousens Apr 19, 2022
0e1c65f
Rename `id` to `key` in array fields
emmatown Apr 20, 2022
9256337
Align preview props for child field with other fields
emmatown Apr 20, 2022
ab17dc7
Flatten accessing elements in an array field
emmatown Apr 20, 2022
1d25922
Fix little things
emmatown Apr 20, 2022
cfc69cf
Linting
emmatown Apr 20, 2022
63a57fe
chore(deps): update dependency @svgr/plugin-svgo to v6 (#7353)
renovate[bot] Apr 21, 2022
b7a21bb
fix(deps): update dependency @preconstruct/next to v4 (#7408)
renovate[bot] Apr 21, 2022
0d091ab
chore(deps): update dependency @testing-library/user-event to v14 (#7…
renovate[bot] Apr 21, 2022
670439d
chore(deps): update dependency @svgr/plugin-jsx to v6 (#7179)
renovate[bot] Apr 21, 2022
bfb6c84
Fixed React key warning on GraphQL error notices (#7121)
moselhy Apr 21, 2022
6c14db4
Fix artifact generation in the repo failing when empty directories ex…
emmatown Apr 22, 2022
71437c7
Add back .map(x => ({ key: x.key })) for now
emmatown Apr 27, 2022
6e3fcf6
Some renaming
emmatown Apr 27, 2022
5c5a32c
More renaming
emmatown Apr 27, 2022
dab6738
Remove new field type
emmatown Apr 27, 2022
9a6bf55
Merge branch 'main' into component-field-type
emmatown Apr 27, 2022
b1dc6bd
Remove some more things
emmatown Apr 27, 2022
93f2afd
Remove entrypoint that no longer exists
emmatown Apr 27, 2022
1183fb1
Rename component to preview
emmatown Apr 27, 2022
51c206a
Little things
emmatown Apr 27, 2022
96cb17d
First pass at changeset
emmatown May 3, 2022
5af9ed2
Fix toolbar preview props
emmatown May 3, 2022
c28f362
Renaming
emmatown May 3, 2022
ec05bdd
Simplify a type
emmatown May 3, 2022
9ceec73
Fix example
emmatown May 3, 2022
3c82770
Docs updates
emmatown May 6, 2022
6e1e750
Insert new child field elements before removing previous elements to …
emmatown May 6, 2022
f2bfa89
Fix a bug
emmatown May 6, 2022
0b8c92c
Remove unused imports
emmatown May 9, 2022
a6497c2
Add checkbox list to website
emmatown May 9, 2022
8430ee7
Fix type errors
emmatown May 9, 2022
f725c01
Merge branch 'main' into component-field-type
emmatown May 13, 2022
b5fc088
Delete famous-cherries-joke.md
emmatown May 13, 2022
5ef1879
Remove old changesets
emmatown May 13, 2022
a1efc5d
prefer const
dcousens May 19, 2022
0e76266
prefer flattened early exits
dcousens May 19, 2022
29e50ab
prefer early exits
dcousens May 19, 2022
357fc68
dont fallthrough in switch statements
dcousens May 19, 2022
7b3f6cb
Update packages/fields-document/package.json
emmatown May 19, 2022
15a2d06
Merge branch 'main' into component-field-type
dcousens May 19, 2022
0e03ec6
yarn format
dcousens May 19, 2022
dfd30b5
remove double-spacing
dcousens May 19, 2022
a50e5c0
remove explicit any
dcousens May 19, 2022
ac64018
rename Sortable* to Orderable*
dcousens May 19, 2022
c265751
prefer const
dcousens May 19, 2022
f7b489d
flatten branches
dcousens May 19, 2022
6a80c89
use lodash, not DIY
dcousens May 19, 2022
a858c7b
shorter error message
dcousens May 19, 2022
d5cf920
shorter recursion error message
dcousens May 19, 2022
8c240be
remove console.log
dcousens May 19, 2022
7e326f7
prefer const
dcousens May 19, 2022
b184440
prefer const
dcousens May 19, 2022
164ee2b
no as any needed
dcousens May 19, 2022
ca1895a
remove duplicate text
dcousens May 19, 2022
c75038c
dont use findLastIndex or lodash
dcousens May 24, 2022
133841d
remove redundant Object.keys
dcousens May 24, 2022
3223413
prefer canonical props pattern
dcousens May 25, 2022
3127c63
prefer const
dcousens May 25, 2022
44e3da4
prefer const, normal for loops
dcousens May 25, 2022
8756714
prefer normal for loops
dcousens May 25, 2022
5cb6f92
prefer const
dcousens May 25, 2022
3e9beb1
add comment and flatten goto
dcousens May 25, 2022
a5e067e
Fix casing
emmatown May 25, 2022
56a2d40
update changeset copy
dcousens May 25, 2022
920e99f
dont shadow types for React component names, ignore eslint
dcousens May 25, 2022
3d40234
yarn format
dcousens May 25, 2022
39757d3
unnest the components, revert to functions
dcousens May 25, 2022
1625f5c
prefer for-of iteration of Object.keys
dcousens May 25, 2022
efa2c79
yarn format
dcousens May 25, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 210 additions & 0 deletions .changeset/silver-bottles-drum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
---
'@keystone-6/fields-document': major
---

This release contains substantial changes to the underlying document-editor component block interfaces, with the addition of array fields.
The breaking changes are only for defining components, _no database migration is needed_.

The primary breaking changes for component blocks are:

- For the arguments to the `component` function from `@keystone-6/fields-document/component-blocks`, the following properties have been renamed
- `component` -> `preview`
- `props` -> `schema`
- When using the fields within your preview component - as defined by your component `.schema` (previous `.props`) - you now use `props.fields.{innerFieldName}` instead of `props.{innerFieldName}`.
For example, `props.fields.title` instead of `props.title`.
For a nested example, `props.fields.someObject.fields.title` instead of `props.someObject.title`.

- The React element to render for a child field is now `props.{innerFieldName}.element` instead of `props.{innerFieldName}`.

As an example, the changes needed for updating the "Hero" component block as seen on https://keystonejs.com/docs/guides/document-field-demo is shown shown below

```diff
hero: component({
- component: props => {
+ preview: props => {
return (
<div
css={{
backgroundColor: 'white',
- backgroundImage: `url(${props.imageSrc.value})`,
+ backgroundImage: `url(${props.fields.imageSrc.value})`,
backgroundPosition: 'center',
backgroundSize: 'cover',
display: 'flex',
@@ -51,7 +189,7 @@ export const componentBlocks = {
textShadow: '0px 1px 3px black',
}}
>
- {props.title}
+ {props.fields.title.element}
</div>
<div
css={{
@@ -63,9 +201,9 @@ export const componentBlocks = {
textShadow: '0px 1px 3px black',
}}
>
- {props.content}
+ {props.fields.content.element}
</div>
- {props.cta.discriminant ? (
+ {props.fields.cta.discriminant ? (
<div
css={{
backgroundColor: '#F9BF12',
@@ -78,14 +216,14 @@ export const componentBlocks = {
padding: '12px 16px',
}}
>
- {props.cta.value.text}
+ {props.fields.cta.value.fields.text.element}
</div>
) : null}
</div>
);
},
label: 'Hero',
- props: {
+ schema: {
title: fields.child({ kind: 'inline', placeholder: 'Title...' }),
content: fields.child({ kind: 'block', placeholder: '...' }),
imageSrc: fields.text({
```

Additionally this release introduces an array field `fields.array` for component block which represents an array of another field type, such as an object, conditional, form or other child field.
See below for an example of a question & answers component block with the new array field:

```tsx
import { fields, component, NotEditable } from '@keystone-6/fields-document/component-blocks';

component({
label: 'Questions & Answers',
schema: {
questions: fields.array(
fields.object({
question: fields.child({ placeholder: 'Question', kind: 'inline' }),
answer: fields.child({ placeholder: 'Answer', formatting: 'inherit', kind: 'block' }),
})
),
},
preview: props => {
return (
<div>
{props.fields.questions.elements.map(questionAndAnswer => {
return (
<div key={questionAndAnswer.key}>
<h2>{questionAndAnswer.fields.question.element}</h2>
<p>{questionAndAnswer.fields.answer.element}</p>
<NotEditable>
<Button
onClick={() => {
props.fields.questions.onChange(
props.fields.questions.elements
.filter(x => x.key !== questionAndAnswer.key)
.map(x => ({ key: x.key }))
);
}}
>
Remove
</Button>
</NotEditable>
</div>
);
})}
<NotEditable>
<Button
onClick={() => {
props.fields.questions.onChange([
...props.fields.questions.elements,
{ key: undefined },
]);
}}
>
Insert
</Button>
</NotEditable>
</div>
);
},
});
```

Similar to the built-in document-editor lists, when an array field has only 1 element, pressing enter adds a new element and pressing delete removes an element.
For example, here's a list of checkboxes:

```tsx
/** @jsxRuntime classic */
/** @jsx jsx */
import { jsx } from '@keystone-ui/core';
import { useEffect } from 'react';
import { fields, component } from '@keystone-6/fields-document/component-blocks';

component({
label: 'Checkbox List',
schema: {
children: fields.array(
fields.object({
done: fields.checkbox({ label: 'Done' }),
content: fields.child({ kind: 'inline', placeholder: '', formatting: 'inherit' }),
})
),
},
chromeless: true,
preview: (props) => {
useEffect(() => {
if (!props.fields.children.elements.length) {
props.fields.children.onChange([{ key: undefined }]);
}
});

return (
<ul css={{ padding: 0 }}>
{props.fields.children.elements.map(element => (
<li css={{ listStyle: 'none' }} key={element.key}>
<input
contentEditable="false"
css={{ marginRight: 8 }}
type="checkbox"
checked={element.fields.done.value}
onChange={event => element.fields.done.onChange(event.target.checked)}
/>
<span
style={{
textDecoration: element.fields.done.value ? 'line-through' : undefined,
}}
>
{element.fields.content.element}
</span>
</li>
))}
</ul>
);
},
});
```

Finally, some other changes introduced in this release are:

- Each of the `preview` props `fields` (and their inner fields, if any) now have an `onChange` function so that you can update more than one field in a component at a time
- Each of the `preview` props `fields` (and their inner fields, if any) now have a `schema` property to access their respective schema at that level
- Generally, preview props are now referentially stable between renders when their value is stable

Some internal breaking changes that are unlikely to affect users are:

- The `ComponentPropField` type is now named `ComponentSchema`
- `FormField`'s are now constrained to prevent storing `undefined`.
They must be a string, number, boolean, null, array of one of these or an object with one of these.
This is required so that they can be represented within a JSON array.
- Within the database, for the `props` object on a component-block node, child fields are now stored as `null` instead of `undefined`.
This is required so that they can be represented within a JSON array.
Component-block nodes that previously had `undefined` instead of `null` for a child field will continue to work though, no data migration is required.
- The `ObjectField` type now has inner fields on a property named `fields` instead of `value`
- The `ConditionalField` type now has two type parameters that look like this:
```ts
type ConditionalField<
DiscriminantField extends FormField<string | boolean, any>,
ConditionalValues extends {
[Key in `${DiscriminantField['defaultValue']}`]: ComponentSchema;
}
> = ...
```
22 changes: 12 additions & 10 deletions docs/components/docs/DocumentEditorDemo.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/** @jsxRuntime classic */
/** @jsx jsx */
import { getInitialPropsValue } from '@keystone-6/fields-document/src/DocumentEditor/component-blocks/initial-values';
import { FormValueContent } from '@keystone-6/fields-document/src/DocumentEditor/component-blocks/form';
import { useKeyDownRef } from '@keystone-6/fields-document/src/DocumentEditor/soft-breaks';
import React, { ReactNode, useContext, useEffect, useMemo, useState } from 'react';
import { Toolbar } from '@keystone-6/fields-document/src/DocumentEditor/Toolbar';
Expand All @@ -19,6 +18,8 @@ import {
} from '@keystone-6/fields-document/component-blocks';
import { Global, jsx } from '@emotion/react';

import { FormValueContentFromPreviewProps } from '@keystone-6/fields-document/src/DocumentEditor/component-blocks/form-from-preview';
import { createGetPreviewProps } from '@keystone-6/fields-document/src/DocumentEditor/component-blocks/preview-props';
import { componentBlocks as componentBlocksInExampleProject } from '../../../examples-staging/basic/admin/fieldViews/Content';
import { initialContent } from '../../lib/initialDocumentDemoContent';
import { Code } from '../primitives/Code';
Expand Down Expand Up @@ -76,7 +77,7 @@ const documentFeaturesProp = fields.object({

type DocumentFeaturesFormValue = Parameters<
InferRenderersForComponentBlocks<
Record<'documentFeatures', ComponentBlock<typeof documentFeaturesProp['value']>>
Record<'documentFeatures', ComponentBlock<typeof documentFeaturesProp['fields']>>
>['documentFeatures']
>[0];

Expand All @@ -86,6 +87,7 @@ const componentBlocks = {
notice: componentBlocksInExampleProject.notice,
hero: componentBlocksInExampleProject.hero,
quote: componentBlocksInExampleProject.quote,
checkboxList: componentBlocksInExampleProject.checkboxList,
};

type DocumentFieldConfig = Parameters<typeof import('@keystone-6/fields-document').document>[0];
Expand Down Expand Up @@ -259,13 +261,14 @@ export function DocumentFeaturesFormAndCode() {
const { formValue, setFormValue } = useContext(DocumentFeaturesContext);
return (
<div>
<FormValueContent
prop={documentFeaturesProp}
forceValidation={false}
path={[]}
stringifiedPropPathToAutoFocus=""
value={formValue}
onChange={setFormValue}
<FormValueContentFromPreviewProps
{...createGetPreviewProps(
documentFeaturesProp,
getNewVal => {
setFormValue(getNewVal(formValue));
},
() => undefined
)(formValue)}
/>
</div>
);
Expand Down Expand Up @@ -305,7 +308,6 @@ export const DocumentEditorDemo = () => {
h5: { fontSize: 'var(--font-xsmall)' },
h6: { fontSize: 'var(--font-xxsmall)' },
'ul, ol': {
paddingLeft: 40,
lineHeight: 1.75,
},
}}
Expand Down
Loading