Skip to content

Commit 1b3f5c5

Browse files
Merge branch 'uiImprovement-3_3_0-1151' into 'stable-3_3_0'
Improved visual appearance of the selection area OJS 3.3.0. See merge request softwares-pkp/plugins_ojs/selectionOfReviewingInterests!19
2 parents 4ba39aa + b0a7a59 commit 1b3f5c5

10 files changed

Lines changed: 185 additions & 35 deletions

File tree

.gitlab-ci.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,8 @@ include:
66
ref: stable-3_3_0
77
file:
88
- 'templates/groups/pkp_plugin.yml'
9-
- 'templates/groups/ojs/cypress_tests.yml'
9+
- 'templates/groups/ojs/cypress_tests.yml'
10+
11+
ojs_integration_tests:
12+
rules:
13+
- when: never

classes/HookCallbacks.inc.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,12 +213,15 @@ public function applyReviewerInterestFilter(string $hookName, array $params)
213213
private function addInterestsScripts($templateMgr, $contextId)
214214
{
215215
$optionsArray = $this->getInterestOptions($contextId);
216+
$placeholderText = __('plugins.generic.selectionOfReviewingInterests.profilePage.placeholder');
216217

217218
$inlineScript = '$.pkp.plugins.generic = $.pkp.plugins.generic || {};';
218219
$inlineScript .= '$.pkp.plugins.generic.selectionOfReviewingInterests = ';
219220
$inlineScript .= '$.pkp.plugins.generic.selectionOfReviewingInterests || {};';
220221
$inlineScript .= '$.pkp.plugins.generic.selectionOfReviewingInterests.interestsOptions = ';
221222
$inlineScript .= json_encode($optionsArray) . ';';
223+
$inlineScript .= '$.pkp.plugins.generic.selectionOfReviewingInterests.placeholder = ';
224+
$inlineScript .= json_encode($placeholderText) . ';';
222225

223226
$templateMgr->addJavaScript(
224227
'interestsOptions',
@@ -231,6 +234,7 @@ private function addInterestsScripts($templateMgr, $contextId)
231234

232235
$request = Application::get()->getRequest();
233236
$patchScriptUrl = $request->getBaseUrl() . '/' . $this->plugin->getPluginPath() . '/js/interestsTagitPatch.js';
237+
$patchStyleUrl = $request->getBaseUrl() . '/' . $this->plugin->getPluginPath() . '/styles/interestsTagitPatch.css';
234238

235239
$templateMgr->addJavaScript(
236240
'interestsTagitPatch',
@@ -240,6 +244,15 @@ private function addInterestsScripts($templateMgr, $contextId)
240244
'priority' => STYLE_SEQUENCE_LATE,
241245
]
242246
);
247+
248+
$templateMgr->addStyleSheet(
249+
'interestsTagitPatch',
250+
$patchStyleUrl,
251+
[
252+
'contexts' => 'backend',
253+
'priority' => STYLE_SEQUENCE_LATE,
254+
]
255+
);
243256
}
244257

245258
private function addRedirectMessageFilter($templateMgr)

