From 2a7e93d97805efaade915a8884ba97b6c11b419f Mon Sep 17 00:00:00 2001 From: Klaus Zanders Date: Thu, 28 May 2026 17:21:10 +0200 Subject: [PATCH] Add compatability for Work Package Queries --- app/models/query.rb | 9 ++++++++ lookbook/docs/patterns/11-filter-forms.md.erb | 19 ++++++++++++++++ spec/models/query_spec.rb | 22 +++++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/app/models/query.rb b/app/models/query.rb index ed9b540a515..f8c2e313a1d 100644 --- a/app/models/query.rb +++ b/app/models/query.rb @@ -208,6 +208,15 @@ class Query < ApplicationRecord filters.delete_if { |f| f.field.to_s == name.to_s } end + # Mirrors `Queries::BaseQuery#find_active_filter` so that consumers built + # on top of the modern query API (e.g. `Filters::FilterForm`) can ask any + # query — including this legacy work-package one — for its active filter + # by name. Signature kept identical to BaseQuery's (symbol arg in, filter + # or nil out). + def find_active_filter(name) + filters.detect { |f| f.name == name } + end + def normalized_name name.parameterize.underscore end diff --git a/lookbook/docs/patterns/11-filter-forms.md.erb b/lookbook/docs/patterns/11-filter-forms.md.erb index 706ff10aafc..898163bbdf2 100644 --- a/lookbook/docs/patterns/11-filter-forms.md.erb +++ b/lookbook/docs/patterns/11-filter-forms.md.erb @@ -85,3 +85,22 @@ itself — the surrounding `IndexSubHeaderComponent` does, so quick filter and advanced form share one. For standalone embeds without a co-located quick filter, `FilterForm`'s `wrap_with_controller: true` is the right default. + +## Compatibility with the legacy `Query` (work packages) + +`Filters::FilterForm` reads three things off the query: `available_advanced_filters`, +`filters`, and `find_active_filter(name)`. The first two come from the +`Queries::Filters::AvailableFilters` concern, which the legacy work-package +`Query` model also includes. `find_active_filter` is defined directly on +`Queries::BaseQuery` and used to live only there — `Query` now mirrors it +with the same signature, so passing a `Query` (or any of its subclasses) +to `FilterForm` works exactly like passing a `BaseQuery` subclass. + +What `FilterForm` does *not* do for you on the legacy side: parsing the +form submission back into a `Query#filters` collection. The work-package +filter pipeline still uses its own serialization (URL `filters=[...]` +JSON / YAML in the DB), so a controller receiving a `FilterForm` submit +either needs to use `hidden_input_name:` with a format the existing +parser understands, or translate the `operator_` / `_value` +fields by hand. The form renders fine either way; what to do with the +submitted values is the caller's call. diff --git a/spec/models/query_spec.rb b/spec/models/query_spec.rb index 4dcbb49f663..81c47965853 100644 --- a/spec/models/query_spec.rb +++ b/spec/models/query_spec.rb @@ -65,6 +65,28 @@ RSpec.describe Query, end end + describe "#find_active_filter" do + let(:query) { described_class.new } + + it "returns the active filter matching the given name" do + query.add_filter("status_id", "=", ["1"]) + + expect(query.find_active_filter(:status_id)) + .to be_a(Queries::WorkPackages::Filter::StatusFilter) + end + + it "returns nil when no filter with that name is active" do + expect(query.find_active_filter(:status_id)).to be_nil + end + + it "matches by symbol, like Queries::BaseQuery#find_active_filter" do + query.add_filter("status_id", "=", ["1"]) + + expect(query.find_active_filter("status_id")).to be_nil + expect(query.find_active_filter(:status_id)).not_to be_nil + end + end + describe "include_subprojects" do let(:query) { described_class.new name: "foo" }