[#70166] Fix accessibility errors found by ERB Lint (#21503)

* Fix GitHub/NoTitleAttribute, LinkHasHref errors

- Replaces `title` attribute with `aria-label` for interactive elements.
- Removes `title` from non-interactive elements.
- Converts `<a>` tags without proper `href` to `<button>` elements,
  using Primer `Button`/`IconButton` where possible.

# Conflicts:
#	app/views/custom_fields/_custom_options.html.erb
#	spec/features/admin/custom_fields/shared_custom_field_expectations.rb
#	spec/features/admin/custom_fields/work_packages/list_spec.rb

* Fix Autocomplete missing errors

* Fix GitHub/NoPositiveTabIndex errors

Removes all positive `tabindex` values.

* Fix Rails/LinkToBlank errors

* Replace toast with Primer Banner on LDAP form

* Add frozen_string_literal

* Ignore erb lint for deprecated files

* Fix linting errors in repository module

* Fix linting errors in budgets and custom actions

* Fix linting errors in member form and 2fa

* Fix linting errors in mcost types and wiki help and storages

* Fix linting errors in multi select filters, ifc viewer, and unsupported browser banner

* Fix failing spec

* Use Primer banner instead of op-toast where ever it is possible

* Use octicon instead of op_icon

* Fix failing tests

* Use no-decoration-on-hover for button links and change the button with only an icon to primer icon button

* Keep webhook response modal activation selector class-based

* use icon button for edit of hourly rate

---------

Co-authored-by: Behrokh Satarnejad <b.satarnejad@openproject.com>
This commit is contained in:
Alexander Brandon Coles
2026-05-07 09:31:10 +01:00
committed by GitHub
parent e378301f3c
commit e8767481e9
41 changed files with 266 additions and 208 deletions
+1
View File
@@ -5,6 +5,7 @@ inherit_gem:
- config/accessibility.yml - config/accessibility.yml
exclude: exclude:
- '**/frontend/**/*' - '**/frontend/**/*'
- 'lookbook/previews/open_project/deprecated/**/*'
- '**/node_modules/**/*' - '**/node_modules/**/*'
- '**/vendor/**/*' - '**/vendor/**/*'
linters: linters:
@@ -112,7 +112,7 @@ See COPYRIGHT and LICENSE files for more details.
<% end %> <% end %>
<li class="simple-filters--filter"> <li class="simple-filters--filter">
<label class='simple-filters--filter-name' for='name'><%= User.human_attribute_name :name %>:</label> <label class='simple-filters--filter-name' for='name'><%= User.human_attribute_name :name %>:</label>
<%= text_field_tag "name", params[:name], class: "simple-filters--filter-value" %> <%= text_field_tag "name", params[:name], class: "simple-filters--filter-value", autocomplete: "off" %>
</li> </li>
<li class="simple-filters--controls"> <li class="simple-filters--controls">
<%= submit_tag t(:button_apply), class: "button -primary -small", name: nil %> <%= submit_tag t(:button_apply), class: "button -primary -small", name: nil %>
+14 -9
View File
@@ -58,8 +58,7 @@
<section class="hide-section" <section class="hide-section"
data-hide-sections-target="section" data-hide-sections-target="section"
data-name="<%= action.key %>" data-name="<%= action.key %>"
<%= tag.attributes(data: { section_name: action.key }, hidden: active_section_keys.include?(action.key) ? nil : true) %> <%= tag.attributes(data: { section_name: action.key }, hidden: active_section_keys.exclude?(action.key)) %>>
>
<div class="form--field"> <div class="form--field">
<%= styled_label_tag("custom_action_actions_#{action.key}", action.human_name, class: "-top") %> <%= styled_label_tag("custom_action_actions_#{action.key}", action.human_name, class: "-top") %>
@@ -157,12 +156,18 @@
step: 1 %> step: 1 %>
</div> </div>
<% end %> <% end %>
<button type="button" <%=
class="spot-link" render(
title="<%= t(:button_close) %>" Primer::Beta::IconButton.new(
data-action="click->hide-sections#hide"> icon: :x,
<%= op_icon("icon-close icon-small") %> size: :small,
</button> scheme: :invisible,
type: :button,
aria: { label: t(:button_close) },
data: { action: "click->hide-sections#hide" }
)
)
%>
</div> </div>
</section> </section>
<% end %> <% end %>
@@ -172,7 +177,7 @@
<div class="form--field"> <div class="form--field">
<label class="form--label" for="add-custom-action-select"> <label class="form--label" for="add-custom-action-select">
<%= op_icon("icon-add icon4") %> <%= render(Primer::Beta::Octicon.new(icon: :plus, size: :small)) %>
<%= I18n.t(:"custom_actions.actions.add") %> <%= I18n.t(:"custom_actions.actions.add") %>
</label> </label>
<span class="form--field-container"> <span class="form--field-container">
@@ -1,4 +1,4 @@
<%#-- copyright <%# -- copyright
OpenProject is an open source project management software. OpenProject is an open source project management software.
Copyright (C) the OpenProject GmbH Copyright (C) the OpenProject GmbH
@@ -25,7 +25,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See COPYRIGHT and LICENSE files for more details. See COPYRIGHT and LICENSE files for more details.
++#%> ++# %>
<% content_controller "admin--custom-fields", <% content_controller "admin--custom-fields",
"admin--custom-fields-multi-select-value": @custom_field.multi_value? %> "admin--custom-fields-multi-select-value": @custom_field.multi_value? %>
@@ -103,39 +103,32 @@ See COPYRIGHT and LICENSE files for more details.
no_label: true %> no_label: true %>
</td> </td>
<td> <td>
<span class="reorder-icons"> <span class="reorder-icons">
<a <%=
aria-label="<%= t(:label_sort_highest) %>" render(Primer::Beta::ButtonGroup.new(scheme: :invisible, size: :small)) do |component|
rel="nofollow" component.with_button(
href="#" icon: :"move-to-top",
class="sort-up-custom-option" "aria-label": t(:label_sort_highest),
data-action="admin--custom-fields#moveRowToTheTop"> data: { action: "admin--custom-fields#moveRowToTheTop" }
<%= op_icon("icon-context icon-sort-up icon-small") %> )
</a> component.with_button(
<a icon: :"chevron-up",
aria-label="<%= t(:label_sort_higher) %>" "aria-label": t(:label_sort_higher),
rel="nofollow" data: { action: "admin--custom-fields#moveRowUp" }
href="#" )
class="move-up-custom-option" component.with_button(
data-action="admin--custom-fields#moveRowUp"> icon: :"chevron-down",
<%= op_icon("icon-context icon-arrow-up2 icon-small") %> "aria-label": t(:label_sort_lower),
</a> data: { action: "admin--custom-fields#moveRowDown" }
<a )
aria-label="<%= t(:label_sort_lower) %>" component.with_button(
rel="nofollow" href="#" icon: :"move-to-bottom",
class="move-down-custom-option" "aria-label": t(:label_sort_lowest),
data-action="admin--custom-fields#moveRowDown"> data: { action: "admin--custom-fields#moveRowToTheBottom" }
<%= op_icon("icon-context icon-arrow-down2 icon-small") %> )
</a> end
<a %>
aria-label="<%= t(:label_sort_lowest) %>" </span>
rel="nofollow"
href="#"
class="sort-down-custom-option"
data-action="admin--custom-fields#moveRowToTheBottom">
<%= op_icon("icon-context icon-sort-down icon-small") %>
</a>
</span>
</td> </td>
<td> <td>
<%= link_to "", <%= link_to "",
@@ -146,7 +139,7 @@ See COPYRIGHT and LICENSE files for more details.
turbo_confirm: t(:"custom_fields.confirm_destroy_option") turbo_confirm: t(:"custom_fields.confirm_destroy_option")
}, },
class: "icon icon-delete delete-custom-option", class: "icon icon-delete delete-custom-option",
title: t(:button_delete) %> "aria-label": t(:button_delete) %>
</td> </td>
</tr> </tr>
<% end %> <% end %>
+1 -1
View File
@@ -22,7 +22,7 @@
tabindex="0" tabindex="0"
data-action="click->filter--filters-form#toggleMultiSelect" data-action="click->filter--filters-form#toggleMultiSelect"
data-filter--filters-form-filter-name-param="<%= filter.name %>"> data-filter--filters-form-filter-name-param="<%= filter.name %>">
<span class="icon-context icon-button <%= multi_value ? "icon-minus2" : "icon-add" %> icon4" title="<%= t(:label_enable_multi_select) %>"> <span class="icon-context icon-button <%= multi_value ? "icon-minus2" : "icon-add" %> icon4">
<span class="sr-only"><%= t(:label_enable_multi_select) %></span> <span class="sr-only"><%= t(:label_enable_multi_select) %></span>
</span> </span>
</a> </a>
+17 -22
View File
@@ -1,4 +1,4 @@
<%#-- copyright <%# -- copyright
OpenProject is an open source project management software. OpenProject is an open source project management software.
Copyright (C) the OpenProject GmbH Copyright (C) the OpenProject GmbH
@@ -25,7 +25,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See COPYRIGHT and LICENSE files for more details. See COPYRIGHT and LICENSE files for more details.
++#%> ++# %>
<%# <%#
needs locals: needs locals:
@@ -35,29 +35,24 @@ See COPYRIGHT and LICENSE files for more details.
<%= error_messages_for :ldap_auth_source %> <%= error_messages_for :ldap_auth_source %>
<% if @ldap_auth_source.new_record? %> <% if @ldap_auth_source.new_record? %>
<div class="op-toast -info"> <%=
<a title="close" class="op-toast--close icon-context icon-close"></a> render(Primer::Alpha::Banner.new(scheme: :warning, mb: 2, dismiss_scheme: :hide)) do
<div class="op-toast--content"> link_translate(
<%= "ldap_auth_sources.technical_warning",
link_translate( links: {
"ldap_auth_sources.technical_warning", docs_url: %i[sysadmin_docs ldap]
links: { }
docs_url: %i[sysadmin_docs ldap] )
} end
) %>
%>
</div>
</div>
<% end %> <% end %>
<% if @ldap_auth_source.seeded_from_env? %> <% if @ldap_auth_source.seeded_from_env? %>
<div class="op-toast -warning"> <%= render(Primer::Alpha::Banner.new(scheme: :warning, icon: :alert, mb: 2)) do %>
<div class="op-toast--content"> <%= t(:label_seeded_from_env_warning) %>
<%= t(:label_seeded_from_env_warning) %> <br>
<br> <%= link_to t("ldap_auth_sources.back_to_index"), { action: :index } %>
<%= link_to t("ldap_auth_sources.back_to_index"), { action: :index } %> <% end %>
</div>
</div>
<% end %> <% end %>
<%= content_tag :fieldset, class: "form--fieldset", disabled: @ldap_auth_source.seeded_from_env? do %> <%= content_tag :fieldset, class: "form--fieldset", disabled: @ldap_auth_source.seeded_from_env? do %>
+32 -15
View File
@@ -39,9 +39,19 @@ See COPYRIGHT and LICENSE files for more details.
"user-limit-member-autocompleter-value": true "user-limit-member-autocompleter-value": true
} }
) do |f| %> ) do |f| %>
<a title="<%= t("js.close_form_title") %>"
class="hide-member-form-button form--close icon-context icon-close" <%=
data-action="members-form#hideAddMemberForm"></a> render(
Primer::Beta::IconButton.new(
icon: :x,
scheme: :invisible,
classes: "hide-member-form-button form--close",
tooltip_direction: :se,
aria: { label: t("js.close_form_title") },
data: { action: "members-form#hideAddMemberForm" }
)
)
%>
<div id="new-member-message"></div> <div id="new-member-message"></div>
<div class="grid-block"> <div class="grid-block">
<div class="grid-content medium-5 small-12 collapse -flex"> <div class="grid-content medium-5 small-12 collapse -flex">
@@ -52,7 +62,7 @@ See COPYRIGHT and LICENSE files for more details.
user_id_title = I18n.t(:label_principal_search) user_id_title = I18n.t(:label_principal_search)
if current_user.admin? if current_user.admin?
user_id_title += I18n.t(:label_principal_invite_via_email) user_id_title = "#{user_id_title}#{I18n.t(:label_principal_invite_via_email)}"
end end
%> %>
<%= styled_label_tag :member_user_ids, user_id_title %> <%= styled_label_tag :member_user_ids, user_id_title %>
@@ -61,7 +71,7 @@ See COPYRIGHT and LICENSE files for more details.
inputs: { inputs: {
inputName: "member[user_ids]", inputName: "member[user_ids]",
inputBindValue: "id", inputBindValue: "id",
url: autocomplete_for_member_project_members_path + ".json", url: "#{autocomplete_for_member_project_members_path}.json",
multiple: true multiple: true
} %> } %>
</div> </div>
@@ -99,16 +109,23 @@ See COPYRIGHT and LICENSE files for more details.
</div> </div>
<% if OpenProject::Enterprise.user_limit.present? %> <% if OpenProject::Enterprise.user_limit.present? %>
<div class="op-toast -warning icon-warning d-none" <%=
data-user-limit-target="limitWarning"> render(
<div class="op-toast--content"> Primer::Alpha::Banner.new(
<p><%= scheme: :warning,
link_translate( icon: :alert,
"warning_user_limit_reached#{'_admin' if current_user.admin?}", classes: "d-none",
links: { upgrade_url: OpenProject::Enterprise.upgrade_url } data: { "user-limit-target": "limitWarning" }
) %> )
</div> ) do
</div> %>
<%=
link_translate(
"warning_user_limit_reached#{'_admin' if current_user.admin?}",
links: { upgrade_url: OpenProject::Enterprise.upgrade_url }
)
%>
<% end %> <% end %>
<% end %>
<% end %> <% end %>
@@ -49,8 +49,7 @@ See COPYRIGHT and LICENSE files for more details.
<button <button
class="button button_no-margin spot-action-bar--action spot-modal--cancel-button" class="button button_no-margin spot-action-bar--action spot-modal--cancel-button"
data-tour-selector="modal-close-button" data-tour-selector="modal-close-button"
dynamic-content-modal-close-button dynamic-content-modal-close-button><%= t(:button_close) %></button>
title=<%= t(:button_close) %>><%= t(:button_close) %></button>
<%= styled_button_tag t(:button_save), class: "button_no-margin -primary -with-icon icon-checkmark spot-action-bar--action" %> <%= styled_button_tag t(:button_save), class: "button_no-margin -primary -with-icon icon-checkmark spot-action-bar--action" %>
</div> </div>
</div> </div>
@@ -27,7 +27,7 @@ See COPYRIGHT and LICENSE files for more details.
++#%> ++#%>
<div class="form--field"> <div class="form--field">
<label class="form--label" title="<%= label %>"><%= label %></label> <label class="form--label"><%= label %></label>
<span class="form--field-container"> <span class="form--field-container">
<td align="center"> <td align="center">
<%= number_to_human_size(value, precision: 2) %></td> <%= number_to_human_size(value, precision: 2) %></td>
@@ -30,9 +30,10 @@ See COPYRIGHT and LICENSE files for more details.
<div id="repository--checkout-instructions" <div id="repository--checkout-instructions"
class="op-toast -info persistent-toggle--toaster" class="op-toast -info persistent-toggle--toaster"
hidden> hidden>
<a title="{{ ::I18n.t('js.close_popup_title') }}" <button type="button"
class="op-toast--close icon-context icon-close"> aria-label="{{ ::I18n.t('js.close_popup_title') }}"
</a> class="op-toast--close icon-context icon-close button--link">
</button>
<div class="op-toast--content"> <div class="op-toast--content">
<p> <p>
<%= simple_format instructions.instructions %> <%= simple_format instructions.instructions %>
@@ -45,12 +46,7 @@ See COPYRIGHT and LICENSE files for more details.
</div> </div>
</div> </div>
<% elsif @instructions.supported_but_not_enabled? %> <% elsif @instructions.supported_but_not_enabled? %>
<div class="op-toast -warning"> <%= render(Primer::Alpha::Banner.new(scheme: :warning, icon: :alert, dismiss_scheme: :hide)) do %>
<a title="{{ ::I18n.t('js.close_popup_title') }}" <%= t("repositories.checkout.not_available") %>
class="op-toast--close icon-context icon-close"> <% end %>
</a>
<div class="op-toast--content">
<p><%= t("repositories.checkout.not_available") %></p>
</div>
</div>
<% end %> <% end %>
+7 -7
View File
@@ -27,9 +27,9 @@ See COPYRIGHT and LICENSE files for more details.
++#%> ++#%>
<%= render(Repositories::Revision::PageHeaderComponent.new(changeset: @changeset, repository: @repository, project: @project)) %> <%= render(Repositories::Revision::PageHeaderComponent.new(changeset: @changeset, repository: @repository, project: @project)) %>
<%= form_tag({ controller: "/repositories", action: "revision", project_id: @project }, method: :get) do %> <%= form_tag({ controller: "/repositories", action: "revision", project_id: @project }, method: :get) do %>
<%= text_field_tag :rev, @rev, placeholder: t(:label_revision) %> <%= text_field_tag :rev, @rev, placeholder: t(:label_revision), autocomplete: "off" %>
<% end %> <% end %>
<p><% if @changeset.scmid %>ID: <%= h(@changeset.scmid) %><br> <p><% if @changeset.scmid %>ID: <%= h(@changeset.scmid) %><br>
<% end %> <% end %>
@@ -46,11 +46,11 @@ See COPYRIGHT and LICENSE files for more details.
<% if User.current.allowed_in_project?(:browse_repository, @project) %> <% if User.current.allowed_in_project?(:browse_repository, @project) %>
<h3><%= t(:label_attachment_plural) %></h3> <h3><%= t(:label_attachment_plural) %></h3>
<ul id="changes-legend"> <ul id="changes-legend">
<li class="change change-A icon icon-add" title=<%= t(:label_added) %>><%= t(:label_added) %></li> <li class="change change-A icon icon-add" aria-label="<%= t(:label_added) %>"><%= t(:label_added) %></li>
<li class="change change-M icon icon-arrow-left-right" title=<%= t(:label_modified) %>><%= t(:label_modified) %></li> <li class="change change-M icon icon-arrow-left-right" aria-label="<%= t(:label_modified) %>"><%= t(:label_modified) %></li>
<li class="change change-C icon icon-copy" title=<%= t(:label_copied) %>><%= t(:label_copied) %></li> <li class="change change-C icon icon-copy" aria-label="<%= t(:label_copied) %>"><%= t(:label_copied) %></li>
<li class="change change-R icon icon-rename" title=<%= t(:label_renamed) %>><%= t(:label_renamed) %></li> <li class="change change-R icon icon-rename" aria-label="<%= t(:label_renamed) %>"><%= t(:label_renamed) %></li>
<li class="change change-D icon icon-delete" title=<%= t(:label_deleted) %>><%= t(:label_deleted) %></li> <li class="change change-D icon icon-delete" aria-label="<%= t(:label_deleted) %>"><%= t(:label_deleted) %></li>
</ul> </ul>
<p><%= link_to(t(:label_view_diff), action: "diff", project_id: @project, repo_path: nil, rev: @changeset.identifier) if @changeset.file_changes.any? %></p> <p><%= link_to(t(:label_view_diff), action: "diff", project_id: @project, repo_path: nil, rev: @changeset.identifier) if @changeset.file_changes.any? %></p>
<div class="changeset-changes"> <div class="changeset-changes">
@@ -1,5 +1,7 @@
<div id="unsupported-browser-warning" class="warning-bar--item"> <div id="unsupported-browser-warning" class="warning-bar--item">
<span title="<%= t("unsupported_browser.close_warning") %>" class="icon3 icon-warning warning-bar--disable-on-hover"></span> <button type="button"
aria-label="<%= t("unsupported_browser.close_warning") %>"
class="icon3 icon-warning warning-bar--disable-on-hover button--link"></button>
<p> <p>
<strong><%= t("unsupported_browser.title") %></strong> <strong><%= t("unsupported_browser.title") %></strong>
@@ -27,8 +29,8 @@
} }
// Click handler to hide // Click handler to hide
var span = message.querySelector('.warning-bar--disable-on-hover'); var closeButton = message.querySelector('.warning-bar--disable-on-hover');
span.onclick = function() { closeButton.onclick = function() {
message.style.display = 'none'; message.style.display = 'none';
try { try {
window.localStorage.setItem('unsupported-browser-warning-ignore', '1'); window.localStorage.setItem('unsupported-browser-warning-ignore', '1');
@@ -66,7 +66,7 @@ a.icon, a.icon-context
a.icon:hover, a.icon-context:hover a.icon:hover, a.icon-context:hover
text-decoration: none text-decoration: none
#content table th a.no-decoration-on-hover:hover, a.no-decoration-on-hover:hover #content table th a.no-decoration-on-hover:hover, a.no-decoration-on-hover:hover, button.no-decoration-on-hover:hover
text-decoration: none text-decoration: none
.skip-navigation-link .skip-navigation-link
@@ -1,5 +1,5 @@
<fieldset class="form--fieldset"> <fieldset class="form--fieldset">
<legend class="form--fieldset-legend" title="<%= t "avatars.label_gravatar" %>"><%= t "avatars.label_gravatar" %></legend> <legend class="form--fieldset-legend"><%= t "avatars.label_gravatar" %></legend>
<p> <p>
<%= t "avatars.text_your_current_gravatar" %> <%= t "avatars.text_your_current_gravatar" %>
</p> </p>
@@ -1,5 +1,5 @@
<fieldset class="form--fieldset"> <fieldset class="form--fieldset">
<legend class="form--fieldset-legend" title="<%= t "avatars.label_local_avatar" %>"><%= t "avatars.label_local_avatar" %></legend> <legend class="form--fieldset-legend"><%= t "avatars.label_local_avatar" %></legend>
<p> <p>
<%= t "avatars.text_your_local_avatar" %> <%= t "avatars.text_your_local_avatar" %>
<% if @manager.gravatar_enabled? %> <% if @manager.gravatar_enabled? %>
@@ -1,7 +1,7 @@
<% manager = ::OpenProject::Avatars::AvatarManager %> <% manager = ::OpenProject::Avatars::AvatarManager %>
<fieldset class="form--fieldset"> <fieldset class="form--fieldset">
<legend class="form--fieldset-legend" title="<%= t "avatars.label_gravatar" %>"><%= t "avatars.label_gravatar" %></legend> <legend class="form--fieldset-legend"><%= t "avatars.label_gravatar" %></legend>
<div class="form--field"> <div class="form--field">
<%= styled_label_tag "settings-enable-gravatars", t("avatars.settings.enable_gravatars") %> <%= styled_label_tag "settings-enable-gravatars", t("avatars.settings.enable_gravatars") %>
@@ -13,7 +13,7 @@
</fieldset> </fieldset>
<fieldset class="form--fieldset"> <fieldset class="form--fieldset">
<legend class="form--fieldset-legend" title="<%= t "avatars.label_local_avatar" %>"><%= t "avatars.label_local_avatar" %></legend> <legend class="form--fieldset-legend"><%= t "avatars.label_local_avatar" %></legend>
<div class="form--field"> <div class="form--field">
<%= styled_label_tag "settings-enable-local-avatars", t("avatars.settings.enable_local_avatars") %> <%= styled_label_tag "settings-enable-local-avatars", t("avatars.settings.enable_local_avatars") %>
<%= hidden_field_tag "settings[enable_local_avatars]", 0 %> <%= hidden_field_tag "settings[enable_local_avatars]", 0 %>
@@ -1,11 +1,8 @@
<div class="op-toast -info"> <%= render(Primer::Alpha::Banner.new(scheme: :default, icon: :info, dismiss_scheme: :hide)) do %>
<a href="#" title="close" class="op-toast--close icon-context icon-close"></a> <p><%= t("ifc_models.processing_notice.processing_default") %></p>
<div class="op-toast--content"> <ul>
<p><%= t("ifc_models.processing_notice.processing_default") %></p> <% unconverted.each do |model| %>
<ul> <li><%= model.title %></li>
<% unconverted.each do |model| %> <% end %>
<li><%= model.title %></li> </ul>
<% end %> <% end %>
</ul>
</div>
</div>
@@ -98,9 +98,7 @@ RSpec.describe "show default model", :js, with_config: { edition: "bim" } do
end end
it "renders a notification" do it "renders a notification" do
show_default_page expect(page).to have_css(".Banner", text: I18n.t(:"ifc_models.processing_notice.processing_default"))
.expect_toast(type: :info,
message: I18n.t(:"ifc_models.processing_notice.processing_default"))
end end
end end
end end
@@ -93,11 +93,14 @@ See COPYRIGHT and LICENSE files for more details.
<% end %> <% end %>
<% cost_value = labor_budget_item.amount || labor_budget_item.calculated_costs(@budget.fixed_date, @budget.project_id) %> <% cost_value = labor_budget_item.amount || labor_budget_item.calculated_costs(@budget.fixed_date, @budget.project_id) %>
<a id="<%= "#{id_prefix}_costs" %>" class="costs--edit-planned-costs-btn icon-context icon-edit" title="<%= t(:help_click_to_edit) %>"> <button type="button"
id="<%= "#{id_prefix}_costs" %>"
class="costs--edit-planned-costs-btn icon-context icon-edit button--link no-decoration-on-hover"
aria-label="<%= t(:help_click_to_edit) %>">
<% if labor_budget_item.costs_visible_by?(User.current) %> <% if labor_budget_item.costs_visible_by?(User.current) %>
<%= number_to_currency(cost_value) %> <%= number_to_currency(cost_value) %>
<% end %> <% end %>
</a> </button>
<%= render partial: "/budgets/items/budget_override_cost_form", <%= render partial: "/budgets/items/budget_override_cost_form",
locals: { locals: {
field_name: "#{name_prefix}[amount]", field_name: "#{name_prefix}[amount]",
@@ -95,9 +95,12 @@ See COPYRIGHT and LICENSE files for more details.
<% end %> <% end %>
<% cost_value = material_budget_item.amount || material_budget_item.calculated_costs(@budget.fixed_date) %> <% cost_value = material_budget_item.amount || material_budget_item.calculated_costs(@budget.fixed_date) %>
<a id="<%= id_prefix %>_costs" class="costs--edit-planned-costs-btn icon-context icon-edit" role="button" title="<%= t(:help_click_to_edit) %>"> <button type="button"
id="<%= id_prefix %>_costs"
class="costs--edit-planned-costs-btn icon-context icon-edit button--link no-decoration-on-hover"
aria-label="<%= t(:help_click_to_edit) %>">
<%= number_to_currency(cost_value) %> <%= number_to_currency(cost_value) %>
</a> </button>
<%= render partial: "/budgets/items/budget_override_cost_form", <%= render partial: "/budgets/items/budget_override_cost_form",
locals: { locals: {
field_name: "#{name_prefix}[amount]", field_name: "#{name_prefix}[amount]",
@@ -76,11 +76,18 @@ See COPYRIGHT and LICENSE files for more details.
</span> </span>
</td> </td>
<td class="buttons"> <td class="buttons">
<a href="#" <%=
class="delete-row-button no-decoration-on-hover" render(
data-action="subform#deleteRow"> Primer::Beta::IconButton.new(
<%= op_icon("icon-context icon-delete", title: t(:button_delete)) %> icon: :trash,
</a> scheme: :invisible,
type: :button,
classes: "delete-row-button",
aria: { label: t(:button_delete) },
data: { action: "subform#deleteRow" }
)
)
%>
</td> </td>
<% end %> <% end %>
<% end %> <% end %>
@@ -122,13 +122,19 @@ See COPYRIGHT and LICENSE files for more details.
</div> </div>
<div class="wp-inline-create-button"> <div class="wp-inline-create-button">
<label class="sr-only" for="add_rate_date"> <%= t(:description_date_for_new_rate) %></label> <label class="sr-only" for="add_rate_date"> <%= t(:description_date_for_new_rate) %></label>
<a id="add_rate_date" <%=
href="#" render(
class="add-row-button wp-inline-create--add-link" Primer::Beta::IconButton.new(
title="<%= t(:button_add_rate) %>" icon: :plus,
data-action="subform#addRow"> id: "add_rate_date",
<%= op_icon("icon icon-add") %> scheme: :invisible,
</a> type: :button,
classes: "add-row-button wp-inline-create--add-link",
aria: { label: t(:button_add_rate) },
data: { action: "subform#addRow" }
)
)
%>
</div> </div>
<%= styled_button_tag t(:button_save), class: "-with-icon icon-checkmark" %> <%= styled_button_tag t(:button_save), class: "-with-icon icon-checkmark" %>
<% end %> <% end %>
@@ -130,13 +130,12 @@ See COPYRIGHT and LICENSE files for more details.
<label for="cost_entry_costs_edit" class="form--label"><%= CostEntry.human_attribute_name(:costs) %></label> <label for="cost_entry_costs_edit" class="form--label"><%= CostEntry.human_attribute_name(:costs) %></label>
<% if User.current.allowed_in_project?(:view_cost_rates, @cost_entry.project) %> <% if User.current.allowed_in_project?(:view_cost_rates, @cost_entry.project) %>
<span class="form--field-container"> <span class="form--field-container">
<a href="#" <button type="button"
id="cost_entry_costs" id="cost_entry_costs"
class="costs--edit-planned-costs-btn icon-context icon-edit" class="costs--edit-planned-costs-btn icon-context icon-edit button--link no-decoration-on-hover"
role="button" aria-label="<%= t(:help_click_to_edit) %>">
title="<%= t(:help_click_to_edit) %>"> <%= number_to_currency(@cost_entry.real_costs) %>
<%= number_to_currency(@cost_entry.real_costs) %> </button>
</a>
<%= render partial: "/budgets/items/budget_override_cost_form", <%= render partial: "/budgets/items/budget_override_cost_form",
locals: { locals: {
field_name: "cost_entry[overridden_costs]", field_name: "cost_entry[overridden_costs]",
@@ -67,9 +67,18 @@ See COPYRIGHT and LICENSE files for more details.
</span> </span>
</td> </td>
<td class="buttons"> <td class="buttons">
<a href="#" class="delete-row-button no-decoration-on-hover" data-action="subform#deleteRow"> <%=
<%= op_icon("icon-context icon-delete", title: t(:button_delete)) %> render(
</a> Primer::Beta::IconButton.new(
icon: :trash,
scheme: :invisible,
type: :button,
classes: "delete-row-button",
aria: { label: t(:button_delete) },
data: { action: "subform#deleteRow" }
)
)
%>
</td> </td>
<% end %> <% end %>
<% end %> <% end %>
@@ -105,10 +105,19 @@ See COPYRIGHT and LICENSE files for more details.
</div> </div>
</div> </div>
<div class="wp-inline-create-button"> <div class="wp-inline-create-button">
<a href="#" class="add-row-button wp-inline-create--add-link" data-action="subform#addRow"> <%=
<%= op_icon("icon icon-add") %> render(
<%= t(:button_add_rate) %> Primer::Beta::Button.new(
</a> scheme: :link,
type: :button,
classes: "add-row-button wp-inline-create--add-link",
data: { action: "subform#addRow" }
)
) do |button|
button.with_leading_visual_icon(icon: :plus)
t(:button_add_rate)
end
%>
</div> </div>
<div class="generic-table--action-buttons"> <div class="generic-table--action-buttons">
<%= styled_button_tag t(:button_save), class: "-with-icon icon-checkmark" %> <%= styled_button_tag t(:button_save), class: "-with-icon icon-checkmark" %>
@@ -1,3 +1,5 @@
# frozen_string_literal: true
#-- copyright #-- copyright
# OpenProject is an open source project management software. # OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH # Copyright (C) the OpenProject GmbH
@@ -64,7 +66,7 @@ RSpec.describe "hourly rates on user edit", :js do
before do before do
click_link "Update" # go to update view for rates click_link "Update" # go to update view for rates
SeleniumHubWaiter.wait SeleniumHubWaiter.wait
find(".icon-delete").click # delete last existing rate find(".delete-row-button").click # delete last existing rate
click_on "Save" # save change click_on "Save" # save change
end end
@@ -88,13 +90,14 @@ RSpec.describe "hourly rates on user edit", :js do
# Expect the german locale output # Expect the german locale output
expect(page).to have_field("user[existing_rate_attributes][#{rate.id}][rate]", with: "1,00") expect(page).to have_field("user[existing_rate_attributes][#{rate.id}][rate]", with: "1,00")
click_link "Satz hinzufügen" click_button "Satz hinzufügen"
fill_in "user_new_rate_attributes_1_valid_from", with: (Time.zone.today + 1.day).iso8601 fill_in "user_new_rate_attributes_1_valid_from", with: (Time.zone.today + 1.day).iso8601
find("input#user_new_rate_attributes_1_valid_from").send_keys :escape find("input#user_new_rate_attributes_1_valid_from").send_keys :escape
fill_in "user_new_rate_attributes_1_rate", with: "5,12" fill_in "user_new_rate_attributes_1_rate", with: "5,12"
click_button "Speichern" click_button "Speichern"
expect_flash(type: :notice)
view_rates view_rates
@@ -67,21 +67,21 @@ See COPYRIGHT and LICENSE files for more details.
<p>OpenProject allows hyperlinking between issues, changesets and wiki pages from anywhere wiki formatting is used.</p> <p>OpenProject allows hyperlinking between issues, changesets and wiki pages from anywhere wiki formatting is used.</p>
<ul> <ul>
<li><strong>#124</strong> displays a link to an issue: <del><a href="#" class="issue" title="bulk edit doesn't change the category or version properties (Closed)">#124</a></del> (link is striked-through if the issue is closed)</li> <li><strong>#124</strong> displays a link to an issue: <del><a href="/issues/124" class="issue">#124</a></del> (link is striked-through if the issue is closed)</li>
<li><strong>##124</strong> displays a link to an issue with context information: <a href="/issues/12" class="issue status-1 priority-2 overdue created-by-me" title="Issue subject (New)">#12 New: Issue subject</a> 2012-05-14 - 2012-05-23 (User Name - assigned to)</li> <li><strong>##124</strong> displays a link to an issue with context information: <a href="/issues/12" class="issue status-1 priority-2 overdue created-by-me">#12 New: Issue subject</a> 2012-05-14 - 2012-05-23 (User Name - assigned to)</li>
<li><strong>###124</strong> displays a link to an issue with context information and an excerpt (first 3 lines) of the description</li> <li><strong>###124</strong> displays a link to an issue with context information and an excerpt (first 3 lines) of the description</li>
<li><strong>r758</strong> displays a link to a changeset: <a href="#" class="changeset" title="Search engine now only searches objects the user is allowed to view.">r758</a></li> <li><strong>r758</strong> displays a link to a changeset: <a href="/repositories/revision/758" class="changeset">r758</a></li>
<li><strong>commit:c6f4d0fd</strong> displays a link to a changeset with a non-numeric hash: <a href="#" class="changeset">c6f4d0fd</a></li> <li><strong>commit:c6f4d0fd</strong> displays a link to a changeset with a non-numeric hash: <a href="/repositories/revision/c6f4d0fd" class="changeset">c6f4d0fd</a></li>
<li><strong>sandbox:r758</strong> displays a link to a changeset of another project: <a href="#" class="changeset" title="Search engine now only searches objects the user is allowed to view.">sandbox:r758</a></li> <li><strong>sandbox:r758</strong> displays a link to a changeset of another project: <a href="/projects/sandbox/repository/revision/758" class="changeset">sandbox:r758</a></li>
<li><strong>sandbox:c6f4d0fd</strong> displays a link to a changeset with a non-numeric hash: <a href="#" class="changeset">sandbox:c6f4d0fd</a></li> <li><strong>sandbox:c6f4d0fd</strong> displays a link to a changeset with a non-numeric hash: <a href="/projects/sandbox/repository/revision/c6f4d0fd" class="changeset">sandbox:c6f4d0fd</a></li>
</ul> </ul>
<p>Wiki links:</p> <p>Wiki links:</p>
<ul> <ul>
<li><strong>[[Guide]]</strong> displays a link to the page named 'Guide': <a href="#" class="wiki-page">Guide</a></li> <li><strong>[[Guide]]</strong> displays a link to the page named 'Guide': <a href="/projects/demo/wiki/Guide" class="wiki-page">Guide</a></li>
<li><strong>[[Guide#further-reading]]</strong> takes you to the anchor "further-reading". Headings get automatically assigned anchors so that you can refer to them: <a href="#" class="wiki-page">Guide</a></li> <li><strong>[[Guide#further-reading]]</strong> takes you to the anchor "further-reading". Headings get automatically assigned anchors so that you can refer to them: <a href="/projects/demo/wiki/Guide#further-reading" class="wiki-page">Guide</a></li>
<li><strong>[[Guide|User manual]]</strong> displays a link to the same page but with a different text: <a href="#" class="wiki-page">User manual</a></li> <li><strong>[[Guide|User manual]]</strong> displays a link to the same page but with a different text: <a href="/projects/demo/wiki/Guide" class="wiki-page">User manual</a></li>
</ul> </ul>
<p>You can also link to pages of an other project wiki:</p> <p>You can also link to pages of an other project wiki:</p>
@@ -91,7 +91,7 @@ See COPYRIGHT and LICENSE files for more details.
<li><strong>[[sandbox:]]</strong> displays a link to the Sandbox wiki main page</li> <li><strong>[[sandbox:]]</strong> displays a link to the Sandbox wiki main page</li>
</ul> </ul>
<p>Wiki links are displayed in red if the page doesn't exist yet, eg: <a href="#" class="wiki-page new">Nonexistent page</a>.</p> <p>Wiki links are displayed in red if the page doesn't exist yet, eg: <a href="/projects/demo/wiki/Nonexistent_page" class="wiki-page new">Nonexistent page</a>.</p>
<p>Links to other resources:</p> <p>Links to other resources:</p>
@@ -6,18 +6,19 @@
id: "submit_backup_code", id: "submit_backup_code",
data: { data: {
turbo: false turbo: false
}) do %> }
) do %>
<h2><%= t "two_factor_authentication.login.enter_backup_code_title" %></h2> <h2><%= t "two_factor_authentication.login.enter_backup_code_title" %></h2>
<p><%= I18n.t("two_factor_authentication.login.enter_backup_code_text") %></p> <p><%= I18n.t("two_factor_authentication.login.enter_backup_code_text") %></p>
<hr class="form--separator"> <hr class="form--separator">
<div class="form--field -required -wide-label"> <div class="form--field -required -wide-label">
<%= styled_label_tag "backup_code", t("two_factor_authentication.backup_codes.singular") %> <%= styled_label_tag "backup_code", t("two_factor_authentication.backup_codes.singular") %>
<div class="form--field-container"> <div class="form--field-container">
<%= styled_text_field_tag "backup_code", nil, required: true, autocomplete: "off", size: 20, maxlength: 20, tabindex: 1, autofocus: true %> <%= styled_text_field_tag "backup_code", nil, required: true, autocomplete: "off", size: 20, maxlength: 20, autofocus: true %>
</div> </div>
</div> </div>
<div class="login-form--footer"> <div class="login-form--footer">
<input type="submit" name="login" value="<%= t(:button_submit) %>" class="button -primary button_no-margin" tabindex="2"> <input type="submit" name="login" value="<%= t(:button_submit) %>" class="button -primary button_no-margin">
</div> </div>
<% end %> <% end %>
</div> </div>
@@ -1,5 +1,5 @@
<% resend_supported = @strategy.mobile_token? %> <% resend_supported = @strategy.mobile_token? %>
<% has_other_devices = @active_devices.count > 1 %> <% has_other_devices = @active_devices.many? %>
<% has_backup_codes = @authenticated_user.otp_backup_codes.exists? %> <% has_backup_codes = @authenticated_user.otp_backup_codes.exists? %>
<% html_title t(:field_otp) %> <% html_title t(:field_otp) %>
<div id="login-form" <div id="login-form"
@@ -25,7 +25,7 @@
<div class="form--field -wide-label"> <div class="form--field -wide-label">
<%= styled_label_tag "otp", t(:field_otp) %> <%= styled_label_tag "otp", t(:field_otp) %>
<div class="form--field-container"> <div class="form--field-container">
<%= styled_text_field_tag "otp", nil, autocomplete: "off", size: 6, maxlength: 6, tabindex: 1, autofocus: true %> <%= styled_text_field_tag "otp", nil, autocomplete: "off", size: 6, maxlength: 6, autofocus: true %>
</div> </div>
</div> </div>
<% else %> <% else %>
@@ -45,7 +45,7 @@
</div> </div>
<% end %> <% end %>
<div class="login-form--footer"> <div class="login-form--footer">
<input type="submit" name="login" value="<%= t(:button_login) %>" class="button -primary button_no-margin" tabindex="2"> <input type="submit" name="login" value="<%= t(:button_login) %>" class="button -primary button_no-margin">
<% if resend_supported || has_other_devices || has_backup_codes %> <% if resend_supported || has_other_devices || has_backup_codes %>
<div class="login-options-container"> <div class="login-options-container">
<div class="login-links"> <div class="login-links">
@@ -53,7 +53,6 @@
t(:text_otp_not_receive), t(:text_otp_not_receive),
"#", "#",
id: "toggle_resend_form", id: "toggle_resend_form",
tabindex: 3,
data: { action: "two-factor-authentication#toggleResendOptions" }, data: { action: "two-factor-authentication#toggleResendOptions" },
class: "login-form--footer-link" class: "login-form--footer-link"
) %> ) %>
@@ -74,7 +73,8 @@
id: "resend_otp", id: "resend_otp",
data: { data: {
turbo: false turbo: false
}) do %> }
) do %>
<%= hidden_field_tag "use_device", @service.device.id %> <%= hidden_field_tag "use_device", @service.device.id %>
<hr> <hr>
<div class="resend-header"><%= t(:text_send_otp_again) %></div> <div class="resend-header"><%= t(:text_send_otp_again) %></div>
@@ -25,7 +25,8 @@
<% configuration_link = OpenProject::Static::Links.url_for :configuration_guide %> <% configuration_link = OpenProject::Static::Links.url_for :configuration_guide %>
<%= link_to t("two_factor_authentication.settings.text_configuration_guide"), <%= link_to t("two_factor_authentication.settings.text_configuration_guide"),
configuration_link, configuration_link,
target: "_blank" %> target: "_blank",
rel: "noopener" %>
</p> </p>
<%= render(AttributeGroups::AttributeGroupComponent.new) do |component| <%= render(AttributeGroups::AttributeGroupComponent.new) do |component|
if configuration["active_strategies"].empty? if configuration["active_strategies"].empty?
@@ -1,13 +1,15 @@
<% html_title(t(:label_my_account), t("two_factor_authentication.devices.confirm_device")) -%> <% html_title(t(:label_my_account), t("two_factor_authentication.devices.confirm_device")) -%>
<%= styled_form_tag(confirm_path, <%= styled_form_tag(
method: :post, confirm_path,
id: "login-form", method: :post,
class: "form -bordered", id: "login-form",
autocomplete: "off", class: "form -bordered",
data: { autocomplete: "off",
turbo: false data: {
}) do %> turbo: false
}
) do %>
<h2><%= t("two_factor_authentication.devices.confirm_device") %></h2> <h2><%= t("two_factor_authentication.devices.confirm_device") %></h2>
<p><%= t("two_factor_authentication.devices.text_confirm_to_complete_html", identifier: @device.identifier) %></p> <p><%= t("two_factor_authentication.devices.text_confirm_to_complete_html", identifier: @device.identifier) %></p>
<hr class="form--separator"> <hr class="form--separator">
@@ -15,10 +17,10 @@
<div class="form--field -required -wide-label"> <div class="form--field -required -wide-label">
<%= styled_label_tag "otp", t(:field_otp) %> <%= styled_label_tag "otp", t(:field_otp) %>
<div class="form--field-container"> <div class="form--field-container">
<%= styled_text_field_tag "otp", nil, required: true, autocomplete: "off", size: 6, maxlength: 6, tabindex: 1, autofocus: true %> <%= styled_text_field_tag "otp", nil, required: true, autocomplete: "off", size: 6, maxlength: 6, autofocus: true %>
</div> </div>
</div> </div>
<div class="login-form--footer"> <div class="login-form--footer">
<input type="submit" name="login" value="<%= t "button_continue" %>" class="button -primary button_no-margin" tabindex="2"> <input type="submit" name="login" value="<%= t "button_continue" %>" class="button -primary button_no-margin">
</div> </div>
<% end %> <% end %>
@@ -1,5 +1,5 @@
<fieldset class="form--fieldset"> <fieldset class="form--fieldset">
<legend class="form--fieldset-legend" title="<%= @device.name %>"><%= @device.name %></legend> <legend class="form--fieldset-legend"><%= @device.name %></legend>
<p><%= t("two_factor_authentication.devices.sms.description") %></p> <p><%= t("two_factor_authentication.devices.sms.description") %></p>
@@ -22,7 +22,7 @@
<% available_channels = @device.class.available_channels_in_strategy %> <% available_channels = @device.class.available_channels_in_strategy %>
<% if available_channels.length > 1 %> <% if available_channels.length > 1 %>
<fieldset class="form--fieldset"> <fieldset class="form--fieldset">
<legend class="form--fieldset-legend" title="<%= t(:label_otp_channel) %>"><%= t(:label_otp_channel) %></legend> <legend class="form--fieldset-legend"><%= t(:label_otp_channel) %></legend>
<% available_channels.each do |channel| %> <% available_channels.each do |channel| %>
<div class="form--field"> <div class="form--field">
@@ -1,5 +1,5 @@
<fieldset class="form--fieldset"> <fieldset class="form--fieldset">
<legend class="form--fieldset-legend" title="<%= @device.name %>"><%= @device.name %></legend> <legend class="form--fieldset-legend"><%= @device.name %></legend>
<p><%= t("two_factor_authentication.devices.totp.description") %></p> <p><%= t("two_factor_authentication.devices.totp.description") %></p>
@@ -1,5 +1,5 @@
<fieldset class="form--fieldset"> <fieldset class="form--fieldset">
<legend class="form--fieldset-legend" title="<%= @device.name %>"><%= @device.name %></legend> <legend class="form--fieldset-legend"><%= @device.name %></legend>
<p><%= t("two_factor_authentication.devices.webauthn.description") %></p> <p><%= t("two_factor_authentication.devices.webauthn.description") %></p>
<div class="form--field -required"> <div class="form--field -required">
<%= f.text_field :identifier, required: true, container_class: "-middle" %> <%= f.text_field :identifier, required: true, container_class: "-middle" %>
@@ -2,13 +2,22 @@
data-augmented-model-wrapper data-augmented-model-wrapper
data-activation-selector=".log-response-<%= id %>-modal--activation-link" data-activation-selector=".log-response-<%= id %>-modal--activation-link"
data-modal-class-name="webhooks--response-body-modal"> data-modal-class-name="webhooks--response-body-modal">
<a class="log-response-<%= id %>-modal--activation-link" title="<%= title %>"> <%=
<%= helpers.op_icon("icon-info1") %> render(
<%= t(:button_show) %> Primer::Beta::Button.new(
</a> scheme: :link,
classes: "log-response-#{id}-modal--activation-link",
aria: { label: response_body_title }
)
) do |link|
link.with_leading_visual_icon(icon: :info)
t(:button_show)
end
%>
<div class="modal-delivery-element"> <div class="modal-delivery-element">
<div class="spot-modal--header"> <div class="spot-modal--header">
<h1 class="spot-subheader-big"> <%= title %> </h1> <h1 class="spot-subheader-big"> <%= response_body_title %> </h1>
</div> </div>
<div class="spot-divider"></div> <div class="spot-divider"></div>
<div class="spot-modal--body spot-container"> <div class="spot-modal--body spot-container">
@@ -22,7 +31,7 @@
<div class="spot-action-bar--right"> <div class="spot-action-bar--right">
<button <button
class="button button_no-margin spot-action-bar--action spot-modal--cancel-button" class="button button_no-margin spot-action-bar--action spot-modal--cancel-button"
title="<%= t(:button_close) %>" aria-label="<%= t(:button_close) %>"
dynamic-content-modal-close-button> dynamic-content-modal-close-button>
<%= t(:button_close) %> <%= t(:button_close) %>
</button> </button>
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module ::Webhooks module ::Webhooks
module Outgoing module Outgoing
module Deliveries module Deliveries
@@ -6,8 +8,8 @@ module ::Webhooks
property :response_headers property :response_headers
property :response_body property :response_body
def title def response_body_title
model.class.human_attribute_name("response_body") model.class.human_attribute_name(:response_body)
end end
end end
end end
@@ -36,7 +36,7 @@
class="form--fieldset" class="form--fieldset"
id="webhooks-selected-events" id="webhooks-selected-events"
data-controller="checkable"> data-controller="checkable">
<legend class="form--fieldset-legend" title="<%= t "webhooks.outgoing.form.events.title" %>"> <legend class="form--fieldset-legend">
<%= t "webhooks.outgoing.form.events.title" %> <%= t "webhooks.outgoing.form.events.title" %>
</legend> </legend>
<div class="form--toolbar"> <div class="form--toolbar">
@@ -71,7 +71,7 @@
</fieldset> </fieldset>
<fieldset class="form--fieldset"> <fieldset class="form--fieldset">
<legend class="form--fieldset-legend" title="<%= "webhooks.outgoing.form.project_ids.title" %>"> <legend class="form--fieldset-legend">
<%= t "webhooks.outgoing.form.project_ids.title" %> <%= t "webhooks.outgoing.form.project_ids.title" %>
</legend> </legend>
<p><%= t("webhooks.outgoing.form.project_ids.description") %></p> <p><%= t("webhooks.outgoing.form.project_ids.description") %></p>
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require "spec_helper" require "spec_helper"
RSpec.describe "Manage webhooks through UI", :js, :selenium do RSpec.describe "Manage webhooks through UI", :js, :selenium do
@@ -98,7 +100,7 @@ RSpec.describe "Manage webhooks through UI", :js, :selenium do
# Open modal # Open modal
SeleniumHubWaiter.wait SeleniumHubWaiter.wait
find("td.response_body a", text: "Show").click find("td.response_body").click_on "Show"
page.within(".spot-modal") do page.within(".spot-modal") do
expect(page).to have_css(".webhooks--response-headers strong", text: "test") expect(page).to have_css(".webhooks--response-headers strong", text: "test")
@@ -121,7 +123,7 @@ RSpec.describe "Manage webhooks through UI", :js, :selenium do
within(row_element) do within(row_element) do
id = find("td.id").text.to_i id = find("td.id").text.to_i
matching_log = [log, log2, log3].find { |l| l.id == id } matching_log = [log, log2, log3].find { |l| l.id == id }
find("td.response_body a", text: "Show").click find("td.response_body").click_on "Show"
end end
page.within(".spot-modal") do page.within(".spot-modal") do
@@ -80,7 +80,7 @@ RSpec.shared_examples_for "list custom fields" do |type|
within all(".custom-option-row").last do within all(".custom-option-row").last do
find(".custom-option-value input").set "Solaris" find(".custom-option-value input").set "Solaris"
click_link accessible_name: "Move to top" click_on accessible_name: "Move to top"
end end
click_on "Save" click_on "Save"
@@ -103,7 +103,7 @@ RSpec.describe "work package list custom fields", :js do
check("custom_field_custom_options_attributes_0_default_value") check("custom_field_custom_options_attributes_0_default_value")
check("custom_field_custom_options_attributes_2_default_value") check("custom_field_custom_options_attributes_2_default_value")
within first(".custom-option-row") do within first(".custom-option-row") do
click_link accessible_name: "Move to bottom" click_on accessible_name: "Move to bottom"
end end
click_on "Save" click_on "Save"
@@ -59,8 +59,7 @@ module Pages
def remove_action(name) def remove_action(name)
within "#custom-actions-form--active-actions" do within "#custom-actions-form--active-actions" do
find(".form--field", text: name) find(".form--field", text: name)
.find(".icon-close") .click_on accessible_name: "Close"
.click
end end
end end