Skip to content

[6.x] PHP UI components#19248

Open
brianjhanson wants to merge 52 commits into
6.xfrom
feature/cp-ui-components
Open

[6.x] PHP UI components#19248
brianjhanson wants to merge 52 commits into
6.xfrom
feature/cp-ui-components

Conversation

@brianjhanson

@brianjhanson brianjhanson commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

A follow on to #19215 but the how is much more interesting than the what.

This establishes a set of PHP UI Components that have a fluent API which somewhat closely resembles the way Filament approaches this problem. These components are all backed by our web components in the @craftcms/cp package and have the same set of functionality. I've explored automatically generating these classes in #19032 but for the moment these are written by hand. I think someday we may be able to generate them, but it also may not prove to be worth the overhead.

With new components joining the party, how we handle legacy code becomes a bit of a challenge. In an ideal world, all of the CP would use the new rendering path, but we don't to break all of the forms.* calls out there in the world.

The solution in this PR is to add a bit of a translation layer in the FormFields class. Let's look at the button for example.

If you have some twig code that loads the macro and creates a button, it probably looks like this.

{% import '_includes/forms.twig' as forms %}

{{ forms.button({
  name: 'myButton',
  id: 'my-button',
  label: "I'm a button!"|t('app'),
}) }}

Previously, forms.button would go through a few layers and end up at the _includes/forms/button.twig file to eventually output our button element.

The change here is that the _includes/forms/button.twig internals have been replaced with {{- craft.cp.button(_context)|raw -}}. That CP function calls the Cp::button() method which calls FormFields::buttonHtmlFromConfig(), which takes the legacy configuration and maps it into the new Button PHP UI component.

That general pattern is followed for ~10 initial components here.

FormFields::*FromConfig() methods are established as the translation layer between the legacy configurations and the new UI components.

The bulk of the work here is creating PHP components with a fluent API that can be used to render our web components from the PHP and twig. To facilitate the migration, our twig includes will now call a corresponding helper in FormFields which will map old config values to the new API and render the component.

The other goal of this is to have all our render contexts covered off on ways to render these components.

Twig

{{ ui('button', {
  appearance: 'plain', 
  label: 'Advance'|t('app'), 
  icon: 'chevron-down' 
}) }}

PHP

Button::make()
    ->label(t('Advanced'))
    ->appearance(Appearance::Plain)
    ->icon('chevron-down')
    ->toHtml();

Blade / HTML / Vue

<craft-button appearance="plain" icon="chevron-down">{{ t('Advanced') }}</craft-button>

Vanilla JS

const button = document.createElement('craft-button');
button.appearance = 'plain';
button.icon = 'chevron-down';
button.innerText = 'Advanced';

document.body.appendChild(button);

Component Updates

  • The <craft-action-menu/> now has a searchable attribute that will show a search field and allow you to filter the available options. Storybook
  • <craft-button/> will now apply better styling between icon options. In practical terms, that means you can display a button with only an icon either with <craft-button icon="x" aria-label="Delete"></craft-button> or <craft-button icon><craft-icon name="x" label="Delete"></craft-icon></craft-button>
  • <craft-checkbox-group/>, <craft-radio-group/> added
  • <craft-disclosure/> API and component has changed. It now inherits from LionCollapsible
  • <craft-field/> component added for situations where you need a label, help-text, etc. but the input focused web components are too simplistic.
  • <craft-reorder-button/> now supports an orientation which will let you render the options as "Move forward" or "Move backward" instead of "Move up" and "Move down"

Module updates

  • GroupedEntryTypeManager ported
  • ComponentSelect ported

Related

#19247

brianjhanson and others added 29 commits July 9, 2026 14:31
A generic form-field shell on Lion's FormControlMixin: renders the standard
CP field chrome (label with required/translation indicators, instructions,
tip/warning callouts, error feedback, status badge, read-only badge) around
any slotted control, without owning the control's value or validation.
Mirrors the server-side field wrapper, including its describedBy wiring and
heading structure (heading-prefix/suffix slots included).

The wrapper generator gains a "chrome components" category for shells like
this one: no v-model; just an error prop bridged into the feedback slot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fluent PHP renderers for the @craftcms/cp web components, following the
shape explored in #19032 so hand-written and (eventually) manifest-generated
components share one API:

