feat: prevent create new for joins (#8929)
### What? Adds a way to prevent creating new documents from the admin UI in a join field. ### Why? There are two reasons: 1. You want to disable this any time as a feature of your admin user experience 2. When creating a new document it is not yet possible to create the relationship, preventing create is necessary for the workflow to make sense. ### How? join field has a new admin property called `allowCreate`, can be set to false. By default the UI will never allow create when the current document being edited does not yet have an `id`. Fixes # #8892 ### Before Even though the document doesn't have an ID yet, the create buttons are shown which doesn't actually work.  ### After Initial document creation:  Prevented using `allowCreate: false` 
This commit is contained in:
@@ -122,22 +122,32 @@ powerful Admin UI.
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
|------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **`name`** \* | To be used as the property name when retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`collection`** \* | The `slug`s having the relationship field. |
|
||||
| **`on`** \* | The name of the relationship or upload field that relates to the collection document. Use dot notation for nested paths, like 'myGroup.relationName'. |
|
||||
| **`maxDepth`** | Default is 1, Sets a maximum population depth for this field, regardless of the remaining depth when this field is reached. [Max Depth](/docs/getting-started/concepts#field-level-max-depth) |
|
||||
| **`maxDepth`** | Default is 1, Sets a maximum population depth for this field, regardless of the remaining depth when this field is reached. [Max Depth](/docs/getting-started/concepts#field-level-max-depth). |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`defaultLimit`** | The number of documents to return. Set to 0 to return all related documents. |
|
||||
| **`defaultSort`** | The field name used to specify the order the joined documents are returned. |
|
||||
| **`admin`** | Admin-specific configuration. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-config-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins). |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema. |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
## Admin Config Options
|
||||
|
||||
You can control the user experience of the join field using the `admin` config properties. The following options are supported:
|
||||
|
||||
| Option | Description |
|
||||
|------------------------|----------------------------------------------------------------------------------------|
|
||||
| **`allowCreate`** | Set to `false` to remove the controls for making new related documents from this field. |
|
||||
| **`components.Label`** | Override the default Label of the Field Component. [More details](#the-label-component). |
|
||||
|
||||
## Join Field Data
|
||||
|
||||
When a document is returned that for a Join field is populated with related documents. The structure returned is an
|
||||
|
||||
@@ -1442,6 +1442,7 @@ export type JoinField = {
|
||||
update?: never
|
||||
}
|
||||
admin?: {
|
||||
allowCreate?: boolean
|
||||
components?: {
|
||||
Error?: CustomComponent<JoinFieldErrorClientComponent | JoinFieldErrorServerComponent>
|
||||
Label?: CustomComponent<JoinFieldLabelClientComponent | JoinFieldLabelServerComponent>
|
||||
@@ -1477,6 +1478,7 @@ export type JoinField = {
|
||||
|
||||
export type JoinFieldClient = {
|
||||
admin?: {
|
||||
allowCreate?: boolean
|
||||
components?: {
|
||||
Label?: MappedComponent
|
||||
} & AdminClient['components']
|
||||
|
||||
@@ -37,6 +37,7 @@ import { RelationshipTableWrapper } from './TableWrapper.js'
|
||||
const baseClass = 'relationship-table'
|
||||
|
||||
type RelationshipTableComponentProps = {
|
||||
readonly allowCreate?: boolean
|
||||
readonly field: JoinFieldClient
|
||||
readonly filterOptions?: boolean | Where
|
||||
readonly initialData?: PaginatedDocs
|
||||
@@ -47,6 +48,7 @@ type RelationshipTableComponentProps = {
|
||||
|
||||
export const RelationshipTable: React.FC<RelationshipTableComponentProps> = (props) => {
|
||||
const {
|
||||
allowCreate = true,
|
||||
field,
|
||||
filterOptions,
|
||||
initialData: initialDataFromProps,
|
||||
@@ -202,16 +204,15 @@ export const RelationshipTable: React.FC<RelationshipTableComponentProps> = (pro
|
||||
|
||||
const preferenceKey = `${relationTo}-list`
|
||||
|
||||
const hasCreatePermission = permissions?.collections?.[relationTo]?.create?.permission
|
||||
const canCreate =
|
||||
allowCreate !== false && permissions?.collections?.[relationTo]?.create?.permission
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<div className={`${baseClass}__header`}>
|
||||
{Label}
|
||||
<div className={`${baseClass}__actions`}>
|
||||
{hasCreatePermission && (
|
||||
<DocumentDrawerToggler>{i18n.t('fields:addNew')}</DocumentDrawerToggler>
|
||||
)}
|
||||
{canCreate && <DocumentDrawerToggler>{i18n.t('fields:addNew')}</DocumentDrawerToggler>}
|
||||
<Pill
|
||||
aria-controls={`${baseClass}-columns`}
|
||||
aria-expanded={openColumnSelector}
|
||||
@@ -233,7 +234,7 @@ export const RelationshipTable: React.FC<RelationshipTableComponentProps> = (pro
|
||||
label: getTranslation(collectionConfig?.labels?.plural, i18n),
|
||||
})}
|
||||
</p>
|
||||
{hasCreatePermission && (
|
||||
{canCreate && (
|
||||
<Button onClick={openDrawer}>
|
||||
{i18n.t('general:createNewLabel', {
|
||||
label: getTranslation(collectionConfig?.labels?.singular, i18n),
|
||||
|
||||
@@ -19,6 +19,7 @@ const JoinFieldComponent: JoinFieldClientComponent = (props) => {
|
||||
name,
|
||||
_path: pathFromProps,
|
||||
admin: {
|
||||
allowCreate = true,
|
||||
components: { Label },
|
||||
},
|
||||
collection,
|
||||
@@ -47,6 +48,7 @@ const JoinFieldComponent: JoinFieldClientComponent = (props) => {
|
||||
return (
|
||||
<div className={[fieldBaseClass, 'join'].filter(Boolean).join(' ')}>
|
||||
<RelationshipTable
|
||||
allowCreate={typeof docID !== 'undefined' && allowCreate}
|
||||
field={field as JoinFieldClient}
|
||||
filterOptions={filterOptions}
|
||||
initialData={docID && value ? value : ({ docs: [] } as PaginatedDocs)}
|
||||
|
||||
Reference in New Issue
Block a user