diff --git a/app/views/admin/settings/repositories_settings/_repositories_checkout.html.erb b/app/views/admin/settings/repositories_settings/_repositories_checkout.html.erb index 9e5b7d87a5a5..a1134640faff 100644 --- a/app/views/admin/settings/repositories_settings/_repositories_checkout.html.erb +++ b/app/views/admin/settings/repositories_settings/_repositories_checkout.html.erb @@ -4,38 +4,37 @@ <% vendor = vendor.to_s %> <% setting_base = "settings[repository_checkout_data][#{vendor}]" %> <% enabled = Setting.repository_checkout_data[vendor]["enabled"].to_i > 0 %> - + <% end %> + <% end %> <% end %> diff --git a/app/views/roles/_permissions.html.erb b/app/views/roles/_permissions.html.erb index 242aae792c8c..9bd098445f9a 100644 --- a/app/views/roles/_permissions.html.erb +++ b/app/views/roles/_permissions.html.erb @@ -28,29 +28,29 @@ See COPYRIGHT and LICENSE files for more details. ++#%> <% permissions.each do |mod, mod_permissions| %> <% global_prefix = show_global_role ? "fieldset--global--" : "fieldset--" %> - <% module_name = mod.blank? ? "fieldset--global--" + Project.model_name.human.downcase.tr(" ", "_") : global_prefix + l_or_humanize(mod, prefix: "project_module_").downcase.tr(" ", "_") %> + <% module_name = mod.blank? ? "fieldset--global--#{Project.model_name.human.downcase.tr(' ', '_')}" : global_prefix + l_or_humanize(mod, prefix: "project_module_").downcase.tr(" ", "_") %> <% module_id = module_name.parameterize %> -
- + + <%= render Primer::OpenProject::CollapsibleSection.new(id: module_id, display: :block, mb: 3) do |section| %> + <% section.with_title do %> <% if show_global_role && mod.blank? %> <%= t(:label_global) %> <% else %> <%= permission_header_for_project_module(mod) %> <% end %> - -
- - (<%= check_all_links module_id %>) - -
- <% unless I18n.t("#{mod}.permission_header_explanation", default: '').empty? %> -
-

- <%= I18n.t("#{mod}.permission_header_explanation").html_safe %> -