- ViewComponent: make(), config-array configure() (with a guard rejecting
  getter/rendering method names), always-merge attributes(), a slot pipeline
  with lazy resolution, nested-component auto-slotting, default-slot child
  lists, encode-by-default strings (Htmlable/Twig Markup pass through), and
  root-element slot attachment. Components render markup directly by
  default; a Blade view can be opted into per component.
- Filament-style callback properties: every setter accepts a Closure,
  resolved at render time with name/type/container injection.
- Concerns (HasVariant/HasAppearance/HasSize/HasId/HasDisabled) with
  get*/is* evaluated getters, and CP-scoped Variant/Appearance/Size enums
  validating string input from templates.
- Components: Field, Callout, FieldGroup, Lightswitch.
- ui() helper (PHP/Blade) and CP-mode `ui` Twig function over a
  plugin-extensible ComponentRegistry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
FormFields::fieldHtml() now builds the Field component (retiring the
c::forms.field Blade view and its notice partial), and the lightswitch
helpers build Lightswitch via a config mapper that preserves the legacy
variable semantics. Config preprocessing (ViewErrorBag unwrapping,
template:/callable input resolution, site orientation, action-menu and
copy-button composition) stays in FormFields; server-side describedBy
defaults are gone since the web component wires aria at runtime.

lightswitch.twig becomes the first strict glue template: it maps the legacy
variable API onto ui('lightswitch'), and unsupported params (descriptionId)
log a deprecation rather than being silently dropped. Template contract
tests pin both the legacy-API rendering and the deprecation behavior —
the pattern for the eventual migration of the forms templates into the
yii2-adapter.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The legacy-to-component key mapping existed twice: in the glue template's
Twig hash and in FormFields::lightswitchFromConfig(). The template now
passes its context to craft.cp.lightswitch() (mirroring craft.cp.field()),
so the legacy semantics live once in PHP, and the template keeps only the
Twig-specific concerns: the attr block and the deprecation guards.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Button is the first component composing the full concern stack
(variant/appearance/size/disabled/id), with label as the default slot,
prefix/suffix slots for icon markup (trust-wrapped after closure
evaluation, so Twig Markup from legacy callers passes through), and
link-mode rendering via href.

ButtonGroup wraps its Button children in the craft-listbox that owns
selection behavior, posting the selected value through a hidden input;
group-level appearance/size defaults and the active state are applied
per option by the FormFields config mapper.

The legacy templates become pure delegations to craft.cp.button()/
craft.cp.buttonGroup(). Legacy semantics preserved in the mappers:
readOnly maps to disabled plus the read-only class, spinner is absorbed
(the web component renders its own spinner while loading), and the
busy/failure/retry/success messages pass through as data attributes for
the legacy submit JS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The native input and label render in the light DOM inside
<craft-checkbox> (a Lion styling shell), so namespacing, posting and
Craft.FieldToggle hooks keep working. Legacy semantics preserved: the
always-post hidden input for scalar names (array names post nothing when
unchecked), checkboxLabel over label, aria-labelledby suppression when an
aria-label is configured, icon/color label chips, markdown info popovers,
and custom-option mode (the mapper supplies the text input, keeping
text-template knowledge out of the component).

