Skip to content

Commit

Permalink
utilizing the ListFilterComponent to display the value for 'in' and '…
Browse files Browse the repository at this point in the history
…not_in' operators
  • Loading branch information
ksierks committed Jan 14, 2025
1 parent 5297f3e commit a85e850
Show file tree
Hide file tree
Showing 12 changed files with 250 additions and 36 deletions.
16 changes: 8 additions & 8 deletions app/components/advanced_search/condition.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
{
"aria-label": t("advanced_search_component.field"),
class:
"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500",
"h-10 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500",
} %>
<%= conditions_form.select :operator,
@operations,
Expand All @@ -18,15 +18,15 @@
},
{
"aria-label": t("advanced_search_component.operator"),
"data-action": "advanced-search#handleOperatorChange",
class:
"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500",
"h-10 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500",
} %>
<%= conditions_form.text_field(
:value,
placeholder: t("advanced_search_component.value"),
"aria-label": t("advanced_search_component.value"),
class:
"keyInput block py-2.5 px-0 w-full text-sm text-slate-900 bg-transparent border-0 border-b-2 border-slate-300 appearance-none dark:text-white dark:border-slate-600 dark:focus:border-primary-500 focus:outline-none focus:ring-0 focus:border-primary-500 peer",
<%= render AdvancedSearch::Value.new(
conditions_form: conditions_form,
group_index: @group_index,
condition: @condition,
condition_index: @condition_index,
) %>
<% end %>
<button
Expand Down
4 changes: 3 additions & 1 deletion app/components/advanced_search/condition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
module AdvancedSearch
# Component for rendering an advanced search condition
class Condition < Component
def initialize(groups_form:, condition:, fields: [], operations: [])
def initialize(groups_form:, group_index:, condition:, condition_index:, fields: [], operations: []) # rubocop:disable Metrics/ParameterLists
@groups_form = groups_form
@group_index = group_index
@condition = condition
@condition_index = condition_index
@fields = fields
@operations = operations
end
Expand Down
4 changes: 3 additions & 1 deletion app/components/advanced_search/group.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
>
<div id="group-error-messages-<%= @group_index %>"></div>
<%= @form.fields_for :groups, @group do |groups_form| %>
<% @group.conditions.each do |condition| %>
<% @group.conditions.each_with_index do |condition, condition_index| %>
<%= render AdvancedSearch::Condition.new(
groups_form: groups_form,
group_index: @group_index,
condition: condition,
condition_index: condition_index,
fields: @fields,
operations: @operations,
) %>
Expand Down
77 changes: 77 additions & 0 deletions app/components/advanced_search/value.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<% if %w[in not_in].any? { |operator| @condition.operator == operator } %>
<div
data-controller="list-filter"
data-list-filter-selection-outlet="#samples-table"
data-list-filter-filters-value="<%= @condition.value.present? ? @condition.value.to_s : '[]' %>"
class="w-full list-filter value"
>
<template data-list-filter-target="template">
<%= viral_pill(color: :blue, classes: "search-tag inline-flex items-center filter-item mb-1 mr-1 py-1.5") do %>
<%= @conditions_form.search_field :value,
multiple: true,
class: "hidden",
data: "turbo-temporary" %>
<span class="mr-1 font-mono text-base font-semibold label"></span>
<button
type="button"
class="
inline-flex items-center p-1 text-sm bg-transparent rounded-full text-slate-400
hover:bg-blue-200 hover:text-blue-900 dark:hover:bg-blue-600
dark:hover:text-blue-300
"
data-action="click->list-filter#remove"
aria-label="<%= t(:'components.list_filter.remove_tag') %>"
>
<%= viral_icon(name: "x_mark", classes: "w-4 h-4") %>
</button>
<% end %>
</template>
<div
class="
relative flex flex-wrap font-mono bg-slate-50 border border-slate-300
text-slate-900 text-sm rounded-lg focus-within:ring-primary-500
focus-within:border-primary-500 w-full p-2.5 dark:bg-slate-700
dark:border-slate-600 dark:placeholder-slate-400 dark:text-white
dark:focus-within:ring-primary-500 dark:focus-within:border-primary-500k
"
data-list-filter-target="tags"
data-action="click->list-filter#focus"
>
<input
type="text"
name="<%="q[groups_attributes][#{@group_index}][conditions_attributes][#{@condition_index}][value][]"%>"
class="bg-transparent border-none focus:outline-none focus:ring-0 grow"
autofocus
aria-label="<%= t(:'components.list_filter.description') %>"
data-action='
keydown->list-filter#handleInput
paste->list-filter#handlePaste
turbo:morph-element->list-filter#idempotentConnect
'
data-list-filter-target="input"
/>
<button
type="button"
class="
absolute inline-flex items-center justify-center w-5 h-5 text-xs font-bold
rounded-full text-slate-600 bg-slate-100 border-3 border-slate-10 bottom-2 end-2
dark:text-slate-300 dark:border-slate-900 dark:bg-slate-600
dark:hover:bg-slate-500 hover:bg-slate-300
"
data-action="click->list-filter#clear"
aria-label="<%= t(:'components.list_filter.clear') %>"
title="<%= t(:'components.list_filter.clear') %>"
>
<%= viral_icon(name: "x_mark", classes: "h-4 w-4") %>
</button>
</div>
</div>
<% else %>
<%= @conditions_form.text_field(
:value,
placeholder: t("advanced_search_component.value"),
"aria-label": t("advanced_search_component.value"),
class:
"keyInput block py-2.5 px-0 w-full text-sm text-slate-900 bg-transparent border-0 border-b-2 border-slate-300 appearance-none dark:text-white dark:border-slate-600 dark:focus:border-primary-500 focus:outline-none focus:ring-0 focus:border-primary-500 peer value",
) %>
<% end %>
13 changes: 13 additions & 0 deletions app/components/advanced_search/value.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module AdvancedSearch
# Component for rendering an advanced search value
class Value < Component
def initialize(conditions_form:, group_index:, condition:, condition_index:)
@conditions_form = conditions_form
@group_index = group_index
@condition = condition
@condition_index = condition_index
end
end
end
106 changes: 99 additions & 7 deletions app/components/advanced_search_component.html.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<div data-controller="advanced-search">
<div
data-controller="advanced-search"
data-advanced-search-list-filter-outlet=".list-filter"
>
<%= viral_dialog(id: 'advanced-search-dialog', size: :large, open: @open) do |dialog| %>
<%= dialog.with_trigger do %>
<button
Expand Down Expand Up @@ -56,20 +59,108 @@
<% end %>
<% end %>
<% end %>
<template data-advanced-search-target="listValueTemplate">
<div
data-controller="list-filter"
data-list-filter-selection-outlet="#samples-table"
data-list-filter-filters-value="'[]'"
class="w-full list-filter value"
>
<template data-list-filter-target="template">
<%= viral_pill(color: :blue, classes: "search-tag inline-flex items-center filter-item mb-1 mr-1 py-1.5") do %>
<input
multiple="multiple"
class="hidden"
data="turbo-temporary"
type="search"
name="q[groups_attributes][GROUP_INDEX_PLACEHOLDER][conditions_attributes][CONDITION_INDEX_PLACEHOLDER][value][]"
>
<span class="mr-1 font-mono text-base font-semibold label"></span>
<button
type="button"
class="
inline-flex items-center p-1 text-sm bg-transparent rounded-full text-slate-400
hover:bg-blue-200 hover:text-blue-900 dark:hover:bg-blue-600
dark:hover:text-blue-300
"
data-action="click->list-filter#remove"
aria-label="<%= t(:'components.list_filter.remove_tag') %>"
>
<%= viral_icon(name: "x_mark", classes: "w-4 h-4") %>
</button>
<% end %>
</template>
<div
class="
relative flex flex-wrap font-mono bg-slate-50 border border-slate-300
text-slate-900 text-sm rounded-lg focus-within:ring-primary-500
focus-within:border-primary-500 w-full p-2.5 dark:bg-slate-700
dark:border-slate-600 dark:placeholder-slate-400 dark:text-white
dark:focus-within:ring-primary-500 dark:focus-within:border-primary-500k
"
data-list-filter-target="tags"
data-action="click->list-filter#focus"
>
<input
type="text"
name="q[groups_attributes][GROUP_INDEX_PLACEHOLDER][conditions_attributes][CONDITION_INDEX_PLACEHOLDER][value][]"
class="
bg-transparent border-none focus:outline-none focus:ring-0 grow
"
autofocus
aria-label="<%= t(:'components.list_filter.description') %>"
data-action='
keydown->list-filter#handleInput
paste->list-filter#handlePaste
turbo:morph-element->list-filter#idempotentConnect
'
data-list-filter-target="input"
/>
<button
type="button"
class="
absolute inline-flex items-center justify-center w-5 h-5 text-xs font-bold
rounded-full text-slate-600 bg-slate-100 border-3 border-slate-10 bottom-2 end-2
dark:text-slate-300 dark:border-slate-900 dark:bg-slate-600
dark:hover:bg-slate-500 hover:bg-slate-300
"
data-action="click->list-filter#clear"
aria-label="<%= t(:'components.list_filter.clear') %>"
title="<%= t(:'components.list_filter.clear') %>"
>
<%= viral_icon(name: "x_mark", classes: "h-4 w-4") %>
</button>
</div>
</div>
</template>
<template data-advanced-search-target="valueTemplate">
<input
type="text"
name="q[groups_attributes][GROUP_INDEX_PLACEHOLDER][conditions_attributes][CONDITION_INDEX_PLACEHOLDER][value]"
placeholder="<%= t(".value") %>"
aria-label="<%= t(".value") %>"
class="
keyInput block py-2.5 px-0 w-full text-sm text-slate-900 bg-transparent border-0
border-b-2 border-slate-300 appearance-none dark:text-white
dark:border-slate-600 dark:focus:border-primary-500 focus:outline-none
focus:ring-0 focus:border-primary-500 peer value
"
/>
</template>
<template data-advanced-search-target="conditionTemplate">
<div
data-advanced-search-target="conditionsContainer"
class="flex space-x-2 p-4"
>
<select
class="
bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg
h-10 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg
focus:ring-primary-500 focus:border-primary-500 block p-2.5 dark:bg-gray-700
dark:border-gray-600 dark:placeholder-gray-400 dark:text-white
dark:focus:ring-primary-500 dark:focus:border-primary-500
"
aria-label="<%= t(".field") %>"
name="q[groups_attributes][GROUP_INDEX_PLACEHOLDER][conditions][CONDITION_INDEX_PLACEHOLDER][field]"
name="q[groups_attributes][GROUP_INDEX_PLACEHOLDER][conditions_attributes][CONDITION_INDEX_PLACEHOLDER][field]"
>
<option selected><%= t(".field") %></option>
<% @fields.each do |field| %>
Expand All @@ -78,13 +169,14 @@
</select>
<select
class="
bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg
h-10 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg
focus:ring-primary-500 focus:border-primary-500 block p-2.5 dark:bg-gray-700
dark:border-gray-600 dark:placeholder-gray-400 dark:text-white
dark:focus:ring-primary-500 dark:focus:border-primary-500
"
aria-label="<%= t(".operator") %>"
name="q[groups_attributes][GROUP_INDEX_PLACEHOLDER][conditions][CONDITION_INDEX_PLACEHOLDER][operator]"
data-action="advanced-search#handleOperatorChange"
name="q[groups_attributes][GROUP_INDEX_PLACEHOLDER][conditions_attributes][CONDITION_INDEX_PLACEHOLDER][operator]"
>
<option selected><%= t(".operator") %></option>
<% @operations.each do |operation| %>
Expand All @@ -93,14 +185,14 @@
</select>
<input
type="text"
name="q[groups_attributes][GROUP_INDEX_PLACEHOLDER][conditions][CONDITION_INDEX_PLACEHOLDER][value]"
name="q[groups_attributes][GROUP_INDEX_PLACEHOLDER][conditions_attributes][CONDITION_INDEX_PLACEHOLDER][value]"
placeholder="<%= t(".value") %>"
aria-label="<%= t(".value") %>"
class="
keyInput block py-2.5 px-0 w-full text-sm text-slate-900 bg-transparent border-0
border-b-2 border-slate-300 appearance-none dark:text-white
dark:border-slate-600 dark:focus:border-primary-500 focus:outline-none
focus:ring-0 focus:border-primary-500 peer
focus:ring-0 focus:border-primary-500 peer value
"
/>
<button
Expand Down
31 changes: 30 additions & 1 deletion app/javascript/controllers/advanced_search_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,18 @@ export default class extends Controller {
"conditionTemplate",
"groupsContainer",
"groupTemplate",
"valueTemplate",
"listValueTemplate",
];
static outlets = ["list-filter"];

connect() {}

idempotentConnect() {}
idempotentConnect() {
this.listFilterOutlets.forEach((outlet) => {
outlet.idempotentConnect();
});
}

addCondition(event) {
let group = event.currentTarget.parentElement.closest(
Expand Down Expand Up @@ -108,4 +115,26 @@ export default class extends Controller {
}
});
}

handleOperatorChange(event) {
let operator = event.target.value;
let condition = event.target.parentElement;
let value = condition.querySelector(".value");
let group = condition.parentElement;
let group_index = this.groupsContainerTargets.indexOf(group);
let condition_index =
group.querySelectorAll(
"div[data-advanced-search-target='conditionsContainer']",
).length - 1;

if (["in", "not_in"].includes(operator)) {
value.outerHTML = this.listValueTemplateTarget.innerHTML
.replace(/GROUP_INDEX_PLACEHOLDER/g, group_index)
.replace(/CONDITION_INDEX_PLACEHOLDER/g, condition_index);
} else {
value.outerHTML = this.valueTemplateTarget.innerHTML
.replace(/GROUP_INDEX_PLACEHOLDER/g, group_index)
.replace(/CONDITION_INDEX_PLACEHOLDER/g, condition_index);
}
}
}
14 changes: 8 additions & 6 deletions app/javascript/controllers/list_filter_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,13 @@ export default class extends Controller {
}

#updateCount() {
const count = this.filtersValue.filter(
(sample) => sample.length > 0,
).length;
this.countTarget.innerText = count;
this.countTarget.classList.toggle("hidden", count === 0);
this.countTarget.classList.toggle("inline-flex", count > 0);
if (this.hasCountTarget) {
const count = this.filtersValue.filter(
(sample) => sample.length > 0,
).length;
this.countTarget.innerText = count;
this.countTarget.classList.toggle("hidden", count === 0);
this.countTarget.classList.toggle("inline-flex", count > 0);
}
}
}
4 changes: 2 additions & 2 deletions app/models/sample/condition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ class Sample::Condition # rubocop:disable Style/ClassAndModuleChildren

attribute :field, :string
attribute :operator, :string
attribute :value, :string
attribute :value

validates :operator, inclusion: { in: %w[= != <= >= contains in not_in] }

def empty?
field.empty? && operator.empty? && value.empty?
field.empty? && operator.empty? && ((value.is_a?(Array) && value.compact!.nil?) || value.empty?)
end
end
Loading

0 comments on commit a85e850

Please sign in to comment.