feat(components): tag_field adopts phlex-reactive 0.12.2 escape-hatch sugar - #20
Merged
Conversation
… sugar ## Summary Bumps phlex-reactive to >= 0.12.2 and migrates Forms::TagField off its raw wire-contract data attributes onto the 0.12.2 escape hatches (reactive_tags(name:) + reactive_filter(input:)), resolving issue #6 Caveats 1 & 2. Two payoffs: - Render-time validation of the instance-dynamic wire name — reactive_tags(name:) rejects a blank / double-quote / backslash / control-char name (which would silently mis-bind or throw in the browser's querySelectorAll) at render. - Restores client-side type-ahead filtering. The old raw attr emitted ONLY data-reactive-filter-input; the 0.12.x client #syncFilter early-returns unless BOTH -input AND -option are present, so filtering was dead. reactive_filter(input:) emits both, so the type-ahead runs again — standalone and on the live form root. Forms::Live hoists the same wire attrs by calling reactive_tags/reactive_filter directly (it is itself a reactive component); TagField.query_id centralizes the shared "#{id}_query" derivation so the standalone and hoisted ids never drift. ## Test Coverage - tag_field_spec: emits data-reactive-filter-option="[role=option]" (the filter regression guard); a malformed wire name raises ArgumentError at render. - live_tag_field_spec: the hoisted <form> root also carries -filter-option. ## Verification - [x] bundle exec rubocop lib spec passes (clean, 87 files) - [x] bundle exec rspec passes (162 passed, 1 pending) - [x] 4-lens adversarial verification clean (byte-equivalence, live-hoist parity, plain-theme degradation, render-validation edge cases)
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
Closes #6.
Bumps phlex-reactive to
>= 0.12.2and migratesForms::TagFieldoff its raw wire-contract data attributes onto the 0.12.2 escape-hatch sugar —reactive_tags(name:)+reactive_filter(input:)— resolving issue #6 Caveats 1 & 2 (the sugar gap the issue was drafted against, since landed upstream in phlex-reactive #225).tag_fielditself already shipped in #7; this closes the remaining upstream-dependent work: dropping the raw-attr workaround now that phlex-reactive exposes the instance-dynamic escape hatches.Two payoffs
Render-time validation of the wire name.
reactive_tags(name: "user[tags]")runs the name throughverbatim_name_selector!, which rejects a blank / double-quote / backslash / control-char name at render. The raw%([name="#{name}"])interpolation had no such guard — a stray"would silently break the[name="…"]selector the client queries with, or a control char would makequerySelectorAllthrow and abort the controller connect, both only in the browser.Restores client-side type-ahead filtering (the real find). The old code emitted only
data-reactive-filter-input. The phlex-reactive 0.12.x client#syncFilterearly-returns unless bothdata-reactive-filter-inputanddata-reactive-filter-optionare present (reactive_controller.js:if (!inputSelector || !optionSelector) return). So under 0.12.x the tag widget's type-ahead was dead — the existing specs only asserted emitted markup, so nothing caught it.reactive_filter(input:)emits both selectors (optiondefaults to[role=option], exactly what the suggestion buttons use), so filtering runs again — standalone and on the live-form root.Shape of the change
Forms::TagField#root_tag_attributesis now a private instance method calling theClientBindingshelpers onself; a new publicTagField.query_id(id)centralizes the"#{id}_query"derivation so the standalone and hoisted query-input ids can't drift.Forms::Live#form_attributeshoists the wire attrs by callingreactive_tags/reactive_filterdirectly — it's itself a reactive component with those helpers — instead of routing throughTagField's (removed) class method.>= 0.12.2in the gem'sGemfile(a soft dev/test dep — no gemspec dependency) and the docs site (docs/Gemfile+ regenerateddocs/Gemfile.lock,0.11.5→0.12.2) so the live docs demo filters correctly.Test Coverage
spec/forms/tag_field_spec.rbdata-reactive-filter-option="[role=option]"— the filter-regression guard (both filter selectors present ⇒ client type-ahead runs).") raisesArgumentErrorat render — the new render-time validation.spec/forms/live_tag_field_spec.rb<form>reactive root also carriesdata-reactive-filter-option(the live path gets the fix too).Verification
bundle exec rubocop lib spec— clean (87 files)bundle exec rspec— 162 passed, 1 pendingtags-field+filter-input+filter-option, query input carries the matching id and noname(only the hidden field submits).Deviations & judgment calls
tag_fieldwas already merged (feat(components): tag_field composed from phlex-reactive tag primitives #7, commit43ca088); the staleissue-6-tag-fieldbranch was the earlier draft and was not reused — this work started fresh frommain. The genuinely-remaining task was the 0.12.2 sugar adoption the issue's own comment (2026-07-11) called for.data-reactive-filter-optionattr was inert. It isn't — the 0.12.x client needs it — so the migration is a real fix, not cosmetic. Added a spec as the regression guard.reactive_filter(input:)was the caveat-2 fix; a WebFetch of the changelog suggestedinput:might have been removed. The installed gem source settled it: bothreactive_tags(name:)andreactive_filter(input:)exist in 0.12.2 and emit exactly what this change assumes.bundle lock --update phlex-reactiveregenerateddocs/Gemfile.lockcleanly — onlyphlex-reactivemoved, valid checksum. The docs deploybundle installs fresh on release regardless.Forms::Field#field_idtransforms brackets in the scope whileForms::Form#field_iddoesn't. For live tag fields the scope is always a bracket-free param_key, so the two agree (verified); a nested-scope live tag field would be a latent edge — untouched by this change, out of scope for tag_field: composed from phlex-reactive 0.11.4's reactive_tags primitives (draft) #6.