You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[6.x] Restore the New entry button in entry selection modals - #20652
Craft 6's Vue element selector modal never got Craft 5's “New {section} entry” button, so entries couldn't be created from a relation field's modal.
<craft-element-selector-modal>
<ElementPicker> # now also publishes its sources and site
<div slot="secondary-actions">
<AssetUploadButton> # assets
+ <NewEntryButton> # entries: one button, or a menu of entry types
It offers the sections the modal's index shows that the user can publish in on the index's site. These are the same rules as the entry index's New entry button, now shared through useCreatableEntryTypes().
The selector is a top-layer <dialog>, and slideouts open beneath it, so the flow differs from Craft 5's:
click New entry
POST entries/create # modal stays open, busy
controller.close()
openSlideout(new draft) # focus returns to the field's Add button
onSaved (not an autosave)
options.onSelect([entry]) # the relation field relates it
This is how Craft 5's search-input “Create entry” option behaved. In a section whose entries need approval, the slideout's primary action saves the draft for review, so the field relates the entry while it's still an unpublished draft. The button stays away when the field has a selection condition: the modal is closed while the entry is edited, so nothing could check the saved entry against the condition before relating it. The rest of #20598, the relation field's missing search input, isn't covered here.
Evidence
Before: in the Field Showcase workbench entry with the Entries field limited to one section, the modal footer had only Cancel and Add an entry. The new test fails: expected undefined to be 'New Authors entry'. After: the footer shows “+ New Pages entry” (or a “New entry” menu grouped by section with sources: '*'). Creating an entry from it opens a slideout, and saving it relates the entry to the field, with focus back on Add an entry. Cancelling the slideout relates nothing. Keyboard: Tab reaches the button, Enter opens the slideout.
ElementSelectorModal › the new entry button
emit sources/source/site → button reads “New Authors entry”
click → posts {siteId: 2, section: authors, type: author}, modal closes, slideout opens for draftId 5
onSaved({draft: true}) → onSelect not called
onSaved({element: {id: 34, title: 'Jane Austen'}}) → onSelect([{id: 34, siteId: 2, label: 'Jane Austen'}])
vp test run (element-selector-modal, NewEntryButton, content index): 157 passed. vue-tsc --noEmit passes.
Screenshots aren't attached. I couldn't upload them to GitHub from here.
The Vue element selector modal never got Craft 5's "New {section} entry"
button. It now offers one for the sections its index shows that the user
can publish in, sharing NewEntryButton's rules via useCreatableEntryTypes.
The modal is a top-layer dialog that a slideout can't open over, so the
button closes it, opens the new entry in a slideout, and hands the entry
to the opener's onSelect once it's saved.
Fixes#20598
Amp-Thread-ID: https://ampcode.com/threads/T-e712b71f-c4c3-44f5-a7d6-ebcba118cb03
Co-authored-by: Amp <amp@ampcode.com>
Restores entry creation from relation-field selection modals, addressing the missing button reported in #20598. The separate search-input feature remains out of scope.
Changes:
Adds a section-aware “New entry” button or entry-type menu that opens an editor slideout and selects the saved entry.
Shares creation eligibility rules with the content index and passes selector sources, site, and focus target through the modal.
Adds regression coverage and documents the restored workflow.
- Report a failed entries/create request and keep the selector open.
- Relate the new entry once, not again on each later save.
- Keep a pending Replace across the modal-to-slideout handoff: the
jQuery inputs now clear it when Add opens the modal, not when the modal
closes.
- Remount the entry type menu when its section label changes, since
ActionMenu renders its invoker once.
Amp-Thread-ID: https://ampcode.com/threads/T-e712b71f-c4c3-44f5-a7d6-ebcba118cb03
Co-authored-by: Amp <amp@ampcode.com>
Destructive actions also notify onSaved without setting draft (useElementActionMenu.ts:194,321). Deleting the newly created draft therefore reaches onSelect with its deleted ID, potentially adding an invalid relation or replacing an existing one. Distinguish deletion notifications from successful saves in the slideout result contract, and select only after a save.
- Unwrap the Yii2 adapter's jQuery trigger before using it as the
slideout opener, so the store can find its panel.
- A full relation field has no Add button, so it offers its own root as
the trigger, and a slideout opened from its modal nests in the panel
the field is in rather than replacing it.
- Relate the new entry only on a save that returns an undeleted element,
not when its draft is deleted from the panel's action menu.
- Search results clear an abandoned Replace in the jQuery inputs.
Amp-Thread-ID: https://ampcode.com/threads/T-e712b71f-c4c3-44f5-a7d6-ebcba118cb03
Co-authored-by: Amp <amp@ampcode.com>
Re the previously missed issue (deleted drafts selected as saved entries): fixed in 519cd66. NewEntryButton.vue now relates the entry only when the save payload includes an element without dateDeleted, so neither the deletion manager's bare saved() nor a destructive action's response selects it. The modal test now asserts both are ignored.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
A missing opener can cause the new-entry flow to replace the parent editor instead of nesting, while save callbacks can select the original draft or an entry excluded by the field’s selection condition.
No open issues 🎉
3 previously missed issues
In code that hasn't changed since last review
Fall back to the field container when resolving the opener
For a full class-backed relation field, both inputs’ getNextLogicalFocusElement() still look for .action-btn, which current chip menu invokers do not have, so this opener can be null. Replace → New entry inside a slideout can then replace the parent editor instead of nesting, because the still-focused selector is mounted outside that panel. Add a connected field/container fallback to the trigger callbacks in both resources/js/modules/element-select-input/base-element-select-input.ts:1139 and yii2-adapter/resources/js/element-select-input/base-element-select-input.ts:1144, handling the adapter’s empty jQuery collection explicitly.
Use the saved entry ID and title for slideout selection
Choosing “Save as a new entry” in the slideout returns a different record in data.element, but this callback relates the original unfinished draft while displaying the saved entry’s title. Use the saved element’s ID for the selection and fallback label; the one-shot chosen guard otherwise prevents later saves from correcting the relation.
Validate saved entries against the relation selection condition
A relation field restricted to one entry type can still offer other types from the same section, and this callback unconditionally relates them after saving. The index applies selectionCondition separately (ContentIndexViewModel.php:1341), but the creation menu only filters by sources and site. Validate the saved entry against the field’s selection condition before calling onSelect, and show a clear explanation if it cannot be selected.
- Relate the entry the slideout saved, which "Save as a new entry" makes
a different one than the draft.
- Hide the button when the field has a selection condition: the modal is
closed while the entry is edited, so nothing could check it against the
condition before relating it.
- Give a full field's selector its container as the opener, since its
chips' action menus have no `.action-btn` to fall back on, so a
slideout opened from Replace still nests in the field's panel.
Amp-Thread-ID: https://ampcode.com/threads/T-e712b71f-c4c3-44f5-a7d6-ebcba118cb03
Co-authored-by: Amp <amp@ampcode.com>
Addressed the three "previously missed" items from the latest review in d8671ce:
Opener fallback for full fields: confirmed. Current chips render a craft-action-menu invoker with no .action-btn, so getNextLogicalFocusElement() returned null (core) or an empty jQuery set (adapter) once the field was full. Both inputs' triggerElement callbacks now fall back to the field container, with tabindex="-1" as the existing fallback sets. getLastActionBtn() itself is unchanged, since other focus paths use it.
"Save as a new entry": the slideout callback now relates data.element.id, and takes its siteId and title from the saved element, instead of the draft's.
Selection condition: the New entry button now stays hidden when the field has a selection condition. The modal is closed while the entry is edited, so nothing could check the saved entry against the condition before relating it. Craft 5 kept its modal open and re-ran the index search, which quietly left out entries the condition excluded. Validating instead would need a new endpoint; that can follow if the button is wanted on conditioned fields.
Resolves the overlap with #20653's focus return: the modal's `returnFocus` now also opens the New entry slideout, and a full field's trigger falls back from the chosen element to the field itself.
Amp-Thread-ID: https://ampcode.com/threads/T-e712b71f-c4c3-44f5-a7d6-ebcba118cb03
Co-authored-by: Amp <amp@ampcode.com>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Missing opener context can close the parent editor instead of nesting the new-entry slideout, while removed or non-focusable return targets can prevent focus restoration after saving or canceling.
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 0ca0dee. The Link field now passes its Choose button as triggerElement, and the slideout store's originPanel() looks past shadow roots (through closestComposed(), now exported from focus-return), so the new entry's slideout nests in the panel the Link field is in. I didn't use the craft-link-field host itself because it can't take focus, and isFocusable() would still accept it as a return target. In the compact (popover) Link control the button is hidden while the selector is open, so focus falls back to the popover's invoker the way it already does for menus.
The reason will be displayed to describe this comment to others. Learn more.
Followed up in b6ea601 for the popover case. The Link control's own popover is teleported into the panel, so 0ca0dee already covered it. The Markdown field's link popover, though, is appended to <body> and anchored to its toolbar button, so an opener inside it still had no panel ancestry. originPanel() now follows a popover or menu to its invoker (through menuInvokerOf(), now exported from focus-return) until it finds a [data-slideout-id], and also looks past shadow roots. A test covers an opener in a body-mounted popover anchored inside a panel.
- The Link field's element selector now passes its Choose button as the
trigger, and slideouts find the panel an opener is in across shadow
roots, so a new entry created from a Link field inside a slideout nests
there instead of replacing the parent editor.
- The New entry slideout asks the field where focus goes again once it
closes: relating the entry can fill the field, taking its Add button
away.
- A full relation field's trigger falls back to the chip being replaced,
or its last chip, before the field itself, which can't take focus.
Amp-Thread-ID: https://ampcode.com/threads/T-e712b71f-c4c3-44f5-a7d6-ebcba118cb03
Co-authored-by: Amp <amp@ampcode.com>
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Human review is needed to assess selection-failure recovery and popover ownership edge cases that can leave saved entries unrelated or disrupt the parent editor.
The Markdown field's link popover is mounted on <body> and anchored to its
toolbar button, so a slideout opened from inside it, such as a new entry
from the Link field's selector, found no panel above it and replaced the
parent editor. The store now follows a popover or menu to its invoker
when working out which panel an opener is in.
Amp-Thread-ID: https://ampcode.com/threads/T-e712b71f-c4c3-44f5-a7d6-ebcba118cb03
Co-authored-by: Amp <amp@ampcode.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Craft 6's Vue element selector modal never got Craft 5's “New {section} entry” button, so entries couldn't be created from a relation field's modal.
<craft-element-selector-modal> <ElementPicker> # now also publishes its sources and site <div slot="secondary-actions"> <AssetUploadButton> # assets + <NewEntryButton> # entries: one button, or a menu of entry typesIt offers the sections the modal's index shows that the user can publish in on the index's site. These are the same rules as the entry index's New entry button, now shared through
useCreatableEntryTypes().The selector is a top-layer
<dialog>, and slideouts open beneath it, so the flow differs from Craft 5's:This is how Craft 5's search-input “Create entry” option behaved. In a section whose entries need approval, the slideout's primary action saves the draft for review, so the field relates the entry while it's still an unpublished draft. The button stays away when the field has a selection condition: the modal is closed while the entry is edited, so nothing could check the saved entry against the condition before relating it. The rest of #20598, the relation field's missing search input, isn't covered here.
Evidence
expected undefined to be 'New Authors entry'.After: the footer shows “+ New Pages entry” (or a “New entry” menu grouped by section with
sources: '*'). Creating an entry from it opens a slideout, and saving it relates the entry to the field, with focus back on Add an entry. Cancelling the slideout relates nothing. Keyboard: Tab reaches the button, Enter opens the slideout.vp test run(element-selector-modal, NewEntryButton, content index): 157 passed.vue-tsc --noEmitpasses.Screenshots aren't attached. I couldn't upload them to GitHub from here.
Related issues