summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src/js')
-rw-r--r--addons/web/static/src/js/core/dialog.js31
-rw-r--r--addons/web/static/src/js/fields/abstract_field.js2
-rw-r--r--addons/web/static/src/js/fields/basic_fields.js22
-rw-r--r--addons/web/static/src/js/fields/relational_fields.js2
-rw-r--r--addons/web/static/src/js/views/basic/basic_renderer.js1
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_column_progressbar.js3
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_column_quick_create.js2
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_record.js31
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_record_quick_create.js2
-rw-r--r--addons/web/static/src/js/views/list/list_editable_renderer.js6
-rw-r--r--addons/web/static/src/js/views/list/list_renderer.js4
-rw-r--r--addons/web/static/src/js/views/pivot/pivot_renderer.js5
-rw-r--r--addons/web/static/src/js/widgets/change_password.js2
-rw-r--r--addons/web/static/src/js/widgets/debug_manager.js2
-rw-r--r--addons/web/static/src/js/widgets/domain_selector_dialog.js2
15 files changed, 92 insertions, 25 deletions
diff --git a/addons/web/static/src/js/core/dialog.js b/addons/web/static/src/js/core/dialog.js
index 7d4796886b7..6ebd94ef13e 100644
--- a/addons/web/static/src/js/core/dialog.js
+++ b/addons/web/static/src/js/core/dialog.js
@@ -17,12 +17,13 @@ var _t = core._t;
* always exists during the lifecycle of the dialog.
**/
var Dialog = Widget.extend({
+ tagName: 'main',
xmlDependencies: ['/web/static/src/xml/dialog.xml'],
custom_events: _.extend({}, Widget.prototype.custom_events, {
focus_control_button: '_onFocusControlButton',
}),
events: _.extend({} , Widget.prototype.events, {
- 'keydown .modal-footer button':'_onFooterButtonKeyDown',
+ 'keydown footer.modal-footer button':'_onFooterButtonKeyDown',
}),
/**
* @param {Widget} parent
@@ -92,7 +93,7 @@ var Dialog = Widget.extend({
self.$modal.find('.modal-dialog').addClass('modal-sm');
break;
}
- self.$footer = self.$modal.find(".modal-footer");
+ self.$footer = self.$modal.find("footer");
self.set_buttons(self.buttons);
self.$modal.on('hidden.bs.modal', _.bind(self.destroy, self));
});
@@ -168,7 +169,17 @@ var Dialog = Widget.extend({
var self = this;
this.appendTo($('<div/>')).then(function () {
- self.$modal.find(".modal-body").replaceWith(self.$el);
+ var $main = self.$modal.find("main:first");
+ if ($main.length === 0) {
+ $main = self.$modal.find(".modal-body:first");
+ }
+ if (self.$el.tagName !== 'MAIN') {
+ self.$el.removeClass(".modal-body");
+ self.$el = $('<main class="modal-body"/>').append(self.$el);
+ }
+ $main.replaceWith(self.$el);
+ self.$modal.attr('open', true);
+ self.$modal.removeAttr("aria-hidden");
self.$modal.modal('show');
self._opened.resolve();
});
@@ -201,11 +212,13 @@ var Dialog = Widget.extend({
$('.tooltip').remove(); //remove open tooltip if any to prevent them staying when modal has disappeared
if (this.$modal) {
this.$modal.modal('hide');
+ this.$modal.removeAttr('open');
+ this.$modal.attr('aria-hidden', true);
this.$modal.remove();
}
if (!isFocusSet) {
- var modals = $('body > .modal').filter(':visible');
+ var modals = $('body > [role="dialog"]').filter(':visible');
if (modals.length) {
modals.last().focus();
// Keep class modal-open (deleted by bootstrap hide fnct) on body to allow scrolling inside the modal
@@ -287,9 +300,9 @@ Dialog.alert = function (owner, message, options) {
return new Dialog(owner, _.extend({
size: 'medium',
buttons: buttons,
- $content: $('<div>', {
+ $content: $('<main role="alert"/>').append($('<p>', {
text: message,
- }),
+ })),
title: _t("Alert"),
}, options)).open({shouldFocusButtons:true});
};
@@ -312,9 +325,9 @@ Dialog.confirm = function (owner, message, options) {
return new Dialog(owner, _.extend({
size: 'medium',
buttons: buttons,
- $content: $('<div>', {
+ $content: $('<main role="alert">').append($('<p>', {
text: message,
- }),
+ })),
title: _t("Confirmation"),
}, options)).open({shouldFocusButtons:true});
};
@@ -345,7 +358,7 @@ Dialog.safeConfirm = function (owner, message, options) {
text: message,
});
}
- $content = $('<div/>').append($content, $securityCheck);
+ $content = $('<main role="alert"/>').append($content, $securityCheck);
var buttons = [
{
diff --git a/addons/web/static/src/js/fields/abstract_field.js b/addons/web/static/src/js/fields/abstract_field.js
index 9bb61475b83..eab0093ed50 100644
--- a/addons/web/static/src/js/fields/abstract_field.js
+++ b/addons/web/static/src/js/fields/abstract_field.js
@@ -310,6 +310,7 @@ var AbstractField = Widget.extend({
*/
removeInvalidClass: function () {
this.$el.removeClass('o_field_invalid');
+ this.$el.removeAttr('aria-invalid');
},
/**
@@ -317,6 +318,7 @@ var AbstractField = Widget.extend({
*/
setInvalidClass: function () {
this.$el.addClass('o_field_invalid');
+ this.$el.attr('aria-invalid', 'true');
},
/**
diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js
index 43b4e349a66..0cf9271de9d 100644
--- a/addons/web/static/src/js/fields/basic_fields.js
+++ b/addons/web/static/src/js/fields/basic_fields.js
@@ -397,9 +397,10 @@ var LinkButton = AbstractField.extend({
if (this.value) {
var className = this.attrs.icon || 'fa-globe';
- this.$el.html("<span />");
+ this.$el.html("<span role='img'/>");
this.$el.addClass("fa "+ className);
this.$el.attr('title', this.value);
+ this.$el.attr('aria-label', this.value);
}
},
@@ -1460,6 +1461,7 @@ var PriorityWidget = AbstractField.extend({
_renderStar: function (tag, isFull, index, tip) {
return $(tag)
.attr('title', tip)
+ .attr('aria-label', tip)
.attr('data-index', index)
.addClass('o_priority_star fa')
.toggleClass('fa-star', isFull)
@@ -1529,7 +1531,8 @@ var AttachmentImage = AbstractField.extend({
if (this.value) {
this.$el.empty().append($('<img>/', {
src: "/web/image/" + this.value.data.id + "?unique=1",
- title: this.value.data.display_name
+ title: this.value.data.display_name,
+ alt: _("Image")
}));
}
}
@@ -1598,7 +1601,9 @@ var StateSelectionWidget = AbstractField.extend({
this.$('.o_status')
.removeClass('o_status_red o_status_green')
.addClass(currentState.state_class)
- .prop('special_click', true);
+ .prop('special_click', true)
+ .parent().attr('title', currentState.state_name)
+ .attr('aria-label', currentState.state_name);
// Render "FormSelection.Items" and move it into "FormSelection"
var $items = $(qweb.render('FormSelection.items', {
@@ -1662,8 +1667,9 @@ var FavoriteWidget = AbstractField.extend({
* @private
*/
_render: function () {
- var template = this.attrs.nolabel ? '<a href="#"><i class="fa %s" title="%s"></i></a>' : '<a href="#"><i class="fa %s"></i> %s</a>';
- this.$el.empty().append(_.str.sprintf(template, this.value ? 'fa-star' : 'fa-star-o', this.value ? _t('Remove from Favorites') : _t('Add to Favorites')));
+ var tip = this.value ? _t('Remove from Favorites') : _t('Add to Favorites');
+ var template = this.attrs.nolabel ? '<a href="#"><i class="fa %s" title="%s" aria-label="%s" role="img"></i></a>' : '<a href="#"><i class="fa %s" role="img" aria-label="%s"> %s</i></a>';
+ this.$el.empty().append(_.str.sprintf(template, this.value ? 'fa-star' : 'fa-star-o', tip, tip));
},
//--------------------------------------------------------------------------
@@ -2043,7 +2049,10 @@ var FieldProgressBar = AbstractField.extend({
widthComplete = 100;
}
- this.$('.o_progress').toggleClass('o_progress_overflow', value > max_value);
+ this.$('.o_progress').toggleClass('o_progress_overflow', value > max_value)
+ .attr('aria-valuemin', '0')
+ .attr('aria-valuemax', max_value)
+ .attr('aria-valuenow', value);
this.$('.o_progressbar_complete').css('width', widthComplete + '%');
if (!this.write_mode) {
@@ -2100,6 +2109,7 @@ var FieldToggleBoolean = AbstractField.extend({
.toggleClass('text-muted', !this.value);
var title = this.value ? this.attrs.options.active : this.attrs.options.inactive;
this.$el.attr('title', title);
+ this.$el.attr('aria-pressed', this.value);
},
//--------------------------------------------------------------------------
diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js
index c86411b6cd9..83ce59f1eb6 100644
--- a/addons/web/static/src/js/fields/relational_fields.js
+++ b/addons/web/static/src/js/fields/relational_fields.js
@@ -1867,7 +1867,7 @@ var FormFieldMany2ManyTags = FieldMany2ManyTags.extend({
var $target = $(ev.currentTarget);
var color = $target.data('color');
var id = $target.data('id');
- var tag = this.$("span.badge[data-id='" + id + "']");
+ var tag = this.$("button.badge[data-id='" + id + "']");
var currentColor = tag.data('color');
var changes = {};
diff --git a/addons/web/static/src/js/views/basic/basic_renderer.js b/addons/web/static/src/js/views/basic/basic_renderer.js
index 9834c9c18de..13a00dda046 100644
--- a/addons/web/static/src/js/views/basic/basic_renderer.js
+++ b/addons/web/static/src/js/views/basic/basic_renderer.js
@@ -50,6 +50,7 @@ var BasicRenderer = AbstractRenderer.extend({
invalidFields.push(widget.name);
}
widget.$el.toggleClass('o_field_invalid', !canBeSaved);
+ widget.$el.attr('aria-invalid', !canBeSaved);
});
return invalidFields;
},
diff --git a/addons/web/static/src/js/views/kanban/kanban_column_progressbar.js b/addons/web/static/src/js/views/kanban/kanban_column_progressbar.js
index fad822efdb9..7af6b677dcf 100644
--- a/addons/web/static/src/js/views/kanban/kanban_column_progressbar.js
+++ b/addons/web/static/src/js/views/kanban/kanban_column_progressbar.js
@@ -166,6 +166,9 @@ var KanbanColumnProgressBar = Widget.extend({
if (count > 0) {
$bar.addClass('o_bar_has_records');
$bar.css('width', (count * 100 / self.groupCount) + '%');
+ $bar.attr('aria-valuemin', 0);
+ $bar.attr('aria-valuemax', self.groupCount);
+ $bar.attr('aria-valuenow', count);
} else {
$bar.css('width', '');
}
diff --git a/addons/web/static/src/js/views/kanban/kanban_column_quick_create.js b/addons/web/static/src/js/views/kanban/kanban_column_quick_create.js
index 170b950a49d..f554d333bc9 100644
--- a/addons/web/static/src/js/views/kanban/kanban_column_quick_create.js
+++ b/addons/web/static/src/js/views/kanban/kanban_column_quick_create.js
@@ -216,7 +216,7 @@ var ColumnQuickCreate = Widget.extend({
}
// ignore clicks in modals
- if ($(event.target).closest('.modal').length) {
+ if ($(event.target).closest('[role="dialog"]').length) {
return;
}
diff --git a/addons/web/static/src/js/views/kanban/kanban_record.js b/addons/web/static/src/js/views/kanban/kanban_record.js
index 30bafaab117..66171d2693c 100644
--- a/addons/web/static/src/js/views/kanban/kanban_record.js
+++ b/addons/web/static/src/js/views/kanban/kanban_record.js
@@ -17,7 +17,21 @@ var widgetRegistry = require('web.widget_registry');
var _t = core._t;
var QWeb = core.qweb;
-var NB_KANBAN_RECORD_COLORS = 12;
+var KANBAN_RECORD_COLORS = [
+ _t('No color'),
+ _t('Red'),
+ _t('Orange'),
+ _t('Yellow'),
+ _t('Light blue'),
+ _t('Dark purple'),
+ _t('Salmon pink'),
+ _t('Medium blue'),
+ _t('Dark blue'),
+ _t('Fushia'),
+ _t('Green'),
+ _t('Purple'),
+];
+var NB_KANBAN_RECORD_COLORS = KANBAN_RECORD_COLORS.length;
var KanbanRecord = Widget.extend({
events: {
@@ -127,6 +141,17 @@ var KanbanRecord = Widget.extend({
return 0;
},
/**
+ * Computes a color name from value
+ *
+ * @private
+ * @param {number | string} variable
+ * @returns {integer} the color name
+ */
+ _getColorname: function (variable) {
+ var colorID = this._getColorID(variable);
+ return KANBAN_RECORD_COLORS[colorID];
+ },
+ /**
* @private
* @param {string} model the name of the model
* @param {string} field the name of the field
@@ -284,6 +309,7 @@ var KanbanRecord = Widget.extend({
_render: function () {
this._replaceElement(this.qweb.render('kanban-box', this.qweb_context));
this.$el.addClass('o_kanban_record').attr("tabindex",0);
+ this.$el.attr('role', 'article');
this.$el.data('record', this);
if (this.$el.hasClass('oe_kanban_global_click') ||
this.$el.hasClass('oe_kanban_global_click_edit')) {
@@ -338,6 +364,7 @@ var KanbanRecord = Widget.extend({
kanban_image: this._getImageURL.bind(this),
kanban_color: this._getColorClassname.bind(this),
kanban_getcolor: this._getColorID.bind(this),
+ kanban_getcolorname: this._getColorname.bind(this),
kanban_compute_domain: this._computeDomain.bind(this),
read_only_mode: this.read_only_mode,
record: this.record,
@@ -354,8 +381,10 @@ var KanbanRecord = Widget.extend({
_setupColor: function () {
var color_field = this.$el.attr('color');
if (color_field && color_field in this.fields) {
+ var colorHelp = _.str.sprintf(_t("Card color: %s"), this._getColorname(this.recordData[color_field]));
var colorClass = this._getColorClassname(this.recordData[color_field]);
this.$el.addClass(colorClass);
+ this.$el.prepend('<span title="' + colorHelp + '" aria-label="' + colorHelp +'" role="img" class="oe_kanban_color_help"/>');
}
},
/**
diff --git a/addons/web/static/src/js/views/kanban/kanban_record_quick_create.js b/addons/web/static/src/js/views/kanban/kanban_record_quick_create.js
index bf7e60b68c3..749938e8b9e 100644
--- a/addons/web/static/src/js/views/kanban/kanban_record_quick_create.js
+++ b/addons/web/static/src/js/views/kanban/kanban_record_quick_create.js
@@ -232,7 +232,7 @@ var RecordQuickCreate = Widget.extend({
}
// ignore clicks in modals
- if ($(ev.target).closest('.modal').length) {
+ if ($(ev.target).closest('[role="dialog"]').length) {
return;
}
diff --git a/addons/web/static/src/js/views/list/list_editable_renderer.js b/addons/web/static/src/js/views/list/list_editable_renderer.js
index 07aa8eacb4e..9ec82878e70 100644
--- a/addons/web/static/src/js/views/list/list_editable_renderer.js
+++ b/addons/web/static/src/js/views/list/list_editable_renderer.js
@@ -527,7 +527,7 @@ ListRenderer.include({
_renderRows: function () {
var $rows = this._super();
if (this.addCreateLine) {
- var $a = $('<a href="#">').text(_t("Add a line"));
+ var $a = $('<a href="#" role="button">').text(_t("Add a line"));
var $td = $('<td>')
.attr('colspan', this._getNumberOfCols())
.addClass('o_field_x2many_list_row_add')
@@ -923,9 +923,9 @@ ListRenderer.include({
// ignore clicks in modals, except if the list is in a modal, and the
// click is performed in that modal
- var $clickModal = $(event.target).closest('.modal');
+ var $clickModal = $(event.target).closest('[role="dialog"]');
if ($clickModal.length) {
- var $listModal = this.$el.closest('.modal');
+ var $listModal = this.$el.closest('[role="dialog"]');
if ($clickModal.prop('id') !== $listModal.prop('id')) {
return;
}
diff --git a/addons/web/static/src/js/views/list/list_renderer.js b/addons/web/static/src/js/views/list/list_renderer.js
index bf206eae32c..e84d6687cc9 100644
--- a/addons/web/static/src/js/views/list/list_renderer.js
+++ b/addons/web/static/src/js/views/list/list_renderer.js
@@ -549,6 +549,10 @@ var ListRenderer = BasicRenderer.extend({
.toggleClass('o-sort-up', isNodeSorted ? order[0].asc : false)
.addClass(field.sortable && 'o_column_sortable');
+ if (isNodeSorted) {
+ $th.attr('aria-sort', order[0].asc ? 'ascending': 'descending');
+ }
+
if (field.type === 'float' || field.type === 'integer' || field.type === 'monetary') {
$th.css({textAlign: 'right'});
}
diff --git a/addons/web/static/src/js/views/pivot/pivot_renderer.js b/addons/web/static/src/js/views/pivot/pivot_renderer.js
index b5648d358a9..b3953076161 100644
--- a/addons/web/static/src/js/views/pivot/pivot_renderer.js
+++ b/addons/web/static/src/js/views/pivot/pivot_renderer.js
@@ -96,6 +96,11 @@ var PivotRenderer = AbstractRenderer.extend({
$cell.data('id', cell.id).data('measure', cell.measure);
if (cell.id === this.state.sortedColumn.id && cell.measure === this.state.sortedColumn.measure) {
$cell.addClass('o_pivot_measure_row_sorted_' + this.state.sortedColumn.order);
+ if (this.state.sortedColumn.order == 'asc') {
+ $cell.attr('aria-sorted', 'ascending');
+ } else {
+ $cell.attr('aria-sorted', 'descending');
+ }
}
}
$row.append($cell);
diff --git a/addons/web/static/src/js/widgets/change_password.js b/addons/web/static/src/js/widgets/change_password.js
index c05fa30408b..b2e4e013335 100644
--- a/addons/web/static/src/js/widgets/change_password.js
+++ b/addons/web/static/src/js/widgets/change_password.js
@@ -28,7 +28,7 @@ var ChangePassword = AbstractAction.extend({
var $button = self.$('.oe_form_button');
$button.appendTo(this.getParent().$footer);
$button.eq(1).click(function () {
- self.$el.parents('.modal').modal('hide');
+ self.$el.parents('[role="dialog"]').modal('hide');
});
$button.eq(0).click(function () {
self._rpc({
diff --git a/addons/web/static/src/js/widgets/debug_manager.js b/addons/web/static/src/js/widgets/debug_manager.js
index 4b86b76c36d..4bdc27448a0 100644
--- a/addons/web/static/src/js/widgets/debug_manager.js
+++ b/addons/web/static/src/js/widgets/debug_manager.js
@@ -848,7 +848,7 @@ if (config.debug) {
if (action) {
var controller = parent.getCurrentControllerInDialog();
self.debugManager = new DebugManager(self);
- var $header = self.$modal.find('.modal-header:first');
+ var $header = self.$modal.find('header.modal-header:first');
return self.debugManager.prependTo($header).then(function () {
self.debugManager.update('action', action, controller.widget);
});
diff --git a/addons/web/static/src/js/widgets/domain_selector_dialog.js b/addons/web/static/src/js/widgets/domain_selector_dialog.js
index 46b54089c5f..d0c0030b100 100644
--- a/addons/web/static/src/js/widgets/domain_selector_dialog.js
+++ b/addons/web/static/src/js/widgets/domain_selector_dialog.js
@@ -43,7 +43,7 @@ return Dialog.extend({
var self = this;
this.opened().then(function () {
// this restores default modal height (bootstrap) and allows field selector to overflow
- self.$el.css('overflow', 'visible').closest('.modal-dialog').css('height', 'auto');
+ self.$el.css('overflow', 'visible').closest('[role="dialog"]').css('height', 'auto');
});
return $.when(
this._super.apply(this, arguments),