Project

General

Profile

Patch #43364 ยป project_selector_hierarchy_sync.patch

[Agileware]Kota Uchino, 2025-10-17 13:52

View differences:

app/helpers/application_helper.rb
473 473
        end
474 474
        classes = (ancestors.empty? ? 'root' : 'child')
475 475
        classes += ' archived' if project.archived?
476
        s << "<li class='#{classes}'><div class='#{classes}'>"
476
        s << "<li class='#{classes}' data-project-id='#{project.id}'><div class='#{classes}'>"
477 477
        s << h(block ? capture(project, &block) : project.name)
478 478
        s << "</div>\n"
479 479
        ancestors << project
app/javascript/controllers/project_selector_controller.js
1
import { Controller } from "@hotwired/stimulus";
2

  
3
export default class extends Controller {
4
  toggleChildProjects(event) {
5
    const checkbox = event.target;
6
    if (checkbox.type !== 'checkbox') return;
7

  
8
    const listItem = checkbox.closest('li');
9
    if (!listItem) return;
10

  
11
    const childList = listItem.querySelector('ul.projects');
12
    if (!childList) return;
13

  
14
    const childCheckboxes = childList.querySelectorAll('input[type="checkbox"]');
15

  
16
    childCheckboxes.forEach(child => {
17
      child.checked = checkbox.checked;
18
    });
19
  }
20
}
app/views/custom_fields/_visibility_by_project_selector.html.erb
1 1
<fieldset class="box"><legend><%= toggle_checkboxes_link("#custom_field_project_ids input[type=checkbox]:enabled") %><%= l(:label_project_plural) %></legend>
2 2
  <p><%= f.check_box :is_for_all, :data => {:disables => '#custom_field_project_ids input'} %></p>
3 3

  
4
  <div id="custom_field_project_ids">
4
  <div id="custom_field_project_ids" data-controller="project-selector">
5 5
    <% project_ids = @custom_field.project_ids.to_a %>
6 6
    <%= render_project_nested_lists(Project.all) do |p|
7
      content_tag('label', check_box_tag('custom_field[project_ids][]', p.id, project_ids.include?(p.id), :id => nil) + ' ' + p.to_s)
7
      content_tag('label', check_box_tag('custom_field[project_ids][]', p.id, project_ids.include?(p.id), :id => nil, :data => {:action => 'change->project-selector#toggleChildProjects'}) + ' ' + p.to_s)
8 8
    end %>
9 9
    <%= hidden_field_tag('custom_field[project_ids][]', '', :id => nil) %>
10 10
  </div>
11
</fieldset>
11
</fieldset>
test/system/custom_field_project_selector_test.rb
1
# frozen_string_literal: true
2

  
3
require_relative '../application_system_test_case'
4

  
5
class CustomFieldProjectSelectorTest < ApplicationSystemTestCase
6
  setup do
7
    @parent = Project.generate!(name: 'Parent Project')
8
    @child1 = Project.generate!(name: 'Child Project 1', parent_id: @parent.id)
9
    @child2 = Project.generate!(name: 'Child Project 2', parent_id: @parent.id)
10
    @grandchild = Project.generate!(name: 'Grandchild Project', parent_id: @child1.id)
11
  end
12

  
13
  def test_checking_parent_project_should_check_child_projects
14
    log_user('admin', 'admin')
15

  
16
    custom_field = IssueCustomField.generate!(
17
      field_format: 'string',
18
      name: 'Test Field',
19
      is_for_all: false
20
    )
21

  
22
    visit "/custom_fields/#{custom_field.id}/edit"
23

  
24
    within '#custom_field_project_ids' do
25
      parent_checkbox = find("input[value='#{@parent.id}']")
26
      parent_checkbox.click
27

  
28
      child1_checkbox = find("input[value='#{@child1.id}']")
29
      child2_checkbox = find("input[value='#{@child2.id}']")
30

  
31
      assert child1_checkbox.checked?, 'Child project 1 should be checked'
32
      assert child2_checkbox.checked?, 'Child project 2 should be checked'
33
    end
34
  end
35

  
36
  def test_unchecking_parent_project_should_uncheck_child_projects
37
    log_user('admin', 'admin')
38

  
39
    custom_field = IssueCustomField.generate!(
40
      field_format: 'string',
41
      name: 'Test Field',
42
      is_for_all: false,
43
      project_ids: [@parent.id, @child1.id, @child2.id]
44
    )
45

  
46
    visit "/custom_fields/#{custom_field.id}/edit"
47

  
48
    within '#custom_field_project_ids' do
49
      parent_checkbox = find("input[value='#{@parent.id}']")
50
      child1_checkbox = find("input[value='#{@child1.id}']")
51
      child2_checkbox = find("input[value='#{@child2.id}']")
52

  
53
      assert parent_checkbox.checked?, 'Parent should be checked initially'
54
      assert child1_checkbox.checked?, 'Child 1 should be checked initially'
55
      assert child2_checkbox.checked?, 'Child 2 should be checked initially'
56

  
57
      parent_checkbox.click
58

  
59
      assert !child1_checkbox.checked?, 'Child project 1 should be unchecked'
60
      assert !child2_checkbox.checked?, 'Child project 2 should be unchecked'
61
    end
62
  end
63

  
64
  def test_grandchild_projects_are_also_synced
65
    log_user('admin', 'admin')
66

  
67
    custom_field = IssueCustomField.generate!(
68
      field_format: 'string',
69
      name: 'Test Field',
70
      is_for_all: false
71
    )
72

  
73
    visit "/custom_fields/#{custom_field.id}/edit"
74

  
75
    within '#custom_field_project_ids' do
76
      parent_checkbox = find("input[value='#{@parent.id}']")
77
      parent_checkbox.click
78

  
79
      grandchild_checkbox = find("input[value='#{@grandchild.id}']")
80
      assert grandchild_checkbox.checked?, 'Grandchild project should be checked'
81
    end
82
  end
83
end
    (1-1/1)