Merge pull request #663 from basecamp/custom-views

Custom views redux
This commit is contained in:
Jason Zimdars
2025-06-26 19:08:28 -05:00
committed by GitHub
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
@@ -13,7 +13,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",
@@ -25,12 +25,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