Merge branch 'main' into logo-placeholder-update

* main:
  Need to return all collections so it's not blank
  Button to add a new filter
  Update test for new redirect
  Comparing params is fragile, use `id` for saved filters
  Update summary tests
  Improve summary display in menu
  Add keyboard navigation and filtering to quick filters
  Copy
  Keep stage when clearing
  Keep the current filter params after deleting a saved filter so you can change your mind
  Update test because we now drop all filters on destroy
  New UI for exposing filter options and saving/deleting them
  Unnecessary
  These aren't actually in the events namespace, use utility class
This commit is contained in:
Jason Zimdars
2025-06-26 20:08:54 -05:00
20 changed files with 296 additions and 180 deletions
-4
View File
@@ -122,10 +122,6 @@
text-align: center;
}
.events__popup {
margin-block-start: var(--block-space-half);
}
/* Event
/* ------------------------------------------------------------------------ */
+5 -13
View File
@@ -1,8 +1,11 @@
@layer components {
.filters {
view-transition-name: "filters";
z-index: var(--z-popup);
#header:has(&) {
position: relative;
z-index: 1;
z-index: var(--z-popup);
}
.btn {
@@ -94,19 +97,8 @@
}
}
.panel:is(.filter__popup) {
--panel-size: 100ch;
--panel-padding: var(--block-space);
inline-size: auto !important;
min-inline-size: var(--panel-size);
max-block-size: calc(90dvh - (2 * var(--block-space-double)));
max-inline-size: calc(100dvw - (2 * var(--block-space-double)));
z-index: 1;
}
.quick-filter {
&:has(input:not(.default-value):checked) {
&:not(.default-value):has(.checked) {
.input--select {
--input-background: var(--color-selected);
}
+1 -1
View File
@@ -94,7 +94,7 @@
}
&:has(input:checked) {
--btn-background: var(--color-ink-lightest);
--btn-background: transparent;
--btn-color: var(--color-ink);
.icon {
+6 -1
View File
@@ -9,6 +9,11 @@ module FilterScoped
DEFAULT_PARAMS = { indexed_by: "latest" }
def set_filter
@filter = Current.user.filters.from_params params.reverse_merge(**DEFAULT_PARAMS).permit(*Filter::PERMITTED_PARAMS)
@expand_all = params[:expand_all]
if params[:filter_id].present?
@filter = Current.user.filters.find(params[:filter_id])
else
@filter = Current.user.filters.from_params params.reverse_merge(**DEFAULT_PARAMS).permit(*Filter::PERMITTED_PARAMS)
end
end
end
+3 -2
View File
@@ -3,16 +3,17 @@ class FiltersController < ApplicationController
def create
@filter = Current.user.filters.remember filter_params
redirect_to cards_path(@filter.as_params)
redirect_to cards_path(filter_id: @filter.id)
end
def destroy
filter_params = @filter.as_params
@filter.destroy!
if request.referer == root_url
redirect_to root_path
else
redirect_to cards_path(@filter.as_params)
redirect_to cards_path(filter_params)
end
end
+8
View File
@@ -21,6 +21,14 @@ module FiltersHelper
hidden_field_tag name, value, id: nil
end
def filter_selected_collections_sentence(filter)
if filter.collections.any?
filter.collections.collect { "<strong>#{it.name}</strong>" }.uniq.sort.to_sentence
else
tag.strong "All collections"
end
end
def filter_selected_collections_label(filter)
selected_collections = if filter.collections.any?
filter.collections.collect { "<strong>#{it.name}</strong>" }.uniq.sort.to_sentence
+5 -9
View File
@@ -1,16 +1,18 @@
module Filter::Summarized
def summary
[ index_summary, tag_summary, assignee_summary, creator_summary, stage_summary, terms_summary ].compact.to_sentence + " #{collection_summary}"
[ index_summary, tag_summary, assignee_summary, creator_summary, stage_summary, terms_summary ].compact.to_sentence
end
private
def index_summary
indexed_by.humanize
unless indexed_by.latest?
indexed_by.humanize
end
end
def tag_summary
if tags.any?
"tagged #{tags.map(&:hashtag).to_choice_sentence}"
"#{tags.map(&:hashtag).to_choice_sentence}"
end
end
@@ -28,12 +30,6 @@ module Filter::Summarized
end
end
def collection_summary
if collections.any?
"in #{collections.pluck(:name).to_choice_sentence}"
end
end
def terms_summary
if terms.any?
"matching #{terms.map { |term| %Q("#{term}") }.to_sentence}"
@@ -3,7 +3,7 @@
Recently closed
</button>
<dialog class="events__popup popup panel flex-column align-start gap-half fill-white shadow"
<dialog class="margin-block-start-half popup panel flex-column align-start gap-half fill-white shadow"
aria-label="Closed with reason…" aria-description="Closed with reason…"
data-dialog-target="dialog" data-action="turbo:before-cache@document->dialog#close">
<strong class="popup__title margin-block-start-half pad-inline-half">Closed with reason…</strong>
@@ -7,7 +7,7 @@
<% end %>
</button>
<dialog class="events__popup popup panel flex-column align-start gap-half fill-white shadow"
<dialog class="margin-block-start-half popup panel flex-column align-start gap-half fill-white shadow"
aria-label="In stage…" aria-description="In stage…"
data-dialog-target="dialog" data-action="turbo:before-cache@document->dialog#close">
<strong class="popup__title margin-block-start-half pad-inline-half">In stage(s)…</strong>
+2 -2
View File
@@ -20,7 +20,7 @@
<strong class="txt-medium overflow-ellipsis margin-none">Fizzy</strong>
<% end %>
<%= tag.dialog class: "fizzy-dialog filter events__popup popup popup--animated panel flex-column align-start fill-white",
<%= tag.dialog class: "fizzy-dialog filter margin-block-start-half popup popup--animated panel flex-column align-start fill-white",
data: {
action: "turbo:before-cache@document->dialog#close keydown->navigable-list#navigate filter:changed->navigable-list#reset toggle->filter#filter",
controller: "filter navigable-list",
@@ -32,12 +32,12 @@
<%= render "events/filter/header" %>
<strong class="popup__group-title">Collections</strong>
<%= form_with url: events_path, method: :get, class: "popup__list",
data: { controller: "form" } do |form| %>
<% if Current.user.collections.many? %>
<ul class="popup__list">
<%= render "events/filter/custom_collections" %>
<strong class="popup__group-title">Collections</strong>
<%= render "events/filter/all_collections_option", form: form, filter: filter %>
<%= render partial: "events/filter/collection_option", collection: Current.user.collections.ordered_by_recently_accessed, as: :collection, locals: { form: form, filter: filter } %>
</ul>
@@ -1,11 +1,22 @@
<div class="popup__list">
<% @filters.each do |filter| %>
<div class="popup__group flex align-center full-width gap-half overflow-ellipsis" data-filter-target="item" data-navigable-list-target="item">
<%= link_to cards_path(**filter.as_params), class: "btn popup__new popup__item" do %>
<%= icon_tag "filter" %>
<span class="overflow-ellipsis"><%= filter.summary %></span>
<span class="translucent flex-item-no-shrink flex-item-justify-end"> </span>
<% end %>
<strong class="popup__group-title">Saved filters</strong>
<div class="popup__group flex align-center full-width gap-half overflow-ellipsis" data-navigable-list-target="item">
<%= link_to cards_path(expand_all: true), class: "btn popup__new popup__item" do %>
<%= icon_tag "bookmark" %>
<div class="flex flex-column txt-tight-lines min-width txt-small">
<strong class="overflow-ellipsis">Create a new filter</strong>
</div>
<span class="translucent flex-item-no-shrink flex-item-justify-end"> </span>
<% end %>
</div>
<% @filters.each do |filter| %>
<div class="popup__group flex align-center full-width gap-half overflow-ellipsis" data-navigable-list-target="item">
<%= link_to cards_path(filter_id: filter.id), class: "btn popup__new popup__item" do %>
<%= icon_tag "bookmark" %>
<div class="flex flex-column txt-tight-lines min-width txt-small">
<span class="overflow-ellipsis"><%= filter_selected_collections_sentence(filter).html_safe %></span>
<span class="overflow-ellipsis"><%= filter.summary %></span>
</div>
<span class="translucent flex-item-no-shrink flex-item-justify-end"> </span>
<% end %>
</div>
<% end %>
+69 -37
View File
@@ -1,53 +1,85 @@
<% if filter.assignees.any? %>
<% if filter.assignees.any? || @expand_all %>
<div class="quick-filter position-relative" data-controller="dialog" data-action="keydown.esc->dialog#close click@document->dialog#closeOnClickOutside">
<button class="btn input input--select flex-inline txt-x-small" data-action="click->dialog#open:stop">
<span class="overflow-ellipsis">
<% if filter.assignment_status.unassigned? %>
No one
<% elsif filter.assignees.any? %>
Assigned to <%= filter.assignees.map(&:familiar_name).to_sentence %>
Assigned to <%= filter.assignees.map(&:familiar_name).to_sentence(two_words_connector: " or ", last_word_connector: ", or ") %>
<% else %>
Assigned to…
<% end %>
</span>
</button>
<dialog class="events__popup popup panel flex-column align-start gap-half fill-white shadow" data-dialog-target="dialog" data-action="turbo:before-cache@document->dialog#close">
<strong class="popup__title margin-block-start-half pad-inline-half overflow-ellipsis">Assigned to…</strong>
<%= form_with url: cards_path, method: :get, class: "popup__list",
data: { controller: "form" } do |form| %>
<% filter.as_params.except(:assignee_ids, :assignment_status).each do |key, value| %>
<%= filter_hidden_field_tag key, value %>
<% end %>
<%= tag.dialog class: "margin-block-start-half popup panel flex-column align-start gap-half fill-white shadow txt-small", data: {
action: "turbo:before-cache@document->dialog#close keydown->navigable-list#navigate filter:changed->navigable-list#reset toggle->filter#filter",
aria: { label: "Assigned to…", aria_description: "Assigned to…" },
controller: "filter navigable-list",
dialog_target: "dialog",
navigable_list_focus_on_selection_value: false,
navigable_list_actionable_items_value: true } do %>
<strong class="popup__title txt-nowrap">Assigned to…</strong>
<%= link_to cards_path(filter.as_params.except(:assignee_ids, :assignment_status)), class: "btn popup__item" do %>
<span class="overflow-ellipsis">Clear all</span>
<% end %>
<div class="btn popup__item">
<%= form.check_box "assignment_status", {
checked: filter.assignment_status.unassigned?,
data: { action: "change->form#submit" },
include_hidden: false,
}, "unassigned" %>
<%= form.label :assignment_status, "No one", class: "overflow-ellipsis" %>
<%= icon_tag "check", class: "checked flex-item-justify-end" %>
</div>
<% User.active.order(:name).each do |user| %>
<div class="btn popup__item">
<%= form.check_box "assignee_ids[]", {
checked: filter.assignees.include?(user),
data: { action: "change->form#submit" },
include_hidden: false,
}, user.id %>
<%= form.label "assignee_ids[]", user.name, for: dom_id(user, :filter), class: "overflow-ellipsis" %>
<%= icon_tag "check", class: "checked flex-item-justify-end" %>
</div>
<% end %>
<% if User.active.many? %>
<%= text_field_tag :search, nil, placeholder: "Filter…", class: "input input--transparent txt-small", autofocus: true,
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", action: "input->filter#filter" } %>
<% end %>
</dialog>
<ul class="popup__list" data-filter-target="list">
<li class="popup__group flex align-center" data-value="no one" data-filter-target="item" data-navigable-list-target="item">
<%= form_with url: cards_path, method: :get, class: "full-width", data: { controller: "form" } do |form| %>
<% filter.as_params.except(:assignee_ids, :assignment_status).each do |key, value| %>
<%= filter_hidden_field_tag key, value %>
<% end %>
<% unless filter.assignment_status.unassigned? %>
<%= hidden_field_tag :assignment_status, "unassigned" %>
<% end %>
<% if @expand_all %>
<%= hidden_field_tag :expand_all, @expand_all %>
<% end %>
<%= form.button type: "submit", class: "btn popup__item min-width full-width unpad-inline" do %>
<span class="overflow-ellipsis flex-item-grow">No one</span>
<% if filter.assignment_status.unassigned? %>
<%= icon_tag "check", class: "checked flex-item-justify-end" %>
<% end %>
<% end %>
<% end %>
</li>
<% User.active.order(:name).each do |user| %>
<li class="popup__group flex align-center" data-value="<%= user.name.downcase %>" data-filter-target="item" data-navigable-list-target="item">
<%= form_with url: cards_path, method: :get, class: "full-width", data: { controller: "form" } do |form| %>
<% filter.as_params.except(:assignee_ids, :assignment_status).each do |key, value| %>
<%= filter_hidden_field_tag key, value %>
<% end %>
<% filter.assignees.each do |existing_assignee| %>
<% unless existing_assignee == user %>
<%= hidden_field_tag "assignee_ids[]", existing_assignee.id %>
<% end %>
<% end %>
<% unless filter.assignees.include?(user) %>
<%= hidden_field_tag "assignee_ids[]", user.id %>
<% end %>
<% if @expand_all %>
<%= hidden_field_tag :expand_all, @expand_all %>
<% end %>
<%= form.button type: "submit", class: "btn popup__item min-width full-width unpad-inline" do %>
<span class="overflow-ellipsis flex-item-grow"><%= user.name %></span>
<% if filter.assignees.include?(user) %>
<%= icon_tag "check", class: "checked flex-item-justify-end" %>
<% end %>
<% end %>
<% end %>
</li>
<% end %>
</ul>
<% end %>
</div>
<% end %>
+47 -41
View File
@@ -1,56 +1,62 @@
<% if filter.creators.any? %>
<% if filter.creators.any? || @expand_all %>
<div class="quick-filter position-relative" data-controller="dialog"
data-action="keydown.esc->dialog#close click@document->dialog#closeOnClickOutside">
<button class="btn input input--select flex-inline txt-x-small" data-action="click->dialog#open:stop">
<span class="overflow-ellipsis">
<% if filter.creators.any? %>
<%= "Added by #{filter.creators.map(&:familiar_name).to_sentence}" %>
<%= "Added by #{filter.creators.map(&:familiar_name).to_sentence(two_words_connector: " or ", last_word_connector: ", or ")}" %>
<% else %>
Added by…
<% end %>
</span>
</button>
<dialog class="events__popup popup panel flex-column align-start gap-half fill-white shadow"
aria-label="Added by…" aria-description="Added by…"
data-dialog-target="dialog" data-action="turbo:before-cache@document->dialog#close">
<strong class="popup__title margin-block-start-half pad-inline-half">Added by…</strong>
<%= form_with url: cards_path, method: :get, class: "popup__list",
data: { controller: "form" } do |form| %>
<% filter.as_params.except(:creator_ids).each do |key, value| %>
<%= filter_hidden_field_tag key, value %>
<% end %>
<%= tag.dialog class: "margin-block-start-half popup panel flex-column align-start gap-half fill-white shadow txt-small", data: {
action: "turbo:before-cache@document->dialog#close keydown->navigable-list#navigate filter:changed->navigable-list#reset toggle->filter#filter",
aria: { label: "Added by…", aria_description: "Added by…" },
controller: "filter navigable-list",
dialog_target: "dialog",
navigable_list_focus_on_selection_value: false,
navigable_list_actionable_items_value: true } do %>
<strong class="popup__title txt-nowrap">Added by…</strong>
<%= link_to cards_path(filter.as_params.except(:creator_ids)), class: "btn popup__item" do %>
<span class="overflow-ellipsis">Clear all</span>
<% end %>
<div class="btn popup__item">
<%= form.check_box :creator_ids, {
multiple: true,
checked: filter.creators.include?(Current.user),
data: { action: "change->form#submit" },
include_hidden: false,
}, Current.user.id %>
<%= form.label :creator_ids, "Me", for: form.field_id(:creator_ids, Current.user.id), class: "overflow-ellipsis" %>
<%= icon_tag "check", class: "checked flex-item-justify-end" %>
</div>
<% User.active.without(Current.user).order(:name).each do |user| %>
<div class="btn popup__item">
<%= form.check_box :creator_ids, {
multiple: true,
checked: filter.creators.include?(user),
data: { action: "change->form#submit" },
include_hidden: false,
}, user.id %>
<%= form.label :creator_ids, user.name, for: form.field_id(:creator_ids, user.id), class: "overflow-ellipsis" %>
<%= icon_tag "check", class: "checked flex-item-justify-end" %>
</div>
<% end %>
<% if User.active.many? %>
<%= text_field_tag :search, nil, placeholder: "Filter…", class: "input input--transparent txt-small", autofocus: true,
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", action: "input->filter#filter" } %>
<% end %>
</dialog>
<ul class="popup__list" data-filter-target="list">
<% User.active.order(:name).each do |user| %>
<li class="popup__group flex align-center" data-value="<%= user.name.downcase %>" data-filter-target="item" data-navigable-list-target="item">
<%= form_with url: cards_path, method: :get, class: "full-width", data: { controller: "form" } do |form| %>
<% filter.as_params.except(:creator_ids).each do |key, value| %>
<%= filter_hidden_field_tag key, value %>
<% end %>
<% filter.creators.each do |existing_creators| %>
<% unless existing_creators == user %>
<%= hidden_field_tag "creator_ids[]", existing_creators.id %>
<% end %>
<% end %>
<% unless filter.creators.include?(user) %>
<%= hidden_field_tag "creator_ids[]", user.id %>
<% end %>
<% if @expand_all %>
<%= hidden_field_tag :expand_all, @expand_all %>
<% end %>
<%= form.button type: "submit", class: "btn popup__item min-width full-width unpad-inline" do %>
<span class="overflow-ellipsis flex-item-grow"><%= user.name %></span>
<% if filter.creators.include?(user) %>
<%= icon_tag "check", class: "checked flex-item-justify-end" %>
<% end %>
<% end %>
<% end %>
</li>
<% end %>
</ul>
<% end %>
</div>
<% end %>
+1 -1
View File
@@ -1,5 +1,5 @@
<%# Don't delete this file just yet, it's coming back soon. %>
<dialog class="filter__popup dialog panel fill-white shadow" aria-label="Filter" aria-description="Filter" data-dialog-target="dialog">
<dialog class="dialog panel fill-white shadow" aria-label="Filter" aria-description="Filter" data-dialog-target="dialog">
<div class="flex flex-column gap-half">
<div class="flex align-center gap-half justify-space-between">
<span class="btn btn--placeholder txt-small" aria-hidden="true"></span>
+34 -22
View File
@@ -1,32 +1,44 @@
<% if !filter.indexed_by.latest? %>
<div class="quick-filter position-relative" data-controller="dialog" data-action="keydown.esc->dialog#close click@document->dialog#closeOnClickOutside">
<button class="btn input input--select flex-inline txt-small" data-action="click->dialog#open:stop">
<% if !filter.indexed_by.latest? || @expand_all %>
<div class="quick-filter position-relative <%= "default-value" if filter.indexed_by.latest? %>" data-controller="dialog" data-action="keydown.esc->dialog#close click@document->dialog#closeOnClickOutside">
<button class="btn input input--select flex-inline txt-x-small" data-action="click->dialog#open:stop">
<span class="overflow-ellipsis">
<%= filter.indexed_by.humanize %>
</span>
</button>
<dialog class="events__popup popup panel flex-column align-start gap-half fill-white shadow"
aria-label="Sort by…" aria-description="Sort by…"
data-dialog-target="dialog" data-action="turbo:before-cache@document->dialog#close">
<strong class="popup__title margin-block-start-half pad-inline-half">Sort by…</strong>
<%= form_with url: cards_path, method: :get, class: "popup__list",
data: { controller: "form" } do |form| %>
<% filter.as_params.except(:indexed_by).each do |key, value| %>
<%= filter_hidden_field_tag key, value %>
<% end %>
<%= tag.dialog class: "margin-block-start-half popup panel flex-column align-start gap-half fill-white shadow txt-small", data: {
action: "turbo:before-cache@document->dialog#close keydown->navigable-list#navigate",
aria: { label: "Sort by…", aria_description: "Sort by…" },
controller: "navigable-list",
dialog_target: "dialog",
navigable_list_focus_on_selection_value: false,
navigable_list_actionable_items_value: true } do %>
<strong class="popup__title txt-nowrap">Sort by…</strong>
<ul class="popup__list" data-filter-target="list">
<% Filter::INDEXES.each do |index| %>
<div class="btn popup__item">
<%= form.radio_button :indexed_by, index,
checked: filter.indexed_by == index,
data: { action: "change->form#submit" } %>
<li class="popup__group flex align-center" data-navigable-list-target="item">
<%= form_with url: cards_path, method: :get, class: "full-width", data: { controller: "form" } do |form| %>
<% filter.as_params.except(:indexed_by).each do |key, value| %>
<%= filter_hidden_field_tag key, value %>
<% end %>
<%= form.label :indexed_by, index.humanize, value: index, class: "overflow-ellipsis" %>
<%= icon_tag "check", class: "checked flex-item-justify-end" %>
</div>
<%= hidden_field_tag :indexed_by, index %>
<% if @expand_all %>
<%= hidden_field_tag :expand_all, @expand_all %>
<% end %>
<%= form.button type: "submit", class: "btn popup__item min-width full-width unpad-inline" do %>
<span class="overflow-ellipsis flex-item-grow"><%= index.humanize %></span>
<% if filter.indexed_by == index %>
<%= icon_tag "check", class: "checked flex-item-justify-end" %>
<% end %>
<% end %>
<% end %>
</li>
<% end %>
<% end %>
</dialog>
</ul>
<% end %>
</div>
<% end %>
+31 -2
View File
@@ -1,17 +1,46 @@
<div class="filters flex align-center gap-half margin-none-block-end margin-block-start-half justify-center center">
<div class="flex-inline center align-center gap-half">
<% if any_filters?(filter) %>
<% if @expand_all %>
<%= link_to cards_path(filter.as_params), class: "btn txt-x-small btn--reversed", aria: { selected: true} do %>
<%= icon_tag "filter" %>
<span class="for-screen-reader">Edit filters</span>
<% end %>
<% else %>
<%= link_to cards_path(filter.as_params.merge(expand_all: true)), class: "btn txt-x-small" do %>
<%= icon_tag "filter" %>
<span class="for-screen-reader">Edit filters</span>
<% end %>
<% end %>
<% end %>
<%= render "filters/indexed_by", filter: filter %>
<%= render "filters/tags", filter: filter %>
<%= render "filters/assignees", filter: filter %>
<%= render "filters/creators", filter: filter %>
<%= render "filters/stages", filter: filter %>
<%# FIXME LATER: this needs to be able to allow selection of unique stages across all active workflows %>
<%#= render "filters/stages", filter: filter %>
<%= render "filters/cards", filter: filter %>
<%= render "filters/indexed_by", filter: filter %>
<% filter.terms.each do |term| %>
<%= filter_chip_tag %Q("#{term}"), filter.as_params_without(:terms, term) %>
<% end %>
<% if any_filters?(filter) %>
<% if filter.persisted? %>
<%= button_to filter_path(filter), method: :delete, class: "btn txt-x-small btn--reversed", form_class: "inline" do %>
<%= icon_tag "bookmark" %>
<span class="for-screen-reader">Remove saved filter</span>
<% end %>
<% else %>
<% filter_params = filter.as_params.transform_values { |v| v.respond_to?(:to_str) ? v.to_str : v } %>
<%= button_to filters_path, method: :post, params: filter_params, class: "btn txt-x-small", form_class: "inline" do %>
<%= icon_tag "bookmark" %>
<span class="for-screen-reader">Save filter</span>
<% end %>
<% end %>
<%= link_to cards_path(filter.as_params.except(:assignee_ids, :assignment_status, :card_ids, :creator_ids, :stage_ids, :tag_ids, :terms, :indexed_by)), class: "btn btn--remove txt-x-small" do %>
<%= icon_tag "close" %>
<span class="for-screen-reader">Clear all</span>
+6 -2
View File
@@ -1,4 +1,4 @@
<% if filter.stages.any? %>
<% if filter.stages.any? || @expand_all %>
<div class="quick-filter position-relative" data-controller="dialog" data-action="keydown.esc->dialog#close click@document->dialog#closeOnClickOutside">
<button class="btn input input--select flex-inline txt-x-small" data-action="click->dialog#open:stop">
<span class="overflow-ellipsis">
@@ -10,7 +10,7 @@
</span>
</button>
<dialog class="events__popup popup panel flex-column align-start gap-half fill-white shadow"
<dialog class="margin-block-start-half popup panel flex-column align-start gap-half fill-white shadow"
aria-label="In stage…" aria-description="In stage…"
data-dialog-target="dialog" data-action="turbo:before-cache@document->dialog#close">
<strong class="popup__title margin-block-start-half pad-inline-half overflow-ellipsis">In stage…</strong>
@@ -20,6 +20,10 @@
<%= filter_hidden_field_tag key, value %>
<% end %>
<% if @expand_all %>
<%= hidden_field_tag :expand_all, @expand_all %>
<% end %>
<% filter.stages.first.workflow.stages.each do |stage| %>
<div class="btn popup__item">
<%= form.check_box :stage_ids, {
+47 -26
View File
@@ -1,40 +1,61 @@
<% if filter.tags.any? %>
<% if filter.tags.any?|| @expand_all %>
<div class="quick-filter position-relative" data-controller="dialog" data-action="keydown.esc->dialog#close click@document->dialog#closeOnClickOutside">
<button class="btn input input--select flex-inline txt-x-small" data-action="click->dialog#open:stop">
<span class="overflow-ellipsis">
<% if filter.tags.any? %>
<%= filter.tags.map(&:hashtag).to_sentence %>
<%= filter.tags.map(&:hashtag).to_sentence(two_words_connector: " or ", last_word_connector: ", or ") %>
<% else %>
Tagged…
<% end %>
</span>
</button>
<dialog class="events__popup popup panel flex-column align-start gap-half fill-white shadow" data-dialog-target="dialog" data-action="turbo:before-cache@document->dialog#close">
<strong class="popup__title margin-block-start-half pad-inline-half overflow-ellipsis">Tagged…</strong>
<%= form_with url: cards_path, method: :get, class: "popup__list",
data: { controller: "form" } do |form| %>
<% filter.as_params.except(:tag_ids).each do |key, value| %>
<%= filter_hidden_field_tag key, value %>
<% end %>
<%= tag.dialog class: "margin-block-start-half popup panel flex-column align-start gap-half fill-white shadow txt-small", data: {
action: "turbo:before-cache@document->dialog#close keydown->navigable-list#navigate filter:changed->navigable-list#reset toggle->filter#filter",
aria: { label: "Tagged…", aria_description: "Tagged…" },
controller: "filter navigable-list",
dialog_target: "dialog",
navigable_list_focus_on_selection_value: false,
navigable_list_actionable_items_value: true } do %>
<strong class="popup__title txt-nowrap">Tagged…</strong>
<%= link_to cards_path(filter.as_params.except(:tag_ids)), class: "btn popup__item" do %>
<span class="overflow-ellipsis">Clear all</span>
<% end %>
<% Tag.order(:title).each do |tag| %>
<div class="btn popup__item">
<%= form.check_box "tag_ids[]", {
checked: filter.tags.include?(tag),
data: { action: "change->form#submit" },
include_hidden: false,
}, tag.id %>
<%= form.label "tag_ids[]", tag.hashtag, for: dom_id(tag, :filter), class: "overflow-ellipsis" %>
<%= icon_tag "check", class: "checked flex-item-justify-end" %>
</div>
<% end %>
<% if Tag.many? %>
<%= text_field_tag :search, nil, placeholder: "Filter…", class: "input input--transparent txt-small", autofocus: true,
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", action: "input->filter#filter" } %>
<% end %>
</dialog>
<ul class="popup__list" data-filter-target="list">
<% Tag.order(:title).each do |tag| %>
<li class="popup__group flex align-center" data-value="<%= tag.title.downcase %>" data-filter-target="item" data-navigable-list-target="item">
<%= form_with url: cards_path, method: :get, class: "full-width", data: { controller: "form" } do |form| %>
<% filter.as_params.except(:tag_ids).each do |key, value| %>
<%= filter_hidden_field_tag key, value %>
<% end %>
<% filter.tags.each do |existing_tag| %>
<% unless existing_tag == tag %>
<%= hidden_field_tag "tag_ids[]", existing_tag.id %>
<% end %>
<% end %>
<% unless filter.tags.include?(tag) %>
<%= hidden_field_tag "tag_ids[]", tag.id %>
<% end %>
<% if @expand_all %>
<%= hidden_field_tag :expand_all, @expand_all %>
<% end %>
<%= form.button type: "submit", class: "btn popup__item min-width full-width unpad-inline" do %>
<span class="overflow-ellipsis flex-item-grow"><%= tag.hashtag %></span>
<% if filter.tags.include?(tag) %>
<%= icon_tag "check", class: "checked flex-item-justify-end" %>
<% end %>
<% end %>
<% end %>
</li>
<% end %>
</ul>
<% end %>
</div>
<% end %>
+6 -3
View File
@@ -14,7 +14,7 @@ class FiltersControllerTest < ActionDispatch::IntegrationTest
assignee_ids: [ users(:jz).id ],
collection_ids: [ collections(:writebook).id ] }
end
assert_redirected_to cards_path(Filter.last.as_params)
assert_redirected_to cards_path(filter_id: Filter.last.id)
filter = Filter.last
assert_predicate filter.indexed_by, :closed?
@@ -25,9 +25,12 @@ class FiltersControllerTest < ActionDispatch::IntegrationTest
end
test "destroy" do
filter = filters(:jz_assignments)
expected_params = filter.as_params
assert_difference "users(:david).filters.count", -1 do
delete filter_path(filters(:jz_assignments))
delete filter_path(filter)
end
assert_redirected_to cards_path(filters(:jz_assignments).as_params)
assert_redirected_to cards_path(expected_params)
end
end
+4 -4
View File
@@ -114,16 +114,16 @@ class FilterTest < ActiveSupport::TestCase
end
test "summary" do
assert_equal "Newest, tagged #mobile, and assigned to JZ ", filters(:jz_assignments).summary
assert_equal "Newest, #mobile, and assigned to JZ", filters(:jz_assignments).summary
filters(:jz_assignments).update!(stages: workflow_stages(:qa_triage, :qa_in_progress))
assert_equal "Newest, tagged #mobile, assigned to JZ, and staged in Triage or In progress ", filters(:jz_assignments).summary
assert_equal "Newest, #mobile, assigned to JZ, and staged in Triage or In progress", filters(:jz_assignments).summary
filters(:jz_assignments).update!(stages: [], assignees: [], tags: [], collections: [ collections(:writebook) ])
assert_equal "Newest in Writebook", filters(:jz_assignments).summary
assert_equal "Newest", filters(:jz_assignments).summary
filters(:jz_assignments).update!(indexed_by: "stalled")
assert_equal "Stalled in Writebook", filters(:jz_assignments).summary
assert_equal "Stalled", filters(:jz_assignments).summary
end
test "get a clone with some changed params" do