cypress/tests/Test2_reviewersValidation.cy.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
describe('Reviewers must not use OJS without reviewing interests', function () {
22
it('Reviewer without interests login', function () {
3+
let notificationText = 'You must select at least one reviewing interest in the "Roles" tab before you can access the system.';
4+
let itemText = 'Estudos teóricos e de campo em escalas que variam do local ao regional/global, abrangendo períodos de curta e longa duração, incluindo tempo geológico';
5+
36
cy.login('agallego', null, 'publicknowledge');
4-
cy.get('.pkpNotification').contains('You must select at least one reviewing interest in the "Roles" tab before you can access the system.').should('be.visible');
7+
cy.get('.pkpNotification').contains(notificationText).should('be.visible');
58

69
cy.get('.app__returnHeaderLink').click();
7-
cy.get('.pkpNotification').contains('You must select at least one reviewing interest in the "Roles" tab before you can access the system.').should('be.visible');
10+
cy.get('.pkpNotification').contains(notificationText).should('be.visible');
811

912
cy.get('#ui-id-5').click();
1013
cy.get('.interests').click();
11-
cy.get('li[class=ui-menu-item').contains("Estudos teóricos e de campo em escalas que variam do local ao regional/global, abrangendo períodos de curta e longa duração, incluindo tempo geológico").click();
14+
cy.get('li[class=ui-menu-item').contains(itemText).click();
15+
cy.get('.tagit-label').contains(itemText).click();
1216

1317
cy.get('#rolesForm > .formButtons > button[id^=submitFormButton]')
1418
.click();

cypress/tests/Test4_accessProfileFromOtherContexts.cy.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ describe('Accessing profile from other contexts works normally', function () {
4242
cy.get('.app__pageHeading').contains('Submissions').should('be.visible');
4343

4444
cy.get('.pkpNotification').should('not.exist');
45+
cy.logout();
4546
});
4647

4748
it('Reviewer adds a free-text interest from the second journal', function () {

js/interestsTagitPatch.js

Lines changed: 93 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,118 @@
11
(function($) {
22
var originalTagit = $.fn.tagit;
3+
4+
var updateAutocompleteWidth = function(el) {
5+
var $field = $(el);
6+
var $widget = $field
7+
.find('.tagit-new input[type="text"]')
8+
.autocomplete('widget');
9+
var fieldOffset = $field.offset();
10+
11+
$widget
12+
.addClass('sori-interests-autocomplete')
13+
.css({
14+
'width': $field.outerWidth() + 'px',
15+
'max-width': 'none',
16+
'box-sizing': 'border-box',
17+
'left': fieldOffset.left + 'px'
18+
});
19+
};
20+
21+
var updateInterestsUi = function(el, placeholder) {
22+
var $field = $(el);
23+
var assignedTags = originalTagit.call(el, 'assignedTags') || [];
24+
var isEmpty = assignedTags.length === 0;
25+
var $input = $field.find('.tagit-new input[type="text"]');
26+
27+
$field.addClass('sori-interests-selectLike');
28+
$field.toggleClass('sori-interests-empty', isEmpty);
29+
$input.attr('placeholder', isEmpty ? placeholder : '');
30+
};
31+
332
$.fn.tagit = function(method) {
4-
var result = originalTagit.apply(this, arguments);
533
if (typeof method !== 'string'
634
&& $(this).hasClass('interests')
735
&& !$(this).data('soriReinit')) {
836
var ns = $.pkp && $.pkp.plugins
937
&& $.pkp.plugins.generic
1038
&& $.pkp.plugins.generic.selectionOfReviewingInterests;
1139
if (ns && ns.interestsOptions) {
12-
var existingTags = originalTagit.call(this, 'assignedTags') || [];
13-
var existingTagsLower = $.map(existingTags, function(tag) {
14-
return tag.toLowerCase();
15-
});
16-
17-
originalTagit.call(this, 'destroy');
1840
var el = this;
19-
originalTagit.call(this, {
41+
var placeholder = ns.placeholder || 'Selecione uma ou mais opcoes';
42+
var baseOptions = method || {};
43+
var preExistingTagsLower = $.map($(this).children('li:not(.tagit-new)'), function(tag) {
44+
return $.trim($(tag).text()).toLowerCase();
45+
});
46+
var originalBeforeTagAdded = baseOptions.beforeTagAdded;
47+
var originalAfterTagAdded = baseOptions.afterTagAdded;
48+
var originalAfterTagRemoved = baseOptions.afterTagRemoved;
49+
var configuredOptions = $.extend(true, {}, baseOptions, {
2050
fieldName: 'interests[]',
21-
availableTags: ns.interestsOptions,
2251
allowSpaces: true,
23-
autocomplete: {delay: 0, minLength: 0},
52+
showAutocompleteOnFocus: true,
53+
autocomplete: $.extend({}, baseOptions.autocomplete || {}, {
54+
delay: 0,
55+
minLength: 0,
56+
source: function(search, showChoices) {
57+
var filter = search.term.toLowerCase();
58+
var choices = $.grep(ns.interestsOptions, function(option) {
59+
return option.toLowerCase().indexOf(filter) === 0;
60+
});
61+
if (!this.options.allowDuplicates) {
62+
choices = this._subtractArray(choices, this.assignedTags());
63+
}
64+
showChoices(choices);
65+
}
66+
}),
2467
beforeTagAdded: function(event, ui) {
25-
var availableTags = originalTagit.call(el, 'option', 'availableTags');
2668
var tagLower = ui.tagLabel.toLowerCase();
27-
var inAllowedList = $.map(availableTags, function(tag) {
69+
var normalizedAllowedList = $.map(ns.interestsOptions, function(tag) {
2870
return tag.toLowerCase();
29-
}).indexOf(tagLower) !== -1;
30-
var isPreExisting = existingTagsLower.indexOf(tagLower) !== -1;
31-
return inAllowedList || isPreExisting;
32-
}
33-
});
71+
});
72+
var inAllowedList = normalizedAllowedList.indexOf(tagLower) !== -1;
73+
var isPreExisting = preExistingTagsLower.indexOf(tagLower) !== -1;
3474

35-
$.each(existingTags, function(i, tag) {
36-
var currentTags = originalTagit.call(el, 'assignedTags') || [];
37-
var currentTagsLower = $.map(currentTags, function(t) {
38-
return t.toLowerCase();
39-
});
40-
if (currentTagsLower.indexOf(tag.toLowerCase()) === -1) {
41-
originalTagit.call(el, 'createTag', tag);
75+
if (!inAllowedList && !isPreExisting) {
76+
return false;
77+
}
78+
79+
if (typeof originalBeforeTagAdded === 'function') {
80+
return originalBeforeTagAdded.call(this, event, ui);
81+
}
82+
},
83+
afterTagAdded: function(event, ui) {
84+
$(el).find('.tagit-new input[type="text"]').autocomplete();
85+
updateInterestsUi(el, placeholder);
86+
if (typeof originalAfterTagAdded === 'function') {
87+
originalAfterTagAdded.call(this, event, ui);
88+
}
89+
},
90+
afterTagRemoved: function(event, ui) {
91+
$(el).find('.tagit-new input[type="text"]').autocomplete();
92+
updateInterestsUi(el, placeholder);
93+
if (typeof originalAfterTagRemoved === 'function') {
94+
originalAfterTagRemoved.call(this, event, ui);
95+
}
4296
}
4397
});
44-
$(this).data('soriReinit', true);
45-
$(document)
46-
.off('focus.sori click.sori', '.tagit-new input')
47-
.on('focus.sori click.sori', '.tagit-new input', function() {
98+
var result = originalTagit.call(this, configuredOptions);
99+
var $input = $(this).find('.tagit-new input[type="text"]');
100+
101+
updateInterestsUi(el, placeholder);
102+
$input
103+
.off('autocompleteopen.sori click.sori')
104+
.on('autocompleteopen.sori', function() {
105+
updateAutocompleteWidth(el);
106+
})
107+
.on('click.sori', function() {
48108
$(this).autocomplete('search', '');
49109
});
110+
$(this).data('soriReinit', true);
111+
112+
return result;
50113
}
51114
}
52-
return result;
115+
116+
return originalTagit.apply(this, arguments);
53117
};
54118
})(jQuery);

locale/en_US/locale.po

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ msgstr "Adds a selection field in the Reviewing Interests Area, with options def
77
msgid "plugins.generic.selectionOfReviewingInterests.profilePage.emptyInterestsMessage"
88
msgstr "You must select at least one reviewing interest in the \"Roles\" tab before you can access the system."
99

10+
msgid "plugins.generic.selectionOfReviewingInterests.profilePage.placeholder"
11+
msgstr "Select one or more options"
12+
1013
msgid "plugins.generic.selectionOfReviewingInterests.configuration.description"
1114
msgstr "Define the reviewing interest options available to reviewers. These options replace the free-text interests field in the user profile. Reviewers will only be able to select from the options listed below. Reviewers who have not selected at least one interest will be redirected to their profile page upon login."
1215

locale/es_ES/locale.po

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ msgstr "Añade un campo de selección en el área de Intereses de Revisión, con
77
msgid "plugins.generic.selectionOfReviewingInterests.profilePage.emptyInterestsMessage"
88
msgstr "Debe seleccionar al menos un interés de revisión en la pestaña \"Roles\" antes de poder acceder al sistema."
99

10+
msgid "plugins.generic.selectionOfReviewingInterests.profilePage.placeholder"
11+
msgstr "Seleccione una o más opciones"
12+
1013
msgid "plugins.generic.selectionOfReviewingInterests.configuration.description"
1114
msgstr "Defina las opciones de interés de revisión disponibles para los revisores. Estas opciones reemplazan el campo de texto libre de intereses en el perfil del usuario. Los revisores solo podrán seleccionar entre las opciones listadas a continuación. Los revisores que no hayan seleccionado al menos un interés serán redirigidos a su página de perfil al iniciar sesión."
1215

locale/pt_BR/locale.po

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ msgstr "Adiciona um campo de seleção na Áreas de Interesse de Avaliação, co
77
msgid "plugins.generic.selectionOfReviewingInterests.profilePage.emptyInterestsMessage"
88
msgstr "É necessário selecionar pelo menos uma área de interesse para avaliação na aba \"Papéis\" antes de acessar o sistema."
99

10+
msgid "plugins.generic.selectionOfReviewingInterests.profilePage.placeholder"
11+
msgstr "Selecione uma ou mais opções"
12+
1013
msgid "plugins.generic.selectionOfReviewingInterests.configuration.description"
1114
msgstr "Defina as opções de interesse de avaliação disponíveis para os avaliadores. Essas opções substituem o campo de texto livre de interesses no perfil do usuário. Os avaliadores só poderão selecionar entre as opções listadas abaixo. Avaliadores que não tiverem selecionado pelo menos um interesse serão redirecionados para a página de perfil ao fazer login."
1215

styles/interestsTagitPatch.css

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
.ui-helper-hidden-accessible {
2+
clip: rect(1px, 1px, 1px, 1px);
3+
position: absolute !important;
4+
left: -2000px;
5+
}
6+
7+
.ui-helper-hidden-accessible:focus {
8+
clip: auto !important;
9+
left: 0;
10+
}
11+
12+
.tagit.interests.sori-interests-selectLike {
13+
position: relative;
14+
padding: 0 2rem 0 0.25em;
15+
}
16+
17+
.tagit.interests.sori-interests-selectLike::after {
18+
content: "";
19+
position: absolute;
20+
top: 50%;
21+
right: 0.75rem;
22+
width: 0;
23+
height: 0;
24+
border-left: 4px solid transparent;
25+
border-right: 4px solid transparent;
26+
border-top: 5px solid #000;
27+
transform: translateY(-50%);
28+
pointer-events: none;
29+
}
30+
31+
.tagit.interests.sori-interests-selectLike.sori-interests-empty .tagit-new {
32+
display: block;
33+
width: 100%;
34+
}
35+
36+
.tagit.interests.sori-interests-selectLike.sori-interests-empty .tagit-new input[type="text"] {
37+
width: 100%;
38+
height: 2rem;
39+
line-height: 2rem;
40+
padding-top: 0;
41+
padding-bottom: 0;
42+
padding-right: 1.25rem;
43+
}
44+
45+
.sori-interests-autocomplete {
46+
max-height: 12rem;
47+
overflow-y: auto;
48+
overflow-x: hidden;
49+
}
50+
51+
.sori-interests-autocomplete > li:hover {
52+
background: #2a6fd6;
53+
color: #fff;
54+
box-shadow: inset 0 0 0 1px #2a6fd6;
55+
}

version.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
<version>
1212
<application>selectionOfReviewingInterests</application>
1313
<type>plugins.generic</type>
14-
<release>1.0.1.0</release>
15-
<date>2026-04-22</date>
14+
<release>1.0.2.0</release>
15+
<date>2026-04-27</date>
1616
<lazy-load>1</lazy-load>
1717
<class>SelectionOfReviewingInterestsPlugin</class>
1818
</version>

0 commit comments

Comments
 (0)