Skip to content

Commit b55eb47

Browse files
committed
Introduce a new setting to disable the keyboard shortcuts
1 parent c52571c commit b55eb47

File tree

29 files changed

+164
-36
lines changed

29 files changed

+164
-36
lines changed

app/components/projects/settings/general/show_component.html.erb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ See COPYRIGHT and LICENSE files for more details.
3333
settings_primer_form_with(model: project, url: project_settings_general_path(project)) do |f|
3434
concat(
3535
render(Primer::Beta::Subhead.new) do |component|
36-
component.with_heading(tag: :h2, size: :medium) { I18n.t("projects.settings.header_details") }
36+
component.with_heading(tag: :h3, size: :medium) { I18n.t("projects.settings.header_details") }
3737
end
3838
)
3939
concat(
@@ -52,7 +52,7 @@ See COPYRIGHT and LICENSE files for more details.
5252
settings_primer_form_with(model: project, url: project_settings_general_path(project)) do |f|
5353
concat(
5454
render(Primer::Beta::Subhead.new) do |component|
55-
component.with_heading(tag: :h2, size: :medium) { I18n.t("projects.settings.header_status") }
55+
component.with_heading(tag: :h3, size: :medium) { I18n.t("projects.settings.header_status") }
5656
end
5757
)
5858
concat(
@@ -71,7 +71,7 @@ See COPYRIGHT and LICENSE files for more details.
7171
settings_primer_form_with(model: project, url: project_settings_general_path(project)) do |f|
7272
concat(
7373
render(Primer::Beta::Subhead.new) do |component|
74-
component.with_heading(tag: :h2, size: :medium) { I18n.t("projects.settings.header_relations") }
74+
component.with_heading(tag: :h3, size: :medium) { I18n.t("projects.settings.header_relations") }
7575
end
7676
)
7777
concat(

app/forms/my/look_and_feel_form.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ class My::LookAndFeelForm < ApplicationForm
6262
end
6363
end
6464

65+
f.check_box name: :disable_keyboard_shortcuts,
66+
label: I18n.t("activerecord.attributes.user_preference.disable_keyboard_shortcuts"),
67+
caption: I18n.t("activerecord.attributes.user_preference.disable_keyboard_shortcuts_caption_html",
68+
href: OpenProject::Static::Links.links[:shortcuts][:href]).html_safe
69+
6570
f.submit(name: :submit,
6671
label: I18n.t("activerecord.attributes.user_preference.button_update_look_and_feel"),
6772
scheme: :default)

app/models/permitted_params.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,11 @@ def wiki_page
264264
end
265265

266266
def pref
267-
params.fetch(:pref, {}).permit(:time_zone, :theme,
268-
:comments_sorting, :warn_on_leaving_unsaved,
267+
params.fetch(:pref, {}).permit(:time_zone,
268+
:theme,
269+
:comments_sorting,
270+
:disable_keyboard_shortcuts,
271+
:warn_on_leaving_unsaved,
269272
:auto_hide_popups)
270273
end
271274

app/models/user_preference.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ def comments_in_reverse_order?
8686
comments_sorting == "desc"
8787
end
8888

89+
def disable_keyboard_shortcuts?
90+
settings.fetch(:disable_keyboard_shortcuts) { Setting.disable_keyboard_shortcuts? }
91+
end
92+
93+
def disable_keyboard_shortcuts=(value)
94+
settings[:disable_keyboard_shortcuts] = to_boolean(value)
95+
end
96+
8997
def diff_type
9098
settings.fetch(:diff_type, "inline")
9199
end
@@ -110,6 +118,7 @@ def warn_on_leaving_unsaved=(value)
110118
alias :comments_in_reverse_order :comments_in_reverse_order?
111119
alias :warn_on_leaving_unsaved :warn_on_leaving_unsaved?
112120
alias :auto_hide_popups :auto_hide_popups?
121+
alias :disable_keyboard_shortcuts :disable_keyboard_shortcuts?
113122

114123
def comments_in_reverse_order=(value)
115124
settings[:comments_sorting] = to_boolean(value) ? "desc" : "asc"

app/views/my/interface.html.erb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ See COPYRIGHT and LICENSE files for more details.
4747
end
4848
%>
4949
<%=
50-
settings_primer_form_with(model: @user.pref, scope: :pref, url: { action: "update_settings" }) do |form|
50+
settings_primer_form_with(model: @user.pref, scope: :pref, url: { action: "update_settings" }, data: { turbo: false }) do |form|
5151
render(My::LookAndFeelForm.new(form))
5252
end
5353
%>
@@ -58,7 +58,7 @@ See COPYRIGHT and LICENSE files for more details.
5858
end
5959
%>
6060
<%=
61-
settings_primer_form_with(model: @user.pref, scope: :pref, url: { action: "update_settings" }) do |form|
61+
settings_primer_form_with(model: @user.pref, scope: :pref, url: { action: "update_settings" }, data: { turbo: false }) do |form|
6262
render(My::AlertsForm.new(form))
6363
end
6464
%>

app/views/my/settings.html.erb

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,14 @@ See COPYRIGHT and LICENSE files for more details.
4747
<div class="form--field">
4848
<%= f.select :language, lang_options_for_select, container_class: "-middle" %>
4949
</div>
50-
<%= render Settings::TimeZoneSettingComponent.new(
51-
"time_zone",
52-
form: f,
53-
include_blank: false,
54-
container_class: "-middle"
55-
) %>
50+
<%= fields_for :pref, @user.pref, builder: TabularFormBuilder, lang: current_language do |pref_fields| %>
51+
<%= render Settings::TimeZoneSettingComponent.new(
52+
"time_zone",
53+
form: pref_fields,
54+
include_blank: false,
55+
container_class: "-middle"
56+
) %>
57+
<% end %>
5658
</fieldset>
5759
<%= styled_button_tag t(:button_save), class: "-primary -with-icon icon-checkmark" %>
5860
<% end %>

app/views/users/_general.html.erb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ See COPYRIGHT and LICENSE files for more details.
4848
},
4949
data: {
5050
controller: "admin--users",
51+
turbo: false,
5152
"admin--users-password-auth-selected-value": @user.ldap_auth_source_id.blank?
5253
},
5354
as: :user do |f| %>

app/views/users/_preferences.html.erb

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,15 @@ See COPYRIGHT and LICENSE files for more details.
3939
<%= I18n.t("activerecord.attributes.user_preference.mode_guideline") %>
4040
</div>
4141
</div>
42+
4243
<div class="form--field">
43-
<%= pref_fields.select :comments_sorting,
44-
[[t("activities.work_packages.activity_tab.label_sort_asc"), "asc"],
45-
[t("activities.work_packages.activity_tab.label_sort_desc"), "desc"]],
46-
container_class: defined?(input_size) ? "-#{input_size}" : "" %>
44+
<%= pref_fields.check_box :disable_keyboard_shortcuts,
45+
label: I18n.t("activerecord.attributes.user_preference.disable_keyboard_shortcuts") %>
46+
<span class="form--field-instructions">
47+
<%= I18n.t(
48+
"activerecord.attributes.user_preference.disable_keyboard_shortcuts_caption_html",
49+
href: OpenProject::Static::Links.links[:shortcuts][:href]
50+
).html_safe %>
51+
</span>
4752
</div>
48-
<div class="form--field"><%= pref_fields.check_box :warn_on_leaving_unsaved %></div>
49-
<div class="form--field"><%= pref_fields.check_box :auto_hide_popups %></div>
5053
<% end %>

app/views/users/new.html.erb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ See COPYRIGHT and LICENSE files for more details.
4848
html: { class: nil, autocomplete: "off" },
4949
data: {
5050
controller: "admin--users",
51+
turbo: false,
5152
"admin--users-password-auth-selected-value": @user.ldap_auth_source_id.blank?
5253
},
5354
as: :user do |f| %>

config/constants/settings/definition.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,10 @@ class Definition
333333
description: "Default sort order for activities",
334334
default: "asc"
335335
},
336+
disable_keyboard_shortcuts: {
337+
description: "Whether keyboard short cuts should be disabled (e.g. for better screen reader support)",
338+
default: false
339+
},
336340
default_language: {
337341
default: "en",
338342
allowed: -> { Redmine::I18n.all_languages }

config/locales/en.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,6 +1175,9 @@ en:
11751175
button_update_look_and_feel: "Update look and feel"
11761176
button_update_alerts: "Update alerts"
11771177
comments_sorting: "Display work package activity sorted by"
1178+
disable_keyboard_shortcuts: "Disable keyboard shortcuts"
1179+
disable_keyboard_shortcuts_caption_html: |-
1180+
You can choose to disable default <a href="%{href}">keyboard shortcuts</a> if you use a screen reader or want to avoid accidentally triggering an action with a shortcut.
11781181
dismissed_enterprise_banners: "Hidden enterprise banners"
11791182
impaired: "Accessibility mode"
11801183
auto_hide_popups: "Auto-hide success notifications"

config/schemas/user_preferences.schema.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
"type": "string",
1414
"enum": ["light", "light_high_contrast", "dark"]
1515
},
16+
"disable_keyboard_shortcuts": {
17+
"type": "boolean"
18+
},
1619
"warn_on_leaving_unsaved": {
1720
"type": "boolean"
1821
},

docs/api/apiv3/components/schemas/user_preferences_model.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ example:
1313
method: "patch"
1414
_type: UserPreferences
1515
commentSortDescending: true
16+
disableKeyboardShortcuts: false
1617
timeZone: "Europe/Berlin"
1718
warnOnLeavingUnsaved: true
1819
notifications:

docs/api/apiv3/paths/my_preferences.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ get:
1919
method: "patch"
2020
_type: "UserPreferences"
2121
commentSortDescending: true
22+
disableKeyboardShortcuts: false
2223
timeZone: "Europe/Berlin"
2324
warnOnLeavingUnsaved: true
2425
notifications:
@@ -82,6 +83,7 @@ patch:
8283
method: "patch"
8384
_type: UserPreferences
8485
commentSortDescending: true
86+
disableKeyboardShortcuts: false
8587
timeZone: Europe/Berlin
8688
warnOnLeavingUnsaved: true
8789
notifications:

frontend/src/app/core/config/configuration.service.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ export class ConfigurationService {
5151
return this.userPreference('commentSortDescending');
5252
}
5353

54+
public disableKeyboardShortcuts():boolean {
55+
return this.userPreference('disableKeyboardShortcuts');
56+
}
57+
5458
public warnOnLeavingUnsaved():boolean {
5559
return this.userPreference('warnOnLeavingUnsaved');
5660
}

frontend/src/app/features/user-preferences/state/user-preferences.model.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export interface ImmediateRemindersSettings {
1717
export interface IUserPreference {
1818
autoHidePopups:boolean;
1919
commentSortDescending:boolean;
20+
disableKeyboardShortcuts:boolean;
2021
timeZone:string|null;
2122
warnOnLeavingUnsaved:boolean;
2223
workdays:number[];

frontend/src/app/features/user-preferences/state/user-preferences.store.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ function createInitialState():IUserPreference {
3636
return {
3737
autoHidePopups: true,
3838
commentSortDescending: false,
39+
disableKeyboardShortcuts: false,
3940
timeZone: null,
4041
warnOnLeavingUnsaved: true,
4142
notifications: [],

frontend/src/app/shared/directives/a11y/keyboard-shortcut.service.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { FocusHelperService } from 'core-app/shared/directives/focus/focus-helpe
3131
import { PathHelperService } from 'core-app/core/path-helper/path-helper.service';
3232
import { CurrentProjectService } from 'core-app/core/current-project/current-project.service';
3333
import * as Mousetrap from 'mousetrap';
34+
import { ConfigurationService } from 'core-app/core/config/configuration.service';
3435

3536
const accessKeys = {
3637
preview: 1,
@@ -45,7 +46,6 @@ const accessKeys = {
4546

4647
// this could be extracted into a separate component if it grows
4748
const accessibleListSelector = 'table.keyboard-accessible-list';
48-
const accessibleRowSelector = 'table.keyboard-accessible-list tbody tr';
4949

5050
@Injectable({
5151
providedIn: 'root',
@@ -75,17 +75,22 @@ export class KeyboardShortcutService {
7575
/* eslint-enable quote-props */
7676
};
7777

78-
constructor(private readonly PathHelper:PathHelperService,
78+
constructor(
79+
private readonly PathHelper:PathHelperService,
7980
private readonly FocusHelper:FocusHelperService,
80-
private readonly currentProject:CurrentProjectService) {
81-
this.register();
82-
}
81+
private readonly currentProject:CurrentProjectService,
82+
private readonly configurationService:ConfigurationService,
83+
) {}
8384

8485
/**
8586
* Register the keyboard shortcuts.
8687
*/
8788
public register():void {
88-
_.each(this.shortcuts, (action:() => void, key:string) => Mousetrap.bind(key, action));
89+
void this.configurationService.initialize().then(() => {
90+
if (!this.configurationService.disableKeyboardShortcuts()) {
91+
_.each(this.shortcuts, (action:() => void, key:string) => Mousetrap.bind(key, action));
92+
}
93+
});
8994
}
9095

9196
public accessKey(keyName:'preview'|'newWorkPackage'|'edit'|'quickSearch'|'projectSearch'|'help'|'moreMenu'|'details'):() => void {

lib/api/v3/user_preferences/user_preference_representer.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ class UserPreferenceRepresenter < ::API::Decorators::Single
5656
property :time_zone,
5757
render_nil: true
5858

59+
property :disable_keyboard_shortcuts
60+
5961
property :warn_on_leaving_unsaved
6062
property :comments_in_reverse_order,
6163
as: :commentSortDescending

modules/backlogs/app/forms/my/backlogs_form.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@
3030

3131
class My::BacklogsForm < ApplicationForm
3232
form do |f|
33-
f.text_field name: :"backlogs[task_color]",
33+
f.text_field name: :task_color,
3434
label: I18n.t("backlogs.task_color"),
3535
value: @color,
3636
input_width: :xsmall
3737

38-
f.check_box name: :"backlogs[versions_default_fold_state]",
38+
f.check_box name: :versions_default_fold_state,
3939
value: DEFAULT_FOLD_STATE,
4040
checked: default_fold_state_checked?,
4141
label: I18n.t("backlogs.label_versions_default_fold_state"),

modules/backlogs/app/views/shared/_view_my_settings.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ See COPYRIGHT and LICENSE files for more details.
3333
end
3434
%>
3535
<%=
36-
settings_primer_form_with(model: user.pref, scope: :pref, url: { action: "update_settings" }) do |form|
36+
settings_primer_form_with(model: user.pref, scope: :backlogs, url: { action: "update_settings" }, data: { turbo: false }) do |form|
3737
render(My::BacklogsForm.new(form, color:, versions_default_fold_state:))
3838
end
3939
%>

modules/backlogs/spec/features/backlogs_in_backlog_view_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,11 @@
130130
.expect_story_not_in_sprint(sprint_story1, sprint)
131131

132132
# The backlogs can be folded by default
133-
visit my_settings_path
133+
visit my_interface_path
134134

135135
check "Show versions folded"
136136

137-
click_button "Save"
137+
click_button "Update backlogs module"
138138

139139
backlogs_page.visit!
140140

modules/backlogs/spec/features/tasks_on_taskboard_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,11 +238,11 @@
238238
text: "Burndown Chart")
239239

240240
# Tasks can get a color per assigned user
241-
visit my_settings_path
241+
visit my_interface_path
242242

243243
fill_in "Task color", with: "#FBC4B3"
244244

245-
click_button "Save"
245+
click_button "Update backlogs module"
246246

247247
taskboard_page.visit!
248248

spec/contracts/user_preferences/update_contract_spec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
},
5252
time_zone: "America/Sao_Paulo",
5353
warn_on_leaving_unsaved: true,
54+
disable_keyboard_shortcuts: true,
5455
workdays: [1, 2, 4, 6]
5556
}
5657
end

spec/controllers/my_controller_spec.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,17 +230,17 @@
230230
end
231231
end
232232

233-
describe "settings:auto_hide_popups" do
233+
describe "interface:auto_hide_popups" do
234234
context "with render_views" do
235235
before do
236236
as_logged_in_user user do
237-
get :settings
237+
get :interface
238238
end
239239
end
240240

241241
render_views
242242
it "renders auto hide popups checkbox" do
243-
expect(response.body).to have_css("#my_account_form #pref_auto_hide_popups")
243+
expect(response.body).to have_css("form #pref_auto_hide_popups")
244244
end
245245
end
246246

0 commit comments

Comments
 (0)