checkbox.twig becomes glue over craft.cp.checkbox(); checkboxGroup and
checkboxSelect flow through it via their existing includes until their own
ports. Adds a public FormFields::checkboxHtml() alongside the field
variant.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
craft-button extends LionButtonSubmit, whose default type is `submit` —
suppressing type="button" as a redundant default made every plain button
(e.g. the image editor's orientation button group) submit its surrounding
form. The type attribute now always renders: the configured type normally,
and `button` in link mode, matching what the web component forces
internally for links.

Pins the regression in the buttonGroup template contract test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The *FromConfig mappers now log deprecations for legacy config keys the
new components no longer support or support with changed behavior, via a
shared FormFields::deprecateConfig() helper — covering the PHP and Twig
paths at once, so the per-template {% deprecated %} guard moves out of
lightswitch.twig and into the mapper.

Audit results: lightswitch `descriptionId` (dropped; the switch describes
itself) and button `spinner` (the web component renders its own spinner
while loading) are the only mismatches. Faithfully mapped keys — button
`readOnly` (disabled + read-only class, matching legacy), buttonGroup
`static` (folded into disabled, as the legacy template did), and all of
checkbox's keys — deliberately don't warn.

Core's inert `spinner: true` usages are removed so the Deprecations
utility only surfaces plugin callers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown

brianjhanson and others added 7 commits July 14, 2026 15:50
The manager runs one DragSort across every group's chip list so a chip can be dragged between groups; each craft-component-select releases its own sorter (releaseSort) to the coordinator. Merged cleanly with the multi-select fold (disjoint regions of component-select.ts).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Restores the per-field entry-type name/handle/description override slideout (legacy Craft.EntryTypeSelectInput createSettings/applySettings) on the Matrix web-component path: a gear Settings chip action opens Craft.Slideout, writes overrides into the chip's {id,group,...} JSON, and refreshes label + indicators. Wired via the manager's define-chip-actions listener. Merged with the multi-select + cross-group-drag folds (code disjoint; only README needed resolving).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
entryTypeSelect.twig now defaults to the web-component path (jsClass ?? false) instead of the legacy jQuery class; deletes EntryTypeSelectInput.js and drops it from the legacy bundle include (its per-field override editor is now ported to the web-component path). Matrix is the only core consumer and already passed jsClass:false. Salvaged the file deletion from an abandoned session and completed the flip + bundle removal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Multi-word @Property fields without an explicit attribute silently observe a lowercased attribute name (accessibleName -> accessiblename), so kebab-case server-emitted attributes were ignored. Fixes the live craft-button accessible-name a11y bug and a craft-copy-button tooltip-label mismatch, migrates craft-action-item icon-color usages, and future-proofs the rest. Makes the custom-elements manifest authoritative (attribute names now match the DOM), unblocking the drift-detection spelling check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two guards over the Custom Elements Manifest: (1) a manifest-wide CONVENTION guard failing on any @Property with a camelCase name and no explicit attribute: (the silent accessibleName->accessiblename class of bug), and (2) an exact-name COVERAGE check that every registered ViewComponent emits each of its element's attributes, with a tripwire for the known craft-button rel/download gap. Requires 'npm run build:manifest' first (CI must regenerate the dist manifest).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
brianjhanson and others added 2 commits July 15, 2026 10:40
Extends the buttonHtml removal to the rest of the component-backed form
fields (buttonGroup, checkbox, checkboxSelect, radio, radioGroup,
checkboxGroup, lightswitch): drops each thin `*Html` wrapper and promotes
its `*FromConfig` twin to public as the recognized legacy-config->component
translation layer. Callers (the craft.cp.* Twig variables, the yii2-adapter
shims, and the two internal PHP sites) now call `*FromConfig(...)->toHtml()`
directly. The `*FieldHtml` layout wrappers and the template-backed inputs
(select/text/date/etc., which have no component yet) are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@brianjhanson
brianjhanson marked this pull request as ready for review July 15, 2026 16:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces a new PHP-side CP UI component system (fluent API) that renders the existing @craftcms/cp web components, and adds a legacy-variable translation layer so existing forms.* Twig macros can keep working while gradually migrating to the new rendering path.

Changes:

  • Added a PHP UI component registry + ui() helper/Twig function, plus enums/traits for consistent fluent configuration and closure evaluation.
  • Rewired several legacy _includes/forms/* templates (button, checkbox, lightswitch, radio, and related group/select templates) to render via craft.cp.*FromConfig() → new PHP components.
  • Updated CP/legacy JS + web components for SSR/light-DOM adoption (checkbox/radio + groups), sortable checkbox select compatibility, and fragment rendering/container changes; expanded unit test coverage.

Reviewed changes

Copilot reviewed 168 out of 170 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/Unit/View/HtmlStackTest.php Adds regression coverage to ensure buffered bundles are only emitted once per request and across captures.
tests/Unit/Twig/Templates/RadioTemplateTest.php Verifies legacy radio include renders the <craft-radio> SSR surface correctly (including custom option mode).
tests/Unit/Twig/Templates/RadioGroupTemplateTest.php Verifies legacy radioGroup include renders <craft-radio-group> correctly (checked option + fieldtoggle wiring).
tests/Unit/Twig/Templates/LightswitchTemplateTest.php Verifies legacy lightswitch include renders <craft-switch> and logs a deprecation for unsupported params.
tests/Unit/Twig/Templates/CheckboxTemplateTest.php Verifies legacy checkbox include renders <craft-checkbox> SSR correctly and handles aria/label precedence.
tests/Unit/Twig/Templates/CheckboxSelectTemplateTest.php Verifies legacy checkboxSelect include renders new markup and preserves All/sortable behaviors.
tests/Unit/Twig/Templates/CheckboxGroupTemplateTest.php Verifies legacy checkboxGroup include renders <craft-checkbox-group> and merges attr-block attributes.
tests/Unit/Twig/Templates/ButtonTemplateTest.php Verifies legacy button/buttonGroup includes render web components and map readOnly/busy-message attributes.
tests/Unit/Cp/Components/UiTest.php Tests component configure() mapping and ui() helper + Twig integration/escaping semantics.
tests/Unit/Cp/Components/LightswitchTest.php Tests Lightswitch component SSR output, slotting, ARIA, hidden input semantics, and closure evaluation.
tests/Unit/Cp/Components/FieldGroupTest.php Tests <craft-field-group> PHP component child rendering + gap custom property behavior.
tests/Unit/Cp/Components/CheckboxTest.php Tests Checkbox component SSR output, always-post input rules, labels, custom-option mode, and closure evaluation.
tests/Unit/Cp/Components/CheckboxSelectTest.php Tests CheckboxSelect component fieldset/All/hidden-input/sortable wrapper behaviors.
tests/Unit/Cp/Components/CheckboxGroupTest.php Tests CheckboxGroup component container/hidden input/custom option template + add-option button behavior.
tests/Unit/Cp/Components/CalloutTest.php Tests Callout component attributes, encoding/trusted content, enums, and closure evaluation.
tests/Unit/Cp/Components/ButtonTest.php Tests Button component attributes (type/link/state), slots, encoding/trusted markup, and closure evaluation.
tests/Unit/Cp/Components/ButtonGroupTest.php Tests ButtonGroup listbox wrapper + hidden input posting and attribute merging.
src/View/LegacyAssets/InternalAssetRegistry.php Changes bundle-flush behavior to avoid re-emitting already-flushed bundles in a request.
src/Twig/Variables/Cp.php Adds craft.cp.* helpers that render new components from legacy config arrays.
src/Twig/Extensions/CpExtension.php Registers the ui() Twig function as HTML-safe.
src/Http/Controllers/App/RenderController.php Makes menuId optional in components endpoint validation consumption.
src/helpers.php Adds global ui(string $name, array $config = []) helper backed by ComponentRegistry.
src/Field/Matrix.php Updates entry type manager/select rendering path (component-select + template namespacing behavior).
src/Field/Link.php Replaces legacy Advanced toggle with <craft-disclosure> + new Button component usage.
src/Field/Icon.php Wraps settings in <craft-field-group> and ports Advanced toggle to <craft-disclosure> + Button.
src/Field/Concerns/ProvidesLinkField.php Wraps link-type settings HTML in <craft-field-group> for consistent chrome/layout.
src/Field/Color.php Wraps Color field settings in <craft-field-group> (note: contains a closing-tag bug called out in comments).
src/Field/ButtonGroup.php Wraps settings in <craft-field-group> and switches ButtonGroup input rendering to component-based path.
src/Field/BaseOptionsField.php Wraps options setting UI in <craft-field-group> and sets editable table width default.
src/Cp/Enums/Variant.php Adds CP-wide semantic Variant enum shared with web components.
src/Cp/Enums/Size.php Adds CP-wide Size enum.
src/Cp/Enums/Appearance.php Adds CP-wide Appearance enum.
src/Cp/Concerns/HasVariant.php Adds fluent + validated variant() support for PHP components.
src/Cp/Concerns/HasSize.php Adds fluent + validated size() support for PHP components.
src/Cp/Concerns/HasId.php Adds fluent id() support for PHP components.
src/Cp/Concerns/HasDisabled.php Adds fluent disabled() support for PHP components.
src/Cp/Concerns/HasAppearance.php Adds fluent + validated appearance() support for PHP components.
src/Cp/Concerns/EvaluatesClosures.php Adds Filament-style closure evaluation with named/typed/container injection.
src/Cp/Components/RadioGroup.php Adds PHP RadioGroup component mapping to <craft-radio-group>.
src/Cp/Components/Radio.php Adds PHP Radio component (Checkbox-derived) mapping to <craft-radio>.
src/Cp/Components/FieldGroup.php Adds PHP FieldGroup component mapping to <craft-field-group>.
src/Cp/Components/ComponentRegistry.php Adds singleton registry for mapping template component names → PHP component classes.
src/Cp/Components/ChoiceGroup.php Adds base component for checkbox/radio group-like containers.
src/Cp/Components/CheckboxSelect.php Adds PHP CheckboxSelect component mapping legacy checkboxSelect template semantics.
src/Cp/Components/CheckboxGroup.php Adds PHP CheckboxGroup component including custom option JS wiring.
src/Cp/Components/Callout.php Adds PHP Callout component mapping to <craft-callout>.
src/Cp/Components/ButtonGroup.php Adds PHP ButtonGroup component mapping to <craft-button-group> wrapped in <craft-listbox>.
src/Condition/BaseLightswitchConditionRule.php Switches condition rule UI to use lightswitchFromConfig() component output.
resources/views/forms/field.blade.php Removes Blade field wrapper view (field chrome moves to web-component rendering path).
resources/views/forms/field-notice.blade.php Removes Blade field notice view (tip/warning chrome moves to web-component rendering path).
resources/templates/users/_passkeys.twig Removes legacy spinner config usage from button macro calls.
resources/templates/users/_auth-methods.twig Removes legacy spinner config usage from button macro calls.
resources/templates/dashboard/_index.twig Removes legacy spinner config usage from submitButton macro call.
resources/templates/_special/licensing-issues.twig Removes legacy spinner config usage from button macro call.
resources/templates/_includes/forms/radioGroup.twig Replaces legacy template internals with craft.cp.radioGroup(_context) translation layer.
resources/templates/_includes/forms/radio.twig Replaces legacy template internals with craft.cp.radio(_context) translation layer.
resources/templates/_includes/forms/lightswitch.twig Replaces legacy template internals with craft.cp.lightswitch(_context) translation layer.
resources/templates/_includes/forms/fld/custom-field-settings.twig Ports component-select change handler from jQuery .data() to native DOM APIs.
resources/templates/_includes/forms/entryTypeSelect.twig Defaults jsClass to false to prefer self-booting web-component path.
resources/templates/_includes/forms/checkboxSelect.twig Replaces legacy template internals with craft.cp.checkboxSelect(_context) translation layer.
resources/templates/_includes/forms/checkboxGroup.twig Replaces legacy template internals with craft.cp.checkboxGroup(_context) translation layer.
resources/templates/_includes/forms/checkbox.twig Replaces legacy template internals with craft.cp.checkbox(_context) translation layer.
resources/templates/_includes/forms/buttonGroup.twig Replaces legacy template internals with craft.cp.buttonGroup(_context) translation layer.
resources/templates/_includes/forms/button.twig Replaces legacy template internals with craft.cp.button(_context) translation layer.
resources/templates/_includes/forms.twig Sets editable table field default width to full.
resources/templates/_components/widgets/CraftSupport/body.twig Removes legacy spinner config usage from submitButton macro call.
resources/templates/_components/fieldtypes/PlainText/settings.twig Wraps related settings controls in <craft-field-group>.
resources/templates/_components/fieldtypes/Matrix/create-button.twig Removes legacy spinner config usage from button macro call.
resources/templates/_components/fieldtypes/Link/link-settings.twig Wraps Link settings in <craft-field-group> and converts per-type settings toggles to <craft-disclosure>.
resources/templates/_components/fieldtypes/Entries/settings.twig Formatting-only adjustments (indentation normalization).
resources/templates/_components/fieldtypes/elementfieldsettings.twig Adds maxlength to min/max relations fields and migrates Advanced section to <craft-disclosure>.
resources/templates/_components/fieldtypes/Date/settings.twig Formatting-only adjustments (indentation normalization).
resources/templates/_components/fieldtypes/Assets/settings.twig Refactors upload-location settings layout, adds field groups and an “Allowed Kinds” legend.
resources/templates/_components/fieldtypes/Addresses/settings.twig Migrates “View Mode” wrapper to <craft-field> web component with slot-based input.
resources/templates/_components/auth/methods/TOTP/form.twig Removes legacy spinner config usage from submitButton macro call.
resources/templates/_components/auth/methods/RecoveryCodes/setup.twig Removes legacy spinner config usage from submitButton macro call.
resources/templates/_components/auth/methods/RecoveryCodes/form.twig Removes legacy spinner config usage from submitButton macro call.
resources/js/pages/settings/fields/Edit.vue Wraps legacy settings fragment rendering in craft-field-group via HtmlFragmentRenderer as prop.
resources/js/modules/sortable-checkbox-select/sortable-checkbox-select.ts Supports SSR <craft-checkbox> markup by reading native input checked state and binding listeners via descendant matching.
resources/js/modules/sortable-checkbox-select/README.md Renames .wc.ts reference to .ce.ts for custom element file naming.
resources/js/modules/listbox/listbox.ts Uses shared toHtmlElement() helper for jQuery/native element unwrapping.
resources/js/modules/grouped-entry-type-manager/support.ts Adds WeakMap registry for manager instances (jQuery .data() replacement).
resources/js/modules/grouped-entry-type-manager/index.ts Registers <craft-entry-type-manager> and assigns window.Craft.GroupedEntryTypeManager shim.
resources/js/modules/grouped-entry-type-manager/grouped-entry-type-manager.ce.ts Implements controller custom element bootstrapping GroupedEntryTypeManager around SSR markup.
resources/js/modules/entry-types/components/EntryTypeChip.vue Adjusts chip prefix styling (heightmin-height) for consistent chip layout.
resources/js/modules/editable-table/editable-table.ts Expands overflow container lookup to include <craft-field> wrappers.
resources/js/modules/component-select/support.ts Adds WeakMap registry for component-select instances (jQuery .data() replacement).
resources/js/modules/component-select/index.ts Registers <craft-component-select> without adding a window.Craft.* shim.
resources/js/legacy.ts Adds grouped-entry-type-manager to legacy bundle bootstrap.
resources/js/cp.ts Adds component-select and grouped-entry-type-manager modules to CP bootstrap.
resources/js/common/layouts/AppLayout.vue Changes main wrapper to always render a <form> (commented as problematic due to nested forms).
resources/js/common/components/HtmlFragmentRenderer.vue Adds as prop to control container tag and makes fragment re-append sensitive to container identity changes.
packages/craftcms-legacy/cp/src/css/_main.scss Removes legacy checkbox styling and adds chip list-item layout fix for component-select.
packages/craftcms-legacy/cp/src/Craft.js Removes legacy EntryTypeSelectInput/GroupedEntryType* imports from legacy bundle.
packages/craftcms-garnish/tests/utils.test.ts Adds tests for new DOM utility functions (nearestSibling, closestRegistered).
packages/craftcms-garnish/tests/select.test.ts Adds selection model tests for new/ported Select class.
packages/craftcms-garnish/src/utils/index.ts Re-exports new DOM utils (nearestSibling, closestRegistered).
packages/craftcms-garnish/src/utils/dom.ts Adds nearestSibling and closestRegistered utilities.
packages/craftcms-garnish/src/index.ts Exposes Select in public API and Garnish global export.
packages/craftcms-garnish/src/compat.ts Adds toHtmlElement() helper for safe jQuery/native unwrapping.
packages/craftcms-garnish/README.md Documents Select API and porting/usage notes.
packages/craftcms-garnish/docs/api-reference.md Adds Select API reference documentation.
packages/craftcms-cp/src/utilities/dom.ts Extends serializeFormInputs to include host-valued custom elements (Lion-style modelValue/serializedValue/value).
packages/craftcms-cp/src/utilities/dom.test.ts Adds tests covering custom-element serialization semantics.
packages/craftcms-cp/src/styles/form.styles.ts Makes native slotted inputs span full width by default.
packages/craftcms-cp/src/index.ts Exports ReorderOrientation type.
packages/craftcms-cp/src/components/select-color/select-color.ts Reflects modelValue from model-value attribute (kebab-case).
packages/craftcms-cp/src/components/reorder-button/reorder-button.ts Adds horizontal orientation support (forward/backward labels + RTL-aware icons).
packages/craftcms-cp/src/components/reorder-button/reorder-button.stories.ts Adds Storybook controls/story for horizontal orientation.
packages/craftcms-cp/src/components/radio/radio.ts Adds SSR choice-input hydration mixin to radio.
packages/craftcms-cp/src/components/radio/radio.test.ts Adds SSR hydration tests for craft-radio.
packages/craftcms-cp/src/components/radio-group/radio-group.ts Adopts group name from SSR inputs to prevent Lion from stripping names; preserves SSR checked state.
packages/craftcms-cp/src/components/radio-group/radio-group.test.ts Adds SSR name adoption + modelValue aggregation tests.
packages/craftcms-cp/src/components/radio-group/radio-group.stories.ts Adds Storybook stories including SSR markup example.
packages/craftcms-cp/src/components/input/input.ts Adds width attribute and sets intrinsic size from maxlength.
packages/craftcms-cp/src/components/input/input.styles.ts Implements width/maxlength shrink-vs-full-width behavior.
packages/craftcms-cp/src/components/input/input.stories.ts Adds stories for width override behaviors.
packages/craftcms-cp/src/components/field/field.styles.ts Adds <craft-field> styles including status badge, chrome layout, error hooks, and width behaviors.
packages/craftcms-cp/src/components/field/field.stories.ts Adds Storybook stories for <craft-field> component usage and states.
packages/craftcms-cp/src/components/disclosure/disclosure.ts Rebuilds disclosure on LionCollapsible and adds default invoker behavior via label.
packages/craftcms-cp/src/components/disclosure/disclosure.stories.ts Adds Storybook story verifying default invoker behavior.
packages/craftcms-cp/src/components/copy-button/copy-button.ts Maps tooltipLabel to tooltip-label attribute (kebab-case).
packages/craftcms-cp/src/components/checkbox/checkbox.ts Adds SSR hydration + patches slotted input property writes (checked/disabled) to keep Lion host state in sync with jQuery .prop().
packages/craftcms-cp/src/components/checkbox-group/checkbox-group.ts Adopts group name from SSR inputs to prevent Lion from stripping names; preserves SSR checked state.
packages/craftcms-cp/src/components/checkbox-group/checkbox-group.test.ts Adds SSR name adoption + modelValue aggregation tests.
packages/craftcms-cp/src/components/checkbox-group/checkbox-group.stories.ts Adds Storybook stories including SSR markup example.
packages/craftcms-cp/src/components/button/button.ts Adds kebab-case attribute mapping for accessible-name.
packages/craftcms-cp/src/components/button/button.styles.ts Adjusts icon-only styling selector (commented as problematic for icon="name" usage).
packages/craftcms-cp/src/components/badge-indicator/badge-indicator.ts Maps several properties to kebab-case attributes.
packages/craftcms-cp/src/components/action-menu/action-menu.types.ts Adds keywords support for searchable menus.
packages/craftcms-cp/src/components/action-menu/action-menu.stories.ts Adds Storybook examples for searchable action menus (slot + data-driven).
packages/craftcms-cp/src/components/action-item/action-item.ts Enables delegatesFocus and maps some properties to kebab-case attributes.
packages/craftcms-cp/src/components/action-item/action-item.styles.ts Ensures [hidden] action items are not displayed.
packages/craftcms-cp/scripts/generate-vue-wrappers.js Adds “chrome-only” wrapper generation (e.g. craft-field) bridging an error prop into feedback slot.
AGENTS.md Documents the standard module-porting pattern for migrating legacy jQuery modules into modern TS + custom elements.

Comment thread resources/templates/_components/fieldtypes/Link/link-settings.twig Outdated
Comment thread resources/templates/_components/fieldtypes/elementfieldsettings.twig Outdated
Comment on lines 253 to 255
<main id="main" tabindex="-1">
<component
:is="form ? 'form' : 'div'"
method="post"
@submit.prevent="save()"
>
<form method="post" @submit.prevent="form && save()">
<slot name="header">
Comment thread src/Field/Color.php Outdated
Comment thread packages/craftcms-cp/src/components/button/button.styles.ts Outdated
@brianjhanson
brianjhanson force-pushed the feature/cp-ui-components branch from 1703bce to a2450db Compare July 15, 2026 19:27
@brandonkelly

brandonkelly commented Jul 16, 2026

Copy link
Copy Markdown
Member

FormFields::*FromConfig() methods are established as the translation layer between the legacy configurations and the new UI components.

Should those methods be defined somewhere in yii2-adapter then? (And probably everything in _includes too?)

@brianjhanson

Copy link
Copy Markdown
Contributor Author

FormFields::*FromConfig() methods are established as the translation layer between the legacy configurations and the new UI components.

Should those methods be defined somewhere in yii2-adapter then? (And probably everything in _includes too?)

That's the goal, we're just not quite ready yet. We still have too much twig in core that has old configuration objects. Moving this into the adapter now would mean any forms.* twig calls would error when the FormFields::*FromConfig method is trying to be called.

I figure that will be one of the many cleanup passes once we have the CP fully migrated to Inertia / Vue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants