Skip to content

Commit

Permalink
Text updates (#6461)
Browse files Browse the repository at this point in the history
  • Loading branch information
emmatown authored Sep 27, 2021
1 parent 79108f5 commit e81947d
Show file tree
Hide file tree
Showing 93 changed files with 763 additions and 485 deletions.
5 changes: 5 additions & 0 deletions .changeset/dirty-crabs-bathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@keystone-next/keystone': major
---

In the `text` field, `defaultValue` is now a static value, `isRequired` has moved to `validation.isRequired` and also requires that the value has a length of at least one, along with new `validation.lenght.min`, `validation.length.max` and `validation.match` options. The `text` field is also now non-nullable at the database-level by default and can be made nullable by setting the `isNullable` option to `true`. `graphql.read.isNonNull` can also be set if the field does not have `isNullable: true` and you have no read access control and you don't intend to add any in the future, it will make the GraphQL output field non-nullable. `graphql.create.isNonNull` can also be set if you have no create access control and you don't intend to add any in the future, it will make the GraphQL create input field non-nullable.
26 changes: 20 additions & 6 deletions docs/pages/docs/apis/fields.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ Options:
`value` is either a `string` (for `{ type: 'string' }` or `{ type: 'enum' }`), or a `number` (for `{ type: 'integer' }`).
The `value` will be used in the GraphQL API and stored in the database.
- `isNullable` (default: `true`): If `false` then this field will be made non-nullable in the database and it will never be possible to set as `null`.
- `defaultValue` (default: `false`): This value will be used for the field when creating items if no explicit value is set.
- `defaultValue` (default: `undefined`): This value will be used for the field when creating items if no explicit value is set.
- `validation.isRequired` (default: `false`): If `true` then this field can never be set to `null`.
Unlike `isNullable`, this will require that a value is provided in the Admin UI.
It will also validate this when creating and updating an item through the GraphQL API but it will not enforce it at the database level.
Expand Down Expand Up @@ -439,16 +439,30 @@ A `text` field represents a string value.

Options:

- `defaultValue` (default: `undefined`): Can be either a string value or an async function which takes an argument `({ context, originalInput })` and returns a string value.
This value will be used for the field when creating items if no explicit value is set.
`context` is a [`KeystoneContext`](./context) object.
`originalInput` is an object containing the data passed in to the `create` mutation.
- `isRequired` (default: `false`): If `true` then this field can never be set to `null`.
- `isNullable` (default: `false`): If `true` then this field will be made nullable in the database and it will be possible to set as `null`.
- `defaultValue` (default: `isNullable === true ? undefined : ''`): This value will be used for the field when creating items if no explicit value is set.
- `validation.isRequired` (default: `false`): If `true` then this field can never be set to `null` or an empty string.
Unlike `isNullable`, this will require that a value with at least 1 character is provided in the Admin UI.
It will also validate this when creating and updating an item through the GraphQL API but it will not enforce it at the database level.
If you would like to enforce it being non-null at the database-level and in the Admin UI, you can set both `isNullable: false` and `validation.isRequired: true`.
- `validation.length.min` (default: `0`): This describes the minimum number allowed. If you attempt to submit a string shorter than this, you will get a validation error.
- `validation.length.max` (default: `undefined`): This describes the maximum length allowed. If you attempt to submit a string longer than this, you will get a validation error.
- `validation.match` (default: `undefined`): This describes a pattern that values for this field must match
- `validation.match.regex`: The regular expression
- `validation.match.explanation` (default: `${fieldLabel} must match ${validation.match.regex}`): A message shown in the Admin when a value doesn't match the regex and returned as a validation error from the GraphQL API
- `isIndexed` (default: `false`)
- If `true` then this field will be indexed by the database.
- If `'unique'` then all values of this field must be unique.
- `ui` (default: `{ displayMode: 'input' }`): Configures the display mode of the field in the Admin UI.
Can be one of `['input', 'textarea']`.
- `graphql.read.isNonNull` (default: `false`): If you have no read access control and you don't intend to add any in the future,
you can set this to true and the output field will be non-nullable. This is only allowed when you have no read access control because otherwise,
when access is denied, `null` will be returned which will cause an error since the field is non-nullable and the error
will propagate up until a nullable field is found which means the entire item will be unreadable and when doing an `items` query, all the items will be unreadable.
- `graphql.create.isNonNull` (default: `false`): If you have no create access control and you want to explicitly show that this is field is non-nullable in the create input
you can set this to true and the create field will be non-nullable and have a default value at the GraphQL level.
This is only allowed when you have no create access control because otherwise, the item will always fail access control
if a user doesn't have access to create the particular field regardless of whether or not they specify the field in the create.

```typescript
import { config, list } from '@keystone-next/keystone';
Expand Down
9 changes: 5 additions & 4 deletions docs/pages/docs/guides/hooks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ export default config({
lists: {
Post: list({
fields: {
title: text({ isRequired: true }),
content: text({ isRequired: true }),
title: text({ validation: { isRequired: true } }),
content: text({ validation: { isRequired: true } }),
},
hooks: {
resolveInput: ({ resolvedData }) => {
Expand Down Expand Up @@ -108,8 +108,8 @@ export default config({
lists: {
Post: list({
fields: {
title: text({ isRequired: true }),
content: text({ isRequired: true }),
title: text({ validation: { isRequired: true } }),
content: text({ validation: { isRequired: true } }),
},
hooks: {
validateInput: ({ resolvedData, addValidationError }) => {
Expand Down Expand Up @@ -207,6 +207,7 @@ export default config({
fields: {
name: text(),
email: text({
isNullable: true,
hooks: {
validateInput: ({ addValidationError, resolvedData, fieldKey }) => {
const email = resolvedData[fieldKey];
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/docs/guides/testing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ runner(async ({ context }) => {
expect(errors).toHaveLength(1);
expect(errors[0].path).toEqual(['createPerson']);
expect(errors[0].message).toEqual(
'You provided invalid data for this operation.\n - Person.name: Required field "name" is null or undefined.'
'You provided invalid data for this operation.\n - Person.name: Name must not be empty'
);
})
```
Expand Down
6 changes: 3 additions & 3 deletions docs/pages/docs/guides/virtual-fields.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export default config({
}),
Author: list({
fields: {
name: text({ isRequired: true }),
name: text({ validation: { isRequired: true } }),
},
}),
},
Expand Down Expand Up @@ -273,8 +273,8 @@ export const lists = {
}),
Author: list({
fields: {
name: text({ isRequired: true }),
email: text({ isRequired: true, isIndexed: 'unique' }),
name: text({ validation: { isRequired: true } }),
email: text({ isIndexed: 'unique', validation: { isRequired: true } }),
posts: relationship({ ref: 'Post.author', many: true }),
latestPost: virtual({
field: lists =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ import { text } from '@keystone-next/fields';

const Post = list({
fields: {
title: text({ isRequired: true }),
title: text({ validation: { isRequired: true } }),
slug: text(),
content: text(),
},
Expand Down
6 changes: 3 additions & 3 deletions docs/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -420,16 +420,16 @@ import { document, text, timestamp, password, relationship } from '@keystone-nex
export const lists = {
Post: list({
fields: {
title: text({ isRequired: true }),
title: text({ validation: { isRequired: true } }),
content: document(),
publishDate: timestamp(),
author: relationship({ ref: 'Author.posts', many: false }),
},
}),
Author: list({
fields: {
name: text({ isRequired: true }),
email: text({ isRequired: true, isIndexed: 'unique' }),
name: text({ validation: { isRequired: true } }),
email: text({ isIndexed: 'unique', validation: { isRequired: true } }),
password: password(),
posts: relationship({ ref: 'Post.author', many: true }),
},
Expand Down
4 changes: 2 additions & 2 deletions docs/pages/updates/new-graphql-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ To illustrate the changes, we’ll refer to the `Task` list in the following sch
export const lists = {
Task: list({
fields: {
label: text({ isRequired: true }),
label: text({ validation: { isRequired: true } }),
priority: select({
type: 'enum',
options: [
Expand All @@ -34,7 +34,7 @@ export const lists = {
}),
Person: list({
fields: {
name: text({ isRequired: true }),
name: text({ validation: { isRequired: true } }),
tasks: relationship({ ref: 'Task.assignedTo', many: true }),
},
}),
Expand Down
12 changes: 6 additions & 6 deletions examples-staging/assets-cloud/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ generator client {

model Post {
id String @id @default(cuid())
title String?
title String @default("")
status String?
content String?
content String @default("")
publishDate DateTime?
author Author? @relation("Post_author", fields: [authorId], references: [id])
authorId String? @map("author")
Expand All @@ -34,8 +34,8 @@ model Post {
}

model Author {
id String @id @default(cuid())
name String?
email String? @unique
posts Post[] @relation("Post_author")
id String @id @default(cuid())
name String @default("")
email String @unique @default("")
posts Post[] @relation("Post_author")
}
6 changes: 3 additions & 3 deletions examples-staging/assets-cloud/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { select, relationship, text, timestamp, image, file } from '@keystone-ne
export const lists = {
Post: list({
fields: {
title: text({ isRequired: true }),
title: text({ validation: { isRequired: true } }),
status: select({
type: 'enum',
options: [
Expand All @@ -21,8 +21,8 @@ export const lists = {
}),
Author: list({
fields: {
name: text({ isRequired: true }),
email: text({ isRequired: true, isIndexed: 'unique' }),
name: text({ validation: { isRequired: true } }),
email: text({ isIndexed: 'unique', validation: { isRequired: true } }),
posts: relationship({ ref: 'Post.author', many: true }),
},
}),
Expand Down
12 changes: 6 additions & 6 deletions examples-staging/assets-local/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ generator client {

model Post {
id String @id @default(cuid())
title String?
title String @default("")
status String?
content String?
content String @default("")
publishDate DateTime?
author Author? @relation("Post_author", fields: [authorId], references: [id])
authorId String? @map("author")
Expand All @@ -31,8 +31,8 @@ model Post {
}

model Author {
id String @id @default(cuid())
name String?
email String? @unique
posts Post[] @relation("Post_author")
id String @id @default(cuid())
name String @default("")
email String @unique @default("")
posts Post[] @relation("Post_author")
}
6 changes: 3 additions & 3 deletions examples-staging/assets-local/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { select, relationship, text, timestamp, image } from '@keystone-next/key
export const lists = {
Post: list({
fields: {
title: text({ isRequired: true }),
title: text({ validation: { isRequired: true } }),
status: select({
type: 'enum',
options: [
Expand All @@ -20,8 +20,8 @@ export const lists = {
}),
Author: list({
fields: {
name: text({ isRequired: true }),
email: text({ isRequired: true, isIndexed: 'unique' }),
name: text({ validation: { isRequired: true } }),
email: text({ isIndexed: 'unique', validation: { isRequired: true } }),
posts: relationship({ ref: 'Post.author', many: true }),
},
}),
Expand Down
10 changes: 5 additions & 5 deletions examples-staging/auth/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ input UserWhereInput {
OR: [UserWhereInput!]
NOT: [UserWhereInput!]
id: IDFilter
email: StringNullableFilter
email: StringFilter
}

input IDFilter {
Expand All @@ -75,7 +75,7 @@ input IDFilter {
not: IDFilter
}

input StringNullableFilter {
input StringFilter {
equals: String
in: [String!]
notIn: [String!]
Expand All @@ -86,10 +86,10 @@ input StringNullableFilter {
contains: String
startsWith: String
endsWith: String
not: NestedStringNullableFilter
not: NestedStringFilter
}

input NestedStringNullableFilter {
input NestedStringFilter {
equals: String
in: [String!]
notIn: [String!]
Expand All @@ -100,7 +100,7 @@ input NestedStringNullableFilter {
contains: String
startsWith: String
endsWith: String
not: NestedStringNullableFilter
not: NestedStringFilter
}

input UserOrderByInput {
Expand Down
4 changes: 2 additions & 2 deletions examples-staging/auth/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ generator client {

model User {
id String @id @default(cuid())
name String?
email String? @unique
name String @default("")
email String @unique @default("")
password String?
isAdmin Boolean @default(false)
}
4 changes: 2 additions & 2 deletions examples-staging/auth/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ export const lists = {
},
fields: {
// The user's name
name: text({ isRequired: true }),
name: text({ validation: { isRequired: true } }),
// The user's email address, used as the identity field for auth
email: text({ isRequired: true, isIndexed: 'unique', isFilterable: true }),
email: text({ isIndexed: 'unique', isFilterable: true, validation: { isRequired: true } }),
// The user's password, used as the secret field for auth
password: password({
access: {
Expand Down
12 changes: 8 additions & 4 deletions examples-staging/basic/admin/fieldViews/Test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@ export const Field = ({ field, value, onChange, autoFocus }: FieldProps<typeof c
field.displayMode === 'textarea' ? (
<TextArea
autoFocus={autoFocus}
onChange={event => onChange(event.target.value)}
value={value}
onChange={event => {
onChange({ ...value, inner: { kind: 'value', value: event.target.value } });
}}
value={value.inner.kind === 'null' ? '' : value.inner.value}
/>
) : (
<TextInput
autoFocus={autoFocus}
onChange={event => onChange(event.target.value)}
value={value}
onChange={event => {
onChange({ ...value, inner: { kind: 'value', value: event.target.value } });
}}
value={value.inner.kind === 'null' ? '' : value.inner.value}
/>
)
) : (
Expand Down
10 changes: 5 additions & 5 deletions examples-staging/basic/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ input UserWhereInput {
OR: [UserWhereInput!]
NOT: [UserWhereInput!]
id: IDFilter
email: StringNullableFilter
email: StringFilter
}

input IDFilter {
Expand All @@ -155,7 +155,7 @@ input IDFilter {
not: IDFilter
}

input StringNullableFilter {
input StringFilter {
equals: String
in: [String!]
notIn: [String!]
Expand All @@ -166,10 +166,10 @@ input StringNullableFilter {
contains: String
startsWith: String
endsWith: String
not: NestedStringNullableFilter
not: NestedStringFilter
}

input NestedStringNullableFilter {
input NestedStringFilter {
equals: String
in: [String!]
notIn: [String!]
Expand All @@ -180,7 +180,7 @@ input NestedStringNullableFilter {
contains: String
startsWith: String
endsWith: String
not: NestedStringNullableFilter
not: NestedStringFilter
}

input UserOrderByInput {
Expand Down
Loading

0 comments on commit e81947d

Please sign in to comment.