-
<% end %> -
+ + <% if I18n.exists?("permission_header_explanation", scope: mod) %> + <% section.with_caption do %> + <%= I18n.t("permission_header_explanation", scope: mod) %> + <% end %> + <% end %> + + <% section.with_additional_information do %> + <%= check_all_links module_id %> + <% end %> + + <% section.with_collapsible_content do %> <% Array(mod_permissions).each do |permission| %> <% next if permission.global? && !show_global_role %>
@@ -67,7 +67,7 @@ See COPYRIGHT and LICENSE files for more details.
<% end %> - -
+ <% end %> + <% end %> <% end %> <%= hidden_field_tag "role[permissions][]", "" %> diff --git a/app/views/roles/report.html.erb b/app/views/roles/report.html.erb index 8368d1c9fed6..645d991bf2fa 100644 --- a/app/views/roles/report.html.erb +++ b/app/views/roles/report.html.erb @@ -48,86 +48,88 @@ See COPYRIGHT and LICENSE files for more details. <%= hidden_field_tag "permissions[0]", "", id: nil %> <% group_permissions_by_module(@permissions).each do |mod, mod_permissions| %> - <% module_name = mod.blank? ? "form--" + I18n.t("attributes.project") : "form--" + l_or_humanize(mod, prefix: "project_module_").tr(" ", "_") %> + <% module_name = mod.blank? ? "form--#{I18n.t('attributes.project')}" : "form--#{l_or_humanize(mod, prefix: 'project_module_').tr(' ', '_')}" %> <% escaped_name = module_name.parameterize %> -
- + + <%= render Primer::OpenProject::CollapsibleSection.new(id: escaped_name, display: :block, mb: 3) do |section| %> + <% section.with_title do %> <%= permission_header_for_project_module(mod) %> - -
- - (<%= check_all_links escaped_name %>) - -
- -
-
- - - - - - - - - - - - - <% @roles.each do |role| %> + <% end %> + + <% section.with_additional_information do %> + <%= check_all_links escaped_name %> + <% end %> + + <% section.with_collapsible_content do %> +
+
+
-
-
- - <%= t(:label_permissions) %> - -
-
-
+ + + + + + + + + + - <% end %> - - - - <% mod_permissions.each do |permission| %> - - <% @roles.each do |role| %> - + <% end %> - <% end %> - -
- <%= content_tag(role.builtin? ? "em" : "span", h(role.name)) %> - <%= link_to_function( - icon_wrapper("icon-context icon-checkmark", "#{t(:button_check_all)}/#{t(:button_uncheck_all)}"), - "OpenProject.helpers.toggleCheckboxesBySelector('input.role-#{role.id}')", - class: "no-decoration-on-hover", - title: "#{t(:button_check_all)}/#{t(:button_uncheck_all)}" - ) %> + <%= t(:label_permissions) %>
- <%= link_to_function( - icon_wrapper("icon-context icon-checkmark", "#{t(:button_check_all)}/#{t(:button_uncheck_all)}"), - "OpenProject.helpers.toggleCheckboxesBySelector('.permission-#{permission.name} input')", - class: "no-decoration-on-hover", - title: "#{t(:button_check_all)}/#{t(:button_uncheck_all)}" - ) %> - <%= l_or_humanize(permission.name, prefix: "permission_") %> - - <% if setable_permissions(role).include? permission %> - <%= check_box_tag "permissions[#{role.id}][]", permission.name, (role.permissions.include? permission.name), id: nil, class: "role-#{role.id}" %> - <% end %> - +
+
+ + <%= content_tag(role.builtin? ? "em" : "span", h(role.name)) %> + <%= link_to_function( + icon_wrapper("icon-context icon-checkmark", "#{t(:button_check_all)}/#{t(:button_uncheck_all)}"), + "OpenProject.helpers.toggleCheckboxesBySelector('input.role-#{role.id}')", + class: "no-decoration-on-hover", + title: "#{t(:button_check_all)}/#{t(:button_uncheck_all)}" + ) %> + +
+
+
+ + + <% mod_permissions.each do |permission| %> + + + <%= link_to_function( + icon_wrapper("icon-context icon-checkmark", "#{t(:button_check_all)}/#{t(:button_uncheck_all)}"), + "OpenProject.helpers.toggleCheckboxesBySelector('.permission-#{permission.name} input')", + class: "no-decoration-on-hover", + title: "#{t(:button_check_all)}/#{t(:button_uncheck_all)}" + ) %> + <%= l_or_humanize(permission.name, prefix: "permission_") %> + + <% @roles.each do |role| %> + + <% if setable_permissions(role).include? permission %> + <%= check_box_tag "permissions[#{role.id}][]", permission.name, (role.permissions.include? permission.name), id: nil, class: "role-#{role.id}" %> + <% end %> + + <% end %> + + <% end %> + + +
- -
+ <% end %> + <% end %> <% end %>

<%= styled_button_tag t(:button_save), class: "-primary -with-icon icon-checkmark" %>

diff --git a/app/views/versions/index.html.erb b/app/views/versions/index.html.erb index fa1f8e9bf3d2..9a1e6dd01d8a 100644 --- a/app/views/versions/index.html.erb +++ b/app/views/versions/index.html.erb @@ -60,18 +60,18 @@ See COPYRIGHT and LICENSE files for more details. <%= render partial: "versions/overview", locals: { version: version } %> <%= render(partial: "wiki/text", locals: { page: version.wiki_page }) if version.wiki_page %> <% if (issues = @wps_by_version[version]) && issues.size > 0 %> -
-
- <%= t(:label_related_work_packages) %> -
-
    - <%- issues.each do |issue| -%> -
  • <%= link_to_work_package(issue, project: (@project != issue.project)) %>
  • - <%- end -%> -
