From 0d13c63d17fb2aee74ab973c290d845da8034f50 Mon Sep 17 00:00:00 2001
From: Daniel Cousens <413395+dcousens@users.noreply.github.com>
Date: Tue, 20 Aug 2024 11:10:15 +1000
Subject: [PATCH] remove Card components
---
docs/content/docs/fields/relationship.md | 19 +-
docs/content/docs/guides/custom-fields.md | 21 -
examples/custom-field/1-text-field/views.tsx | 10 -
examples/custom-field/2-stars-field/views.tsx | 11 -
.../custom-field/3-pair-field-json/views.tsx | 10 -
.../3-pair-field-nested/views.tsx | 10 -
examples/custom-field/3-pair-field/views.tsx | 10 -
.../4-conditional-field/views.tsx | 10 -
examples/usecase-blog/schema.ts | 25 +-
packages/cloudinary/src/views/index.tsx | 12 -
.../admin-ui/id-field-view.tsx | 12 +-
packages/core/src/admin-ui/context.tsx | 3 +-
.../src/fields/types/bigInt/views/index.tsx | 10 -
.../fields/types/calendarDay/views/index.tsx | 10 -
.../src/fields/types/checkbox/views/index.tsx | 11 +-
.../src/fields/types/decimal/views/index.tsx | 10 -
.../src/fields/types/file/views/index.tsx | 12 -
.../src/fields/types/float/views/index.tsx | 10 -
.../src/fields/types/image/views/index.tsx | 18 +-
.../src/fields/types/integer/views/index.tsx | 10 -
.../src/fields/types/json/views/index.tsx | 10 -
.../fields/types/multiselect/views/index.tsx | 13 -
.../src/fields/types/password/views/index.tsx | 10 -
.../src/fields/types/relationship/index.ts | 106 +----
.../relationship/views/cards/InlineCreate.tsx | 130 ------
.../relationship/views/cards/InlineEdit.tsx | 147 ------
.../types/relationship/views/cards/index.tsx | 429 ------------------
.../relationship/views/cards/useItemState.tsx | 144 ------
.../fields/types/relationship/views/index.tsx | 147 +-----
.../src/fields/types/select/views/index.tsx | 21 +-
.../src/fields/types/text/views/index.tsx | 16 +-
.../fields/types/timestamp/views/index.tsx | 10 -
.../src/fields/types/virtual/views/index.tsx | 10 -
packages/core/src/types/admin-meta.ts | 8 -
.../fields-document/src/structure-views.tsx | 5 -
packages/fields-document/src/views.tsx | 11 -
.../label-search-field-validation.test.ts | 96 ----
...d-item-to-relationship-in-hook-cards-ui.ts | 44 --
tests/sandbox/configs/all-the-things.ts | 19 +-
39 files changed, 31 insertions(+), 1589 deletions(-)
delete mode 100644 packages/core/src/fields/types/relationship/views/cards/InlineCreate.tsx
delete mode 100644 packages/core/src/fields/types/relationship/views/cards/InlineEdit.tsx
delete mode 100644 packages/core/src/fields/types/relationship/views/cards/index.tsx
delete mode 100644 packages/core/src/fields/types/relationship/views/cards/useItemState.tsx
delete mode 100644 tests/sandbox/configs/7590-add-item-to-relationship-in-hook-cards-ui.ts
diff --git a/docs/content/docs/fields/relationship.md b/docs/content/docs/fields/relationship.md
index fd85a4a58b9..b82ab605ca9 100644
--- a/docs/content/docs/fields/relationship.md
+++ b/docs/content/docs/fields/relationship.md
@@ -13,21 +13,14 @@ Read our [relationships guide](../guides/relationships) for details on Keystone
- `map`: Changes the column name in the database
- `ui` (default: `{ hideCreate: false, displayMode: 'select' }`): Configures the display mode of the field in the Admin UI.
- `hideCreate` (default: `false`). If `true`, the "Create related item" button is not shown in the item view.
- - `displayMode` (default: `'select'`): Controls the mode used to display the field in the item view. The mode `'select'` displays related items in a select component, while `'cards'` displays the related items in a card layout. Each display mode supports further configuration.
+ - `displayMode` (default: `'select'`): Controls the mode used to display the field in the item view. The mode `'select'` displays related items in a select component
- `ui.displayMode === 'select'` options:
- `labelField`: The field path from the related list to use for item labels in the select. Defaults to the `labelField` configured on the related list.
-- `searchFields`: The fields used by the UI to search for this item, in context of this relationship field. Defaults to `searchFields` configured on the related list.
-- `ui.displayMode === 'cards'` options:
- - `cardFields`: A list of field paths from the related list to render in the card component. Defaults to `'id'` and the `labelField` configured on the related list.
- - `linkToItem` (default `false`): If `true`, the default card component will render as a link to navigate to the related item.
- - `removeMode` (default: `'disconnect'`): Controls whether the `Remove` button is present in the card. If `'disconnect'`, the button will be present. If `'none'`, the button will not be present.
- - `inlineCreate` (default: `null`): If not `null`, an object of the form `{ fields: [...] }`, where `fields` is a list of field paths from the related list should be provided. An inline `Create` button will be included in the cards allowing a new related item to be created based on the configured field paths.
- - `inlineEdit` (default: `null`): If not `null`, an object of the form `{ fields: [...] }`, where `fields` is a list of field paths from the related list should be provided. An `Edit` button will be included in each card, allowing the configured fields to be edited for each related item.
- - `inlineConnect` (default: `false`): If `true`, an inline `Link existing item` button will be present, allowing existing items of the related list to be connected in this field.
-Alternatively this can be an object with the properties:
- - `labelField`: The field path from the related list to use for item labels in select. Defaults to the `labelField` configured on the related list.
- - `searchFields`: The fields used by the UI to search for this item, in context of this relationship field. Defaults to `searchFields` configured on the related list.
-- `ui.displayMode === 'count'` only supports `many` relationships
+ - `searchFields`: The fields used by the UI to search for this item, in context of this relationship field. Defaults to `searchFields` configured on the related list.
+
+- `ui.displayMode === 'count'` options, which only support `many` relationships:
+ - `labelField`: The field path from the related list to use for item labels in select. Defaults to the `labelField` configured on the related list.
+ - `searchFields`: The fields used by the UI to search for this item, in context of this relationship field. Defaults to `searchFields` configured on the related list.
```typescript
import { config, list } from '@keystone-6/core';
diff --git a/docs/content/docs/guides/custom-fields.md b/docs/content/docs/guides/custom-fields.md
index ec8f629917e..faa18086927 100644
--- a/docs/content/docs/guides/custom-fields.md
+++ b/docs/content/docs/guides/custom-fields.md
@@ -187,27 +187,6 @@ export const Cell: CellComponent = ({ item, field, linkTo }) => {
Cell.supportsLinkTo = true;
```
-### CardValue
-
-The `CardValue` export is a React component which is shown on the **item view** in relationship fields with `displayMode: 'cards'` when the related item is not being edited.
-Note it does not allow modifying the value.
-
-```tsx
-// view.tsx
-
-import { FieldContainer, FieldLabel } from '@keystone-ui/fields';
-import { CardValueComponent } from '@keystone-6/core/types';
-
-export const CardValue: CardValueComponent = ({ item, field }) => {
- return (
-
- {field.label}
- {item[field.path]}
-
- );
-};
-```
-
## Related resources
{% related-content %}
diff --git a/examples/custom-field/1-text-field/views.tsx b/examples/custom-field/1-text-field/views.tsx
index 3d0c9a60322..34e6d602b50 100644
--- a/examples/custom-field/1-text-field/views.tsx
+++ b/examples/custom-field/1-text-field/views.tsx
@@ -3,7 +3,6 @@ import { FieldContainer, FieldDescription, FieldLabel, TextInput } from '@keysto
import { CellLink, CellContainer } from '@keystone-6/core/admin-ui/components'
import {
- type CardValueComponent,
type CellComponent,
type FieldController,
type FieldControllerConfig,
@@ -38,15 +37,6 @@ export const Cell: CellComponent = ({ item, field, linkTo }) => {
}
Cell.supportsLinkTo = true
-export const CardValue: CardValueComponent = ({ item, field }) => {
- return (
-
- {field.label}
- {item[field.path]}
-
- )
-}
-
export const controller = (
config: FieldControllerConfig<{}>
): FieldController => {
diff --git a/examples/custom-field/2-stars-field/views.tsx b/examples/custom-field/2-stars-field/views.tsx
index a93dac82d5b..a73da92d9dc 100644
--- a/examples/custom-field/2-stars-field/views.tsx
+++ b/examples/custom-field/2-stars-field/views.tsx
@@ -3,7 +3,6 @@ import { FieldContainer, FieldDescription, FieldLabel } from '@keystone-ui/field
import { CellLink, CellContainer } from '@keystone-6/core/admin-ui/components'
import {
- type CardValueComponent,
type CellComponent,
type FieldController,
type FieldControllerConfig,
@@ -30,16 +29,6 @@ export const Cell: CellComponent = ({ item, field, linkTo }) => {
// their cell component links to the related item so it can't link to the item that the relationship is on
Cell.supportsLinkTo = true
-// this is shown on the item page in relationship fields with `displayMode: 'cards'`
-export const CardValue: CardValueComponent = ({ item, field }) => {
- return (
-
- {field.label}
- {item[field.path]}
-
- )
-}
-
export const controller = (
// the type parameter here needs to align with what is returned from `getAdminMeta`
// in the server-side portion of the field type
diff --git a/examples/custom-field/3-pair-field-json/views.tsx b/examples/custom-field/3-pair-field-json/views.tsx
index 4f464a2fe81..8238cdb9e86 100644
--- a/examples/custom-field/3-pair-field-json/views.tsx
+++ b/examples/custom-field/3-pair-field-json/views.tsx
@@ -3,7 +3,6 @@ import { FieldContainer, FieldDescription, FieldLabel, TextInput } from '@keysto
import { CellLink, CellContainer } from '@keystone-6/core/admin-ui/components'
import {
- type CardValueComponent,
type CellComponent,
type FieldController,
type FieldControllerConfig,
@@ -60,15 +59,6 @@ export const Cell: CellComponent = ({ item, field, linkTo }) => {
}
Cell.supportsLinkTo = true
-export const CardValue: CardValueComponent = ({ item, field }) => {
- return (
-
- {field.label}
- {item[field.path]}
-
- )
-}
-
export const controller = (
config: FieldControllerConfig<{}>
): FieldController<
diff --git a/examples/custom-field/3-pair-field-nested/views.tsx b/examples/custom-field/3-pair-field-nested/views.tsx
index 4f464a2fe81..8238cdb9e86 100644
--- a/examples/custom-field/3-pair-field-nested/views.tsx
+++ b/examples/custom-field/3-pair-field-nested/views.tsx
@@ -3,7 +3,6 @@ import { FieldContainer, FieldDescription, FieldLabel, TextInput } from '@keysto
import { CellLink, CellContainer } from '@keystone-6/core/admin-ui/components'
import {
- type CardValueComponent,
type CellComponent,
type FieldController,
type FieldControllerConfig,
@@ -60,15 +59,6 @@ export const Cell: CellComponent = ({ item, field, linkTo }) => {
}
Cell.supportsLinkTo = true
-export const CardValue: CardValueComponent = ({ item, field }) => {
- return (
-
- {field.label}
- {item[field.path]}
-
- )
-}
-
export const controller = (
config: FieldControllerConfig<{}>
): FieldController<
diff --git a/examples/custom-field/3-pair-field/views.tsx b/examples/custom-field/3-pair-field/views.tsx
index 4ad1ec86f02..000297e5220 100644
--- a/examples/custom-field/3-pair-field/views.tsx
+++ b/examples/custom-field/3-pair-field/views.tsx
@@ -3,7 +3,6 @@ import { FieldContainer, FieldDescription, FieldLabel, TextInput } from '@keysto
import { CellLink, CellContainer } from '@keystone-6/core/admin-ui/components'
import {
- type CardValueComponent,
type CellComponent,
type FieldController,
type FieldControllerConfig,
@@ -38,15 +37,6 @@ export const Cell: CellComponent = ({ item, field, linkTo }) => {
}
Cell.supportsLinkTo = true
-export const CardValue: CardValueComponent = ({ item, field }) => {
- return (
-
- {field.label}
- {item[field.path]}
-
- )
-}
-
export const controller = (
config: FieldControllerConfig<{}>
): FieldController => {
diff --git a/examples/custom-field/4-conditional-field/views.tsx b/examples/custom-field/4-conditional-field/views.tsx
index 0c50f1b5f93..b327658a0dd 100644
--- a/examples/custom-field/4-conditional-field/views.tsx
+++ b/examples/custom-field/4-conditional-field/views.tsx
@@ -3,7 +3,6 @@ import { FieldContainer, FieldDescription, FieldLabel, TextInput } from '@keysto
import { CellLink, CellContainer } from '@keystone-6/core/admin-ui/components'
import {
- type CardValueComponent,
type CellComponent,
type FieldController,
type FieldControllerConfig,
@@ -52,15 +51,6 @@ export const Cell: CellComponent = ({ item, field, linkTo }) => {
}
Cell.supportsLinkTo = true
-export const CardValue: CardValueComponent = ({ item, field }) => {
- return (
-
- {field.label}
- {item[field.path]}
-
- )
-}
-
export const controller = (
config: FieldControllerConfig<{
dependency: {
diff --git a/examples/usecase-blog/schema.ts b/examples/usecase-blog/schema.ts
index 0f278ce9127..53e1e10d607 100644
--- a/examples/usecase-blog/schema.ts
+++ b/examples/usecase-blog/schema.ts
@@ -72,16 +72,6 @@ export const lists = {
author: relationship({
// we could have used 'Author', but then the relationship would only be 1-way
ref: 'Author.posts',
-
- // we customise how this will look in the AdminUI, for fun
- ui: {
- displayMode: 'cards',
- cardFields: ['name', 'email'],
- inlineEdit: { fields: ['name', 'email'] },
- linkToItem: true,
- inlineConnect: true,
- },
-
many: false, // only 1 author for each Post (the default)
}),
@@ -90,15 +80,12 @@ export const lists = {
ref: 'Tag.posts',
many: true, // a Post can have many Tags, not just one
- // we customise how this will look in the AdminUI, for fun
- ui: {
- displayMode: 'cards',
- cardFields: ['name'],
- inlineEdit: { fields: ['name'] },
- linkToItem: true,
- inlineConnect: true,
- inlineCreate: { fields: ['name'] },
- },
+ // TODO: restore after breaking change
+// ui: {
+// inlineCreate: {
+// fields: ['name']
+// }
+// }
}),
},
}),
diff --git a/packages/cloudinary/src/views/index.tsx b/packages/cloudinary/src/views/index.tsx
index 0f2ee54266f..522d5472386 100644
--- a/packages/cloudinary/src/views/index.tsx
+++ b/packages/cloudinary/src/views/index.tsx
@@ -3,12 +3,10 @@
import { jsx } from '@keystone-ui/core'
import {
- type CardValueComponent,
type CellComponent,
type FieldController,
type FieldControllerConfig,
} from '@keystone-6/core/types'
-import { FieldContainer, FieldLabel } from '@keystone-ui/fields'
import { validateImage } from './Field'
export { Field } from './Field'
@@ -35,16 +33,6 @@ export const Cell: CellComponent = ({ item, field }) => {
)
}
-export const CardValue: CardValueComponent = ({ item, field }) => {
- const data = item[field.path]
- return (
-
- {field.label}
- {data && }
-
- )
-}
-
type ImageData = {
id: string
filename: string
diff --git a/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view.tsx b/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view.tsx
index e4bb0c87905..9c1eba2faaf 100644
--- a/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view.tsx
+++ b/packages/core/src/___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view.tsx
@@ -4,9 +4,8 @@
import { TextField } from '@keystar/ui/text-field'
import { jsx } from '@keystone-ui/core'
-import { FieldContainer, FieldLabel, TextInput } from '@keystone-ui/fields'
+import { TextInput } from '@keystone-ui/fields'
import type {
- CardValueComponent,
CellComponent,
FieldController,
FieldControllerConfig,
@@ -24,15 +23,6 @@ export const Cell: CellComponent = ({ item, field, linkTo }) => {
}
Cell.supportsLinkTo = true
-export const CardValue: CardValueComponent = ({ item, field }) => {
- return (
-
- {field.label}
- {item[field.path]}
-
- )
-}
-
export function controller (
config: FieldControllerConfig
): FieldController {
diff --git a/packages/core/src/admin-ui/context.tsx b/packages/core/src/admin-ui/context.tsx
index 906bc71ca9b..745312f7143 100644
--- a/packages/core/src/admin-ui/context.tsx
+++ b/packages/core/src/admin-ui/context.tsx
@@ -65,8 +65,7 @@ function InternalKeystoneProvider ({
const { push: navigate } = useRouter()
const keystarRouter = useMemo(() => ({ navigate }), [navigate])
const adminMeta = useAdminMeta(adminMetaHash, fieldViews)
- const { authenticatedItem, visibleLists, createViewFieldModes, refetch } =
- useLazyMetadata(lazyMetadataQuery)
+ const { authenticatedItem, visibleLists, createViewFieldModes, refetch } = useLazyMetadata(lazyMetadataQuery)
const reinitContext = async () => {
await adminMeta?.refetch?.()
await refetch()
diff --git a/packages/core/src/fields/types/bigInt/views/index.tsx b/packages/core/src/fields/types/bigInt/views/index.tsx
index 9851c81f58a..7434ccf04d5 100644
--- a/packages/core/src/fields/types/bigInt/views/index.tsx
+++ b/packages/core/src/fields/types/bigInt/views/index.tsx
@@ -5,7 +5,6 @@ import { jsx } from '@keystone-ui/core'
import { FieldContainer, FieldDescription, FieldLabel, TextInput } from '@keystone-ui/fields'
import { useState } from 'react'
import {
- type CardValueComponent,
type CellComponent,
type FieldController,
type FieldControllerConfig,
@@ -131,15 +130,6 @@ export const Cell: CellComponent = ({ item, field, linkTo }) => {
}
Cell.supportsLinkTo = true
-export const CardValue: CardValueComponent = ({ item, field }) => {
- return (
-
- {field.label}
- {item[field.path] === null ? '' : item[field.path]}
-
- )
-}
-
function validate (
state: Value,
validation: Validation,
diff --git a/packages/core/src/fields/types/calendarDay/views/index.tsx b/packages/core/src/fields/types/calendarDay/views/index.tsx
index be5fac3f837..bbdef450b32 100644
--- a/packages/core/src/fields/types/calendarDay/views/index.tsx
+++ b/packages/core/src/fields/types/calendarDay/views/index.tsx
@@ -5,7 +5,6 @@ import { useState } from 'react'
import { jsx, Inline, Stack, Text } from '@keystone-ui/core'
import { FieldContainer, FieldLabel, DatePicker, FieldDescription } from '@keystone-ui/fields'
import {
- type CardValueComponent,
type CellComponent,
type FieldController,
type FieldControllerConfig,
@@ -95,15 +94,6 @@ export const Cell: CellComponent = ({ item, field, linkTo }) => {
}
Cell.supportsLinkTo = true
-export const CardValue: CardValueComponent = ({ item, field }) => {
- return (
-
- {field.label}
- {formatOutput(item[field.path])}
-
- )
-}
-
function formatOutput (isoDateString: string | null) {
if (!isoDateString) {
return null
diff --git a/packages/core/src/fields/types/checkbox/views/index.tsx b/packages/core/src/fields/types/checkbox/views/index.tsx
index ea78c06242f..05da4709d44 100644
--- a/packages/core/src/fields/types/checkbox/views/index.tsx
+++ b/packages/core/src/fields/types/checkbox/views/index.tsx
@@ -4,8 +4,8 @@ import { FieldLabel } from '@keystar/ui/field'
import { VStack } from '@keystar/ui/layout'
import { Text } from '@keystar/ui/typography'
+import { jsx, useTheme } from '@keystone-ui/core'
import type {
- CardValueComponent,
CellComponent,
FieldController,
FieldControllerConfig,
@@ -35,15 +35,6 @@ export const Cell: CellComponent = ({ item, field }) => {
)
}
-export const CardValue: CardValueComponent = ({ item, field }) => {
- return (
-
- {field.label}
- {item[field.path] + ''}
-
- )
-}
-
type CheckboxController = FieldController
export const controller = (
diff --git a/packages/core/src/fields/types/decimal/views/index.tsx b/packages/core/src/fields/types/decimal/views/index.tsx
index 85a29c132da..b484ee23f95 100644
--- a/packages/core/src/fields/types/decimal/views/index.tsx
+++ b/packages/core/src/fields/types/decimal/views/index.tsx
@@ -6,7 +6,6 @@ import { FieldContainer, FieldDescription, FieldLabel, TextInput } from '@keysto
import { Decimal } from 'decimal.js'
import { useState } from 'react'
import {
- type CardValueComponent,
type CellComponent,
type FieldController,
type FieldControllerConfig,
@@ -82,15 +81,6 @@ export const Cell: CellComponent = ({ item, field, linkTo }) => {
}
Cell.supportsLinkTo = true
-export const CardValue: CardValueComponent = ({ item, field }) => {
- return (
-
- {field.label}
- {item[field.path]}
-
- )
-}
-
export type DecimalFieldMeta = {
precision: number
scale: number
diff --git a/packages/core/src/fields/types/file/views/index.tsx b/packages/core/src/fields/types/file/views/index.tsx
index c6854fb85ee..5f0a6d6eacb 100644
--- a/packages/core/src/fields/types/file/views/index.tsx
+++ b/packages/core/src/fields/types/file/views/index.tsx
@@ -2,9 +2,7 @@
/** @jsx jsx */
import { jsx } from '@keystone-ui/core'
-import { FieldContainer, FieldLabel } from '@keystone-ui/fields'
import {
- type CardValueComponent,
type CellComponent,
type FieldController,
type FieldControllerConfig,
@@ -32,16 +30,6 @@ export const Cell: CellComponent = ({ item, field }) => {
)
}
-export const CardValue: CardValueComponent = ({ item, field }) => {
- const data = item[field.path]
- return (
-
- {field.label}
- {data && data.filename}
-
- )
-}
-
type FileData = {
src: string
filesize: number
diff --git a/packages/core/src/fields/types/float/views/index.tsx b/packages/core/src/fields/types/float/views/index.tsx
index 0ab2c8c85f1..d9e8ca4ac00 100644
--- a/packages/core/src/fields/types/float/views/index.tsx
+++ b/packages/core/src/fields/types/float/views/index.tsx
@@ -5,7 +5,6 @@ import { jsx } from '@keystone-ui/core'
import { FieldContainer, FieldDescription, FieldLabel, TextInput } from '@keystone-ui/fields'
import { useState } from 'react'
import {
- type CardValueComponent,
type CellComponent,
type FieldController,
type FieldControllerConfig,
@@ -162,15 +161,6 @@ export const Cell: CellComponent = ({ item, field, linkTo }) => {
}
Cell.supportsLinkTo = true
-export const CardValue: CardValueComponent = ({ item, field }) => {
- return (
-
- {field.label}
- {item[field.path]}
-
- )
-}
-
export const controller = (
config: FieldControllerConfig<{ validation: Validation, defaultValue: number | null }>
): FieldController & {
diff --git a/packages/core/src/fields/types/image/views/index.tsx b/packages/core/src/fields/types/image/views/index.tsx
index 1bf5d97061a..cdee1d0a840 100644
--- a/packages/core/src/fields/types/image/views/index.tsx
+++ b/packages/core/src/fields/types/image/views/index.tsx
@@ -2,14 +2,12 @@
/** @jsx jsx */
import { jsx } from '@keystone-ui/core'
-import { FieldContainer, FieldLabel } from '@keystone-ui/fields'
import {
- type CardValueComponent,
type CellComponent,
type FieldController,
type FieldControllerConfig,
} from '../../../../types'
-import { validateImage, ImageWrapper } from './Field'
+import { validateImage } from './Field'
export { Field } from './Field'
@@ -31,20 +29,6 @@ export const Cell: CellComponent = ({ item, field }) => {
)
}
-export const CardValue: CardValueComponent = ({ item, field }) => {
- const data = item[field.path]
- return (
-
- {field.label}
- {data && (
-
-
-
- )}
-
- )
-}
-
type ImageData = {
src: string
height: number
diff --git a/packages/core/src/fields/types/integer/views/index.tsx b/packages/core/src/fields/types/integer/views/index.tsx
index d543940e066..75d83e06cff 100644
--- a/packages/core/src/fields/types/integer/views/index.tsx
+++ b/packages/core/src/fields/types/integer/views/index.tsx
@@ -5,7 +5,6 @@ import { jsx } from '@keystone-ui/core'
import { FieldContainer, FieldDescription, FieldLabel, TextInput } from '@keystone-ui/fields'
import { useState } from 'react'
import {
- type CardValueComponent,
type CellComponent,
type FieldController,
type FieldControllerConfig,
@@ -119,15 +118,6 @@ export const Cell: CellComponent = ({ item, field, linkTo }) => {
}
Cell.supportsLinkTo = true
-export const CardValue: CardValueComponent = ({ item, field }) => {
- return (
-
- {field.label}
- {item[field.path] === null ? '' : item[field.path]}
-
- )
-}
-
function validate (
value: Value,
validation: Validation,
diff --git a/packages/core/src/fields/types/json/views/index.tsx b/packages/core/src/fields/types/json/views/index.tsx
index 457afe9ba0a..137eb118b69 100644
--- a/packages/core/src/fields/types/json/views/index.tsx
+++ b/packages/core/src/fields/types/json/views/index.tsx
@@ -4,7 +4,6 @@
import { jsx, Stack, Text } from '@keystone-ui/core'
import { FieldContainer, FieldDescription, FieldLabel, TextArea } from '@keystone-ui/fields'
import {
- type CardValueComponent,
type CellComponent,
type FieldController,
type FieldControllerConfig,
@@ -62,15 +61,6 @@ export const Cell: CellComponent = ({ item, field, linkTo }) => {
}
Cell.supportsLinkTo = true
-export const CardValue: CardValueComponent = ({ item, field }) => {
- return (
-
- {field.label}
- {item[field.path]}
-
- )
-}
-
type Config = FieldControllerConfig<{ defaultValue: JSONValue }>
export const controller = (config: Config): FieldController => {
diff --git a/packages/core/src/fields/types/multiselect/views/index.tsx b/packages/core/src/fields/types/multiselect/views/index.tsx
index 163fd001efc..62ac18c6c4a 100644
--- a/packages/core/src/fields/types/multiselect/views/index.tsx
+++ b/packages/core/src/fields/types/multiselect/views/index.tsx
@@ -4,7 +4,6 @@ import { Fragment } from 'react'
import { jsx } from '@keystone-ui/core'
import { FieldContainer, FieldDescription, FieldLabel, MultiSelect } from '@keystone-ui/fields'
import {
- type CardValueComponent,
type CellComponent,
type FieldController,
type FieldControllerConfig,
@@ -43,18 +42,6 @@ export const Cell: CellComponent = ({ item, field, linkTo })
}
Cell.supportsLinkTo = true
-export const CardValue: CardValueComponent = ({ item, field }) => {
- const value: readonly string[] | readonly number[] = item[field.path] ?? []
- const label = value.map(value => field.valuesToOptionsWithStringValues[value].label).join(', ')
-
- return (
-
- {field.label}
- {label}
-
- )
-}
-
export type AdminMultiSelectFieldMeta = {
options: readonly { label: string, value: string | number }[]
type: 'string' | 'integer' | 'enum'
diff --git a/packages/core/src/fields/types/password/views/index.tsx b/packages/core/src/fields/types/password/views/index.tsx
index cfde4fed694..ed5e1065183 100644
--- a/packages/core/src/fields/types/password/views/index.tsx
+++ b/packages/core/src/fields/types/password/views/index.tsx
@@ -13,7 +13,6 @@ import { SegmentedControl } from '@keystone-ui/segmented-control'
// @ts-expect-error
import dumbPasswords from 'dumb-passwords'
import {
- type CardValueComponent,
type CellComponent,
type FieldController,
type FieldControllerConfig,
@@ -175,15 +174,6 @@ export const Cell: CellComponent = ({ item, field }) => {
return {isSetText(item[field.path]?.isSet)}
}
-export const CardValue: CardValueComponent = ({ item, field }) => {
- return (
-
- {field.label}
- {isSetText(item[field.path]?.isSet)}
-
- )
-}
-
type Validation = {
isRequired: boolean
rejectCommon: boolean
diff --git a/packages/core/src/fields/types/relationship/index.ts b/packages/core/src/fields/types/relationship/index.ts
index b37b6a388ac..37b4e313175 100644
--- a/packages/core/src/fields/types/relationship/index.ts
+++ b/packages/core/src/fields/types/relationship/index.ts
@@ -22,34 +22,6 @@ type SelectDisplayConfig = {
}
}
-type CardsDisplayConfig = {
- ui?: {
- // Sets the relationship to display as a list of Cards
- displayMode: 'cards'
- /* The set of fields to render in the default Card component **/
- cardFields: readonly string[]
- /** Causes the default Card component to render as a link to navigate to the related item */
- linkToItem?: boolean
- /** Determines whether removing a related item in the UI will delete or unlink it */
- removeMode?: 'disconnect' | 'none' // | 'delete'
- /** Configures inline create mode for cards (alternative to opening the create modal) */
- inlineCreate?: { fields: readonly string[] }
- /** Configures inline edit mode for cards */
- inlineEdit?: { fields: readonly string[] }
- /** Configures whether a select to add existing items should be shown or not */
- inlineConnect?:
- | boolean
- | {
- /**
- * The path of the field to use from the related list for item labels in the inline connect
- * Defaults to the labelField configured on the related list.
- */
- labelField: string
- searchFields?: string[]
- }
- }
-}
-
type CountDisplayConfig = {
many: true
ui?: {
@@ -86,7 +58,7 @@ export type RelationshipFieldConfig =
hideCreate?: boolean
}
} & (OneDbConfig | ManyDbConfig) &
- (SelectDisplayConfig | CardsDisplayConfig | CountDisplayConfig)
+ (SelectDisplayConfig | CountDisplayConfig)
export const relationship =
({
@@ -107,37 +79,12 @@ export const relationship =
views: '@keystone-6/core/fields/types/relationship/views',
getAdminMeta: (): Parameters[0]['fieldMeta'] => {
const adminMetaRoot = getAdminMetaForRelationshipField()
- const localListMeta = adminMetaRoot.listsByKey[listKey]
const foreignListMeta = adminMetaRoot.listsByKey[foreignListKey]
if (!foreignListMeta) {
throw new Error(`The ref [${ref}] on relationship [${listKey}.${fieldKey}] is invalid`)
}
- if (config.ui?.displayMode === 'cards') {
- // we're checking whether the field which will be in the admin meta at the time that getAdminMeta is called.
- // in newer versions of keystone, it will be there and it will not be there for older versions of keystone.
- // this is so that relationship fields doesn't break in confusing ways
- // if people are using a slightly older version of keystone
- const currentField = localListMeta.fields.find(x => x.key === fieldKey)
- if (currentField) {
- const allForeignFields = new Set(foreignListMeta.fields.map(x => x.key))
- for (const [configOption, foreignFields] of [
- ['ui.cardFields', config.ui.cardFields],
- ['ui.inlineCreate.fields', config.ui.inlineCreate?.fields ?? []],
- ['ui.inlineEdit.fields', config.ui.inlineEdit?.fields ?? []],
- ] as const) {
- for (const foreignField of foreignFields) {
- if (!allForeignFields.has(foreignField)) {
- throw new Error(
- `The ${configOption} option on the relationship field at ${listKey}.${fieldKey} includes the "${foreignField}" field but that field does not exist on the "${foreignListKey}" list`
- )
- }
- }
- }
- }
- }
-
const hideCreate = config.ui?.hideCreate ?? false
const refLabelField: typeof foreignFieldKey = foreignListMeta.labelField
const refSearchFields: (typeof foreignFieldKey)[] = foreignListMeta.fields
@@ -156,57 +103,6 @@ export const relationship =
}
}
- if (config.ui?.displayMode === 'cards') {
- // prefer the local definition to the foreign list, if provided
- const inlineConnectConfig =
- typeof config.ui.inlineConnect === 'object'
- ? {
- refLabelField: config.ui.inlineConnect.labelField ?? refLabelField,
- refSearchFields: config.ui.inlineConnect?.searchFields ?? refSearchFields,
- }
- : {
- refLabelField,
- refSearchFields,
- }
-
- if (!(inlineConnectConfig.refLabelField in foreignListMeta.fieldsByKey)) {
- throw new Error(
- `The ui.inlineConnect.labelField option for field '${listKey}.${fieldKey}' uses '${inlineConnectConfig.refLabelField}' but that field doesn't exist.`
- )
- }
-
- for (const searchFieldKey of inlineConnectConfig.refSearchFields) {
- if (!(searchFieldKey in foreignListMeta.fieldsByKey)) {
- throw new Error(
- `The ui.inlineConnect.searchFields option for relationship field '${listKey}.${fieldKey}' includes '${searchFieldKey}' but that field doesn't exist.`
- )
- }
-
- const field = foreignListMeta.fieldsByKey[searchFieldKey]
- if (field.search) continue
-
- throw new Error(
- `The ui.searchFields option for field '${listKey}.${fieldKey}' includes '${searchFieldKey}' but that field doesn't have a contains filter that accepts a GraphQL String`
- )
- }
-
- return {
- refFieldKey: foreignFieldKey,
- refListKey: foreignListKey,
- many,
- hideCreate,
- displayMode: 'cards',
- cardFields: config.ui.cardFields,
- linkToItem: config.ui.linkToItem ?? false,
- removeMode: config.ui.removeMode ?? 'disconnect',
- inlineCreate: config.ui.inlineCreate ?? null,
- inlineEdit: config.ui.inlineEdit ?? null,
- inlineConnect: config.ui.inlineConnect ? true : false,
-
- ...inlineConnectConfig,
- }
- }
-
// prefer the local definition to the foreign list, if provided
const specificRefLabelField = config.ui?.labelField || refLabelField
const specificRefSearchFields = config.ui?.searchFields || refSearchFields
diff --git a/packages/core/src/fields/types/relationship/views/cards/InlineCreate.tsx b/packages/core/src/fields/types/relationship/views/cards/InlineCreate.tsx
deleted file mode 100644
index c38a889a23e..00000000000
--- a/packages/core/src/fields/types/relationship/views/cards/InlineCreate.tsx
+++ /dev/null
@@ -1,130 +0,0 @@
-/** @jsxRuntime classic */
-/** @jsx jsx */
-
-import { useState } from 'react'
-import { jsx, Stack } from '@keystone-ui/core'
-import isDeepEqual from 'fast-deep-equal'
-import { useToasts } from '@keystone-ui/toast'
-import { Button } from '@keystone-ui/button'
-import { type ListMeta } from '../../../../../types'
-import {
- type ItemData,
- makeDataGetter,
- type DataGetter,
- type Value,
- useInvalidFields,
- serializeValueToObjByFieldKey,
- Fields,
-} from '../../../../../admin-ui/utils'
-import { gql, useMutation } from '../../../../../admin-ui/apollo'
-import { GraphQLErrorNotice } from '../../../../../admin-ui/components'
-import { useFieldsObj } from './useItemState'
-
-export function InlineCreate ({
- list,
- onCancel,
- onCreate,
- fields: fieldPaths,
- selectedFields,
-}: {
- list: ListMeta
- selectedFields: string
- fields: readonly string[]
- onCancel: () => void
- onCreate: (itemGetter: DataGetter) => void
-}) {
- const toasts = useToasts()
- const fields = useFieldsObj(list, fieldPaths)
-
- const [createItem, { loading, error }] = useMutation(
- gql`mutation($data: ${list.gqlNames.createInputName}!) {
- item: ${list.gqlNames.createMutationName}(data: $data) {
- ${selectedFields}
- }
- }`
- )
-
- const [value, setValue] = useState(() => {
- const value: Value = {}
- Object.keys(fields).forEach(fieldPath => {
- value[fieldPath] = { kind: 'value', value: fields[fieldPath].controller.defaultValue }
- })
- return value
- })
-
- const invalidFields = useInvalidFields(fields, value)
- const [forceValidation, setForceValidation] = useState(false)
-
- const onSubmit = () => {
- const newForceValidation = invalidFields.size !== 0
- setForceValidation(newForceValidation)
-
- if (newForceValidation) return
- const data: Record = {}
- const allSerializedValues = serializeValueToObjByFieldKey(fields, value)
- Object.keys(allSerializedValues).forEach(fieldPath => {
- const { controller } = fields[fieldPath]
- const serialized = allSerializedValues[fieldPath]
- if (!isDeepEqual(serialized, controller.serialize(controller.defaultValue))) {
- Object.assign(data, serialized)
- }
- })
-
- createItem({
- variables: {
- data,
- },
- })
- .then(({ data, errors }) => {
- // we're checking for path.length === 1 because errors with a path larger than 1 will be field level errors
- // which are handled seperately and do not indicate a failure to update the item
- const error = errors?.find(x => x.path?.length === 1)
- if (error) {
- toasts.addToast({
- title: 'Failed to create item',
- tone: 'negative',
- message: error.message,
- })
- } else {
- toasts.addToast({
- title: data.item[list.labelField] || data.item.id,
- tone: 'positive',
- message: 'Saved successfully',
- })
- onCreate(makeDataGetter(data, errors).get('item'))
- }
- })
- .catch(err => {
- toasts.addToast({
- title: 'Failed to update item',
- tone: 'negative',
- message: err.message,
- })
- })
- }
-
- return (
-
-
- {error && (
-
- )}
-
-
-
-
-
-
-
- )
-}
diff --git a/packages/core/src/fields/types/relationship/views/cards/InlineEdit.tsx b/packages/core/src/fields/types/relationship/views/cards/InlineEdit.tsx
deleted file mode 100644
index 4857dd930dd..00000000000
--- a/packages/core/src/fields/types/relationship/views/cards/InlineEdit.tsx
+++ /dev/null
@@ -1,147 +0,0 @@
-/** @jsxRuntime classic */
-/** @jsx jsx */
-
-import { Button } from '@keystone-ui/button'
-import { jsx, Stack } from '@keystone-ui/core'
-import { useToasts } from '@keystone-ui/toast'
-import { useCallback, useState } from 'react'
-import { type ListMeta } from '../../../../../types'
-import {
- deserializeValue,
- type ItemData,
- useInvalidFields,
- Fields,
- useChangedFieldsAndDataForUpdate,
- makeDataGetter,
- type DataGetter,
-} from '../../../../../admin-ui/utils'
-import { gql, useMutation } from '../../../../../admin-ui/apollo'
-import { GraphQLErrorNotice } from '../../../../../admin-ui/components'
-import { useFieldsObj } from './useItemState'
-
-export function InlineEdit ({
- fields,
- list,
- selectedFields,
- itemGetter,
- onCancel,
- onSave,
-}: {
- fields: readonly string[]
- list: ListMeta
- selectedFields: string
- itemGetter: DataGetter
- onCancel: () => void
- onSave: (newItemGetter: DataGetter) => void
-}) {
- const fieldsObj = useFieldsObj(list, fields)
-
- const [update, { loading, error }] = useMutation(
- gql`mutation ($data: ${list.gqlNames.updateInputName}!, $id: ID!) {
- item: ${list.gqlNames.updateMutationName}(where: { id: $id }, data: $data) {
- ${selectedFields}
- }
- }`,
- { errorPolicy: 'all' }
- )
-
- const [state, setValue] = useState(() => {
- const value = deserializeValue(fieldsObj, itemGetter)
- return { value, item: itemGetter.data }
- })
-
- if (state.item !== itemGetter.data && itemGetter.errors?.every(x => x.path?.length !== 1)) {
- const value = deserializeValue(fieldsObj, itemGetter)
- setValue({ value, item: itemGetter.data })
- }
-
- const { changedFields, dataForUpdate } = useChangedFieldsAndDataForUpdate(
- fieldsObj,
- itemGetter,
- state.value
- )
-
- const invalidFields = useInvalidFields(fieldsObj, state.value)
-
- const [forceValidation, setForceValidation] = useState(false)
- const toasts = useToasts()
-
- return (
-
- )
-}
diff --git a/packages/core/src/fields/types/relationship/views/cards/index.tsx b/packages/core/src/fields/types/relationship/views/cards/index.tsx
deleted file mode 100644
index eda8be6ebc2..00000000000
--- a/packages/core/src/fields/types/relationship/views/cards/index.tsx
+++ /dev/null
@@ -1,429 +0,0 @@
-/** @jsxRuntime classic */
-/** @jsx jsx */
-
-import Link from 'next/link'
-import { type ReactNode, useEffect, useRef, useState } from 'react'
-
-import {
- Box,
- type BoxProps,
- Stack,
- Text,
- jsx,
- useTheme,
- forwardRefWithAs,
- VisuallyHidden,
-} from '@keystone-ui/core'
-import { FieldContainer, FieldLabel } from '@keystone-ui/fields'
-import { Button } from '@keystone-ui/button'
-import { Tooltip } from '@keystone-ui/tooltip'
-import { LoadingDots } from '@keystone-ui/loading'
-
-import { type FieldProps, type ListMeta } from '../../../../../types'
-import {
- getRootGraphQLFieldsFromFieldController,
- makeDataGetter,
-} from '../../../../../admin-ui/utils'
-import { gql, useApolloClient } from '../../../../../admin-ui/apollo'
-import { type controller } from '../index'
-import { RelationshipSelect } from '../RelationshipSelect'
-import { useItemState } from './useItemState'
-import { InlineEdit } from './InlineEdit'
-import { InlineCreate } from './InlineCreate'
-
-type CardContainerProps = {
- children: ReactNode
- mode: 'view' | 'create' | 'edit'
-} & BoxProps
-const CardContainer = forwardRefWithAs(({ mode = 'view', ...props }: CardContainerProps, ref) => {
- const { tones } = useTheme()
-
- const tone = tones[mode === 'edit' ? 'active' : mode === 'create' ? 'positive' : 'passive']
-
- return (
-
- )
-})
-
-export function Cards ({
- localList,
- field,
- foreignList,
- id,
- value,
- onChange,
- forceValidation,
-}: {
- foreignList: ListMeta
- localList: ListMeta
- id: string | null
- value: { kind: 'cards-view' }
-} & FieldProps) {
- const { displayOptions } = value
- let selectedFields = [
- ...new Set([...displayOptions.cardFields, ...(displayOptions.inlineEdit?.fields || [])]),
- ]
- .map(fieldPath => {
- return foreignList.fields[fieldPath].controller.graphqlSelection
- })
- .join('\n')
- if (!displayOptions.cardFields.includes('id')) {
- selectedFields += '\nid'
- }
- if (
- !displayOptions.cardFields.includes(foreignList.labelField) &&
- foreignList.labelField !== 'id'
- ) {
- selectedFields += `\n${foreignList.labelField}`
- }
-
- const {
- items,
- setItems,
- state: itemsState,
- } = useItemState({
- selectedFields,
- localList,
- id,
- field,
- })
-
- const client = useApolloClient()
-
- const [isLoadingLazyItems, setIsLoadingLazyItems] = useState(false)
- const [showConnectItems, setShowConnectItems] = useState(false)
- const [hideConnectItemsLabel, setHideConnectItemsLabel] = useState<'Cancel' | 'Done'>('Cancel')
- const editRef = useRef(null)
-
- const isMountedRef = useRef(false)
- useEffect(() => {
- isMountedRef.current = true
- return () => {
- isMountedRef.current = false
- }
- })
-
- useEffect(() => {
- if (value.itemsBeingEdited) {
- editRef?.current?.focus()
- }
- }, [value])
-
- if (itemsState.kind === 'loading') {
- return (
-
-
-
- )
- }
- if (itemsState.kind === 'error') {
- return {itemsState.message}
- }
-
- const currentIdsArrayWithFetchedItems = [...value.currentIds]
- .map(id => ({ itemGetter: items[id], id }))
- .filter(x => x.itemGetter)
-
- return (
-
- {currentIdsArrayWithFetchedItems.length !== 0 && (
-
- {currentIdsArrayWithFetchedItems.map(({ id, itemGetter }, index) => {
- const isEditMode = !!(onChange !== undefined) && value.itemsBeingEdited.has(id)
- return (
-
- {`${field.label} ${index + 1} ${
- isEditMode ? 'edit' : 'view'
- } mode`}
- {isEditMode ? (
- {
- setItems({
- ...items,
- [id]: newItemGetter,
- })
- const itemsBeingEdited = new Set(value.itemsBeingEdited)
- itemsBeingEdited.delete(id)
- onChange!({
- ...value,
- itemsBeingEdited,
- })
- }}
- selectedFields={selectedFields}
- itemGetter={itemGetter}
- onCancel={() => {
- const itemsBeingEdited = new Set(value.itemsBeingEdited)
- itemsBeingEdited.delete(id)
- onChange!({
- ...value,
- itemsBeingEdited,
- })
- }}
- />
- ) : (
-
- {displayOptions.cardFields.map(fieldPath => {
- const field = foreignList.fields[fieldPath]
- const itemForField: Record = {}
- for (const graphqlField of getRootGraphQLFieldsFromFieldController(
- field.controller
- )) {
- const fieldGetter = itemGetter.get(graphqlField)
- if (fieldGetter.errors) {
- const errorMessage = fieldGetter.errors[0].message
- return (
-
- {field.label}
- {errorMessage}
-
- )
- }
- itemForField[graphqlField] = fieldGetter.data
- }
- return (
-
- )
- })}
-
- {displayOptions.inlineEdit && onChange !== undefined && (
-
- )}
- {displayOptions.removeMode === 'disconnect' && onChange !== undefined && (
-
- {props => (
-
- )}
-
- )}
- {displayOptions.linkToItem && (
-
- )}
-
-
- )}
-
- )
- })}
-
- )}
- {onChange === undefined ? null : displayOptions.inlineConnect && showConnectItems ? (
-
-
- {
- if (!value.currentIds.has(item.id)) {
- itemsToFetchAndConnect.push(item.id)
- }
- })
- if (itemsToFetchAndConnect.length) {
- try {
- const { data, errors } = await client.query({
- query: gql`query ($ids: [ID!]!) {
- items: ${foreignList.gqlNames.listQueryName}(where: { id: { in: $ids }}) {
- ${selectedFields}
- }
- }`,
- variables: { ids: itemsToFetchAndConnect },
- })
- if (isMountedRef.current) {
- const dataGetters = makeDataGetter(data, errors)
- const itemsDataGetter = dataGetters.get('items')
- let newItems = { ...items }
- let newCurrentIds = field.many
- ? new Set(value.currentIds)
- : new Set()
- if (Array.isArray(itemsDataGetter.data)) {
- itemsDataGetter.data.forEach((item, i) => {
- if (item?.id != null) {
- newCurrentIds.add(item.id)
- newItems[item.id] = itemsDataGetter.get(i)
- }
- })
- }
- if (newCurrentIds.size) {
- setItems(newItems)
- onChange({
- ...value,
- currentIds: newCurrentIds,
- })
- setHideConnectItemsLabel('Done')
- }
- }
- } finally {
- if (isMountedRef.current) {
- setIsLoadingLazyItems(false)
- }
- }
- }
- },
- value: (() => {
- let options: { label: string, id: string }[] = []
- Object.keys(items).forEach(id => {
- if (value.currentIds.has(id)) {
- options.push({ id, label: id })
- }
- })
- return options
- })(),
- }}
- />
-
-
-
- ) : value.itemBeingCreated ? (
-
- {
- onChange({ ...value, itemBeingCreated: false })
- }}
- onCreate={itemGetter => {
- const id = itemGetter.data.id
- setItems({ ...items, [id]: itemGetter })
- onChange({
- ...value,
- itemBeingCreated: false,
- currentIds: field.many ? new Set([...value.currentIds, id]) : new Set([id]),
- })
- }}
- />
-
- ) : displayOptions.inlineCreate || displayOptions.inlineConnect ? (
-
-
- {displayOptions.inlineCreate && (
-
- )}
- {displayOptions.inlineConnect && (
-
- )}
-
-
- ) : null}
- {/* TODO: this may not be visible to the user when they invoke the save action. Maybe scroll to it? */}
- {forceValidation && (
-
- You must finish creating and editing any related {foreignList.label.toLowerCase()} before
- saving the {localList.singular.toLowerCase()}
-
- )}
-
- )
-}
diff --git a/packages/core/src/fields/types/relationship/views/cards/useItemState.tsx b/packages/core/src/fields/types/relationship/views/cards/useItemState.tsx
deleted file mode 100644
index 27a1dc9e1d6..00000000000
--- a/packages/core/src/fields/types/relationship/views/cards/useItemState.tsx
+++ /dev/null
@@ -1,144 +0,0 @@
-import { useCallback, useMemo, useState } from 'react'
-import { type FieldMeta, type ListMeta } from '../../../../../types'
-import { type DataGetter, makeDataGetter } from '../../../../../admin-ui/utils'
-import { gql, useQuery } from '../../../../../admin-ui/apollo'
-import { type controller } from '../index'
-
-type ItemsState =
- | { kind: 'loading' }
- | { kind: 'error', message: string }
- | { kind: 'loaded' }
-
-type Items = Record>
-
-export function useItemState ({
- selectedFields,
- localList,
- id,
- field,
-}: {
- selectedFields: string
- localList: ListMeta
- field: ReturnType
- id: string | null
-}) {
- const { data, error, loading } = useQuery(
- gql`query($id: ID!) {
- item: ${localList.gqlNames.itemQueryName}(where: {id: $id}) {
- id
- relationship: ${field.path} {
- ${selectedFields}
- }
- }
-}`,
- { variables: { id }, errorPolicy: 'all', skip: id === null }
- )
- const { itemsArrFromData, relationshipGetter } = useMemo(() => {
- const dataGetter = makeDataGetter(data, error?.graphQLErrors)
- const relationshipGetter = dataGetter.get('item').get('relationship')
- const isMany = Array.isArray(relationshipGetter.data)
- const itemsArrFromData: DataGetter<{ id: string, [key: string]: any }>[] = (
- isMany
- ? relationshipGetter.data.map((_: any, i: number) => relationshipGetter.get(i))
- : [relationshipGetter]
- ).filter((x: DataGetter) => x.data?.id != null)
- return { relationshipGetter, itemsArrFromData }
- }, [data, error])
- let [{ items, itemsArrFromData: itemsArrFromDataState }, setItemsState] = useState<{
- itemsArrFromData: DataGetter[]
- items: Record<
- string,
- {
- current: DataGetter<{ id: string, [key: string]: any }>
- fromInitialQuery: DataGetter<{ id: string, [key: string]: any }> | undefined
- }
- >
- }>({ itemsArrFromData: [], items: {} })
-
- if (itemsArrFromDataState !== itemsArrFromData) {
- let newItems: Record<
- string,
- {
- current: DataGetter<{ id: string, [key: string]: any }>
- fromInitialQuery: DataGetter<{ id: string, [key: string]: any }> | undefined
- }
- > = {}
-
- itemsArrFromData.forEach(item => {
- const initialItemInState = items[item.data.id]?.fromInitialQuery
- if (
- ((items[item.data.id] && initialItemInState) || !items[item.data.id]) &&
- (!initialItemInState ||
- item.data !== initialItemInState.data ||
- item.errors?.length !== initialItemInState.errors?.length ||
- (item.errors || []).some((err, i) => err !== initialItemInState.errors?.[i]))
- ) {
- newItems[item.data.id] = { current: item, fromInitialQuery: item }
- } else {
- newItems[item.data.id] = items[item.data.id]
- }
- })
- items = newItems
- setItemsState({
- items: newItems,
- itemsArrFromData,
- })
- }
-
- return {
- items: useMemo(() => {
- const itemsToReturn: Items = {}
- Object.keys(items).forEach(id => {
- itemsToReturn[id] = items[id].current
- })
- return itemsToReturn
- }, [items]),
- setItems: useCallback(
- (items: Items) => {
- setItemsState(state => {
- let itemsForState: (typeof state)['items'] = {}
- Object.keys(items).forEach(id => {
- if (items[id] === state.items[id]?.current) {
- itemsForState[id] = state.items[id]
- } else {
- itemsForState[id] = {
- current: items[id],
- fromInitialQuery: state.items[id]?.fromInitialQuery,
- }
- }
- })
- return {
- itemsArrFromData: state.itemsArrFromData,
- items: itemsForState,
- }
- })
- },
- [setItemsState]
- ),
- state: ((): ItemsState => {
- if (id === null) {
- return { kind: 'loaded' }
- }
- if (loading) {
- return { kind: 'loading' }
- }
- if (error?.networkError) {
- return { kind: 'error', message: error.networkError.message }
- }
- if (field.many && !relationshipGetter.data) {
- return { kind: 'error', message: relationshipGetter.errors?.[0].message || '' }
- }
- return { kind: 'loaded' }
- })(),
- }
-}
-
-export function useFieldsObj (list: ListMeta, fields: readonly string[] | undefined) {
- return useMemo(() => {
- const editFields: Record = {}
- fields?.forEach(fieldPath => {
- editFields[fieldPath] = list.fields[fieldPath]
- })
- return editFields
- }, [fields, list.fields])
-}
diff --git a/packages/core/src/fields/types/relationship/views/index.tsx b/packages/core/src/fields/types/relationship/views/index.tsx
index bd41245c728..efdd4a19faf 100644
--- a/packages/core/src/fields/types/relationship/views/index.tsx
+++ b/packages/core/src/fields/types/relationship/views/index.tsx
@@ -9,7 +9,6 @@ import { jsx, Stack, useTheme } from '@keystone-ui/core'
import { FieldContainer, FieldDescription, FieldLabel, FieldLegend } from '@keystone-ui/fields'
import { DrawerController } from '@keystone-ui/modals'
import {
- type CardValueComponent,
type CellComponent,
type FieldController,
type FieldControllerConfig,
@@ -20,7 +19,6 @@ import { useKeystone, useList } from '../../../../admin-ui/context'
import { gql, useQuery } from '../../../../admin-ui/apollo'
import { CellContainer, CreateItemDrawer } from '../../../../admin-ui/components'
-import { Cards } from './cards'
import { RelationshipSelect } from './RelationshipSelect'
function LinkToRelatedItems ({
@@ -84,25 +82,6 @@ export const Field = ({
const localList = useList(field.listKey)
const [isDrawerOpen, setIsDrawerOpen] = useState(false)
- if (value.kind === 'cards-view') {
- return (
-
- {field.label}
- {field.description}
-
-
- )
- }
-
if (value.kind === 'count') {
return (
@@ -283,26 +262,6 @@ export const Cell: CellComponent = ({ field, item }) => {
)
}
-export const CardValue: CardValueComponent = ({ field, item }) => {
- const list = useList(field.refListKey)
- const data = item[field.path]
- return (
-
- {field.label}
- {(Array.isArray(data) ? data : [data])
- .filter(item => item)
- .map((item, index) => (
-
- {!!index ? ', ' : ''}
-
- {item.label || item.id}
-
-
- ))}
-
- )
-}
-
type SingleRelationshipValue = {
kind: 'one'
id: null | string
@@ -315,34 +274,17 @@ type ManyRelationshipValue = {
initialValue: { label: string, id: string }[]
value: { label: string, id: string }[]
}
-type CardsRelationshipValue = {
- kind: 'cards-view'
- id: null | string
- itemsBeingEdited: ReadonlySet
- itemBeingCreated: boolean
- initialIds: ReadonlySet
- currentIds: ReadonlySet
- displayOptions: CardsDisplayModeOptions
-}
type CountRelationshipValue = {
kind: 'count'
id: null | string
count: number
}
-type CardsDisplayModeOptions = {
- cardFields: readonly string[]
- linkToItem: boolean
- removeMode: 'disconnect' | 'none'
- inlineCreate: { fields: readonly string[] } | null
- inlineEdit: { fields: readonly string[] } | null
- inlineConnect: boolean
-}
type RelationshipController = FieldController<
- ManyRelationshipValue | SingleRelationshipValue | CardsRelationshipValue | CountRelationshipValue,
+ ManyRelationshipValue | SingleRelationshipValue | CountRelationshipValue,
string
> & {
- display: 'count' | 'cards-or-select'
+ display: 'select' | 'count'
listKey: string
refListKey: string
refFieldKey?: string
@@ -365,33 +307,12 @@ export function controller (
| {
displayMode: 'select'
}
- | {
- displayMode: 'cards'
- cardFields: readonly string[]
- linkToItem: boolean
- removeMode: 'disconnect' | 'none'
- inlineCreate: { fields: readonly string[] } | null
- inlineEdit: { fields: readonly string[] } | null
- inlineConnect: boolean
- }
| {
displayMode: 'count'
}
)
>
): RelationshipController {
- const cardsDisplayOptions =
- config.fieldMeta.displayMode === 'cards'
- ? {
- cardFields: config.fieldMeta.cardFields,
- inlineCreate: config.fieldMeta.inlineCreate,
- inlineEdit: config.fieldMeta.inlineEdit,
- linkToItem: config.fieldMeta.linkToItem,
- removeMode: config.fieldMeta.removeMode,
- inlineConnect: config.fieldMeta.inlineConnect,
- }
- : undefined
-
const refLabelField = config.fieldMeta.refLabelField
const refSearchFields = config.fieldMeta.refSearchFields
@@ -402,7 +323,7 @@ export function controller (
path: config.path,
label: config.label,
description: config.description,
- display: config.fieldMeta.displayMode === 'count' ? 'count' : 'cards-or-select',
+ display: config.fieldMeta.displayMode,
refLabelField,
refSearchFields,
refListKey: config.fieldMeta.refListKey,
@@ -419,17 +340,7 @@ export function controller (
// because our other UIs don't handle relationships with a large number of items well
// but that's not a problem here since we're creating a new item so we might as well them a better UI
defaultValue:
- cardsDisplayOptions !== undefined
- ? {
- kind: 'cards-view',
- currentIds: new Set(),
- id: null,
- initialIds: new Set(),
- itemBeingCreated: false,
- itemsBeingEdited: new Set(),
- displayOptions: cardsDisplayOptions,
- }
- : config.fieldMeta.many
+ config.fieldMeta.many
? {
id: null,
kind: 'many',
@@ -441,25 +352,6 @@ export function controller (
if (config.fieldMeta.displayMode === 'count') {
return { id: data.id, kind: 'count', count: data[`${config.path}Count`] ?? 0 }
}
- if (cardsDisplayOptions !== undefined) {
- const initialIds = new Set(
- (Array.isArray(data[config.path])
- ? data[config.path]
- : data[config.path]
- ? [data[config.path]]
- : []
- ).map((x: any) => x.id)
- )
- return {
- kind: 'cards-view',
- id: data.id,
- itemsBeingEdited: new Set(),
- itemBeingCreated: false,
- initialIds,
- currentIds: initialIds,
- displayOptions: cardsDisplayOptions,
- }
- }
if (config.fieldMeta.many) {
let value = (data[config.path] || []).map((x: any) => ({
id: x.id,
@@ -561,10 +453,7 @@ export function controller (
},
},
validate (value) {
- return (
- value.kind !== 'cards-view' ||
- (value.itemsBeingEdited.size === 0 && !value.itemBeingCreated)
- )
+ return true
},
serialize: state => {
if (state.kind === 'many') {
@@ -601,32 +490,6 @@ export function controller (
},
}
}
- } else if (state.kind === 'cards-view') {
- let disconnect = [...state.initialIds]
- .filter(id => !state.currentIds.has(id))
- .map(id => ({ id }))
- let connect = [...state.currentIds]
- .filter(id => !state.initialIds.has(id))
- .map(id => ({ id }))
-
- if (config.fieldMeta.many) {
- if (disconnect.length || connect.length) {
- return {
- [config.path]: {
- connect: connect.length ? connect : undefined,
- disconnect: disconnect.length ? disconnect : undefined,
- },
- }
- }
- } else if (connect.length) {
- return {
- [config.path]: {
- connect: connect[0],
- },
- }
- } else if (disconnect.length) {
- return { [config.path]: { disconnect: true } }
- }
}
return {}
},
diff --git a/packages/core/src/fields/types/select/views/index.tsx b/packages/core/src/fields/types/select/views/index.tsx
index c300ccf4797..fe7e97bd4e7 100644
--- a/packages/core/src/fields/types/select/views/index.tsx
+++ b/packages/core/src/fields/types/select/views/index.tsx
@@ -13,7 +13,6 @@ import { NullableFieldWrapper } from '../../../../admin-ui/components'
import { SegmentedControl } from './SegmentedControl'
import type {
- CardValueComponent,
CellComponent,
FieldController,
FieldControllerConfig,
@@ -32,7 +31,7 @@ export const Field = (props: FieldProps) => {
return item.label.length > acc ? item.label.length : acc
}, 0)
}, [field.options])
-
+
const selectedKey = value.value?.value || preNullValue?.value || null
const isNullable = !field.isRequired
const isNull = isNullable && value.value?.value == null
@@ -138,7 +137,7 @@ export const Field = (props: FieldProps) => {
)}
)
- }
+ }
})()
@@ -163,18 +162,6 @@ export const Cell: CellComponent = ({ item, field, linkTo })
}
Cell.supportsLinkTo = true
-export const CardValue: CardValueComponent = ({ item, field }) => {
- let value = item[field.path] + ''
- const valueLabel = field.options.find(x => x.value === value)?.label
-
- return (
-
- {field.label}
- {valueLabel}
-
- )
-}
-
export type AdminSelectFieldMeta = {
options: readonly { label: string, value: string | number }[]
type: 'string' | 'integer' | 'enum'
@@ -323,7 +310,7 @@ export const controller = (
if (value.length === 0) {
return type === 'not_matches' ? `is set` : `is not set`
}
-
+
const labels = value.map(i => i.label)
const prefix = type === 'not_matches' ? `is not` : `is`
@@ -331,7 +318,7 @@ export const controller = (
return `${prefix} ${labels[0]}`
}
if (value.length === 2) {
- return `${prefix} ${listFormatter.format(labels)}`
+ return `${prefix} ${listFormatter.format(labels)}`
}
return `${prefix} ${listFormatter.format([labels[0], `${value.length - 1} more`])}`
diff --git a/packages/core/src/fields/types/text/views/index.tsx b/packages/core/src/fields/types/text/views/index.tsx
index 59b4fac4880..e16499568a8 100644
--- a/packages/core/src/fields/types/text/views/index.tsx
+++ b/packages/core/src/fields/types/text/views/index.tsx
@@ -6,7 +6,6 @@ import { TextArea, TextField } from '@keystar/ui/text-field'
import { Text } from '@keystar/ui/typography'
import type {
- CardValueComponent,
CellComponent,
FieldController,
FieldControllerConfig,
@@ -20,7 +19,7 @@ export function Field (props: FieldProps) {
const [shouldShowErrors, setShouldShowErrors] = useState(false)
const validationMessages = validate(value, field.validation, field.label)
-
+
const isReadOnly = onChange == null
const isNull = value.inner.kind === 'null'
const isTextArea = field.displayMode === 'textarea'
@@ -35,11 +34,11 @@ export function Field (props: FieldProps) {
isNull={isNull}
onChange={() => {
if (!onChange) return
-
+
const inner = value.inner.kind === 'value'
? { kind: 'null', prev: value.inner.value } as const
: { kind: 'value', value: value.inner.prev } as const
-
+
onChange({ ...value, inner })
}}
>
@@ -87,15 +86,6 @@ export const Cell: CellComponent = ({ item, field, linkTo }) => {
}
Cell.supportsLinkTo = true
-export const CardValue: CardValueComponent = ({ item, field }) => {
- return (
-
- {field.label}
- {item[field.path]}
-
- )
-}
-
type Config = FieldControllerConfig
type Validation = {
diff --git a/packages/core/src/fields/types/timestamp/views/index.tsx b/packages/core/src/fields/types/timestamp/views/index.tsx
index a08cf689091..edbe80c3538 100644
--- a/packages/core/src/fields/types/timestamp/views/index.tsx
+++ b/packages/core/src/fields/types/timestamp/views/index.tsx
@@ -11,7 +11,6 @@ import {
FieldDescription,
} from '@keystone-ui/fields'
import {
- type CardValueComponent,
type CellComponent,
type FieldController,
type FieldControllerConfig,
@@ -213,15 +212,6 @@ export const Cell: CellComponent = ({ item, field, linkTo }) => {
}
Cell.supportsLinkTo = true
-export const CardValue: CardValueComponent = ({ item, field }) => {
- return (
-
- {field.label}
- {formatOutput(item[field.path])}
-
- )
-}
-
export type TimestampFieldMeta = {
defaultValue: string | { kind: 'now' } | null
updatedAt: boolean
diff --git a/packages/core/src/fields/types/virtual/views/index.tsx b/packages/core/src/fields/types/virtual/views/index.tsx
index bb60f368d2c..f145cd370b6 100644
--- a/packages/core/src/fields/types/virtual/views/index.tsx
+++ b/packages/core/src/fields/types/virtual/views/index.tsx
@@ -4,7 +4,6 @@
import { jsx } from '@keystone-ui/core'
import { FieldContainer, FieldDescription, FieldLabel } from '@keystone-ui/fields'
import {
- type CardValueComponent,
type CellComponent,
type FieldController,
type FieldControllerConfig,
@@ -26,15 +25,6 @@ export const Cell: CellComponent = ({ item, field }) => {
return
}
-export const CardValue: CardValueComponent = ({ item, field }) => {
- return (
-
- {field.label}
-
-
- )
-}
-
const createViewValue = Symbol('create view virtual field value')
export const controller = (
diff --git a/packages/core/src/types/admin-meta.ts b/packages/core/src/types/admin-meta.ts
index db7df026897..f06fc9d8eaf 100644
--- a/packages/core/src/types/admin-meta.ts
+++ b/packages/core/src/types/admin-meta.ts
@@ -146,7 +146,6 @@ export type FieldViews = Record<
{
Field: (props: FieldProps) => ReactElement | null
Cell: CellComponent
- CardValue: CardValueComponent
controller: (args: FieldControllerConfig) => FieldController
allowedExportsOnCustomViews?: string[]
}
@@ -166,10 +165,3 @@ export type CellComponent<
supportsLinkTo?: boolean
}
-
-export type CardValueComponent<
- FieldControllerFn extends (...args: any) => FieldController = () => FieldController<
- any,
- any
- >
-> = (props: { item: Record, field: ReturnType }) => ReactElement
diff --git a/packages/fields-document/src/structure-views.tsx b/packages/fields-document/src/structure-views.tsx
index a4f49775890..390d083dca5 100644
--- a/packages/fields-document/src/structure-views.tsx
+++ b/packages/fields-document/src/structure-views.tsx
@@ -5,7 +5,6 @@ import { jsx } from '@keystone-ui/core'
import { FieldContainer, FieldLabel } from '@keystone-ui/fields'
import {
- type CardValueComponent,
type CellComponent,
type FieldController,
type FieldControllerConfig,
@@ -54,10 +53,6 @@ export const Cell: CellComponent = () => {
return null
}
-export const CardValue: CardValueComponent = () => {
- return null as any
-}
-
export const allowedExportsOnCustomViews = ['schema']
export function controller (
diff --git a/packages/fields-document/src/views.tsx b/packages/fields-document/src/views.tsx
index 84323a5e775..28920cf86f4 100644
--- a/packages/fields-document/src/views.tsx
+++ b/packages/fields-document/src/views.tsx
@@ -5,10 +5,8 @@
import { jsx } from '@keystone-ui/core'
import { FieldContainer, FieldDescription, FieldLabel } from '@keystone-ui/fields'
import { Node } from 'slate'
-import { DocumentRenderer } from '@keystone-6/document-renderer'
import {
- type CardValueComponent,
type CellComponent,
type FieldProps,
} from '@keystone-6/core/types'
@@ -69,13 +67,4 @@ export const Cell: CellComponent = ({ item, field, linkTo }) => {
}
Cell.supportsLinkTo = true
-export const CardValue: CardValueComponent = ({ item, field }) => {
- return (
-
- {field.label}
-
-
- )
-}
-
export const allowedExportsOnCustomViews = ['componentBlocks']
diff --git a/tests/api-tests/relationships/label-search-field-validation.test.ts b/tests/api-tests/relationships/label-search-field-validation.test.ts
index 6123fa9aad9..d54d565463f 100644
--- a/tests/api-tests/relationships/label-search-field-validation.test.ts
+++ b/tests/api-tests/relationships/label-search-field-validation.test.ts
@@ -42,38 +42,6 @@ test("labelField that doesn't exist is rejected with displayMode: select", () =>
)
})
-test("labelField that doesn't exist is rejected with displayMode: cards", () => {
- expect(() =>
- getContext(
- ({
- db: {
- provider: 'sqlite',
- url: 'file://'
- },
- lists: {
- A: list({
- access: allowAll,
- fields: {
- something: relationship({
- ref: 'Thing',
- ui: {
- displayMode: 'cards',
- cardFields: ['name'],
- inlineConnect: { labelField: 'doesNotExist' },
- },
- }),
- },
- }),
- Thing,
- },
- }),
- {} as any
- )
- ).toThrowErrorMatchingInlineSnapshot(
- `"The ui.inlineConnect.labelField option for field 'A.something' uses 'doesNotExist' but that field doesn't exist."`
- )
-})
-
test("searchFields that don't exist are rejected with displayMode: select", () => {
expect(() =>
getContext(
@@ -104,38 +72,6 @@ test("searchFields that don't exist are rejected with displayMode: select", () =
)
})
-test("searchFields that don't exist are rejected with displayMode: cards", () => {
- expect(() =>
- getContext(
- ({
- db: {
- provider: 'sqlite',
- url: 'file://'
- },
- lists: {
- A: list({
- access: allowAll,
- fields: {
- something: relationship({
- ref: 'Thing',
- ui: {
- displayMode: 'cards',
- cardFields: ['name'],
- inlineConnect: { labelField: 'name', searchFields: ['doesNotExist'] },
- },
- }),
- },
- }),
- Thing,
- },
- }),
- {} as any
- )
- ).toThrowErrorMatchingInlineSnapshot(
- `"The ui.inlineConnect.searchFields option for relationship field 'A.something' includes 'doesNotExist' but that field doesn't exist."`
- )
-})
-
test("searchFields that aren't searchable are rejected with displayMode: select", () => {
expect(() =>
getContext(
@@ -165,35 +101,3 @@ test("searchFields that aren't searchable are rejected with displayMode: select"
`"The ui.searchFields option for field 'A.something' includes 'notText' but that field doesn't have a contains filter that accepts a GraphQL String"`
)
})
-
-test("searchFields that aren't searchable are rejected with displayMode: cards", () => {
- expect(() =>
- getContext(
- ({
- db: {
- provider: 'sqlite',
- url: 'file://'
- },
- lists: {
- A: list({
- access: allowAll,
- fields: {
- something: relationship({
- ref: 'Thing',
- ui: {
- displayMode: 'cards',
- cardFields: ['name'],
- inlineConnect: { labelField: 'name', searchFields: ['notText'] },
- },
- }),
- },
- }),
- Thing,
- },
- }),
- {} as any
- )
- ).toThrowErrorMatchingInlineSnapshot(
- `"The ui.searchFields option for field 'A.something' includes 'notText' but that field doesn't have a contains filter that accepts a GraphQL String"`
- )
-})
diff --git a/tests/sandbox/configs/7590-add-item-to-relationship-in-hook-cards-ui.ts b/tests/sandbox/configs/7590-add-item-to-relationship-in-hook-cards-ui.ts
deleted file mode 100644
index 5bd8853e16b..00000000000
--- a/tests/sandbox/configs/7590-add-item-to-relationship-in-hook-cards-ui.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import { list, config } from '@keystone-6/core'
-import { allowAll } from '@keystone-6/core/access'
-import { relationship, text } from '@keystone-6/core/fields'
-import { dbConfig } from '../utils'
-
-export const lists = {
- User: list({
- access: allowAll,
- fields: {
- name: text(),
- numbers: relationship({
- ref: 'Number.user',
- many: true,
- ui: {
- displayMode: 'cards',
- cardFields: ['value'],
- },
- hooks: {
- // every time you save, add a random number
- async resolveInput (args) {
- return {
- ...args.resolvedData[args.fieldKey],
- create: {
- value: Math.floor(Math.random() * 100000).toString(),
- },
- }
- },
- },
- }),
- },
- }),
- Number: list({
- access: allowAll,
- fields: {
- user: relationship({ ref: 'User.numbers' }),
- value: text({ validation: { isRequired: true } }),
- },
- }),
-}
-
-export default config({
- db: dbConfig,
- lists
-})
diff --git a/tests/sandbox/configs/all-the-things.ts b/tests/sandbox/configs/all-the-things.ts
index 6ae9b9686f1..feba51a1bf0 100644
--- a/tests/sandbox/configs/all-the-things.ts
+++ b/tests/sandbox/configs/all-the-things.ts
@@ -26,10 +26,9 @@ import { schema as structureRelationshipsSchema } from '../structure-relationshi
import { localStorageConfig, trackingFields } from '../utils'
// import { type Lists } from '.keystone/types' // TODO
-const description =
- 'Some thing to describe to test the length of the text for width, blah blah blah blah blah blah blah blah blah'
+const description = 'Some thing to describe to test the length of the text for width, blah blah blah blah blah blah blah blah blah'
-export const lists = {
+export const lists: any = {
Thing: list({
access: allowAll,
fields: {
@@ -66,26 +65,12 @@ export const lists = {
ref: 'User',
ui: {
description,
- displayMode: 'cards',
- cardFields: ['name', 'email'],
- inlineConnect: {
- labelField: 'email',
- },
- inlineCreate: { fields: ['name', 'email'] },
- linkToItem: true,
- inlineEdit: { fields: ['name', 'email'] },
},
}),
toManyRelationshipCard: relationship({
ref: 'Todo',
ui: {
description,
- displayMode: 'cards',
- cardFields: ['label', 'isComplete', 'assignedTo'],
- inlineConnect: true,
- inlineCreate: { fields: ['label', 'isComplete', 'assignedTo'] },
- linkToItem: true,
- inlineEdit: { fields: ['label', 'isComplete', 'assignedTo'] },
},
many: true,
}),