mirror of
https://github.com/opf/openproject.git
synced 2026-06-13 19:20:00 +00:00
Turn checkbox filters into toggle switch. wp/74380
This commit is contained in:
@@ -30,27 +30,19 @@
|
||||
|
||||
class Filters::Inputs::BooleanForm < Filters::Inputs::BaseFilterForm
|
||||
def add_operand(group)
|
||||
group.check_box(
|
||||
group.segmented_control(
|
||||
name: "#{@filter.name}_value",
|
||||
label: @filter.human_name,
|
||||
visually_hide_label: true,
|
||||
name: "v-#{@filter.class.key}",
|
||||
value: "t",
|
||||
unchecked_value: "f",
|
||||
checked: @filter.values.first == "t"
|
||||
value: @filter.values.first,
|
||||
items: [
|
||||
{ value: "f", label: I18n.t("general_text_No") },
|
||||
{ value: "t", label: I18n.t("general_text_Yes") }
|
||||
],
|
||||
wrapper_data_attributes: {
|
||||
"filter--filters-form-target": "filterValueContainer",
|
||||
"filter-name": @filter.name
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def operand_input_id
|
||||
"v-#{@filter.class.key}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def filter_row_arguments
|
||||
super.tap do |args|
|
||||
args[:data][:"filter--filters-form-target"] = "filter filterValueContainer"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -503,6 +503,12 @@ export default class FiltersFormController extends Controller {
|
||||
return this.parseDateFilterValue(valueContainer, filterName);
|
||||
}
|
||||
|
||||
const hiddenField = valueContainer.querySelector<HTMLInputElement>('input[type="hidden"]');
|
||||
|
||||
if (hiddenField) {
|
||||
return hiddenField.value ? [hiddenField.value] : null;
|
||||
}
|
||||
|
||||
const value = this.findTargetByName(filterName, this.simpleValueTargets)?.value;
|
||||
|
||||
if (value && value.length > 0) {
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* -- copyright
|
||||
* OpenProject is an open source project management software.
|
||||
* Copyright (C) the OpenProject GmbH
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 3.
|
||||
*
|
||||
* OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
* Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
* Copyright (C) 2010-2013 the ChiliProject Team
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* See COPYRIGHT and LICENSE files for more details.
|
||||
* ++
|
||||
*
|
||||
*/
|
||||
|
||||
import { Controller } from '@hotwired/stimulus';
|
||||
|
||||
export default class SegmentedControlController extends Controller {
|
||||
static targets = ['field'];
|
||||
|
||||
declare readonly fieldTarget:HTMLInputElement;
|
||||
|
||||
// Reacts to Primer's itemActivated event (bubbled from segmented-control#select),
|
||||
// which has already updated aria-current visually. Sets the backing hidden field
|
||||
// value and bubbles a change event for filter--filters-form auto-submit.
|
||||
activate(event:CustomEvent<{ item:HTMLButtonElement }>) {
|
||||
const button = event.detail.item;
|
||||
const value = button.dataset.value;
|
||||
|
||||
if (value !== undefined) {
|
||||
this.fieldTarget.value = value;
|
||||
this.fieldTarget.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,10 @@ module Primer
|
||||
add_input AutocompleterInput.new(builder:, form:, **decorate_options(**), &)
|
||||
end
|
||||
|
||||
def segmented_control(**, &)
|
||||
add_input SegmentedControlInput.new(builder:, form:, **decorate_options(**), &)
|
||||
end
|
||||
|
||||
def block_note_editor(**, &)
|
||||
add_input BlockNoteEditorInput.new(builder:, form:, **decorate_options(**), &)
|
||||
end
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Primer
|
||||
module OpenProject
|
||||
module Forms
|
||||
module Dsl
|
||||
class SegmentedControlInput < Primer::Forms::Dsl::Input
|
||||
Item = Data.define(:value, :label)
|
||||
|
||||
attr_reader :name, :label, :current_value, :items, :wrapper_data_attributes
|
||||
|
||||
def initialize(name:, label:, value:, items:, wrapper_data_attributes: {}, **system_arguments)
|
||||
@name = name
|
||||
@label = label
|
||||
@items = items.map { |item| Item.new(value: item[:value], label: item[:label]) }
|
||||
@current_value = value.presence || @items.first&.value
|
||||
@wrapper_data_attributes = wrapper_data_attributes
|
||||
|
||||
super(**system_arguments)
|
||||
end
|
||||
|
||||
def to_component
|
||||
SegmentedControl.new(input: self)
|
||||
end
|
||||
|
||||
def type
|
||||
:segmented_control
|
||||
end
|
||||
|
||||
def focusable?
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,18 @@
|
||||
<%= render(FormControl.new(input: @input, data: @input.wrapper_data_attributes)) do %>
|
||||
<div data-controller="filter--segmented-control"
|
||||
data-action="itemActivated->filter--segmented-control#activate">
|
||||
<%= content_tag(:input, nil,
|
||||
type: "hidden",
|
||||
id: builder.field_id(@input.name),
|
||||
name: builder.field_name(@input.name),
|
||||
value: @input.current_value,
|
||||
data: { "filter--segmented-control-target": "field" }) %>
|
||||
<%= render(Primer::Alpha::SegmentedControl.new("aria-label": @input.label)) do |control| %>
|
||||
<% @input.items.each do |item| %>
|
||||
<% control.with_item(label: item.label,
|
||||
selected: item.value == @input.current_value,
|
||||
data: { value: item.value }) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
@@ -0,0 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Primer
|
||||
module OpenProject
|
||||
module Forms
|
||||
# :nodoc:
|
||||
class SegmentedControl < Primer::Forms::BaseComponent
|
||||
prepend WrappedInput
|
||||
|
||||
delegate :builder, :form, to: :@input
|
||||
|
||||
def initialize(input:)
|
||||
super()
|
||||
@input = input
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -134,16 +134,14 @@ module Components
|
||||
|
||||
def set_toggle_filter(values)
|
||||
should_active = values.first == "yes"
|
||||
checkbox = page.find('input[type="checkbox"]')
|
||||
is_active = checkbox.checked?
|
||||
label = should_active ? I18n.t("general_text_Yes") : I18n.t("general_text_No")
|
||||
hidden = page.find('input[type="hidden"]', visible: :all)
|
||||
is_active = hidden.value == "t"
|
||||
|
||||
checkbox.click if should_active != is_active
|
||||
click_button(label, exact: true) unless should_active == is_active
|
||||
|
||||
if should_active
|
||||
expect(page).to have_field(type: :checkbox, checked: true)
|
||||
else
|
||||
expect(page).to have_field(type: :checkbox, checked: false)
|
||||
end
|
||||
expected_value = should_active ? "t" : "f"
|
||||
expect(page).to have_field(hidden["name"], with: expected_value, type: :hidden)
|
||||
end
|
||||
|
||||
def set_name_and_identifier_filter(values, send_keys: false)
|
||||
|
||||
Reference in New Issue
Block a user