-
-
-
+ <%= render Primer::OpenProject::CollapsibleSection.new(display: :block, mb: 3) do |section| %> + <% section.with_title_content(t(:label_related_work_packages)) %> + <% section.with_collapsible_content do %> +
+ +
+ <% end %> + <% end %> <% end %> <%= call_hook :view_projects_roadmap_version_bottom, version: version %> <% end %> diff --git a/frontend/src/app/core/setup/globals/global-listeners/toggable-fieldset.ts b/frontend/src/app/core/setup/globals/global-listeners/toggable-fieldset.ts deleted file mode 100644 index 7639e03ecdc2..000000000000 --- a/frontend/src/app/core/setup/globals/global-listeners/toggable-fieldset.ts +++ /dev/null @@ -1,101 +0,0 @@ -//-- 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. -//++ - -function createFieldsetToggleStateLabel(legend:JQuery, text:string) { - const labelClass = 'fieldset-toggle-state-label'; - let toggleLabel = legend.find(`a span.${labelClass}`); - const legendLink = legend.children('a'); - - if (toggleLabel.length === 0) { - toggleLabel = jQuery('').addClass(labelClass) - .addClass('sr-only'); - - legendLink.append(toggleLabel); - } - - toggleLabel.text(` ${text}`); -} - -function setFieldsetToggleState(fieldset:JQuery) { - const legend = fieldset.children('legend'); - - if (fieldset.hasClass('collapsed')) { - createFieldsetToggleStateLabel(legend, I18n.t('js.label_collapsed')); - } else { - createFieldsetToggleStateLabel(legend, I18n.t('js.label_expanded')); - } -} - -function getFieldset(el:HTMLElement) { - const element = jQuery(el); - - if (element.is('legend')) { - return jQuery(el).parent(); - } if (element.is('fieldset')) { - return element; - } - - throw new Error('Cannot derive fieldset from element!'); -} - -function toggleFieldset(el:HTMLElement) { - const fieldset = getFieldset(el); - // Mark the fieldset that the user has touched it at least once - fieldset.attr('data-touched', 'true'); - const contentArea = fieldset.find('> div').not('.form--toolbar'); - - fieldset.toggleClass('collapsed'); - contentArea.slideToggle('fast'); - - setFieldsetToggleState(fieldset); -} - -export function setupToggableFieldsets() { - const fieldsets = jQuery('fieldset.form--fieldset.-collapsible'); - - // Toggle on click - fieldsets.on('click', '.form--fieldset-legend', function (evt) { - toggleFieldset(this); - evt.preventDefault(); - evt.stopPropagation(); - return false; - }); - - // Set initial state - fieldsets - .each(function () { - const fieldset = getFieldset(this); - - const contentArea = fieldset.find('> div'); - if (fieldset.hasClass('collapsed')) { - contentArea.hide(); - } - - setFieldsetToggleState(fieldset); - }); -} diff --git a/frontend/src/global_styles/content/_forms.sass b/frontend/src/global_styles/content/_forms.sass index 38cf803e8584..1186ae760698 100644 --- a/frontend/src/global_styles/content/_forms.sass +++ b/frontend/src/global_styles/content/_forms.sass @@ -245,31 +245,14 @@ hr fieldset.form--fieldset @extend %form--fieldset-or-section - &.-collapsible.collapsed - .-hide-when-collapsed - display: none - .form--fieldset-legend @extend %form--fieldset-legend-or-section-title width: 100% - .form--fieldset.-collapsible > & - @include without-link-styling - cursor: pointer - &:before @include icon-common font-size: 0.75rem - .form--fieldset.-collapsible > & - @include icon-mixin-arrow-up1 - padding: 0.625rem 0.25rem 0 0.25rem - - .form--fieldset.-collapsible.-collapsed > &, - .form--fieldset.-collapsible.collapsed > & - @include icon-mixin-arrow-down1 - padding: 0.625rem 0.25rem 0 0.25rem - #main-menu .form--fieldset-legend color: var(--main-menu-font-color) margin-bottom: 1rem diff --git a/frontend/src/turbo/turbo-global-listeners.ts b/frontend/src/turbo/turbo-global-listeners.ts index aa137821a815..5fdd94747019 100644 --- a/frontend/src/turbo/turbo-global-listeners.ts +++ b/frontend/src/turbo/turbo-global-listeners.ts @@ -1,7 +1,6 @@ import { DeviceService } from 'core-app/core/browser/device.service'; import { scrollHeaderOnMobile } from 'core-app/core/setup/globals/global-listeners/top-menu-scroll'; import { detectOnboardingTour } from 'core-app/core/setup/globals/onboarding/onboarding_tour_trigger'; -import { setupToggableFieldsets } from 'core-app/core/setup/globals/global-listeners/toggable-fieldset'; import { installMenuLogic } from 'core-app/core/setup/globals/global-listeners/action-menu'; import { listenToSettingChanges } from 'core-app/core/setup/globals/global-listeners/settings'; import { makeColorPreviews } from 'core-app/core/setup/globals/global-listeners/color-preview'; @@ -38,9 +37,6 @@ export function addTurboGlobalListeners() { // Legacy scripts from app/assets that are not yet component based // - // Toggable fieldsets - setupToggableFieldsets(); - // Action menu logic jQuery('.toolbar-items').each((_, menu:HTMLElement) => { installMenuLogic(jQuery(menu)); @@ -65,7 +61,7 @@ export function addTurboGlobalListeners() { activateFlashError(); }; document.addEventListener('turbo:render', runOnRenderAndLoad); - document.addEventListener('turbo:load', runOnRenderAndLoad); + document.addEventListener('DOMContentLoaded', runOnRenderAndLoad); document.addEventListener('turbo:before-morph-element', (event) => { const element = event.target as HTMLElement; diff --git a/lookbook/previews/open_project/deprecated/forms_preview.rb b/lookbook/previews/open_project/deprecated/forms_preview.rb index 2f3ba14edc5f..94711edde1c5 100644 --- a/lookbook/previews/open_project/deprecated/forms_preview.rb +++ b/lookbook/previews/open_project/deprecated/forms_preview.rb @@ -38,8 +38,6 @@ def bordered; end def bordered_compressed; end - def collapsible; end - def controls; end def field_types; end diff --git a/lookbook/previews/open_project/deprecated/forms_preview/collapsible.html.erb b/lookbook/previews/open_project/deprecated/forms_preview/collapsible.html.erb deleted file mode 100644 index daea2e386bb2..000000000000 --- a/lookbook/previews/open_project/deprecated/forms_preview/collapsible.html.erb +++ /dev/null @@ -1,28 +0,0 @@ -
-
- - Less important information - - -
-
- - More important information - -
- -
-
- -
-
-
-
-
diff --git a/modules/budgets/app/views/budgets/subform/_labor_budget_subform.html.erb b/modules/budgets/app/views/budgets/subform/_labor_budget_subform.html.erb index b9bc85910cb2..a04dcf28e7f7 100644 --- a/modules/budgets/app/views/budgets/subform/_labor_budget_subform.html.erb +++ b/modules/budgets/app/views/budgets/subform/_labor_budget_subform.html.erb @@ -32,89 +32,98 @@ See COPYRIGHT and LICENSE files for more details. i.budget = @budget end -%> -
- <%= Budget.human_attribute_name(:labor_budget) %> -
-
- - - - - - <% if User.current.allowed_in_project?(:view_cost_rates, @project) %> +<%= + render Primer::OpenProject::CollapsibleSection.new( + id: "labor_budget_items_fieldset", + display: :block, + mb: 3, + data: { + controller: "costs--budget-subform", + costs__budget_subform_item_count_value: @budget.labor_budget_items.size, + costs__budget_subform_update_url_value: url_for(action: :update_labor_budget_item, project_id: @project.id) + } + ) do |section| +%> + <% section.with_title_content(Budget.human_attribute_name(:labor_budget)) %> + <% section.with_collapsible_content do %> +
+
+
+ - <% end %> - - - - - - + + <% if User.current.allowed_in_project?(:view_cost_rates, @project) %> + + <% end %> + + + + + - + - <% if User.current.allowed_in_project?(:view_cost_rates, @project) %> - + - <% end %> - - - - - <%- @budget.labor_budget_items.each_with_index do |labor_budget_item, index| -%> - <%= render partial: "budgets/items/labor_budget_item", object: labor_budget_item, locals: { index: index } %> - <%- end -%> - -
-
-
- - <%= LaborBudgetItem.human_attribute_name(:hours) %> - -
-
-
-
-
- - <%= LaborBudgetItem.human_attribute_name(:user) %> - +
+
+
+ + <%= LaborBudgetItem.human_attribute_name(:hours) %> + +
- -
-
-
- - <%= LaborBudgetItem.human_attribute_name(:comment) %> - +
+
+
+ + <%= LaborBudgetItem.human_attribute_name(:user) %> + +
- -
+
- <%= LaborBudgetItem.human_attribute_name(:budget) %> + <%= LaborBudgetItem.human_attribute_name(:comment) %>
+ <% if User.current.allowed_in_project?(:view_cost_rates, @project) %> + +
+
+ + <%= LaborBudgetItem.human_attribute_name(:budget) %> + +
+
+ + <% end %> + + + + + <%- @budget.labor_budget_items.each_with_index do |labor_budget_item, index| -%> + <%= render partial: "budgets/items/labor_budget_item", object: labor_budget_item, locals: { index: index } %> + <%- end -%> + + +
+
+ - - - - <%= render partial: "budgets/items/labor_budget_item", object: template_object, locals: { templated: true } %> -
-
+ + <%= render partial: "budgets/items/labor_budget_item", object: template_object, locals: { templated: true } %> +
+ <% end %> +<% end %> diff --git a/modules/budgets/app/views/budgets/subform/_material_budget_subform.html.erb b/modules/budgets/app/views/budgets/subform/_material_budget_subform.html.erb index 15ca28b8de6e..893b8957c71d 100644 --- a/modules/budgets/app/views/budgets/subform/_material_budget_subform.html.erb +++ b/modules/budgets/app/views/budgets/subform/_material_budget_subform.html.erb @@ -30,12 +30,20 @@ See COPYRIGHT and LICENSE files for more details. <% template_object = @budget.material_budget_items.build(cost_type: CostType.default) %> <% if CostType.exists? %> -
- <%= Budget.human_attribute_name(:material_budget) %> + <%= + render Primer::OpenProject::CollapsibleSection.new( + id: "material_budget_items_fieldset", + display: :block, + mb: 3, + data: { + controller: "costs--budget-subform", + costs__budget_subform_item_count_value: @budget.material_budget_items.size, + costs__budget_subform_update_url_value: url_for(action: :update_material_budget_item, project_id: @project.id) + } + ) do |section| + %> + <% section.with_title_content(Budget.human_attribute_name(:material_budget)) %> + <% section.with_collapsible_content do %>
@@ -48,83 +56,84 @@ See COPYRIGHT and LICENSE files for more details. <% end %> - - - - + + - + - + - <% if User.current.allowed_in_project?(:view_cost_rates, @project) %> - + - <% end %> - - - - - <%- @budget.material_budget_items.each_with_index do |material_budget_item, index| -%> - <%= render partial: "budgets/items/material_budget_item", object: material_budget_item, locals: { index: index } %> - <%- end -%> - -
-
-
- - <%= MaterialBudgetItem.human_attribute_name(:units) %> - -
-
-
-
-
- - <%= CostType.human_attribute_name(:unit) %> - +
+
+
+ + <%= MaterialBudgetItem.human_attribute_name(:units) %> + +
- -
-
-
- - <%= MaterialBudgetItem.human_attribute_name(:cost_type) %> - +
+
+
+ + <%= CostType.human_attribute_name(:unit) %> + +
- -
-
-
- - <%= MaterialBudgetItem.human_attribute_name(:comment) %> - +
+
+
+ + <%= MaterialBudgetItem.human_attribute_name(:cost_type) %> + +
- -
+
- <%= MaterialBudgetItem.human_attribute_name(:budget) %> + <%= MaterialBudgetItem.human_attribute_name(:comment) %>
+ <% if User.current.allowed_in_project?(:view_cost_rates, @project) %> + +
+
+ + <%= MaterialBudgetItem.human_attribute_name(:budget) %> + +
+
+ + <% end %> + + + + + <%- @budget.material_budget_items.each_with_index do |material_budget_item, index| -%> + <%= render partial: "budgets/items/material_budget_item", object: material_budget_item, locals: { index: index } %> + <%- end -%> + + +
+
+ - - - - <%= render partial: "budgets/items/material_budget_item", - object: template_object, - locals: { templated: true } %> -
-
+ + <%= render partial: "budgets/items/material_budget_item", + object: template_object, + locals: { templated: true } %> +
+ <% end %> + <% end %> <% end %> diff --git a/modules/reporting/lib/widget/base.rb b/modules/reporting/lib/widget/base.rb index 986ceff39187..dd824e757ee3 100644 --- a/modules/reporting/lib/widget/base.rb +++ b/modules/reporting/lib/widget/base.rb @@ -93,6 +93,12 @@ def cached? cache? && Rails.cache.exist?(cache_key) end + protected + + def render_view_component(component, &) + component.render_in(controller.view_context, &) + end + private def cache? diff --git a/modules/reporting/lib/widget/settings/fieldset.rb b/modules/reporting/lib/widget/settings/fieldset.rb index 5a3ba97ffe3c..d817b6a7a585 100644 --- a/modules/reporting/lib/widget/settings/fieldset.rb +++ b/modules/reporting/lib/widget/settings/fieldset.rb @@ -36,20 +36,12 @@ def render_with_options(options, &) super end - def render - hash = self.hash - write(content_tag(:fieldset, - id: @id, - class: "form--fieldset -collapsible") do - html = content_tag(:legend, - show_at_id: hash.to_s, - icon: "#{@type}-legend-icon", - tooltip: "#{@type}-legend-tip", - class: "form--fieldset-legend", - id: hash.to_s) do - content_tag(:a, href: "#") { I18n.t(@label) } - end - html + yield - end) + def render(&) + write( + render_view_component Primer::OpenProject::CollapsibleSection.new(id: @id, display: :block, mb: 3) do |section| + section.with_title_content(I18n.t(@label)) + section.with_collapsible_content(&) + end + ) end end diff --git a/spec/features/global_roles/global_role_crud_spec.rb b/spec/features/global_roles/global_role_crud_spec.rb index d22a9e4c6ebf..575705a53f75 100644 --- a/spec/features/global_roles/global_role_crud_spec.rb +++ b/spec/features/global_roles/global_role_crud_spec.rb @@ -45,14 +45,16 @@ it "can create global role with that perm" do # When I go to the new page of "Role" visit new_role_path - # Then I should not see block with "#global_permissions" - expect(page).to have_no_css(".form--fieldset-legend", text: "GLOBAL") + + expect(page).to have_unchecked_field "Global role" + # Then I should not see region with global permissions + expect(page).not_to have_region "Global" # When I check "Global role" check "Global role" - # Then I should see block with "#global_permissions" - expect(page).to have_css(".form--fieldset-legend", text: "GLOBAL") + # Then I should see region with global permissions + expect(page).to have_region "Global" # And I should see "Global group" - expect(page).to have_text "GLOBAL GROUP" + expect(page).to have_region "Global group" # And I should see "Glob test" expect(page).to have_text "Glob test" # And I should not see "Issues can be assigned to this role"