summaryrefslogtreecommitdiff
path: root/addons/web/static
diff options
context:
space:
mode:
authorkujiu <kujiu@users.noreply.github.com>2018-06-22 21:22:21 +0200
committerGitHub <noreply@github.com>2018-06-22 21:22:21 +0200
commit9de1bc0eef6f5bfaa2a8d745431caa361ae91548 (patch)
tree342ddbf7fb2b7303606f864ff9f68cbf1bd8814e /addons/web/static
parent864357203fc6222cc989b99a5e9f428cd53f413f (diff)
[IMP] Improve compatibility with screen readers (accessibility) (#24574)
Today, Odoo is really tricky to use without seeing the screen, it must be improved to be usable. This PR forbid to use labels without a "for" attribute, add some title, rule and aria attributes in HTML. With that, Odoo will be fully usable with a screen reader. * [IMP] Labels must have a for attribute. Improve accessibility. * [IMP] Better error message when trying to read a missing cached value * [FIX] Add some aria-label and title attributes for screen readers. * [FIX] Template name is not included in the error message in case of SyntaxError in QWeb * [FIX] Improve the Tour failed at step error message to be more explicit. * [IMP] Add aria-labels * [FIX] Add missing aria-label on failing test * [IMP] aria-hidden means hidden. Fix all bad aria-hidden and hide aria-hidden for all. * [IMP] Color names on kanban views and many2many tags * [IMP] Add some checks on views for accessibility. * [IMP] Add `alt` attribute on `img` tags. * [IMP] Add aria-label and title on non-described icons * [IMP] Add button role to widgets with btn class * [IMP] Translate aria and formatted attributes. * [IMP] Remove wrong aria-labelledby * [IMP] Add menu role on dropdowns * [IMP] Buttons must be focusable * [IMP] Add aria attributes on progress bars * [IMP] Improve accessibility of basic widgets * [IMP] Change main layout to more semantic tags * [IMP] Add menuitem role when missing * [IMP] Remove wrong role='presentation' * [IMP] Improve accessibility of tab panels * [IMP] Add aria-invalid on invalid fields * [IMP] Add aria-sort on ordered columns * [IMP] Add role on alerts * [IMP] Use dialog role, header, main and footer tags for modals * [IMP] Add labels on o_status * [IMP] Improve accessibility of kanban view with feeds and articles * [IMP] Add alerts in case of new messages * [IMP] Add widget, navigation or img role to aria-labelled items
Diffstat (limited to 'addons/web/static')
-rw-r--r--addons/web/static/lib/fontawesome/css/font-awesome-suffix.css2145
-rw-r--r--addons/web/static/lib/pdfjs/web/ui_utils.js6
-rw-r--r--addons/web/static/lib/qweb/qweb-test-attributes.xml4
-rw-r--r--addons/web/static/lib/qweb/qweb2.js4
-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
-rw-r--r--addons/web/static/src/scss/ui.scss4
-rw-r--r--addons/web/static/src/scss/utils.scss8
-rw-r--r--addons/web/static/src/xml/base.xml414
-rw-r--r--addons/web/static/src/xml/dialog.xml12
-rw-r--r--addons/web/static/src/xml/kanban.xml43
-rw-r--r--addons/web/static/src/xml/web_calendar.xml18
-rw-r--r--addons/web/static/tests/chrome/action_manager_tests.js18
-rw-r--r--addons/web/static/tests/fields/basic_fields_tests.js10
-rw-r--r--addons/web/static/tests/fields/relational_fields_tests.js508
-rw-r--r--addons/web/static/tests/fields/upgrade_fields_tests.js2
-rw-r--r--addons/web/static/tests/services/notification_service_tests.js6
-rw-r--r--addons/web/static/tests/views/calendar_tests.js104
-rw-r--r--addons/web/static/tests/views/form_tests.js184
-rw-r--r--addons/web/static/tests/views/kanban_tests.js62
-rw-r--r--addons/web/static/tests/views/list_tests.js16
-rw-r--r--addons/web/static/tests/views/view_dialogs_tests.js14
-rw-r--r--addons/web/static/tests/widgets/data_export_tests.js28
36 files changed, 2992 insertions, 735 deletions
diff --git a/addons/web/static/lib/fontawesome/css/font-awesome-suffix.css b/addons/web/static/lib/fontawesome/css/font-awesome-suffix.css
new file mode 100644
index 00000000000..f5651cf8e02
--- /dev/null
+++ b/addons/web/static/lib/fontawesome/css/font-awesome-suffix.css
@@ -0,0 +1,2145 @@
+/*!
+ * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
+ * Suffix version by Odoo S.A.
+ * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ */
+/* FONT PATH
+ * -------------------------- */
+/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen
+ readers do not read off random characters that represent icons */
+.fa-suffix-glass:after {
+ content: "\f000";
+}
+.fa-suffix-music:after {
+ content: "\f001";
+}
+.fa-suffix-search:after {
+ content: "\f002";
+}
+.fa-suffix-envelope-o:after {
+ content: "\f003";
+}
+.fa-suffix-heart:after {
+ content: "\f004";
+}
+.fa-suffix-star:after {
+ content: "\f005";
+}
+.fa-suffix-star-o:after {
+ content: "\f006";
+}
+.fa-suffix-user:after {
+ content: "\f007";
+}
+.fa-suffix-film:after {
+ content: "\f008";
+}
+.fa-suffix-th-large:after {
+ content: "\f009";
+}
+.fa-suffix-th:after {
+ content: "\f00a";
+}
+.fa-suffix-th-list:after {
+ content: "\f00b";
+}
+.fa-suffix-check:after {
+ content: "\f00c";
+}
+.fa-suffix-remove:after,
+.fa-suffix-close:after,
+.fa-suffix-times:after {
+ content: "\f00d";
+}
+.fa-suffix-search-plus:after {
+ content: "\f00e";
+}
+.fa-suffix-search-minus:after {
+ content: "\f010";
+}
+.fa-suffix-power-off:after {
+ content: "\f011";
+}
+.fa-suffix-signal:after {
+ content: "\f012";
+}
+.fa-suffix-gear:after,
+.fa-suffix-cog:after {
+ content: "\f013";
+}
+.fa-suffix-trash-o:after {
+ content: "\f014";
+}
+.fa-suffix-home:after {
+ content: "\f015";
+}
+.fa-suffix-file-o:after {
+ content: "\f016";
+}
+.fa-suffix-clock-o:after {
+ content: "\f017";
+}
+.fa-suffix-road:after {
+ content: "\f018";
+}
+.fa-suffix-download:after {
+ content: "\f019";
+}
+.fa-suffix-arrow-circle-o-down:after {
+ content: "\f01a";
+}
+.fa-suffix-arrow-circle-o-up:after {
+ content: "\f01b";
+}
+.fa-suffix-inbox:after {
+ content: "\f01c";
+}
+.fa-suffix-play-circle-o:after {
+ content: "\f01d";
+}
+.fa-suffix-rotate-right:after,
+.fa-suffix-repeat:after {
+ content: "\f01e";
+}
+.fa-suffix-refresh:after {
+ content: "\f021";
+}
+.fa-suffix-list-alt:after {
+ content: "\f022";
+}
+.fa-suffix-lock:after {
+ content: "\f023";
+}
+.fa-suffix-flag:after {
+ content: "\f024";
+}
+.fa-suffix-headphones:after {
+ content: "\f025";
+}
+.fa-suffix-volume-off:after {
+ content: "\f026";
+}
+.fa-suffix-volume-down:after {
+ content: "\f027";
+}
+.fa-suffix-volume-up:after {
+ content: "\f028";
+}
+.fa-suffix-qrcode:after {
+ content: "\f029";
+}
+.fa-suffix-barcode:after {
+ content: "\f02a";
+}
+.fa-suffix-tag:after {
+ content: "\f02b";
+}
+.fa-suffix-tags:after {
+ content: "\f02c";
+}
+.fa-suffix-book:after {
+ content: "\f02d";
+}
+.fa-suffix-bookmark:after {
+ content: "\f02e";
+}
+.fa-suffix-print:after {
+ content: "\f02f";
+}
+.fa-suffix-camera:after {
+ content: "\f030";
+}
+.fa-suffix-font:after {
+ content: "\f031";
+}
+.fa-suffix-bold:after {
+ content: "\f032";
+}
+.fa-suffix-italic:after {
+ content: "\f033";
+}
+.fa-suffix-text-height:after {
+ content: "\f034";
+}
+.fa-suffix-text-width:after {
+ content: "\f035";
+}
+.fa-suffix-align-left:after {
+ content: "\f036";
+}
+.fa-suffix-align-center:after {
+ content: "\f037";
+}
+.fa-suffix-align-right:after {
+ content: "\f038";
+}
+.fa-suffix-align-justify:after {
+ content: "\f039";
+}
+.fa-suffix-list:after {
+ content: "\f03a";
+}
+.fa-suffix-dedent:after,
+.fa-suffix-outdent:after {
+ content: "\f03b";
+}
+.fa-suffix-indent:after {
+ content: "\f03c";
+}
+.fa-suffix-video-camera:after {
+ content: "\f03d";
+}
+.fa-suffix-photo:after,
+.fa-suffix-image:after,
+.fa-suffix-picture-o:after {
+ content: "\f03e";
+}
+.fa-suffix-pencil:after {
+ content: "\f040";
+}
+.fa-suffix-map-marker:after {
+ content: "\f041";
+}
+.fa-suffix-adjust:after {
+ content: "\f042";
+}
+.fa-suffix-tint:after {
+ content: "\f043";
+}
+.fa-suffix-edit:after,
+.fa-suffix-pencil-square-o:after {
+ content: "\f044";
+}
+.fa-suffix-share-square-o:after {
+ content: "\f045";
+}
+.fa-suffix-check-square-o:after {
+ content: "\f046";
+}
+.fa-suffix-arrows:after {
+ content: "\f047";
+}
+.fa-suffix-step-backward:after {
+ content: "\f048";
+}
+.fa-suffix-fast-backward:after {
+ content: "\f049";
+}
+.fa-suffix-backward:after {
+ content: "\f04a";
+}
+.fa-suffix-play:after {
+ content: "\f04b";
+}
+.fa-suffix-pause:after {
+ content: "\f04c";
+}
+.fa-suffix-stop:after {
+ content: "\f04d";
+}
+.fa-suffix-forward:after {
+ content: "\f04e";
+}
+.fa-suffix-fast-forward:after {
+ content: "\f050";
+}
+.fa-suffix-step-forward:after {
+ content: "\f051";
+}
+.fa-suffix-eject:after {
+ content: "\f052";
+}
+.fa-suffix-chevron-left:after {
+ content: "\f053";
+}
+.fa-suffix-chevron-right:after {
+ content: "\f054";
+}
+.fa-suffix-plus-circle:after {
+ content: "\f055";
+}
+.fa-suffix-minus-circle:after {
+ content: "\f056";
+}
+.fa-suffix-times-circle:after {
+ content: "\f057";
+}
+.fa-suffix-check-circle:after {
+ content: "\f058";
+}
+.fa-suffix-question-circle:after {
+ content: "\f059";
+}
+.fa-suffix-info-circle:after {
+ content: "\f05a";
+}
+.fa-suffix-crosshairs:after {
+ content: "\f05b";
+}
+.fa-suffix-times-circle-o:after {
+ content: "\f05c";
+}
+.fa-suffix-check-circle-o:after {
+ content: "\f05d";
+}
+.fa-suffix-ban:after {
+ content: "\f05e";
+}
+.fa-suffix-arrow-left:after {
+ content: "\f060";
+}
+.fa-suffix-arrow-right:after {
+ content: "\f061";
+}
+.fa-suffix-arrow-up:after {
+ content: "\f062";
+}
+.fa-suffix-arrow-down:after {
+ content: "\f063";
+}
+.fa-suffix-mail-forward:after,
+.fa-suffix-share:after {
+ content: "\f064";
+}
+.fa-suffix-expand:after {
+ content: "\f065";
+}
+.fa-suffix-compress:after {
+ content: "\f066";
+}
+.fa-suffix-plus:after {
+ content: "\f067";
+}
+.fa-suffix-minus:after {
+ content: "\f068";
+}
+.fa-suffix-asterisk:after {
+ content: "\f069";
+}
+.fa-suffix-exclamation-circle:after {
+ content: "\f06a";
+}
+.fa-suffix-gift:after {
+ content: "\f06b";
+}
+.fa-suffix-leaf:after {
+ content: "\f06c";
+}
+.fa-suffix-fire:after {
+ content: "\f06d";
+}
+.fa-suffix-eye:after {
+ content: "\f06e";
+}
+.fa-suffix-eye-slash:after {
+ content: "\f070";
+}
+.fa-suffix-warning:after,
+.fa-suffix-exclamation-triangle:after {
+ content: "\f071";
+}
+.fa-suffix-plane:after {
+ content: "\f072";
+}
+.fa-suffix-calendar:after {
+ content: "\f073";
+}
+.fa-suffix-random:after {
+ content: "\f074";
+}
+.fa-suffix-comment:after {
+ content: "\f075";
+}
+.fa-suffix-magnet:after {
+ content: "\f076";
+}
+.fa-suffix-chevron-up:after {
+ content: "\f077";
+}
+.fa-suffix-chevron-down:after {
+ content: "\f078";
+}
+.fa-suffix-retweet:after {
+ content: "\f079";
+}
+.fa-suffix-shopping-cart:after {
+ content: "\f07a";
+}
+.fa-suffix-folder:after {
+ content: "\f07b";
+}
+.fa-suffix-folder-open:after {
+ content: "\f07c";
+}
+.fa-suffix-arrows-v:after {
+ content: "\f07d";
+}
+.fa-suffix-arrows-h:after {
+ content: "\f07e";
+}
+.fa-suffix-bar-chart-o:after,
+.fa-suffix-bar-chart:after {
+ content: "\f080";
+}
+.fa-suffix-twitter-square:after {
+ content: "\f081";
+}
+.fa-suffix-facebook-square:after {
+ content: "\f082";
+}
+.fa-suffix-camera-retro:after {
+ content: "\f083";
+}
+.fa-suffix-key:after {
+ content: "\f084";
+}
+.fa-suffix-gears:after,
+.fa-suffix-cogs:after {
+ content: "\f085";
+}
+.fa-suffix-comments:after {
+ content: "\f086";
+}
+.fa-suffix-thumbs-o-up:after {
+ content: "\f087";
+}
+.fa-suffix-thumbs-o-down:after {
+ content: "\f088";
+}
+.fa-suffix-star-half:after {
+ content: "\f089";
+}
+.fa-suffix-heart-o:after {
+ content: "\f08a";
+}
+.fa-suffix-sign-out:after {
+ content: "\f08b";
+}
+.fa-suffix-linkedin-square:after {
+ content: "\f08c";
+}
+.fa-suffix-thumb-tack:after {
+ content: "\f08d";
+}
+.fa-suffix-external-link:after {
+ content: "\f08e";
+}
+.fa-suffix-sign-in:after {
+ content: "\f090";
+}
+.fa-suffix-trophy:after {
+ content: "\f091";
+}
+.fa-suffix-github-square:after {
+ content: "\f092";
+}
+.fa-suffix-upload:after {
+ content: "\f093";
+}
+.fa-suffix-lemon-o:after {
+ content: "\f094";
+}
+.fa-suffix-phone:after {
+ content: "\f095";
+}
+.fa-suffix-square-o:after {
+ content: "\f096";
+}
+.fa-suffix-bookmark-o:after {
+ content: "\f097";
+}
+.fa-suffix-phone-square:after {
+ content: "\f098";
+}
+.fa-suffix-twitter:after {
+ content: "\f099";
+}
+.fa-suffix-facebook-f:after,
+.fa-suffix-facebook:after {
+ content: "\f09a";
+}
+.fa-suffix-github:after {
+ content: "\f09b";
+}
+.fa-suffix-unlock:after {
+ content: "\f09c";
+}
+.fa-suffix-credit-card:after {
+ content: "\f09d";
+}
+.fa-suffix-feed:after,
+.fa-suffix-rss:after {
+ content: "\f09e";
+}
+.fa-suffix-hdd-o:after {
+ content: "\f0a0";
+}
+.fa-suffix-bullhorn:after {
+ content: "\f0a1";
+}
+.fa-suffix-bell:after {
+ content: "\f0f3";
+}
+.fa-suffix-certificate:after {
+ content: "\f0a3";
+}
+.fa-suffix-hand-o-right:after {
+ content: "\f0a4";
+}
+.fa-suffix-hand-o-left:after {
+ content: "\f0a5";
+}
+.fa-suffix-hand-o-up:after {
+ content: "\f0a6";
+}
+.fa-suffix-hand-o-down:after {
+ content: "\f0a7";
+}
+.fa-suffix-arrow-circle-left:after {
+ content: "\f0a8";
+}
+.fa-suffix-arrow-circle-right:after {
+ content: "\f0a9";
+}
+.fa-suffix-arrow-circle-up:after {
+ content: "\f0aa";
+}
+.fa-suffix-arrow-circle-down:after {
+ content: "\f0ab";
+}
+.fa-suffix-globe:after {
+ content: "\f0ac";
+}
+.fa-suffix-wrench:after {
+ content: "\f0ad";
+}
+.fa-suffix-tasks:after {
+ content: "\f0ae";
+}
+.fa-suffix-filter:after {
+ content: "\f0b0";
+}
+.fa-suffix-briefcase:after {
+ content: "\f0b1";
+}
+.fa-suffix-arrows-alt:after {
+ content: "\f0b2";
+}
+.fa-suffix-group:after,
+.fa-suffix-users:after {
+ content: "\f0c0";
+}
+.fa-suffix-chain:after,
+.fa-suffix-link:after {
+ content: "\f0c1";
+}
+.fa-suffix-cloud:after {
+ content: "\f0c2";
+}
+.fa-suffix-flask:after {
+ content: "\f0c3";
+}
+.fa-suffix-cut:after,
+.fa-suffix-scissors:after {
+ content: "\f0c4";
+}
+.fa-suffix-copy:after,
+.fa-suffix-files-o:after {
+ content: "\f0c5";
+}
+.fa-suffix-paperclip:after {
+ content: "\f0c6";
+}
+.fa-suffix-save:after,
+.fa-suffix-floppy-o:after {
+ content: "\f0c7";
+}
+.fa-suffix-square:after {
+ content: "\f0c8";
+}
+.fa-suffix-navicon:after,
+.fa-suffix-reorder:after,
+.fa-suffix-bars:after {
+ content: "\f0c9";
+}
+.fa-suffix-list-ul:after {
+ content: "\f0ca";
+}
+.fa-suffix-list-ol:after {
+ content: "\f0cb";
+}
+.fa-suffix-strikethrough:after {
+ content: "\f0cc";
+}
+.fa-suffix-underline:after {
+ content: "\f0cd";
+}
+.fa-suffix-table:after {
+ content: "\f0ce";
+}
+.fa-suffix-magic:after {
+ content: "\f0d0";
+}
+.fa-suffix-truck:after {
+ content: "\f0d1";
+}
+.fa-suffix-pinterest:after {
+ content: "\f0d2";
+}
+.fa-suffix-pinterest-square:after {
+ content: "\f0d3";
+}
+.fa-suffix-google-plus-square:after {
+ content: "\f0d4";
+}
+.fa-suffix-google-plus:after {
+ content: "\f0d5";
+}
+.fa-suffix-money:after {
+ content: "\f0d6";
+}
+.fa-suffix-caret-down:after {
+ content: "\f0d7";
+}
+.fa-suffix-caret-up:after {
+ content: "\f0d8";
+}
+.fa-suffix-caret-left:after {
+ content: "\f0d9";
+}
+.fa-suffix-caret-right:after {
+ content: "\f0da";
+}
+.fa-suffix-columns:after {
+ content: "\f0db";
+}
+.fa-suffix-unsorted:after,
+.fa-suffix-sort:after {
+ content: "\f0dc";
+}
+.fa-suffix-sort-down:after,
+.fa-suffix-sort-desc:after {
+ content: "\f0dd";
+}
+.fa-suffix-sort-up:after,
+.fa-suffix-sort-asc:after {
+ content: "\f0de";
+}
+.fa-suffix-envelope:after {
+ content: "\f0e0";
+}
+.fa-suffix-linkedin:after {
+ content: "\f0e1";
+}
+.fa-suffix-rotate-left:after,
+.fa-suffix-undo:after {
+ content: "\f0e2";
+}
+.fa-suffix-legal:after,
+.fa-suffix-gavel:after {
+ content: "\f0e3";
+}
+.fa-suffix-dashboard:after,
+.fa-suffix-tachometer:after {
+ content: "\f0e4";
+}
+.fa-suffix-comment-o:after {
+ content: "\f0e5";
+}
+.fa-suffix-comments-o:after {
+ content: "\f0e6";
+}
+.fa-suffix-flash:after,
+.fa-suffix-bolt:after {
+ content: "\f0e7";
+}
+.fa-suffix-sitemap:after {
+ content: "\f0e8";
+}
+.fa-suffix-umbrella:after {
+ content: "\f0e9";
+}
+.fa-suffix-paste:after,
+.fa-suffix-clipboard:after {
+ content: "\f0ea";
+}
+.fa-suffix-lightbulb-o:after {
+ content: "\f0eb";
+}
+.fa-suffix-exchange:after {
+ content: "\f0ec";
+}
+.fa-suffix-cloud-download:after {
+ content: "\f0ed";
+}
+.fa-suffix-cloud-upload:after {
+ content: "\f0ee";
+}
+.fa-suffix-user-md:after {
+ content: "\f0f0";
+}
+.fa-suffix-stethoscope:after {
+ content: "\f0f1";
+}
+.fa-suffix-suitcase:after {
+ content: "\f0f2";
+}
+.fa-suffix-bell-o:after {
+ content: "\f0a2";
+}
+.fa-suffix-coffee:after {
+ content: "\f0f4";
+}
+.fa-suffix-cutlery:after {
+ content: "\f0f5";
+}
+.fa-suffix-file-text-o:after {
+ content: "\f0f6";
+}
+.fa-suffix-building-o:after {
+ content: "\f0f7";
+}
+.fa-suffix-hospital-o:after {
+ content: "\f0f8";
+}
+.fa-suffix-ambulance:after {
+ content: "\f0f9";
+}
+.fa-suffix-medkit:after {
+ content: "\f0fa";
+}
+.fa-suffix-fighter-jet:after {
+ content: "\f0fb";
+}
+.fa-suffix-beer:after {
+ content: "\f0fc";
+}
+.fa-suffix-h-square:after {
+ content: "\f0fd";
+}
+.fa-suffix-plus-square:after {
+ content: "\f0fe";
+}
+.fa-suffix-angle-double-left:after {
+ content: "\f100";
+}
+.fa-suffix-angle-double-right:after {
+ content: "\f101";
+}
+.fa-suffix-angle-double-up:after {
+ content: "\f102";
+}
+.fa-suffix-angle-double-down:after {
+ content: "\f103";
+}
+.fa-suffix-angle-left:after {
+ content: "\f104";
+}
+.fa-suffix-angle-right:after {
+ content: "\f105";
+}
+.fa-suffix-angle-up:after {
+ content: "\f106";
+}
+.fa-suffix-angle-down:after {
+ content: "\f107";
+}
+.fa-suffix-desktop:after {
+ content: "\f108";
+}
+.fa-suffix-laptop:after {
+ content: "\f109";
+}
+.fa-suffix-tablet:after {
+ content: "\f10a";
+}
+.fa-suffix-mobile-phone:after,
+.fa-suffix-mobile:after {
+ content: "\f10b";
+}
+.fa-suffix-circle-o:after {
+ content: "\f10c";
+}
+.fa-suffix-quote-left:after {
+ content: "\f10d";
+}
+.fa-suffix-quote-right:after {
+ content: "\f10e";
+}
+.fa-suffix-spinner:after {
+ content: "\f110";
+}
+.fa-suffix-circle:after {
+ content: "\f111";
+}
+.fa-suffix-mail-reply:after,
+.fa-suffix-reply:after {
+ content: "\f112";
+}
+.fa-suffix-github-alt:after {
+ content: "\f113";
+}
+.fa-suffix-folder-o:after {
+ content: "\f114";
+}
+.fa-suffix-folder-open-o:after {
+ content: "\f115";
+}
+.fa-suffix-smile-o:after {
+ content: "\f118";
+}
+.fa-suffix-frown-o:after {
+ content: "\f119";
+}
+.fa-suffix-meh-o:after {
+ content: "\f11a";
+}
+.fa-suffix-gamepad:after {
+ content: "\f11b";
+}
+.fa-suffix-keyboard-o:after {
+ content: "\f11c";
+}
+.fa-suffix-flag-o:after {
+ content: "\f11d";
+}
+.fa-suffix-flag-checkered:after {
+ content: "\f11e";
+}
+.fa-suffix-terminal:after {
+ content: "\f120";
+}
+.fa-suffix-code:after {
+ content: "\f121";
+}
+.fa-suffix-mail-reply-all:after,
+.fa-suffix-reply-all:after {
+ content: "\f122";
+}
+.fa-suffix-star-half-empty:after,
+.fa-suffix-star-half-full:after,
+.fa-suffix-star-half-o:after {
+ content: "\f123";
+}
+.fa-suffix-location-arrow:after {
+ content: "\f124";
+}
+.fa-suffix-crop:after {
+ content: "\f125";
+}
+.fa-suffix-code-fork:after {
+ content: "\f126";
+}
+.fa-suffix-unlink:after,
+.fa-suffix-chain-broken:after {
+ content: "\f127";
+}
+.fa-suffix-question:after {
+ content: "\f128";
+}
+.fa-suffix-info:after {
+ content: "\f129";
+}
+.fa-suffix-exclamation:after {
+ content: "\f12a";
+}
+.fa-suffix-superscript:after {
+ content: "\f12b";
+}
+.fa-suffix-subscript:after {
+ content: "\f12c";
+}
+.fa-suffix-eraser:after {
+ content: "\f12d";
+}
+.fa-suffix-puzzle-piece:after {
+ content: "\f12e";
+}
+.fa-suffix-microphone:after {
+ content: "\f130";
+}
+.fa-suffix-microphone-slash:after {
+ content: "\f131";
+}
+.fa-suffix-shield:after {
+ content: "\f132";
+}
+.fa-suffix-calendar-o:after {
+ content: "\f133";
+}
+.fa-suffix-fire-extinguisher:after {
+ content: "\f134";
+}
+.fa-suffix-rocket:after {
+ content: "\f135";
+}
+.fa-suffix-maxcdn:after {
+ content: "\f136";
+}
+.fa-suffix-chevron-circle-left:after {
+ content: "\f137";
+}
+.fa-suffix-chevron-circle-right:after {
+ content: "\f138";
+}
+.fa-suffix-chevron-circle-up:after {
+ content: "\f139";
+}
+.fa-suffix-chevron-circle-down:after {
+ content: "\f13a";
+}
+.fa-suffix-html5:after {
+ content: "\f13b";
+}
+.fa-suffix-css3:after {
+ content: "\f13c";
+}
+.fa-suffix-anchor:after {
+ content: "\f13d";
+}
+.fa-suffix-unlock-alt:after {
+ content: "\f13e";
+}
+.fa-suffix-bullseye:after {
+ content: "\f140";
+}
+.fa-suffix-ellipsis-h:after {
+ content: "\f141";
+}
+.fa-suffix-ellipsis-v:after {
+ content: "\f142";
+}
+.fa-suffix-rss-square:after {
+ content: "\f143";
+}
+.fa-suffix-play-circle:after {
+ content: "\f144";
+}
+.fa-suffix-ticket:after {
+ content: "\f145";
+}
+.fa-suffix-minus-square:after {
+ content: "\f146";
+}
+.fa-suffix-minus-square-o:after {
+ content: "\f147";
+}
+.fa-suffix-level-up:after {
+ content: "\f148";
+}
+.fa-suffix-level-down:after {
+ content: "\f149";
+}
+.fa-suffix-check-square:after {
+ content: "\f14a";
+}
+.fa-suffix-pencil-square:after {
+ content: "\f14b";
+}
+.fa-suffix-external-link-square:after {
+ content: "\f14c";
+}
+.fa-suffix-share-square:after {
+ content: "\f14d";
+}
+.fa-suffix-compass:after {
+ content: "\f14e";
+}
+.fa-suffix-toggle-down:after,
+.fa-suffix-caret-square-o-down:after {
+ content: "\f150";
+}
+.fa-suffix-toggle-up:after,
+.fa-suffix-caret-square-o-up:after {
+ content: "\f151";
+}
+.fa-suffix-toggle-right:after,
+.fa-suffix-caret-square-o-right:after {
+ content: "\f152";
+}
+.fa-suffix-euro:after,
+.fa-suffix-eur:after {
+ content: "\f153";
+}
+.fa-suffix-gbp:after {
+ content: "\f154";
+}
+.fa-suffix-dollar:after,
+.fa-suffix-usd:after {
+ content: "\f155";
+}
+.fa-suffix-rupee:after,
+.fa-suffix-inr:after {
+ content: "\f156";
+}
+.fa-suffix-cny:after,
+.fa-suffix-rmb:after,
+.fa-suffix-yen:after,
+.fa-suffix-jpy:after {
+ content: "\f157";
+}
+.fa-suffix-ruble:after,
+.fa-suffix-rouble:after,
+.fa-suffix-rub:after {
+ content: "\f158";
+}
+.fa-suffix-won:after,
+.fa-suffix-krw:after {
+ content: "\f159";
+}
+.fa-suffix-bitcoin:after,
+.fa-suffix-btc:after {
+ content: "\f15a";
+}
+.fa-suffix-file:after {
+ content: "\f15b";
+}
+.fa-suffix-file-text:after {
+ content: "\f15c";
+}
+.fa-suffix-sort-alpha-asc:after {
+ content: "\f15d";
+}
+.fa-suffix-sort-alpha-desc:after {
+ content: "\f15e";
+}
+.fa-suffix-sort-amount-asc:after {
+ content: "\f160";
+}
+.fa-suffix-sort-amount-desc:after {
+ content: "\f161";
+}
+.fa-suffix-sort-numeric-asc:after {
+ content: "\f162";
+}
+.fa-suffix-sort-numeric-desc:after {
+ content: "\f163";
+}
+.fa-suffix-thumbs-up:after {
+ content: "\f164";
+}
+.fa-suffix-thumbs-down:after {
+ content: "\f165";
+}
+.fa-suffix-youtube-square:after {
+ content: "\f166";
+}
+.fa-suffix-youtube:after {
+ content: "\f167";
+}
+.fa-suffix-xing:after {
+ content: "\f168";
+}
+.fa-suffix-xing-square:after {
+ content: "\f169";
+}
+.fa-suffix-youtube-play:after {
+ content: "\f16a";
+}
+.fa-suffix-dropbox:after {
+ content: "\f16b";
+}
+.fa-suffix-stack-overflow:after {
+ content: "\f16c";
+}
+.fa-suffix-instagram:after {
+ content: "\f16d";
+}
+.fa-suffix-flickr:after {
+ content: "\f16e";
+}
+.fa-suffix-adn:after {
+ content: "\f170";
+}
+.fa-suffix-bitbucket:after {
+ content: "\f171";
+}
+.fa-suffix-bitbucket-square:after {
+ content: "\f172";
+}
+.fa-suffix-tumblr:after {
+ content: "\f173";
+}
+.fa-suffix-tumblr-square:after {
+ content: "\f174";
+}
+.fa-suffix-long-arrow-down:after {
+ content: "\f175";
+}
+.fa-suffix-long-arrow-up:after {
+ content: "\f176";
+}
+.fa-suffix-long-arrow-left:after {
+ content: "\f177";
+}
+.fa-suffix-long-arrow-right:after {
+ content: "\f178";
+}
+.fa-suffix-apple:after {
+ content: "\f179";
+}
+.fa-suffix-windows:after {
+ content: "\f17a";
+}
+.fa-suffix-android:after {
+ content: "\f17b";
+}
+.fa-suffix-linux:after {
+ content: "\f17c";
+}
+.fa-suffix-dribbble:after {
+ content: "\f17d";
+}
+.fa-suffix-skype:after {
+ content: "\f17e";
+}
+.fa-suffix-foursquare:after {
+ content: "\f180";
+}
+.fa-suffix-trello:after {
+ content: "\f181";
+}
+.fa-suffix-female:after {
+ content: "\f182";
+}
+.fa-suffix-male:after {
+ content: "\f183";
+}
+.fa-suffix-gittip:after,
+.fa-suffix-gratipay:after {
+ content: "\f184";
+}
+.fa-suffix-sun-o:after {
+ content: "\f185";
+}
+.fa-suffix-moon-o:after {
+ content: "\f186";
+}
+.fa-suffix-archive:after {
+ content: "\f187";
+}
+.fa-suffix-bug:after {
+ content: "\f188";
+}
+.fa-suffix-vk:after {
+ content: "\f189";
+}
+.fa-suffix-weibo:after {
+ content: "\f18a";
+}
+.fa-suffix-renren:after {
+ content: "\f18b";
+}
+.fa-suffix-pagelines:after {
+ content: "\f18c";
+}
+.fa-suffix-stack-exchange:after {
+ content: "\f18d";
+}
+.fa-suffix-arrow-circle-o-right:after {
+ content: "\f18e";
+}
+.fa-suffix-arrow-circle-o-left:after {
+ content: "\f190";
+}
+.fa-suffix-toggle-left:after,
+.fa-suffix-caret-square-o-left:after {
+ content: "\f191";
+}
+.fa-suffix-dot-circle-o:after {
+ content: "\f192";
+}
+.fa-suffix-wheelchair:after {
+ content: "\f193";
+}
+.fa-suffix-vimeo-square:after {
+ content: "\f194";
+}
+.fa-suffix-turkish-lira:after,
+.fa-suffix-try:after {
+ content: "\f195";
+}
+.fa-suffix-plus-square-o:after {
+ content: "\f196";
+}
+.fa-suffix-space-shuttle:after {
+ content: "\f197";
+}
+.fa-suffix-slack:after {
+ content: "\f198";
+}
+.fa-suffix-envelope-square:after {
+ content: "\f199";
+}
+.fa-suffix-wordpress:after {
+ content: "\f19a";
+}
+.fa-suffix-openid:after {
+ content: "\f19b";
+}
+.fa-suffix-institution:after,
+.fa-suffix-bank:after,
+.fa-suffix-university:after {
+ content: "\f19c";
+}
+.fa-suffix-mortar-board:after,
+.fa-suffix-graduation-cap:after {
+ content: "\f19d";
+}
+.fa-suffix-yahoo:after {
+ content: "\f19e";
+}
+.fa-suffix-google:after {
+ content: "\f1a0";
+}
+.fa-suffix-reddit:after {
+ content: "\f1a1";
+}
+.fa-suffix-reddit-square:after {
+ content: "\f1a2";
+}
+.fa-suffix-stumbleupon-circle:after {
+ content: "\f1a3";
+}
+.fa-suffix-stumbleupon:after {
+ content: "\f1a4";
+}
+.fa-suffix-delicious:after {
+ content: "\f1a5";
+}
+.fa-suffix-digg:after {
+ content: "\f1a6";
+}
+.fa-suffix-pied-piper-pp:after {
+ content: "\f1a7";
+}
+.fa-suffix-pied-piper-alt:after {
+ content: "\f1a8";
+}
+.fa-suffix-drupal:after {
+ content: "\f1a9";
+}
+.fa-suffix-joomla:after {
+ content: "\f1aa";
+}
+.fa-suffix-language:after {
+ content: "\f1ab";
+}
+.fa-suffix-fax:after {
+ content: "\f1ac";
+}
+.fa-suffix-building:after {
+ content: "\f1ad";
+}
+.fa-suffix-child:after {
+ content: "\f1ae";
+}
+.fa-suffix-paw:after {
+ content: "\f1b0";
+}
+.fa-suffix-spoon:after {
+ content: "\f1b1";
+}
+.fa-suffix-cube:after {
+ content: "\f1b2";
+}
+.fa-suffix-cubes:after {
+ content: "\f1b3";
+}
+.fa-suffix-behance:after {
+ content: "\f1b4";
+}
+.fa-suffix-behance-square:after {
+ content: "\f1b5";
+}
+.fa-suffix-steam:after {
+ content: "\f1b6";
+}
+.fa-suffix-steam-square:after {
+ content: "\f1b7";
+}
+.fa-suffix-recycle:after {
+ content: "\f1b8";
+}
+.fa-suffix-automobile:after,
+.fa-suffix-car:after {
+ content: "\f1b9";
+}
+.fa-suffix-cab:after,
+.fa-suffix-taxi:after {
+ content: "\f1ba";
+}
+.fa-suffix-tree:after {
+ content: "\f1bb";
+}
+.fa-suffix-spotify:after {
+ content: "\f1bc";
+}
+.fa-suffix-deviantart:after {
+ content: "\f1bd";
+}
+.fa-suffix-soundcloud:after {
+ content: "\f1be";
+}
+.fa-suffix-database:after {
+ content: "\f1c0";
+}
+.fa-suffix-file-pdf-o:after {
+ content: "\f1c1";
+}
+.fa-suffix-file-word-o:after {
+ content: "\f1c2";
+}
+.fa-suffix-file-excel-o:after {
+ content: "\f1c3";
+}
+.fa-suffix-file-powerpoint-o:after {
+ content: "\f1c4";
+}
+.fa-suffix-file-photo-o:after,
+.fa-suffix-file-picture-o:after,
+.fa-suffix-file-image-o:after {
+ content: "\f1c5";
+}
+.fa-suffix-file-zip-o:after,
+.fa-suffix-file-archive-o:after {
+ content: "\f1c6";
+}
+.fa-suffix-file-sound-o:after,
+.fa-suffix-file-audio-o:after {
+ content: "\f1c7";
+}
+.fa-suffix-file-movie-o:after,
+.fa-suffix-file-video-o:after {
+ content: "\f1c8";
+}
+.fa-suffix-file-code-o:after {
+ content: "\f1c9";
+}
+.fa-suffix-vine:after {
+ content: "\f1ca";
+}
+.fa-suffix-codepen:after {
+ content: "\f1cb";
+}
+.fa-suffix-jsfiddle:after {
+ content: "\f1cc";
+}
+.fa-suffix-life-bouy:after,
+.fa-suffix-life-buoy:after,
+.fa-suffix-life-saver:after,
+.fa-suffix-support:after,
+.fa-suffix-life-ring:after {
+ content: "\f1cd";
+}
+.fa-suffix-circle-o-notch:after {
+ content: "\f1ce";
+}
+.fa-suffix-ra:after,
+.fa-suffix-resistance:after,
+.fa-suffix-rebel:after {
+ content: "\f1d0";
+}
+.fa-suffix-ge:after,
+.fa-suffix-empire:after {
+ content: "\f1d1";
+}
+.fa-suffix-git-square:after {
+ content: "\f1d2";
+}
+.fa-suffix-git:after {
+ content: "\f1d3";
+}
+.fa-suffix-y-combinator-square:after,
+.fa-suffix-yc-square:after,
+.fa-suffix-hacker-news:after {
+ content: "\f1d4";
+}
+.fa-suffix-tencent-weibo:after {
+ content: "\f1d5";
+}
+.fa-suffix-qq:after {
+ content: "\f1d6";
+}
+.fa-suffix-wechat:after,
+.fa-suffix-weixin:after {
+ content: "\f1d7";
+}
+.fa-suffix-send:after,
+.fa-suffix-paper-plane:after {
+ content: "\f1d8";
+}
+.fa-suffix-send-o:after,
+.fa-suffix-paper-plane-o:after {
+ content: "\f1d9";
+}
+.fa-suffix-history:after {
+ content: "\f1da";
+}
+.fa-suffix-circle-thin:after {
+ content: "\f1db";
+}
+.fa-suffix-header:after {
+ content: "\f1dc";
+}
+.fa-suffix-paragraph:after {
+ content: "\f1dd";
+}
+.fa-suffix-sliders:after {
+ content: "\f1de";
+}
+.fa-suffix-share-alt:after {
+ content: "\f1e0";
+}
+.fa-suffix-share-alt-square:after {
+ content: "\f1e1";
+}
+.fa-suffix-bomb:after {
+ content: "\f1e2";
+}
+.fa-suffix-soccer-ball-o:after,
+.fa-suffix-futbol-o:after {
+ content: "\f1e3";
+}
+.fa-suffix-tty:after {
+ content: "\f1e4";
+}
+.fa-suffix-binoculars:after {
+ content: "\f1e5";
+}
+.fa-suffix-plug:after {
+ content: "\f1e6";
+}
+.fa-suffix-slideshare:after {
+ content: "\f1e7";
+}
+.fa-suffix-twitch:after {
+ content: "\f1e8";
+}
+.fa-suffix-yelp:after {
+ content: "\f1e9";
+}
+.fa-suffix-newspaper-o:after {
+ content: "\f1ea";
+}
+.fa-suffix-wifi:after {
+ content: "\f1eb";
+}
+.fa-suffix-calculator:after {
+ content: "\f1ec";
+}
+.fa-suffix-paypal:after {
+ content: "\f1ed";
+}
+.fa-suffix-google-wallet:after {
+ content: "\f1ee";
+}
+.fa-suffix-cc-visa:after {
+ content: "\f1f0";
+}
+.fa-suffix-cc-mastercard:after {
+ content: "\f1f1";
+}
+.fa-suffix-cc-discover:after {
+ content: "\f1f2";
+}
+.fa-suffix-cc-amex:after {
+ content: "\f1f3";
+}
+.fa-suffix-cc-paypal:after {
+ content: "\f1f4";
+}
+.fa-suffix-cc-stripe:after {
+ content: "\f1f5";
+}
+.fa-suffix-bell-slash:after {
+ content: "\f1f6";
+}
+.fa-suffix-bell-slash-o:after {
+ content: "\f1f7";
+}
+.fa-suffix-trash:after {
+ content: "\f1f8";
+}
+.fa-suffix-copyright:after {
+ content: "\f1f9";
+}
+.fa-suffix-at:after {
+ content: "\f1fa";
+}
+.fa-suffix-eyedropper:after {
+ content: "\f1fb";
+}
+.fa-suffix-paint-brush:after {
+ content: "\f1fc";
+}
+.fa-suffix-birthday-cake:after {
+ content: "\f1fd";
+}
+.fa-suffix-area-chart:after {
+ content: "\f1fe";
+}
+.fa-suffix-pie-chart:after {
+ content: "\f200";
+}
+.fa-suffix-line-chart:after {
+ content: "\f201";
+}
+.fa-suffix-lastfm:after {
+ content: "\f202";
+}
+.fa-suffix-lastfm-square:after {
+ content: "\f203";
+}
+.fa-suffix-toggle-off:after {
+ content: "\f204";
+}
+.fa-suffix-toggle-on:after {
+ content: "\f205";
+}
+.fa-suffix-bicycle:after {
+ content: "\f206";
+}
+.fa-suffix-bus:after {
+ content: "\f207";
+}
+.fa-suffix-ioxhost:after {
+ content: "\f208";
+}
+.fa-suffix-angellist:after {
+ content: "\f209";
+}
+.fa-suffix-cc:after {
+ content: "\f20a";
+}
+.fa-suffix-shekel:after,
+.fa-suffix-sheqel:after,
+.fa-suffix-ils:after {
+ content: "\f20b";
+}
+.fa-suffix-meanpath:after {
+ content: "\f20c";
+}
+.fa-suffix-buysellads:after {
+ content: "\f20d";
+}
+.fa-suffix-connectdevelop:after {
+ content: "\f20e";
+}
+.fa-suffix-dashcube:after {
+ content: "\f210";
+}
+.fa-suffix-forumbee:after {
+ content: "\f211";
+}
+.fa-suffix-leanpub:after {
+ content: "\f212";
+}
+.fa-suffix-sellsy:after {
+ content: "\f213";
+}
+.fa-suffix-shirtsinbulk:after {
+ content: "\f214";
+}
+.fa-suffix-simplybuilt:after {
+ content: "\f215";
+}
+.fa-suffix-skyatlas:after {
+ content: "\f216";
+}
+.fa-suffix-cart-plus:after {
+ content: "\f217";
+}
+.fa-suffix-cart-arrow-down:after {
+ content: "\f218";
+}
+.fa-suffix-diamond:after {
+ content: "\f219";
+}
+.fa-suffix-ship:after {
+ content: "\f21a";
+}
+.fa-suffix-user-secret:after {
+ content: "\f21b";
+}
+.fa-suffix-motorcycle:after {
+ content: "\f21c";
+}
+.fa-suffix-street-view:after {
+ content: "\f21d";
+}
+.fa-suffix-heartbeat:after {
+ content: "\f21e";
+}
+.fa-suffix-venus:after {
+ content: "\f221";
+}
+.fa-suffix-mars:after {
+ content: "\f222";
+}
+.fa-suffix-mercury:after {
+ content: "\f223";
+}
+.fa-suffix-intersex:after,
+.fa-suffix-transgender:after {
+ content: "\f224";
+}
+.fa-suffix-transgender-alt:after {
+ content: "\f225";
+}
+.fa-suffix-venus-double:after {
+ content: "\f226";
+}
+.fa-suffix-mars-double:after {
+ content: "\f227";
+}
+.fa-suffix-venus-mars:after {
+ content: "\f228";
+}
+.fa-suffix-mars-stroke:after {
+ content: "\f229";
+}
+.fa-suffix-mars-stroke-v:after {
+ content: "\f22a";
+}
+.fa-suffix-mars-stroke-h:after {
+ content: "\f22b";
+}
+.fa-suffix-neuter:after {
+ content: "\f22c";
+}
+.fa-suffix-genderless:after {
+ content: "\f22d";
+}
+.fa-suffix-facebook-official:after {
+ content: "\f230";
+}
+.fa-suffix-pinterest-p:after {
+ content: "\f231";
+}
+.fa-suffix-whatsapp:after {
+ content: "\f232";
+}
+.fa-suffix-server:after {
+ content: "\f233";
+}
+.fa-suffix-user-plus:after {
+ content: "\f234";
+}
+.fa-suffix-user-times:after {
+ content: "\f235";
+}
+.fa-suffix-hotel:after,
+.fa-suffix-bed:after {
+ content: "\f236";
+}
+.fa-suffix-viacoin:after {
+ content: "\f237";
+}
+.fa-suffix-train:after {
+ content: "\f238";
+}
+.fa-suffix-subway:after {
+ content: "\f239";
+}
+.fa-suffix-medium:after {
+ content: "\f23a";
+}
+.fa-suffix-yc:after,
+.fa-suffix-y-combinator:after {
+ content: "\f23b";
+}
+.fa-suffix-optin-monster:after {
+ content: "\f23c";
+}
+.fa-suffix-opencart:after {
+ content: "\f23d";
+}
+.fa-suffix-expeditedssl:after {
+ content: "\f23e";
+}
+.fa-suffix-battery-4:after,
+.fa-suffix-battery:after,
+.fa-suffix-battery-full:after {
+ content: "\f240";
+}
+.fa-suffix-battery-3:after,
+.fa-suffix-battery-three-quarters:after {
+ content: "\f241";
+}
+.fa-suffix-battery-2:after,
+.fa-suffix-battery-half:after {
+ content: "\f242";
+}
+.fa-suffix-battery-1:after,
+.fa-suffix-battery-quarter:after {
+ content: "\f243";
+}
+.fa-suffix-battery-0:after,
+.fa-suffix-battery-empty:after {
+ content: "\f244";
+}
+.fa-suffix-mouse-pointer:after {
+ content: "\f245";
+}
+.fa-suffix-i-cursor:after {
+ content: "\f246";
+}
+.fa-suffix-object-group:after {
+ content: "\f247";
+}
+.fa-suffix-object-ungroup:after {
+ content: "\f248";
+}
+.fa-suffix-sticky-note:after {
+ content: "\f249";
+}
+.fa-suffix-sticky-note-o:after {
+ content: "\f24a";
+}
+.fa-suffix-cc-jcb:after {
+ content: "\f24b";
+}
+.fa-suffix-cc-diners-club:after {
+ content: "\f24c";
+}
+.fa-suffix-clone:after {
+ content: "\f24d";
+}
+.fa-suffix-balance-scale:after {
+ content: "\f24e";
+}
+.fa-suffix-hourglass-o:after {
+ content: "\f250";
+}
+.fa-suffix-hourglass-1:after,
+.fa-suffix-hourglass-start:after {
+ content: "\f251";
+}
+.fa-suffix-hourglass-2:after,
+.fa-suffix-hourglass-half:after {
+ content: "\f252";
+}
+.fa-suffix-hourglass-3:after,
+.fa-suffix-hourglass-end:after {
+ content: "\f253";
+}
+.fa-suffix-hourglass:after {
+ content: "\f254";
+}
+.fa-suffix-hand-grab-o:after,
+.fa-suffix-hand-rock-o:after {
+ content: "\f255";
+}
+.fa-suffix-hand-stop-o:after,
+.fa-suffix-hand-paper-o:after {
+ content: "\f256";
+}
+.fa-suffix-hand-scissors-o:after {
+ content: "\f257";
+}
+.fa-suffix-hand-lizard-o:after {
+ content: "\f258";
+}
+.fa-suffix-hand-spock-o:after {
+ content: "\f259";
+}
+.fa-suffix-hand-pointer-o:after {
+ content: "\f25a";
+}
+.fa-suffix-hand-peace-o:after {
+ content: "\f25b";
+}
+.fa-suffix-trademark:after {
+ content: "\f25c";
+}
+.fa-suffix-registered:after {
+ content: "\f25d";
+}
+.fa-suffix-creative-commons:after {
+ content: "\f25e";
+}
+.fa-suffix-gg:after {
+ content: "\f260";
+}
+.fa-suffix-gg-circle:after {
+ content: "\f261";
+}
+.fa-suffix-tripadvisor:after {
+ content: "\f262";
+}
+.fa-suffix-odnoklassniki:after {
+ content: "\f263";
+}
+.fa-suffix-odnoklassniki-square:after {
+ content: "\f264";
+}
+.fa-suffix-get-pocket:after {
+ content: "\f265";
+}
+.fa-suffix-wikipedia-w:after {
+ content: "\f266";
+}
+.fa-suffix-safari:after {
+ content: "\f267";
+}
+.fa-suffix-chrome:after {
+ content: "\f268";
+}
+.fa-suffix-firefox:after {
+ content: "\f269";
+}
+.fa-suffix-opera:after {
+ content: "\f26a";
+}
+.fa-suffix-internet-explorer:after {
+ content: "\f26b";
+}
+.fa-suffix-tv:after,
+.fa-suffix-television:after {
+ content: "\f26c";
+}
+.fa-suffix-contao:after {
+ content: "\f26d";
+}
+.fa-suffix-500px:after {
+ content: "\f26e";
+}
+.fa-suffix-amazon:after {
+ content: "\f270";
+}
+.fa-suffix-calendar-plus-o:after {
+ content: "\f271";
+}
+.fa-suffix-calendar-minus-o:after {
+ content: "\f272";
+}
+.fa-suffix-calendar-times-o:after {
+ content: "\f273";
+}
+.fa-suffix-calendar-check-o:after {
+ content: "\f274";
+}
+.fa-suffix-industry:after {
+ content: "\f275";
+}
+.fa-suffix-map-pin:after {
+ content: "\f276";
+}
+.fa-suffix-map-signs:after {
+ content: "\f277";
+}
+.fa-suffix-map-o:after {
+ content: "\f278";
+}
+.fa-suffix-map:after {
+ content: "\f279";
+}
+.fa-suffix-commenting:after {
+ content: "\f27a";
+}
+.fa-suffix-commenting-o:after {
+ content: "\f27b";
+}
+.fa-suffix-houzz:after {
+ content: "\f27c";
+}
+.fa-suffix-vimeo:after {
+ content: "\f27d";
+}
+.fa-suffix-black-tie:after {
+ content: "\f27e";
+}
+.fa-suffix-fonticons:after {
+ content: "\f280";
+}
+.fa-suffix-reddit-alien:after {
+ content: "\f281";
+}
+.fa-suffix-edge:after {
+ content: "\f282";
+}
+.fa-suffix-credit-card-alt:after {
+ content: "\f283";
+}
+.fa-suffix-codiepie:after {
+ content: "\f284";
+}
+.fa-suffix-modx:after {
+ content: "\f285";
+}
+.fa-suffix-fort-awesome:after {
+ content: "\f286";
+}
+.fa-suffix-usb:after {
+ content: "\f287";
+}
+.fa-suffix-product-hunt:after {
+ content: "\f288";
+}
+.fa-suffix-mixcloud:after {
+ content: "\f289";
+}
+.fa-suffix-scribd:after {
+ content: "\f28a";
+}
+.fa-suffix-pause-circle:after {
+ content: "\f28b";
+}
+.fa-suffix-pause-circle-o:after {
+ content: "\f28c";
+}
+.fa-suffix-stop-circle:after {
+ content: "\f28d";
+}
+.fa-suffix-stop-circle-o:after {
+ content: "\f28e";
+}
+.fa-suffix-shopping-bag:after {
+ content: "\f290";
+}
+.fa-suffix-shopping-basket:after {
+ content: "\f291";
+}
+.fa-suffix-hashtag:after {
+ content: "\f292";
+}
+.fa-suffix-bluetooth:after {
+ content: "\f293";
+}
+.fa-suffix-bluetooth-b:after {
+ content: "\f294";
+}
+.fa-suffix-percent:after {
+ content: "\f295";
+}
+.fa-suffix-gitlab:after {
+ content: "\f296";
+}
+.fa-suffix-wpbeginner:after {
+ content: "\f297";
+}
+.fa-suffix-wpforms:after {
+ content: "\f298";
+}
+.fa-suffix-envira:after {
+ content: "\f299";
+}
+.fa-suffix-universal-access:after {
+ content: "\f29a";
+}
+.fa-suffix-wheelchair-alt:after {
+ content: "\f29b";
+}
+.fa-suffix-question-circle-o:after {
+ content: "\f29c";
+}
+.fa-suffix-blind:after {
+ content: "\f29d";
+}
+.fa-suffix-audio-description:after {
+ content: "\f29e";
+}
+.fa-suffix-volume-control-phone:after {
+ content: "\f2a0";
+}
+.fa-suffix-braille:after {
+ content: "\f2a1";
+}
+.fa-suffix-assistive-listening-systems:after {
+ content: "\f2a2";
+}
+.fa-suffix-asl-interpreting:after,
+.fa-suffix-american-sign-language-interpreting:after {
+ content: "\f2a3";
+}
+.fa-suffix-deafness:after,
+.fa-suffix-hard-of-hearing:after,
+.fa-suffix-deaf:after {
+ content: "\f2a4";
+}
+.fa-suffix-glide:after {
+ content: "\f2a5";
+}
+.fa-suffix-glide-g:after {
+ content: "\f2a6";
+}
+.fa-suffix-signing:after,
+.fa-suffix-sign-language:after {
+ content: "\f2a7";
+}
+.fa-suffix-low-vision:after {
+ content: "\f2a8";
+}
+.fa-suffix-viadeo:after {
+ content: "\f2a9";
+}
+.fa-suffix-viadeo-square:after {
+ content: "\f2aa";
+}
+.fa-suffix-snapchat:after {
+ content: "\f2ab";
+}
+.fa-suffix-snapchat-ghost:after {
+ content: "\f2ac";
+}
+.fa-suffix-snapchat-square:after {
+ content: "\f2ad";
+}
+.fa-suffix-pied-piper:after {
+ content: "\f2ae";
+}
+.fa-suffix-first-order:after {
+ content: "\f2b0";
+}
+.fa-suffix-yoast:after {
+ content: "\f2b1";
+}
+.fa-suffix-themeisle:after {
+ content: "\f2b2";
+}
+.fa-suffix-google-plus-circle:after,
+.fa-suffix-google-plus-official:after {
+ content: "\f2b3";
+}
+.fa-suffix-fa:after,
+.fa-suffix-font-awesome:after {
+ content: "\f2b4";
+}
+.fa-suffix-handshake-o:after {
+ content: "\f2b5";
+}
+.fa-suffix-envelope-open:after {
+ content: "\f2b6";
+}
+.fa-suffix-envelope-open-o:after {
+ content: "\f2b7";
+}
+.fa-suffix-linode:after {
+ content: "\f2b8";
+}
+.fa-suffix-address-book:after {
+ content: "\f2b9";
+}
+.fa-suffix-address-book-o:after {
+ content: "\f2ba";
+}
+.fa-suffix-vcard:after,
+.fa-suffix-address-card:after {
+ content: "\f2bb";
+}
+.fa-suffix-vcard-o:after,
+.fa-suffix-address-card-o:after {
+ content: "\f2bc";
+}
+.fa-suffix-user-circle:after {
+ content: "\f2bd";
+}
+.fa-suffix-user-circle-o:after {
+ content: "\f2be";
+}
+.fa-suffix-user-o:after {
+ content: "\f2c0";
+}
+.fa-suffix-id-badge:after {
+ content: "\f2c1";
+}
+.fa-suffix-drivers-license:after,
+.fa-suffix-id-card:after {
+ content: "\f2c2";
+}
+.fa-suffix-drivers-license-o:after,
+.fa-suffix-id-card-o:after {
+ content: "\f2c3";
+}
+.fa-suffix-quora:after {
+ content: "\f2c4";
+}
+.fa-suffix-free-code-camp:after {
+ content: "\f2c5";
+}
+.fa-suffix-telegram:after {
+ content: "\f2c6";
+}
+.fa-suffix-thermometer-4:after,
+.fa-suffix-thermometer:after,
+.fa-suffix-thermometer-full:after {
+ content: "\f2c7";
+}
+.fa-suffix-thermometer-3:after,
+.fa-suffix-thermometer-three-quarters:after {
+ content: "\f2c8";
+}
+.fa-suffix-thermometer-2:after,
+.fa-suffix-thermometer-half:after {
+ content: "\f2c9";
+}
+.fa-suffix-thermometer-1:after,
+.fa-suffix-thermometer-quarter:after {
+ content: "\f2ca";
+}
+.fa-suffix-thermometer-0:after,
+.fa-suffix-thermometer-empty:after {
+ content: "\f2cb";
+}
+.fa-suffix-shower:after {
+ content: "\f2cc";
+}
+.fa-suffix-bathtub:after,
+.fa-suffix-s15:after,
+.fa-suffix-bath:after {
+ content: "\f2cd";
+}
+.fa-suffix-podcast:after {
+ content: "\f2ce";
+}
+.fa-suffix-window-maximize:after {
+ content: "\f2d0";
+}
+.fa-suffix-window-minimize:after {
+ content: "\f2d1";
+}
+.fa-suffix-window-restore:after {
+ content: "\f2d2";
+}
+.fa-suffix-times-rectangle:after,
+.fa-suffix-window-close:after {
+ content: "\f2d3";
+}
+.fa-suffix-times-rectangle-o:after,
+.fa-suffix-window-close-o:after {
+ content: "\f2d4";
+}
+.fa-suffix-bandcamp:after {
+ content: "\f2d5";
+}
+.fa-suffix-grav:after {
+ content: "\f2d6";
+}
+.fa-suffix-etsy:after {
+ content: "\f2d7";
+}
+.fa-suffix-imdb:after {
+ content: "\f2d8";
+}
+.fa-suffix-ravelry:after {
+ content: "\f2d9";
+}
+.fa-suffix-eercast:after {
+ content: "\f2da";
+}
+.fa-suffix-microchip:after {
+ content: "\f2db";
+}
+.fa-suffix-snowflake-o:after {
+ content: "\f2dc";
+}
+.fa-suffix-superpowers:after {
+ content: "\f2dd";
+}
+.fa-suffix-wpexplorer:after {
+ content: "\f2de";
+}
+.fa-suffix-meetup:after {
+ content: "\f2e0";
+}
diff --git a/addons/web/static/lib/pdfjs/web/ui_utils.js b/addons/web/static/lib/pdfjs/web/ui_utils.js
index 7e798e34270..5480224846b 100644
--- a/addons/web/static/lib/pdfjs/web/ui_utils.js
+++ b/addons/web/static/lib/pdfjs/web/ui_utils.js
@@ -348,6 +348,10 @@ var ProgressBar = (function ProgressBarClosure() {
this.div.classList.remove('indeterminate');
var progressSize = this.width * this._percent / 100;
this.div.style.width = progressSize + this.units;
+ // Add attributes for screen readers and analysis tools
+ this.bar.setAttribute('aria-valuemin', 0);
+ this.bar.setAttribute('aria-valuemax', 100);
+ this.bar.setAttribute('aria-valuenow', this._percent);
},
get percent() {
@@ -377,6 +381,7 @@ var ProgressBar = (function ProgressBarClosure() {
}
this.visible = false;
this.bar.classList.add('hidden');
+ this.bar.setAttribute('aria-hidden', "true")
document.body.classList.remove('loadingInProgress');
},
@@ -387,6 +392,7 @@ var ProgressBar = (function ProgressBarClosure() {
this.visible = true;
document.body.classList.add('loadingInProgress');
this.bar.classList.remove('hidden');
+ this.bar.setAttribute('aria-hidden', "false")
}
};
diff --git a/addons/web/static/lib/qweb/qweb-test-attributes.xml b/addons/web/static/lib/qweb/qweb-test-attributes.xml
index e16ef8a3f0e..b599b76b629 100644
--- a/addons/web/static/lib/qweb/qweb-test-attributes.xml
+++ b/addons/web/static/lib/qweb/qweb-test-attributes.xml
@@ -5,9 +5,9 @@
<result id="static"><![CDATA[<div foo="a" bar="b" baz="c"></div>]]></result>
<t t-name="static-void">
- <img src="/test.jpg"/>
+ <img src="/test.jpg" alt="Test"/>
</t>
- <result id="static-void"><![CDATA[<img src="/test.jpg"/>]]></result>
+ <result id="static-void"><![CDATA[<img src="/test.jpg" alt="Test"/>]]></result>
<t t-name="fixed-literal">
<div t-att-foo="'bar'"/>
diff --git a/addons/web/static/lib/qweb/qweb2.js b/addons/web/static/lib/qweb/qweb2.js
index 94a0afaa32a..6ce74539926 100644
--- a/addons/web/static/lib/qweb/qweb2.js
+++ b/addons/web/static/lib/qweb/qweb2.js
@@ -471,10 +471,10 @@ QWeb2.Engine = (function() {
if (this.debug && window.console) {
console.log(code);
}
- this.tools.exception("Error evaluating template: " + error, { template: name });
+ this.tools.exception("Error evaluating template: " + error, { template: template });
}
if (!tcompiled) {
- this.tools.exception("Error evaluating template: (IE?)" + error, { template: name });
+ this.tools.exception("Error evaluating template: (IE?)" + error, { template: template });
}
this.compiled_templates[template] = tcompiled;
return this.render(template, dict);
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),
diff --git a/addons/web/static/src/scss/ui.scss b/addons/web/static/src/scss/ui.scss
index 8911b4f10ab..d88f21f83a1 100644
--- a/addons/web/static/src/scss/ui.scss
+++ b/addons/web/static/src/scss/ui.scss
@@ -9,3 +9,7 @@
.ui-autocomplete {
z-index: $zindex-modal + 1;
}
+
+[aria-hidden="true"], [aria-hidden="1"] {
+ display: none!important;
+}
diff --git a/addons/web/static/src/scss/utils.scss b/addons/web/static/src/scss/utils.scss
index efb8dce7598..cf04a8d32f8 100644
--- a/addons/web/static/src/scss/utils.scss
+++ b/addons/web/static/src/scss/utils.scss
@@ -337,6 +337,14 @@
}
}
}
+.oe_kanban_color_help {
+ bottom: 0px;
+ left: -1px;
+ top: 0px;
+ width: 6px;
+ position: absolute;
+ right: auto;
+}
@mixin o-kanban-slim-col {
position: relative;
@include o-flex(0, 0, auto);
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index 328f1ab93b5..45bd3555d69 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -6,13 +6,13 @@
<div t-name="Loading" class="o_loading"/>
<t t-name="WidgetLabel.tooltip">
- <div class="oe_tooltip_string" t-if="widget.string">
+ <div class="oe_tooltip_string" t-if="widget.string" role="tooltip">
<t t-esc="widget.string"/> <t t-if="debug and widget.nolabel">(nolabel)</t>
</div>
- <p t-if="widget.attrs.help || widget.field.help" class="oe_tooltip_help">
+ <p t-if="widget.attrs.help || widget.field.help" class="oe_tooltip_help" role="tooltip">
<t t-esc="widget.attrs.help || widget.field.help"/>
</p>
- <ul t-if="debug" class="oe_tooltip_technical">
+ <ul t-if="debug" class="oe_tooltip_technical" role="tooltip">
<li data-item="field" t-if="widget.name">
<span class="oe_tooltip_technical_title">Field:</span>
<t t-esc="widget.name"/>
@@ -70,7 +70,7 @@
</ul>
</t>
<t t-name="WidgetButton.tooltip">
- <div class="oe_tooltip_string" t-if="debug || node.attrs.string">
+ <div class="oe_tooltip_string" t-if="debug || node.attrs.string" role="tooltip">
<t t-if="debug">
Button
<t t-if="node.attrs.string">: </t>
@@ -78,10 +78,10 @@
</t>
<t t-esc="node.attrs.string"/>
</div>
- <p t-if="node.attrs.help" class="oe_tooltip_help">
+ <p t-if="node.attrs.help" class="oe_tooltip_help" role="tooltip">
<t t-esc="node.attrs.help"/>
</p>
- <ul t-if="debug" class="oe_tooltip_technical">
+ <ul t-if="debug" class="oe_tooltip_technical" role="tooltip">
<li data-item="object">
<span class="oe_tooltip_technical_title">Object:</span>
<t t-esc="state.model"/>
@@ -115,18 +115,18 @@
</t>
<t t-name="Notification">
- <div t-attf-class="o_notification #{widget.className}" t-translation="off">
- <a t-if="widget.sticky" class="fa fa-times o_close" href="#"/>
+ <div t-attf-class="o_notification #{widget.className}" t-translation="off" role="alertdialog">
+ <a t-if="widget.sticky" class="fa fa-times o_close" href="#" title="Close" aria-label="Close"/>
<div class="o_notification_title">
- <span t-attf-class="o_icon fa fa-3x #{widget.icon}"/>
+ <span t-attf-class="o_icon fa fa-3x #{widget.icon}" role="img" t-attf-aria-label="Notification #{widget.name}" t-attf-title="Notification #{widget.name}"/>
<t t-raw="widget.title"/>
</div>
<div class="o_notification_content" t-if="widget.message"><t t-raw="widget.message"/></div>
<div t-if="widget.buttons.length" class="o_buttons">
<button t-foreach="widget.buttons" t-as="button" t-attf-class="btn btn-sm #{button.primary ? 'btn-primary' : 'btn-default'}" type="button">
<t t-if="button.icon">
- <i t-if="button.icon.indexOf('fa-') === 0" t-attf-class="fa fa-fw o_button_icon #{button.icon}"/>
- <img t-else="" t-att-src="button.icon"/>
+ <i t-if="button.icon.indexOf('fa-') === 0" t-attf-class="fa fa-fw o_button_icon #{button.icon}" role="img" t-att-aria-label="button.name" t-att-title="button.name"/>
+ <img t-else="" t-att-src="button.icon" t-att-alt="button.name"/>
</t>
<span><t t-esc="button.text"/></span>
</button>
@@ -134,7 +134,7 @@
</div>
</t>
-<div t-name="CrashManager.warning" class="o_dialog_warning">
+<div t-name="CrashManager.warning" class="o_dialog_warning" role="alert">
<t t-js="d">
var message = (d.message !== undefined) ? d.message : d.error.data.message;
d.html_error = context.engine.tools.html_escape(message).replace(/\n/g, '<br/>');
@@ -144,7 +144,7 @@
<div t-name="CrashManager.error" class="o_dialog_error">
<div class="alert alert-warning clearfix" role="alert">
<button class="btn btn-sm btn-primary pull-right ml8 o_clipboard_button">
- <i class="fa fa-clipboard mr8"/>Copy the full error to clipboard
+ <span class="fa fa-clipboard mr8">Copy the full error to clipboard</span>
</button>
<p><b>An error occurred</b></p>
<p>Please use the copy button to report the error to your support service.</p>
@@ -152,13 +152,13 @@
<t t-set="errUID" t-value="_.uniqueId()"/>
<button class="btn btn-sm btn-link" t-att-data-target="'#o_error_detail' + errUID" data-toggle="collapse">See details</button>
- <div t-att-id="'o_error_detail' + errUID" class="collapse alert alert-danger o_error_detail">
+ <div t-att-id="'o_error_detail' + errUID" class="collapse alert alert-danger o_error_detail" role="alert">
<pre><t t-esc="error.message"/></pre>
<pre><t t-esc="error.data.debug"/></pre>
</div>
</div>
-<form t-name="ChangePassword" name="change_password_form" method="POST">
+<form t-name="ChangePassword" name="change_password_form" method="POST" aria-atomic="true">
<div class="o_form_view">
<table class="o_group o_inner_group o_label_nowrap">
<tr>
@@ -182,23 +182,23 @@
<t t-name="ControlPanel">
<div class="o_control_panel">
- <ol class="breadcrumb"/>
- <div class="o_cp_searchview"/>
+ <ol class="breadcrumb" role="navigation"/>
+ <div class="o_cp_searchview" role="search"/>
<div class="o_cp_left">
- <div class="o_cp_buttons"/>
- <div class="o_cp_sidebar"/>
+ <div class="o_cp_buttons" role="toolbar" aria-label="Control panel toolbar"/>
+ <aside class="o_cp_sidebar"/>
</div>
<div class="o_cp_right">
- <div class="btn-group o_search_options"/>
- <div class="o_cp_pager"/>
- <div class="btn-group btn-group-sm o_cp_switch_buttons"/>
+ <div class="btn-group o_search_options" role="search"/>
+ <nav class="o_cp_pager" role="search" aria-label="Pager"/>
+ <nav class="btn-group btn-group-sm o_cp_switch_buttons" role="toolbar" aria-label="View switcher"/>
</div>
</div>
</t>
<t t-name="X2ManyControlPanel">
<div class="o_x2m_control_panel">
- <div class="o_cp_buttons"/>
- <div class="o_cp_pager"/>
+ <nav class="o_cp_buttons" aria-label="Control panel toolbar" role="toolbar"/>
+ <nav class="o_cp_pager" aria-label="Pager" role="toolbar"/>
</div>
</t>
@@ -206,41 +206,41 @@
<t t-foreach="views" t-as="view">
<button type="button" t-att-accesskey="view.accessKey"
t-attf-class="btn btn-icon fa fa-lg #{view.icon} o_cp_switch_#{view.type}"
- t-att-aria-label="view.type"
- t-att-data-view-type="view.type" t-att-title="view.label" tabindex="-1"/>
+ t-attf-aria-label="View #{view.type}"
+ t-att-data-view-type="view.type" t-attf-title="View #{view.type}" tabindex="-1"/>
</t>
</t>
<t t-name="WebClient.DebugManager">
- <li class="o_debug_manager">
- <a href="#" title="Open Developer Tools" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false" tabindex="-1">
+ <li class="o_debug_manager" role="menuitem">
+ <a href="#" title="Open Developer Tools" aria-label="Open Developer Tools" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false" tabindex="-1">
<span class="fa fa-bug"/>
</a>
<ul class="dropdown-menu o_debug_dropdown" role="menu"/>
</li>
</t>
<t t-name="WebClient.DebugManager.Global">
- <li><a href="#" data-action="perform_js_tests">Run JS Tests</a></li>
- <li><a href="#" data-action="perform_js_mobile_tests">Run JS Mobile Tests</a></li>
- <li><a href="#" data-action="select_view">Open View</a></li>
+ <li><a role="menuitem" href="#" data-action="perform_js_tests">Run JS Tests</a></li>
+ <li><a role="menuitem" href="#" data-action="perform_js_mobile_tests">Run JS Mobile Tests</a></li>
+ <li><a role="menuitem" href="#" data-action="select_view">Open View</a></li>
<t t-if="manager._events">
- <li class="divider"/>
- <li><a href="#" data-action="show_timelines">Toggle Timelines</a></li>
- <li><a href="#" data-action="requests_clear">Clear Events</a></li>
+ <li class="divider" role="separator"/>
+ <li><a role="menuitem" href="#" data-action="show_timelines">Toggle Timelines</a></li>
+ <li><a role="menuitem" href="#" data-action="requests_clear">Clear Events</a></li>
</t>
- <li class="divider o_debug_leave_section"/>
- <li><a href="#" data-action="split_assets">Activate Assets Debugging</a></li>
- <li><a href="#" data-action="regenerateAssets">Regenerate Assets Bundles</a></li>
- <li><a href="#" data-action="leave_debug_mode">Leave the Developer Tools</a></li>
+ <li class="divider o_debug_leave_section" role="separator"/>
+ <li><a role="menuitem" href="#" data-action="split_assets">Activate Assets Debugging</a></li>
+ <li><a role="menuitem" href="#" data-action="regenerateAssets">Regenerate Assets Bundles</a></li>
+ <li><a role="menuitem" href="#" data-action="leave_debug_mode">Leave the Developer Tools</a></li>
</t>
<t t-name="WebClient.DebugManager.Action">
<t t-if="action">
- <li class="divider"/>
- <li><a href="#" data-action="edit" t-att-data-model="action.type" t-att-data-id="action.id">Edit Action</a></li>
+ <li class="divider" role="separator"/>
+ <li><a role="menuitem" href="#" data-action="edit" t-att-data-model="action.type" t-att-data-id="action.id">Edit Action</a></li>
<t t-if="action.res_model">
- <li><a href="#" data-action="get_view_fields">View Fields</a></li>
- <li><a href="#" data-action="manage_filters">Manage Filters</a></li>
- <li><a href="#" data-action="translate">Technical Translation</a></li>
+ <li><a role="menuitem" href="#" data-action="get_view_fields">View Fields</a></li>
+ <li><a role="menuitem" href="#" data-action="manage_filters">Manage Filters</a></li>
+ <li><a role="menuitem" href="#" data-action="translate">Technical Translation</a></li>
</t>
</t>
</t>
@@ -248,7 +248,7 @@
<dl><t t-foreach="fields" t-as="field">
<dt><h4><t t-esc="field"/></h4></dt>
<dd>
- <dl><ul><li t-foreach="field_value" t-as="attribute">
+ <dl><ul role="menu"><li role="menuitem" t-foreach="field_value" t-as="attribute">
<strong><t t-esc="attribute"/></strong>:
<t t-esc="attribute_value"/>
</li></ul></dl>
@@ -257,21 +257,21 @@
</t>
<t t-name="WebClient.DebugManager.View">
<t t-if="view">
- <li class="divider"/>
+ <li role="separator" class="divider"/>
<t t-if="view.type === 'form'">
- <li><a href="#" data-action="set_defaults">Set Defaults</a></li>
+ <li><a role="menuitem" href="#" data-action="set_defaults">Set Defaults</a></li>
<t t-if="controller.getSelectedIds().length === 1">
- <li><a href="#" data-action="get_metadata">View Metadata</a></li>
+ <li><a role="menuitem" href="#" data-action="get_metadata">View Metadata</a></li>
</t>
</t>
- <li><a href="#" data-action="fvg">Fields View Get</a></li>
+ <li><a role="menuitem" href="#" data-action="fvg">Fields View Get</a></li>
<t t-if="can_edit">
<li>
- <a href="#" data-action="edit" data-model="ir.ui.view" t-att-data-id="view.fieldsView.view_id">
+ <a role="menuitem" href="#" data-action="edit" data-model="ir.ui.view" t-att-data-id="view.fieldsView.view_id">
Edit View: <t t-esc="_.str.capitalize(view.type)"/>
</a>
</li>
- <li t-if="searchview and searchview.$el.is(':visible')"><a href="#" data-action="edit" data-model="ir.ui.view" t-att-data-id="action.searchFieldsView.view_id">Edit SearchView</a></li>
+ <li t-if="searchview and searchview.$el.is(':visible')"><a role="menuitem" href="#" data-action="edit" data-model="ir.ui.view" t-att-data-id="action.searchFieldsView.view_id">Edit SearchView</a></li>
</t>
</t>
</t>
@@ -332,7 +332,7 @@
</a>
</t>
<ul class="dropdown-menu" role="menu">
- <li t-foreach="widget.items[section.name]" t-as="item" t-att-class="item.classname">
+ <li role="menuitem" t-foreach="widget.items[section.name]" t-as="item" t-att-class="item.classname">
<t t-if="section.name == 'files'">
<t t-set="item.title">
<b>Attachment : </b><br/>
@@ -351,10 +351,10 @@
</t>
<a t-att-title="item.title or None" t-att-data-section="section.name" t-att-data-index="item_index" t-att-href="item.url or '#'">
<t t-raw="item.label"/>
- <span t-if="section.name == 'files' and widget.options.editable and !item.callback" class="fa fa-trash-o o_sidebar_delete_attachment" t-att-data-id="item.id" title="Delete this attachment"/>
+ <span t-if="section.name == 'files' and widget.options.editable and !item.callback" class="fa fa-trash-o o_sidebar_delete_attachment" t-att-data-id="item.id" title="Delete this attachment" role="img" aria-label="Delete this attachment"/>
</a>
</li>
- <li t-if="section.name == 'files' and widget.options.editable" class="o_sidebar_add_attachment">
+ <li role="menuitem" t-if="section.name == 'files' and widget.options.editable" class="o_sidebar_add_attachment">
<t t-call="HiddenInputFile">
<t t-set="fileupload_id" t-value="widget.fileuploadId"/>
<t t-set="fileupload_action" t-translation="off">/web/binary/upload_attachment</t>
@@ -371,7 +371,7 @@
</t>
<t t-name="ListView.buttons">
- <div class="o_list_buttons">
+ <div class="o_list_buttons" role="toolbar" aria-label="Main actions">
<t t-if="widget.is_action_enabled('create')">
<button type="button" class="btn btn-primary btn-sm o_list_button_add" accesskey="c">
Create
@@ -387,7 +387,7 @@
</t>
<t t-name="FormView.buttons">
- <div class="o_form_buttons_view">
+ <div class="o_form_buttons_view" role="toolbar" aria-label="Main actions">
<button t-if="widget.is_action_enabled('edit')" type="button"
class="btn btn-primary btn-sm o_form_button_edit" accesskey="a">
Edit
@@ -397,7 +397,7 @@
Create
</button>
</div>
- <div class="o_form_buttons_edit">
+ <div class="o_form_buttons_edit" role="toolbar" aria-label="Main actions">
<button type="button"
class="btn btn-primary btn-sm o_form_button_save" accesskey="s">
Save
@@ -409,7 +409,7 @@
</div>
</t>
<t t-name="SaveCancelButton.tooltip">
- <div class="oe_tooltip_string">
+ <div class="oe_tooltip_string" role="alert">
<div class="tooltip-inner">
Hit ENTER to SAVE<br/>
Hit ESCAPE to DISCARD
@@ -417,7 +417,7 @@
</div>
</t>
<t t-name="CreateButton.tooltip">
- <div class="oe_tooltip_string">
+ <div class="oe_tooltip_string" role="tooltip">
<div class="tooltip-inner">
Hit ENTER to CREATE<br/>
Hit DOWN to navigate to the list bellow
@@ -425,14 +425,14 @@
</div>
</t>
<t t-name="FormButton.tooltip">
- <div class="oe_tooltip_string">
+ <div class="oe_tooltip_string" role="tooltip">
<div class="tooltip-inner">
Hit ENTER to
<t t-esc="title"></t>
</div>
</div>
</t>
-<form t-name="FormView.set_default">
+<form t-name="FormView.set_default" aria-atomic="true">
<t t-set="args" t-value="widget.args"/>
<table style="width: 100%">
<tr>
@@ -489,25 +489,25 @@
</table>
</form>
<t t-name="GraphView.buttons">
- <div class="btn-group btn-group-sm" role="group">
+ <div class="btn-group btn-group-sm" role="toolbar" aria-label="Main actions">
<button class="btn btn-primary btn-sm dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
Measures <span class="caret"/>
</button>
- <ul class="dropdown-menu o_graph_measures_list">
+ <ul class="dropdown-menu o_graph_measures_list" role="menu">
<li t-foreach="measures" t-as="measure" t-att-data-field="measure[0]">
- <a href="#"><t t-esc="measure[1].string"/><t t-if="measure[1].type === 'many2one'"> (count)</t></a>
+ <a role="menuitem" href="#"><t t-esc="measure[1].string"/><t t-if="measure[1].type === 'many2one'"> (count)</t></a>
</li>
- <li class="divider"></li>
- <li data-field="__count__"><a href="#">Count</a></li>
+ <li role="separator" class="divider"></li>
+ <li data-field="__count__"><a role="menuitem" href="#">Count</a></li>
</ul>
</div>
- <div class="btn-group btn-group-sm">
- <button class="btn btn-default fa fa-bar-chart-o o_graph_button" title="Bar Chart" data-mode="bar"/>
- <button class="btn btn-default fa fa-line-chart o_graph_button" title="Line Chart" data-mode="line"/>
- <button class="btn btn-default fa fa-pie-chart o_graph_button" title="Pie Chart" data-mode="pie"/>
+ <div class="btn-group btn-group-sm" role="toolbar" aria-label="Change graph">
+ <button class="btn btn-default fa fa-bar-chart-o o_graph_button" title="Bar Chart" aria-label="Bar Chart" data-mode="bar"/>
+ <button class="btn btn-default fa fa-line-chart o_graph_button" title="Line Chart" aria-label="Line Chart" data-mode="line"/>
+ <button class="btn btn-default fa fa-pie-chart o_graph_button" title="Pie Chart" aria-label="Pie Chart" data-mode="pie"/>
</div>
</t>
-<div t-name="GraphView.error" class="o_view_nocontent" >
+<div t-name="GraphView.error" class="o_view_nocontent" role="alert">
<div class="o_nocontent_help">
<p class="o_view_nocontent_empty_folder"><t t-esc="title"/></p>
<p><t t-esc="description"/></p>
@@ -518,51 +518,51 @@
<div class="o_field_selection"/>
</div>
<t t-name="PivotView.buttons">
- <div class="btn-group btn-group-sm" role="group">
+ <div class="btn-group btn-group-sm" role="toolbar" aria-label="Main actions">
<button class="btn btn-primary btn-sm dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
Measures <span class="caret"/>
</button>
- <ul class="dropdown-menu o_pivot_measures_list">
+ <ul class="dropdown-menu o_pivot_measures_list" role="menu">
<li t-foreach="measures" t-as="measure" t-att-data-field="measure[0]">
- <a href="#"><t t-esc="measure[1].string"/><t t-if="measure[1].type === 'many2one'"> (count)</t></a>
+ <a role="menuitem" href="#"><t t-esc="measure[1].string"/><t t-if="measure[1].type === 'many2one'"> (count)</t></a>
</li>
- <li class="divider"></li>
- <li data-field="__count"><a href="#">Count</a></li>
+ <li role="separator" class="divider"></li>
+ <li data-field="__count"><a role="menuitem" href="#">Count</a></li>
</ul>
</div>
- <div class="btn-group btn-group-sm">
- <button class="btn btn-default fa fa-expand o_pivot_flip_button" title="Flip axis"/>
- <button class="btn btn-default fa fa-arrows-alt o_pivot_expand_button" title="Expand all"/>
- <button class="btn btn-default fa fa-download o_pivot_download" title="Download xls"/>
+ <div class="btn-group btn-group-sm" role="toolbar" aria-label="Pivot settings">
+ <button class="btn btn-default fa fa-expand o_pivot_flip_button" title="Flip axis" aria-label="Flip axis"/>
+ <button class="btn btn-default fa fa-arrows-alt o_pivot_expand_button" title="Expand all" aria-label="Expand all"/>
+ <button class="btn btn-default fa fa-download o_pivot_download" title="Download xls" aria-label="Download xls"/>
</div>
</t>
<t t-name="PivotView.FieldSelection">
<ul class="dropdown-menu o_pivot_field_menu" role="menu">
<t t-foreach="fields" t-as="field">
<t t-if="(field[1].type === 'date') || (field[1].type === 'datetime')">
- <li t-att-data-field="field[0]" t-attf-class="o_inline_dropdown#{field[2] ? ' disabled' : ''}">
+ <li role="menuitem" aria-haspopup="true" t-att-data-field="field[0]" t-attf-class="o_inline_dropdown#{field[2] ? ' disabled' : ''}">
<a href="#" class="o_pivot_field_selection">
<t t-esc="field[1].string"/>
</a>
- <ul class="dropdown-menu">
- <li t-att-data-field="field[0]"><a href="#" t-att-data-interval="'day'">Day</a></li>
- <li t-att-data-field="field[0]"><a href="#" t-att-data-interval="'week'">Week</a></li>
- <li t-att-data-field="field[0]"><a href="#" t-att-data-interval="'month'">Month</a></li>
- <li t-att-data-field="field[0]"><a href="#" t-att-data-interval="'quarter'">Quarter</a></li>
- <li t-att-data-field="field[0]"><a href="#" t-att-data-interval="'year'">Year</a></li>
+ <ul class="dropdown-menu" role="menu">
+ <li t-att-data-field="field[0]"><a role="menuitem" href="#" t-att-data-interval="'day'">Day</a></li>
+ <li t-att-data-field="field[0]"><a role="menuitem" href="#" t-att-data-interval="'week'">Week</a></li>
+ <li t-att-data-field="field[0]"><a role="menuitem" href="#" t-att-data-interval="'month'">Month</a></li>
+ <li t-att-data-field="field[0]"><a role="menuitem" href="#" t-att-data-interval="'quarter'">Quarter</a></li>
+ <li t-att-data-field="field[0]"><a role="menuitem" href="#" t-att-data-interval="'year'">Year</a></li>
</ul>
</li>
</t>
<t t-else="">
<li t-att-data-field="field[0]" t-att-class="(field[2] ? 'disabled' : '')">
- <a href="#"><t t-esc="field[1].string"/></a>
+ <a role="menuitem" href="#"><t t-esc="field[1].string"/></a>
</li>
</t>
</t>
</ul>
</t>
<div t-name="PivotView.nodata">
- <div class="o_view_nocontent">
+ <div class="o_view_nocontent" role="alert">
<div class="o_nocontent_help">
<p class="o_view_nocontent_empty_folder">
No data to display
@@ -575,15 +575,15 @@
</div>
<t t-name="FormSelection">
- <div class="btn-group o_selection">
- <a href="#" data-toggle="dropdown"><span class="o_status"/></a>
+ <div class="btn-group o_selection" aria-atomic="true">
+ <a href="#" data-toggle="dropdown" aria-label="Dropdown menu" title="Dropdown menu"><span class="o_status"/></a>
<ul class="dropdown-menu state" role="menu">
</ul>
</div>
</t>
<t t-name="FormSelection.items">
<li t-foreach="states" t-as="rec" t-att-data-value="rec.name">
- <a href="#">
+ <a role="menuitem" href="#">
<span t-att-class="'o_status ' + (rec.state_class &amp;&amp; rec.state_class || '')"/>
<t t-raw="rec.state_name"/>
</a>
@@ -591,34 +591,34 @@
</t>
<t t-name="FieldDomain.content">
<div t-if="hasModel" class="o_field_domain_panel">
- <i class="fa fa-arrow-right"/>
+ <i class="fa fa-arrow-right" role="img" aria-label="Domain" title="Domain"/>
<button t-if="isValid" class="btn btn-xs btn-default o_domain_show_selection_button" type="button">
<t t-esc="nbRecords"/> record(s)
</button>
- <span t-else="" class="text-warning"><i class="fa fa-exclamation-triangle"/> Invalid domain</span>
+ <span t-else="" class="text-warning" role="alert"><i class="fa fa-exclamation-triangle" role="img" aria-label="Warning" title="Warning"/> Invalid domain</span>
<button t-if="inDialogEdit" class="btn btn-xs btn-primary o_field_domain_dialog_button">Edit Domain</button>
</div>
<div t-else="">Select a model to add a filter.</div>
</t>
<t t-name="DomainNode.ControlPanel">
- <div t-if="!widget.readonly &amp;&amp; !widget.noControlPanel" class="o_domain_node_control_panel">
- <button class="btn o_domain_delete_node_button"><i class="fa fa-times"/></button>
- <button class="btn o_domain_add_node_button"><i class="fa fa-plus-circle"/></button>
- <button class="btn o_domain_add_node_button" data-branch="1"><i class="fa fa-ellipsis-h"/></button>
+ <div t-if="!widget.readonly &amp;&amp; !widget.noControlPanel" class="o_domain_node_control_panel" role="toolbar" aria-label="Domain node">
+ <button class="btn o_domain_delete_node_button" title="Delete node" aria-label="Delete node"><i class="fa fa-times"/></button>
+ <button class="btn o_domain_add_node_button" title="Add node" aria-label="Add node"><i class="fa fa-plus-circle"/></button>
+ <button class="btn o_domain_add_node_button" title="Add branch" aria-label="Add branch" data-branch="1"><i class="fa fa-ellipsis-h"/></button>
</div>
</t>
<t t-name="DomainTree.OperatorSelector">
- <div t-if="!widget.readonly" class="btn-group o_domain_tree_operator_selector">
+ <div t-if="!widget.readonly" class="btn-group o_domain_tree_operator_selector" aria-atomic="true">
<button class="btn btn-xs btn-primary o_domain_tree_operator_caret" data-toggle="dropdown">
<t t-if="widget.operator === '&amp;'">All</t>
<t t-if="widget.operator === '|'">Any</t>
<t t-if="widget.operator === '!'">None</t>
</button>
- <ul class="dropdown-menu">
- <li><a href="#" data-operator="&amp;">All</a></li>
- <li><a href="#" data-operator="|">Any</a></li>
+ <ul class="dropdown-menu" role="menu">
+ <li><a role="menuitem" href="#" data-operator="&amp;">All</a></li>
+ <li><a role="menuitem" href="#" data-operator="|">Any</a></li>
</ul>
</div>
<strong t-else="">
@@ -627,10 +627,10 @@
<t t-if="widget.operator === '!'">NONE</t>
</strong>
</t>
-<div t-name="DomainSelector" t-attf-class="o_domain_node o_domain_tree o_domain_selector #{widget.readonly ? 'o_read_mode' : 'o_edit_mode'}">
+<div aria-atomic="true" t-name="DomainSelector" t-attf-class="o_domain_node o_domain_tree o_domain_selector #{widget.readonly ? 'o_read_mode' : 'o_edit_mode'}">
<t t-if="widget.children.length === 0">
<span>Match <strong>all records</strong></span>
- <button t-if="!widget.readonly" class="btn btn-xs btn-primary o_domain_add_first_node_button"><i class="fa fa-plus"/> Add filter</button>
+ <button t-if="!widget.readonly" class="btn btn-xs btn-primary o_domain_add_first_node_button"><span class="fa fa-plus"> Add filter</span></button>
</t>
<t t-else="">
<div class="o_domain_tree_header">
@@ -686,12 +686,12 @@
<t t-if="_.contains(['in', 'not in'], widget.operator)">
<div class="o_domain_leaf_value_input">
<span class="badge" t-foreach="widget.displayValue" t-as="val">
- <t t-esc="val"/> <i class="o_domain_leaf_value_remove_tag_button fa fa-times" t-att-data-value="val"/>
+ <t t-esc="val"/> <i class="o_domain_leaf_value_remove_tag_button fa fa-times" t-att-data-value="val" role="img" aria-label="Remove tag" title="Remove tag"/>
</span>
</div>
<div class="o_domain_leaf_value_tags">
<input placeholder="Add new value" type="text" class="o_input"/>
- <button class="btn btn-xs btn-primary fa fa-plus o_domain_leaf_value_add_tag_button"/>
+ <button class="btn btn-xs btn-primary fa fa-plus o_domain_leaf_value_add_tag_button" aria-label="Add tag" title="Add tag"/>
</div>
</t>
<t t-else="">
@@ -724,16 +724,16 @@
</t>
</div>
</div>
-<div t-name="ModelFieldSelector" t-attf-class="o_field_selector#{!widget.options.readonly ? ' o_edit_mode o_input' : ''}">
+<div aria-atomic="true" t-name="ModelFieldSelector" t-attf-class="o_field_selector#{!widget.options.readonly ? ' o_edit_mode o_input' : ''}">
<div class="o_field_selector_value" tabindex="0"/>
<div class="o_field_selector_controls" tabindex="0">
- <i class="fa fa-exclamation-triangle o_field_selector_warning hidden" title="Invalid field chain"/>
+ <i role="alert" class="fa fa-exclamation-triangle o_field_selector_warning hidden" title="Invalid field chain" aria-label="Invalid field chain"/>
</div>
<div t-if="!widget.options.readonly" class="o_field_selector_popover hidden" tabindex="0">
<div class="o_field_selector_popover_header text-center">
- <i class="fa fa-arrow-left o_field_selector_popover_option o_field_selector_prev_page"/>
+ <i class="fa fa-arrow-left o_field_selector_popover_option o_field_selector_prev_page" title="Previous" role="img" aria-label="Previous"/>
<div class="o_field_selector_title"/>
- <i class="fa fa-times o_field_selector_popover_option o_field_selector_close"/>
+ <i class="fa fa-times o_field_selector_popover_option o_field_selector_close" title="Close" role="img" aria-label="Close"/>
</div>
<div class="o_field_selector_popover_body">
<ul class="o_field_selector_page"/>
@@ -746,7 +746,7 @@
<t t-name="ModelFieldSelector.value">
<t t-foreach="chain" t-as="fieldName">
<t t-if="fieldName_index > 0">
- <i class="fa fa-chevron-right"/>
+ <i class="fa fa-chevron-right" role="img" aria-label="Followed by" title="Followed by"/>
</t>
<span class="o_field_selector_chain_part">
<t t-set="fieldInfo" t-value="_.findWhere(pages[fieldName_index], {'name': fieldName})"/>
@@ -761,13 +761,13 @@
t-att-data-name="line.name">
<t t-esc="line.string"/>
<div t-if="debug" class="text-muted o_field_selector_item_title"><t t-esc="line.name"/> (<t t-esc="line.type"/>)</div>
- <i t-if="relationToFollow" class="fa fa-chevron-right o_field_selector_relation_icon"/>
+ <i t-if="relationToFollow" class="fa fa-chevron-right o_field_selector_relation_icon" role="img" aria-label="Relation to follow" title="Relation to follow"/>
</li>
</t>
</ul>
<t t-name="web.datepicker">
<t t-set="placeholder" t-value="widget.getParent().node and widget.getParent().node.attrs.placeholder"/>
- <div class="o_datepicker">
+ <div class="o_datepicker" aria-atomic="true">
<input type="text"
class="o_datepicker_input o_input"
t-att-name="widget.name"
@@ -786,7 +786,7 @@
t-att-id="widget.idForLabel"/>
</t>
<t t-name="FieldRadio.button">
- <div class="o_radio_item">
+ <div class="o_radio_item" aria-atomic="true">
<input type="radio" class="o_radio_input" t-att-checked="checked ? true : undefined"
t-att-data-value="value[0]" t-att-data-index="index" t-att-id="id"/>
<label class="o_form_label" t-att-for="id"><t t-esc="value[1]"/></label>
@@ -804,7 +804,7 @@
<a t-if="!widget.nodeOptions.no_open" class="o_form_uri" href="#"/>
<span t-if="widget.nodeOptions.no_open"/>
</t>
- <div t-if="widget.mode === 'edit'" class="o_field_widget o_field_many2one">
+ <div t-if="widget.mode === 'edit'" class="o_field_widget o_field_many2one" aria-atomic="true">
<div class="o_input_dropdown">
<input type="text" class="o_input"
t-att-barcode_events="widget.nodeOptions.barcode_events"
@@ -812,9 +812,9 @@
t-att-autofocus="widget.attrs.autofocus"
t-att-placeholder="widget.attrs.placeholder"
t-att-id="widget.idForLabel"/>
- <span class="o_dropdown_button" draggable="false"/>
+ <a role="button" class="o_dropdown_button" draggable="false"/>
</div>
- <button type="button" t-if="!widget.nodeOptions.no_open" class="fa fa-external-link btn btn-default o_external_button" tabindex="-1" draggable="false"/>
+ <button type="button" t-if="!widget.nodeOptions.no_open" class="fa fa-external-link btn btn-default o_external_button" tabindex="-1" draggable="false" aria-label="External link" title="External link"/>
</div>
</t>
<t t-name="FieldReference" t-extend="FieldMany2One">
@@ -828,23 +828,29 @@
<t t-name="FieldMany2ManyTag">
<t t-foreach="elements" t-as="el">
<t t-set="color" t-value="el[colorField] || 0"/>
- <span t-attf-class="badge dropdown o_tag_color_#{color}" t-att-data-color="color" t-att-data-index="el_index" t-att-data-id="el.id">
- <span class="o_badge_text"><t t-esc="el.display_name"/></span>
- <span t-if="!readonly" class="fa fa-times o_delete"/>
- </span>
+ <t t-set="colornames" t-value="['No color', 'Red', 'Orange', 'Yellow', 'Light blue', 'Dark purple', 'Salmon pink', 'Medium blue', 'Dark blue', 'Fushia', 'Green', 'Purple']"/>
+ <button t-attf-class="badge dropdown o_tag_color_#{color}" t-att-data-color="color" t-att-data-index="el_index" t-att-data-id="el.id" t-attf-title="Tag color: #{colornames[color]}">
+ <span class="o_badge_text"><span role="img" t-attf-aria-label="Tag color: #{colornames[color]}"/><t t-esc="el.display_name"/></span>
+ <a t-if="!readonly" class="fa fa-times o_delete" title="Delete" aria-label="Delete"/>
+ </button>
</t>
</t>
<t t-name="FieldMany2ManyTag.colorpicker">
<div class="o_colorpicker dropdown-menu tagcolor_dropdown_menu" role="menu">
<ul>
- <li t-foreach="11" t-as="color">
- <a href="#"
- t-att-data-id="tag_id"
- t-att-data-color="color+1"
- t-attf-class="o_tag_color_#{color+1}"/>
- </li>
+ <li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_1" data-color="1" title="Red" aria-label="Red"/></li>
+ <li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_2" data-color="2" title="Orange" aria-label="Orange"/></li>
+ <li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_3" data-color="3" title="Yellow" aria-label="Yellow"/></li>
+ <li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_4" data-color="4" title="Light blue" aria-label="Light blue"/></li>
+ <li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_5" data-color="5" title="Dark purple" aria-label="Dark purple"/></li>
+ <li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_6" data-color="6" title="Salmon pink" aria-label="Salmon pink"/></li>
+ <li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_7" data-color="7" title="Medium blue" aria-label="Medium blue"/></li>
+ <li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_8" data-color="8" title="Dark blue" aria-label="Dark blue"/></li>
+ <li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_9" data-color="9" title="Fushia" aria-label="Fushia"/></li>
+ <li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_10" data-color="10" title="Green" aria-label="Green"/></li>
+ <li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_11" data-color="11" title="Purple" aria-label="Purple"/></li>
<li> <!-- checkbox for tag color 0 -->
- <div class="o_hide_in_kanban"
+ <div role="menuitem" class="o_hide_in_kanban"
t-att-data-id="tag_id"
t-att-data-color="0">
<div class="o_checkbox">
@@ -857,7 +863,7 @@
</div>
</t>
<t t-name="ProgressBar">
- <div class="o_progressbar">
+ <div class="o_progressbar" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<div t-if="widget.title" class="o_progressbar_title"><t t-esc="widget.title"/></div><div class="o_progress">
<div class="o_progressbar_complete"/>
</div><div class="o_progressbar_value"/>
@@ -887,18 +893,26 @@
</t>
</t>
<t t-name="FieldStatus.content.button">
- <t t-set="disabled" t-value="!clickable || i.selected"/>
- <button type="button" t-att-data-value="i.id" t-att-disabled="disabled ? 'disabled' : undefined"
- t-attf-class="btn btn-sm o_arrow_button btn-#{i.selected ? 'primary' : 'default'}#{disabled ? ' disabled' : ''}">
- <t t-esc="i.display_name"/>
+ <t t-set="disabled" t-value="!clickable"/>
+ <button type="button" t-att-data-value="i.id" disabled="disabled" title="Current state" aria-pressed="true"
+ class="btn btn-sm o_arrow_button btn-primary disabled" t-if="i.selected" aria-current="step">
+ <t t-esc="i.display_name" role="img" t-attf-aria-label="#{i.display_name} is current state"/>
+ </button>
+ <button type="button" t-att-data-value="i.id" disabled="disabled" title="Not active state" aria-pressed="false"
+ class="btn btn-sm o_arrow_button btn-default disabled" t-if="!i.selected and disabled">
+ <t t-esc="i.display_name" role="img" t-attf-aria-label="#{i.display_name} is not current state"/>
+ </button>
+ <button type="button" t-att-data-value="i.id" title="Not active state, click to change it" aria-pressed="false"
+ class="btn btn-sm o_arrow_button btn-default" t-if="!i.selected and !disabled">
+ <t t-esc="i.display_name" role="img" t-attf-aria-label="Click to change current state to #{i.display_name}"/>
</button>
</t>
<t t-name="FieldBinaryImage">
- <div class="o_field_image">
+ <div class="o_field_image" aria-atomic="true">
<t t-if="widget.mode !== 'readonly'">
<div class="o_form_image_controls">
- <span class="fa fa-pencil fa-lg pull-left o_select_file_button" title="Edit"/>
- <span class="fa fa-trash-o fa-lg pull-right o_clear_file_button" title="Clear"/>
+ <button class="fa fa-pencil fa-lg pull-left o_select_file_button" title="Edit" aria-label="Edit"/>
+ <button class="fa fa-trash-o fa-lg pull-right o_clear_file_button" title="Clear" aria-label="Clear"/>
<span class="o_form_binary_progress">Uploading...</span>
<t t-call="HiddenInputFile">
@@ -911,6 +925,7 @@
</t>
<t t-name="FieldBinaryImage-img">
<img class="img img-responsive"
+ alt="Binary file"
t-att-src='url'
t-att-border="widget.readonly ? 0 : 1"
t-att-name="widget.name"/>
@@ -918,7 +933,7 @@
<t t-name="FieldBinaryFile">
<a t-if="widget.mode === 'readonly'" href="javascript:void(0)" class="o_form_uri"/>
- <div t-if="widget.mode !== 'readonly'" class="o_field_binary_file">
+ <div t-if="widget.mode !== 'readonly'" class="o_field_binary_file" aria-atomic="true">
<input type="text" class="o_input"
readonly="readonly"
t-att-name="widget.name"
@@ -926,8 +941,8 @@
t-att-autofocus="widget.attrs.autofocus"/>
<button type="button" class="btn btn-sm btn-primary o_select_file_button" title="Select">Upload your file</button>
- <button type="button" class="btn btn-sm btn-default fa fa-pencil o_select_file_button" title="Select"/>
- <button type="button" class="btn btn-sm btn-default fa fa-trash-o o_clear_file_button" title="Clear"/>
+ <button type="button" class="btn btn-sm btn-default fa fa-pencil o_select_file_button" title="Select" aria-label="Select"/>
+ <button type="button" class="btn btn-sm btn-default fa fa-trash-o o_clear_file_button" title="Clear" aria-label="Clear"/>
<span class="o_form_binary_progress">Uploading...</span>
<t t-call="HiddenInputFile">
@@ -938,7 +953,7 @@
</t>
<t t-name="HiddenInputFile">
- <div t-attf-class="o_hidden_input_file #{fileupload_class or ''}" t-att-style="fileupload_style">
+ <div t-attf-class="o_hidden_input_file #{fileupload_class or ''}" t-att-style="fileupload_style" aria-atomic="true">
<form class="o_form_binary_form" t-att-target="fileupload_id"
method="post" enctype="multipart/form-data" t-att-action="fileupload_action || '/web/binary/upload'">
<input type="hidden" name="csrf_token" t-att-value="csrf_token"/>
@@ -953,12 +968,12 @@
</t>
<t t-name="FieldPdfViewer">
- <div class="o_field_pdfviewer">
- <div t-if="widget.mode !== 'readonly'" class="o_form_pdf_controls mt8">
+ <div class="o_field_pdfviewer" aria-atomic="true">
+ <div t-if="widget.mode !== 'readonly'" class="o_form_pdf_controls mt8" role="toolbar" aria-label="PDF controls">
<span class="o_form_binary_progress">Uploading...</span>
<button type="button" class="btn btn-sm btn-primary o_select_file_button" title="Select">Upload your file</button>
- <button class="btn btn-sm btn-default fa fa-pencil o_select_file_button" title="Select" type="button"></button>
- <button class="btn btn-sm btn-default fa fa-trash-o o_clear_file_button" title="Clear" type="button"></button>
+ <button class="btn btn-sm btn-default fa fa-pencil o_select_file_button" title="Select" aria-label="Select" type="button"></button>
+ <button class="btn btn-sm btn-default fa fa-trash-o o_clear_file_button" title="Clear" aria-label="Clear" type="button"></button>
</div>
<iframe class="o_pdfview_iframe o_field_pdfviewer"/>
<t t-call="HiddenInputPDFFile">
@@ -976,10 +991,10 @@
</t>
</t>
-<div t-name="FieldBinaryFileUploader" t-attf-class="oe_fileupload #{widget.attrs.class ? widget.attrs.class :''}">
+<div t-name="FieldBinaryFileUploader" t-attf-class="oe_fileupload #{widget.attrs.class ? widget.attrs.class :''}" aria-atomic="true">
<div class="oe_placeholder_files"/>
<div t-if="widget.mode !== 'readonly'" class="oe_add">
- <button class="btn btn-default o_attach"><span class="fa fa-paperclip"/> <t t-esc="widget.string"/></button>
+ <button class="btn btn-default o_attach" title="Attach"><span class="fa fa-paperclip" aria-label="Attach"/> <t t-esc="widget.string"/></button>
<t t-call="HiddenInputFile">
<t t-set="fileupload_id" t-value="widget.fileupload_id"/>
<t t-set="fileupload_action" t-translation="off">/web/binary/upload_attachment</t>
@@ -990,7 +1005,7 @@
</t>
</div>
</div>
-<div t-name="FieldBinaryFileUploader.files" class="oe_attachments">
+<div t-name="FieldBinaryFileUploader.files" class="oe_attachments" aria-atomic="true">
<t t-if="widget.mode === 'readonly'">
<div t-foreach="widget.value.data" t-as="file">
<a target="_blank" t-att-href="widget.metadata[file.id].url" t-attf-title="#{(file.data.name || file.data.filename) + (file.data.date?' \n('+file.data.date+')':'' )}"><t t-raw="file.data.name || file.data.filename"/></a>
@@ -1000,19 +1015,20 @@
<div t-foreach="widget.value.data" t-as="file" class="oe_attachment">
<t t-if="!file.data.upload">
<div>
- <a href="#" class="fa fa-times pull-right oe_delete" title="Delete this file" t-att-data-id="file.data.id"/>
+ <a href="#" class="fa fa-times pull-right oe_delete" title="Delete this file" aria-label="Delete this file" t-att-data-id="file.data.id"/>
<t t-raw="file.data.name || file.data.filename"/>
</div>
<a class="o_image" target="_blank" t-att-href="widget.metadata[file.id] ? widget.metadata[file.id].url : false"
t-att-title="file.data.name"
+ t-att-aria-label="file.data.name"
t-att-data-mimetype="file.data.mimetype"
t-attf-data-src="/web/image/#{file.data.id}/100x80"/>
</t>
</div>
<div t-foreach="widget.uploadingFiles" t-as="file" class="oe_attachment">
<div>Uploading...</div>
- <a t-att-title="file.name" t-att-name="file.name" class="o_image">
- <i class="fa fa-spinner fa-spin fa-5x fa-fw"></i>
+ <a t-att-title="file.name" t-att-aria-label="file.name" t-att-name="file.name" class="o_image">
+ <i class="fa fa-spinner fa-spin fa-5x fa-fw" role="img" aria-label="Loading, please wait..." title="Loading, please wait..."></i>
</a>
<div><t t-esc="file.name"/></div>
</div>
@@ -1087,12 +1103,12 @@
-<div t-name="SearchView" class="o_searchview">
- <span class="o_searchview_more fa" title="Advanced Search..."/>
+<div t-name="SearchView" class="o_searchview" role="search" aria-autocomplete="list">
+ <span class="o_searchview_more fa" title="Advanced Search..." role="img" aria-label="Advanced Search..."/>
<div class="o_searchview_input_container"/>
</div>
<t t-name="SearchViewMobile">
- <div class="o_searchview">
+ <div class="o_searchview" role="search" aria-autocomplete="list">
<button class="o_enable_searchview btn btn-sm fa fa-search"/>
<div class="o_mobile_search o_hidden">
<div class="o_mobile_search_header">
@@ -1109,14 +1125,14 @@
</div>
</t>
-<input t-name="SearchView.InputView" type="text" class="o_searchview_input" accesskey="Q" placeholder="Search..."/>
-<div t-name="SearchView.FacetView" tabindex="0" class="o_searchview_facet">
+<input t-name="SearchView.InputView" type="text" class="o_searchview_input" accesskey="Q" placeholder="Search..." role="searchbox" aria-haspopup="true"/>
+<div t-name="SearchView.FacetView" tabindex="0" class="o_searchview_facet" role="img" aria-label="search">
<span t-if="widget.model.has('icon')" t-att-class="'fa ' + widget.model.get('icon') + ' o_searchview_facet_label'"/>
<span t-if="!widget.model.has('icon')" class="o_searchview_facet_label">
<t t-esc="widget.model.get('category')"/>
</span>
<div class="o_facet_values"/>
- <div class="fa fa-sm fa-remove o_facet_remove"/>
+ <div class="fa fa-sm fa-remove o_facet_remove" role="img" aria-label="Remove" title="Remove"/>
</div>
<span t-name="SearchView.FacetView.Value">
<t t-esc="widget.model.get('label') + (widget.intervalLabel ? (': ' + widget.intervalLabel) : '')"/>
@@ -1129,7 +1145,7 @@
<li t-foreach="widget.filters" t-as="filter" t-if="!filter.visible || filter.visible()"
t-att-title="filter.attrs.string ? filter.attrs.help : undefined"
t-att-data-index="filter_index">
- <a href="#"><t t-esc="filter.attrs.string or filter.attrs.help or filter.attrs.name or 'Ω'"/></a>
+ <a role="menuitem" href="#"><t t-esc="filter.attrs.string or filter.attrs.help or filter.attrs.name or 'Ω'"/></a>
</li>
</t>
<t t-name="SearchView.field">
@@ -1192,7 +1208,7 @@
</div>
</t>
<t t-name="SearchView.extended_search.proposition">
- <li class="o_filter_condition">
+ <li role="menuitem" class="o_filter_condition">
<span class="o_or_filter">or</span>
<span>
<select class="o_input o_searchview_extended_prop_field">
@@ -1203,7 +1219,7 @@
</option>
</t>
</select>
- <span class="o_searchview_extended_delete_prop fa fa-trash-o"/>
+ <span class="o_searchview_extended_delete_prop fa fa-trash-o" role="img" aria-label="Delete" title="Delete"/>
</span>
<select class="o_input o_searchview_extended_prop_op"/>
<span class="o_searchview_extended_prop_value"/>
@@ -1225,32 +1241,32 @@
</t>
<div t-name="SearchView.FavoriteMenu" class="btn-group o_dropdown">
- <button class="o_dropdown_toggler_btn btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false" tabindex="-1">
+ <button class="o_dropdown_toggler_btn btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false" tabindex="-1" aria-label="Favorites" title="Favorites">
<span class="fa fa-star"/> Favorites <span t-attf-class="#{widget.isMobile ? 'fa fa-chevron-right pull-right mt4' : 'caret'}"/>
</button>
<ul class="dropdown-menu o_favorites_menu" role="menu">
<li class="divider user_filter"/>
<li class="divider shared_filter"/>
<li class="o_save_search o_closed_menu">
- <a href="#">Save current search</a>
+ <a role="menuitem" href="#">Save current search</a>
</li>
- <li class="o_save_name">
+ <li role="menuitem" class="o_save_name">
<input type="text"></input>
</li>
- <li class="o_save_name">
+ <li role="menuitem" class="o_save_name">
<span><div class="o_checkbox"><input type="checkbox"/><span/></div> Use by default</span>
</li>
- <li class="o_save_name">
- <span><div class="o_checkbox"><input type="checkbox"/><span/></div> Share with all users </span><span class="fa fa-users"/>
+ <li role="menuitem" class="o_save_name">
+ <span><div class="o_checkbox"><input type="checkbox"/><span/></div> Share with all users </span><span class="fa fa-users" role="img" aria-label="Users" title="Users"/>
</li>
<li class="o_save_name">
- <button type="button" class="btn btn-primary btn-sm">Save</button>
+ <button role="menuitem" type="button" class="btn btn-primary btn-sm">Save</button>
</li>
</ul>
</div>
<div t-name="ExportDialog" class="o_export">
- <p>
+ <p role="status">
This wizard will export all data that matches the current search criteria to a CSV file.
You can export all data or only the fields that can be reimported after modification.
</p>
@@ -1293,16 +1309,16 @@
</div>
</div>
<p t-name="Export.DomainMessage">
- <strong t-if="!record.ids_to_export">Please pay attention that all records matching your search filter will be exported. Not only the selected ids.</strong>
- <strong t-if="record.ids_to_export">Please note that only the selected ids will be exported.</strong>
+ <strong t-if="!record.ids_to_export" role="alert">Please pay attention that all records matching your search filter will be exported. Not only the selected ids.</strong>
+ <strong t-if="record.ids_to_export" role="alert">Please note that only the selected ids will be exported.</strong>
</p>
<div t-name="Export.TreeItems"
t-foreach="fields" t-as="field"
t-att-data-id="field.id"
class="o_export_tree_item"
- tabindex="-1"> <!-- tabindex make the div focusable -->
+ tabindex="-1" role="treeitem"> <!-- tabindex make the div focusable -->
<span t-if="field.children &amp;&amp; (field.id).split('/').length != 3"
- class="o_expand_parent fa fa-plus"/>
+ class="o_expand_parent fa fa-plus" role="img" aria-label="Expand parents" title="Expand parents"/>
<span class="o_tree_column" t-att-title="debug and field.id or None"><t t-esc="field.string"/></span>
</div>
<t t-name="Export.SaveList">
@@ -1322,14 +1338,14 @@
<t t-name="Throbber">
<div>
<div class="oe_blockui_spin" style="height: 50px">
- <img src="/web/static/src/img/spin.png" style="animation: fa-spin 1s infinite steps(12);"/>
+ <img src="/web/static/src/img/spin.png" style="animation: fa-spin 1s infinite steps(12);" alt="Loading..."/>
</div>
<br />
<div class="oe_throbber_message" style="color:white"></div>
</div>
</t>
<t t-name="Spinner">
- <div class="o_spinner"><i class="fa fa-spinner fa-spin"/></div>
+ <div class="o_spinner"><i class="fa fa-spinner fa-spin" role="img" aria-label="Loading, please wait..." title="Loading, please wait..."/></div>
</t>
<t t-name="M2ODialog">
<div>
@@ -1338,7 +1354,7 @@
</div>
</t>
<t t-name="FieldMany2ManyCheckBoxes">
- <div>
+ <div aria-atomic="true">
<div t-foreach="widget.m2mValues" t-as="m2m_value">
<t t-set="id_for_label" t-value="'o_many2many_checkbox_' + _.uniqueId()"/>
<div class="o_checkbox">
@@ -1354,7 +1370,7 @@
<span class="o_stat_text"><t t-esc="text"/></span>
</t>
<t t-name="toggle_button">
- <button type="button" class="o_icon_button" t-att-title="widget.string" t-att-aria-label="widget.string">
+ <button type="button" class="o_icon_button" t-att-title="widget.string" t-att-aria-label="widget.string" aria-pressed="false">
<i class="fa fa-circle" t-att-title="widget.string"/>
</button>
</t>
@@ -1363,14 +1379,14 @@
<span class="o_pager_counter">
<span class="o_pager_value"></span> / <span class="o_pager_limit"></span>
</span>
- <span class="btn-group btn-group-sm">
+ <span class="btn-group btn-group-sm" aria-atomic="true">
<!-- accesskeys not wanted in X2Many widgets -->
<t t-if="widget.options.withAccessKey">
<t t-set="att_prev" t-value="{'accesskey': 'p'}" />
<t t-set="att_next" t-value="{'accesskey': 'n'}" />
</t>
- <button type="button" class="fa fa-chevron-left btn btn-icon o_pager_previous" t-att="att_prev" aria-label="Previous" tabindex="-1"/>
- <button type="button" class="fa fa-chevron-right btn btn-icon o_pager_next" t-att="att_next" aria-label="Next" tabindex="-1"/>
+ <button type="button" class="fa fa-chevron-left btn btn-icon o_pager_previous" t-att="att_prev" aria-label="Previous" title="Next" tabindex="-1"/>
+ <button type="button" class="fa fa-chevron-right btn btn-icon o_pager_next" t-att="att_next" aria-label="Next" title="Next" tabindex="-1"/>
</span>
</div>
@@ -1381,15 +1397,15 @@
</t>
<t t-name="notification-box">
- <div t-attf-class="o_notification_box mb0 alert alert-dismissible alert-{{type}}" role="alert">
+ <div t-attf-class="o_notification_box mb0 alert alert-dismissible alert-{{type}}" role="alertdialog">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
- <span aria-hidden="true" class="fa fa-times"></span>
+ <span class="fa fa-times"></span>
</button>
</div>
</t>
<t t-name="translation-alert">
- <div>
+ <div role="alertdialog">
Please update translations of :
<t t-foreach="fields" t-as="field">
<strong><a class="oe_field_translate" t-att-name="field.name" href="#"><t t-esc="field.string"/><t t-if="!field_last">, </t></a></strong>
@@ -1400,7 +1416,7 @@
<t t-name="UserMenu">
<li class="o_user_menu">
<a class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false" href="#">
- <img class="img-circle oe_topbar_avatar" t-att-src="_s + '/web/static/src/img/user_menu_avatar.png'"/>
+ <img class="img-circle oe_topbar_avatar" t-att-src="_s + '/web/static/src/img/user_menu_avatar.png'" alt="Avatar"/>
<span class="oe_topbar_name"/>
</a>
<ul class="dropdown-menu" role="menu">
@@ -1410,17 +1426,17 @@
</t>
<t t-name="UserMenu.Actions">
- <li><a href="#" data-menu="documentation">Documentation</a></li>
- <li><a href="#" data-menu="support">Support</a></li>
- <li class="divider"/>
- <li><a href="#" data-menu="settings">Preferences</a></li>
- <li><a href="#" data-menu="account">My Odoo.com account</a></li>
- <li><a href="#" data-menu="logout">Log out</a></li>
+ <li><a role="menuitem" href="#" data-menu="documentation">Documentation</a></li>
+ <li><a role="menuitem" href="#" data-menu="support">Support</a></li>
+ <li role="separator" class="divider"/>
+ <li><a role="menuitem" href="#" data-menu="settings">Preferences</a></li>
+ <li><a role="menuitem" href="#" data-menu="account">My Odoo.com account</a></li>
+ <li><a role="menuitem" href="#" data-menu="logout">Log out</a></li>
</t>
<t t-name="SwitchCompanyMenu">
<li class="o_switch_company_menu">
- <a class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false" href="#">
+ <a class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false" href="#" aria-label="Dropdown menu" title="Dropdown menu">
<span t-attf-class="#{widget.isMobile ? 'fa fa-building-o' : 'oe_topbar_name'}"/> <span class="caret"/>
</a>
<ul class="dropdown-menu" role="menu"/>
@@ -1428,35 +1444,35 @@
</t>
<t t-name="EnterpriseUpgrade">
- <div class="row">
+ <div class="row" role="status">
<div class="col-xs-6">
Get this feature and much more with Odoo Enterprise!
<ul class="list-unstyled">
- <li><i class="fa fa-check"></i> Access to all Enterprise Apps</li>
- <li><i class="fa fa-check"></i> New design</li>
- <li><i class="fa fa-check"></i> Mobile support</li>
- <li><i class="fa fa-check"></i> Upgrade to future versions</li>
- <li><i class="fa fa-check"></i> Bugfixes guarantee</li>
- <li><a href="http://www.odoo.com/editions" target="_blank"><i class="fa fa-plus"></i> And more</a></li>
+ <li><span class="fa fa-check"> Access to all Enterprise Apps</span></li>
+ <li><span class="fa fa-check"> New design</span></li>
+ <li><span class="fa fa-check"> Mobile support</span></li>
+ <li><span class="fa fa-check"> Upgrade to future versions</span></li>
+ <li><span class="fa fa-check"> Bugfixes guarantee</span></li>
+ <li><a href="http://www.odoo.com/editions" target="_blank"><span class="fa fa-plus"> And more</span></a></li>
</ul>
</div>
<div class="col-xs-6">
- <img class="img-responsive" t-att-src='_s + "/web/static/src/img/enterprise_upgrade.jpg"' draggable="false"/>
+ <img class="img-responsive" t-att-src='_s + "/web/static/src/img/enterprise_upgrade.jpg"' draggable="false" alt="Upgrade to enterprise"/>
</div>
</div>
</t>
<t t-name="BaseSetting.Tabs">
<t t-foreach="tabItems" t-as="tab">
- <div class="tab" t-attf-data-key="#{tab.key}">
+ <div class="tab" t-attf-data-key="#{tab.key}" role="tab">
<div class="icon hidden-xs" t-attf-style="background : url('#{imgurl}') no-repeat center;background-size:contain;"/> <span class="app_name"><t t-esc="tab.string"/></span>
</div>
</t>
</t>
<t t-name="BaseSetting.SearchHeader">
- <div class="settingSearchHeader o_hidden">
- <img class="icon" t-att-src="imgurl"></img>
+ <div class="settingSearchHeader o_hidden" role="search">
+ <img class="icon" t-att-src="imgurl" alt="Search"></img>
<span class="appName"><t t-esc="string"/></span>
</div>
</t>
diff --git a/addons/web/static/src/xml/dialog.xml b/addons/web/static/src/xml/dialog.xml
index 68c3ab3f2e3..eb1a67ee98d 100644
--- a/addons/web/static/src/xml/dialog.xml
+++ b/addons/web/static/src/xml/dialog.xml
@@ -3,15 +3,15 @@
<!-- These templates are accessible in backend and frontend -->
-<div t-name="Dialog" t-attf-class="modal#{technical ? ' o_technical_modal' : ''}" tabindex="-1" data-backdrop="static" role="dialog" aria-hidden="true" t-att-id="_.uniqueId('modal_')">
+<div role="dialog" t-name="Dialog" t-attf-class="modal#{technical ? ' o_technical_modal' : ''}" tabindex="-1" data-backdrop="static" t-att-id="_.uniqueId('modal_')">
<div class="modal-dialog">
<div class="modal-content">
- <div class="modal-header">
- <button type="button" class="close" data-dismiss="modal" aria-hidden="true" tabindex="-1">×</button>
+ <header class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close" tabindex="-1">×</button>
<h4 class="modal-title"><t t-raw="title"/><span class="o_subtitle text-muted small"><t t-esc="subtitle"/></span></h4>
- </div>
- <div class="modal-body"/>
- <div class="modal-footer"/>
+ </header>
+ <main class="modal-body"/>
+ <footer class="modal-footer"/>
</div>
</div>
</div>
diff --git a/addons/web/static/src/xml/kanban.xml b/addons/web/static/src/xml/kanban.xml
index 67514072c95..4b62f52cda8 100644
--- a/addons/web/static/src/xml/kanban.xml
+++ b/addons/web/static/src/xml/kanban.xml
@@ -9,18 +9,19 @@
</t>
<t t-name="KanbanView.Group">
- <div t-attf-class="o_kanban_group#{widget.data_records.length == 0 ? ' o_kanban_no_records' : ''}" t-att-data-id="widget.id or widget.db_id">
+ <div t-attf-class="o_kanban_group#{widget.data_records.length == 0 ? ' o_kanban_no_records' : ''}" t-att-data-id="widget.id or widget.db_id"
+ role="feed" t-attf-aria-labelledby="kanban_header_#{widget.id or widget.db_id}">
<div class="o_kanban_header">
<div class="o_kanban_header_title" t-att-title="widget.data_records.length + ' records'" data-delay="500">
- <span class="o_column_title"><t t-esc="widget.title"/></span>
- <span class="o_column_unfold"><i class="fa fa-arrows-h"/></span>
+ <span t-attf-id="#kanban_header_#{widget.id or widget.db_id}" class="o_column_title"><t t-esc="widget.title"/></span>
+ <span class="o_column_unfold"><i class="fa fa-arrows-h" role="img" aria-label="Unfold" title="Unfold"/></span>
<span class="o_kanban_config dropdown">
- <a class="dropdown-toggle" data-toggle="dropdown" href="#"><i class="fa fa-gear"/></a>
+ <a class="dropdown-toggle" data-toggle="dropdown" href="#"><i class="fa fa-gear" role="img" aria-label="Settings" title="Settings"/></a>
<ul class="dropdown-menu" role="menu">
- <li><a class="o_kanban_toggle_fold" href="#">Fold</a></li>
+ <li><a role="menuitem" class="o_kanban_toggle_fold" href="#">Fold</a></li>
<t t-if="widget.grouped_by_m2o">
- <li t-if="widget.editable and widget.id"><a class="o_column_edit" href="#">Edit Stage</a></li>
- <li t-if="widget.deletable and widget.id"><a class="o_column_delete" href="#">Delete</a></li>
+ <li t-if="widget.editable and widget.id"><a role="menuitem" class="o_column_edit" href="#">Edit Stage</a></li>
+ <li t-if="widget.deletable and widget.id"><a role="menuitem" class="o_column_delete" href="#">Delete</a></li>
</t>
<t t-if="widget.has_active_field">
<li><a class="o_column_archive_records" href="#">Archive All</a></li>
@@ -28,7 +29,7 @@
</t>
</ul>
</span>
- <span t-if="widget.quick_create" class="o_kanban_quick_add"><i class="fa fa-plus"/></span>
+ <span t-if="widget.quick_create" class="o_kanban_quick_add"><i class="fa fa-plus" role="img" aria-label="Quick add" title="Quick add"/></span>
</div>
</div>
<div t-if="widget.remaining > 0" class="o_kanban_load_more">
@@ -44,7 +45,7 @@
<t t-name="KanbanView.ColumnQuickCreate">
<div class="o_column_quick_create">
<div class="o_quick_create_folded">
- <span class="o_kanban_add_column"><i class="fa fa-plus"/></span>
+ <span class="o_kanban_add_column"><i class="fa fa-plus" role="img" aria-label="Add column" title="Add column"/></span>
<span class="o_kanban_title">Add a Column</span>
</div>
<div class="o_quick_create_unfolded">
@@ -56,7 +57,7 @@
</span>
</div>
<t t-if="widget.examples">
- <span class="btn-link o_kanban_examples">Examples</span>
+ <a href="#" role="button" class="btn-link o_kanban_examples">Examples</a>
</t>
<span class="o_discard_msg text-muted pull-right">Esc to discard</span>
</div>
@@ -76,9 +77,18 @@
</t>
<t t-name="KanbanColorPicker">
- <li t-foreach="12" t-as="color">
- <a href="#" t-att-data-color="color_index" t-attf-class="oe_kanban_color_#{color}"/>
- </li>
+ <li><a role="menuitem" href="#" data-color="0" class="oe_kanban_color_0" title="No color" aria-label="No color"/></li>
+ <li><a role="menuitem" href="#" data-color="1" class="oe_kanban_color_1" title="Red" aria-label="Red"/></li>
+ <li><a role="menuitem" href="#" data-color="2" class="oe_kanban_color_2" title="Orange" aria-label="Orange"/></li>
+ <li><a role="menuitem" href="#" data-color="3" class="oe_kanban_color_3" title="Yellow" aria-label="Yellow"/></li>
+ <li><a role="menuitem" href="#" data-color="4" class="oe_kanban_color_4" title="Light blue" aria-label="Light blue"/></li>
+ <li><a role="menuitem" href="#" data-color="5" class="oe_kanban_color_5" title="Dark purple" aria-label="Dark purple"/></li>
+ <li><a role="menuitem" href="#" data-color="6" class="oe_kanban_color_6" title="Salmon pink" aria-label="Salmon pink"/></li>
+ <li><a role="menuitem" href="#" data-color="7" class="oe_kanban_color_7" title="Medium blue" aria-label="Medium blue"/></li>
+ <li><a role="menuitem" href="#" data-color="8" class="oe_kanban_color_8" title="Dark blue" aria-label="Dark blue"/></li>
+ <li><a role="menuitem" href="#" data-color="9" class="oe_kanban_color_9" title="Fushia" aria-label="Fushia"/></li>
+ <li><a role="menuitem" href="#" data-color="10" class="oe_kanban_color_10" title="Green" aria-label="Green"/></li>
+ <li><a role="menuitem" href="#" data-color="11" class="oe_kanban_color_11" title="Purple" aria-label="Purple"/></li>
</t>
<t t-name="GraphCustomTooltip">
@@ -103,6 +113,7 @@
<div t-attf-class="progress-bar transition-off bg-#{color_value}-full#{count ? ' o_bar_has_records' : ''}#{widget.activeFilter ? ' active progress-bar-striped' : ''}"
t-att-data-filter="color"
t-attf-data-original-title="#{count} #{color}"
+ aria-valuemin="0" aria-valuemax="100" t-att-aria-valuenow="count*100" role="progressbar"
t-attf-style="width: #{count ? (count * 100 / widget.groupCount) : 0}%;"/>
</t>
</div>
@@ -116,7 +127,7 @@
<ul class="nav nav-pills nav-stacked">
<t t-foreach="examples" t-as="example">
<li t-att-class="example_index == 0 and 'active' or None">
- <a data-toggle="tab" t-attf-href="#example_#{example_index}"> <t t-esc="example.name"/> </a>
+ <a t-attf-aria-controls="example_#{example_index}" data-toggle="tab" role="tab" t-attf-href="#example_#{example_index}"> <t t-esc="example.name"/> </a>
</li>
</t>
</ul>
@@ -124,7 +135,7 @@
<div class="o_kanban_examples_dialog_content">
<div class="tab-content">
<t t-foreach="examples" t-as="example">
- <div t-attf-id="example_#{example_index}" t-attf-class="tab-pane fade #{example_index == 0 and 'active in' or ''}">
+ <div role="tabpanel" t-attf-id="example_#{example_index}" t-attf-class="tab-pane fade #{example_index == 0 and 'active in' or ''}">
<div t-if="example.description" class="o_kanban_examples_description">
<t t-raw="example.description"/> <!-- description is supposed to be properly escaped -->
</div>
@@ -147,7 +158,7 @@
<div class="o_kanban_examples_ghost">
<div class="o_ghost_content"/>
<div class="o_ghost_content o_ghost_tag"/>
- <img class="pull-right o_ghost_avatar" src="/base/static/img/avatar.png"/>
+ <img class="pull-right o_ghost_avatar" src="/base/static/img/avatar.png" alt="Avatar"/>
</div>
</t>
diff --git a/addons/web/static/src/xml/web_calendar.xml b/addons/web/static/src/xml/web_calendar.xml
index 7f65ed93a08..41f772cd52f 100644
--- a/addons/web/static/src/xml/web_calendar.xml
+++ b/addons/web/static/src/xml/web_calendar.xml
@@ -1,7 +1,7 @@
<template>
<div t-name="CalendarView" class="o_calendar_container">
<div class="o_calendar_view">
- <div class="o_calendar_buttons"/>
+ <div class="o_calendar_buttons" role="toolbar" aria-label="Calendar toolbar"/>
<div class="o_calendar_widget" />
</div>
<div class="o_calendar_sidebar_container hidden-xs">
@@ -44,17 +44,17 @@
<input type="checkbox" name="selection" t-att-checked="(filter.active ? true : undefined)"/><span/>
</div>
<t t-if="filter.value == 'all'">
- <span><i class="fa fa-users fa-fw o_cal_avatar"/></span>
+ <span><i class="fa fa-users fa-fw o_cal_avatar" role="img" aria-label="Avatar" title="Avatar"/></span>
</t>
<t t-if="widget.avatar_field &amp;&amp; (filter.value != 'all')">
- <img t-attf-src="/web/image/#{widget.avatar_model}/#{filter.value}/#{widget.avatar_field}" class="o_cal_avatar"/>
+ <img t-attf-src="/web/image/#{widget.avatar_model}/#{filter.value}/#{widget.avatar_field}" class="o_cal_avatar" alt="Avatar"/>
</t>
<t t-set="color" t-value="widget.getColor(filter.color_index)"/>
<span t-if="typeof color === 'number'" t-attf-class="color_filter o_underline_color_#{widget.getColor(filter.color_index)}"><t t-esc="filter.label"/></span>
<span t-elif="color" t-attf-style="border-bottom: 4px solid #{color};"><t t-esc="filter.label"/></span>
<span t-else=""><t t-esc="filter.label"/></span>
<t t-if="filter.id">
- <span class="o_remove fa fa-times" title="Remove this favorite from the list"/>
+ <span class="o_remove fa fa-times" title="Remove this favorite from the list" role="img" aria-label="Remove this favorite from the list"/>
</t>
</div>
</div>
@@ -64,9 +64,9 @@
<t t-name="CalendarView.buttons">
<div class="o_calendar_buttons">
<t t-if="!isMobile">
- <button class="o_calendar_button_prev btn btn-sm btn-primary"><span class="fa fa-arrow-left"/></button>
+ <button class="o_calendar_button_prev btn btn-sm btn-primary" aria-label="Previous" title="Previous"><span class="fa fa-arrow-left"/></button>
<button class="o_calendar_button_today btn btn-sm btn-primary">Today</button>
- <button class="o_calendar_button_next btn btn-sm btn-primary"><span class="fa fa-arrow-right"/></button>
+ <button class="o_calendar_button_next btn btn-sm btn-primary" aria-label="Next" title="Next"><span class="fa fa-arrow-right"/></button>
</t>
<div class="btn-group btn-group-sm">
<button type="button" class="o_calendar_button_day btn btn-sm btn-default">Day</button>
@@ -78,15 +78,15 @@
<t t-name="CalendarView.TodayButtonMobile">
<span class="fa-stack o_calendar_button_today">
- <i class="fa fa-calendar-o fa-stack-2x"></i>
+ <i class="fa fa-calendar-o fa-stack-2x" role="img" aria-label="Today" title="Today"></i>
<strong class="o_calendar_text fa-stack-1x"><t t-esc="moment().date()"/></strong>
</span>
</t>
- <div t-name="CalendarView.quick_create" class="o_calendar_quick_create">
+ <main t-name="CalendarView.quick_create" class="o_calendar_quick_create">
<div class="form-group">
<label for='name' class='control-label'>Summary:</label>
<input type='text' name='name' class="o_input" t-att-value="widget.dataTemplate.name or None"/>
</div>
- </div>
+ </main>
</template>
diff --git a/addons/web/static/tests/chrome/action_manager_tests.js b/addons/web/static/tests/chrome/action_manager_tests.js
index b0a90173363..07febe83234 100644
--- a/addons/web/static/tests/chrome/action_manager_tests.js
+++ b/addons/web/static/tests/chrome/action_manager_tests.js
@@ -2624,12 +2624,12 @@ QUnit.module('ActionManager', {
// go back to kanban view
$('.o_control_panel .breadcrumb li:first a').click();
- assert.strictEqual($('.modal .modal-body').text(),
+ assert.strictEqual($('main.modal-body:first').text(),
"The record has been modified, your changes will be discarded. Do you want to proceed?",
"should display a modal dialog to confirm discard action");
// cancel
- $('.modal .modal-footer button.btn-default').click();
+ $('footer.modal-footer button.btn-default').click();
assert.strictEqual(actionManager.$('.o_form_view').length, 1,
"should still be in form view");
@@ -2638,7 +2638,7 @@ QUnit.module('ActionManager', {
$('.o_control_panel .breadcrumb li:first a').click();
// confirm discard
- $('.modal .modal-footer button.btn-primary').click();
+ $('footer.modal-footer button.btn-primary').click();
assert.strictEqual(actionManager.$('.o_form_view').length, 0,
"should no longer be in form view");
@@ -3001,8 +3001,8 @@ QUnit.module('ActionManager', {
assert.strictEqual($('.o_technical_modal .o_form_view').length, 1,
"should have rendered a form view in a modal");
- assert.ok($('.o_technical_modal .modal-body').hasClass('o_act_window'),
- "modal-body element should have classname 'o_act_window'");
+ assert.ok($('.o_technical_modal main').hasClass('o_act_window'),
+ "dialog main element should have classname 'o_act_window'");
assert.ok($('.o_technical_modal .o_form_view').hasClass('o_form_editable'),
"form view should be in edit mode");
@@ -3032,11 +3032,11 @@ QUnit.module('ActionManager', {
});
actionManager.doAction(5);
- assert.strictEqual($('.o_technical_modal .modal-body button.infooter').length, 0,
+ assert.strictEqual($('.o_technical_modal main button.infooter').length, 0,
"the button should not be in the body");
- assert.strictEqual($('.o_technical_modal .modal-footer button.infooter').length, 1,
+ assert.strictEqual($('.o_technical_modal footer button.infooter').length, 1,
"the button should be in the footer");
- assert.strictEqual($('.o_technical_modal .modal-footer button').length, 1,
+ assert.strictEqual($('.o_technical_modal footer button').length, 1,
"the modal footer should only contain one button");
actionManager.destroy();
@@ -3066,7 +3066,7 @@ QUnit.module('ActionManager', {
type: 'ir.actions.client',
});
- assert.strictEqual($('.modal .o_test').length, 1,
+ assert.strictEqual($('[role="dialog"] .o_test').length, 1,
"should have rendered the client action in a dialog");
assert.verifySteps(['on_attach_callback']);
diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js
index 9cda103eedd..e285626f763 100644
--- a/addons/web/static/tests/fields/basic_fields_tests.js
+++ b/addons/web/static/tests/fields/basic_fields_tests.js
@@ -1842,7 +1842,7 @@ QUnit.module('basic_fields', {
// Actual flow: click on an element of the m2m to get its form view
form.$('tbody td:contains(gold)').click();
- assert.strictEqual($('.modal-dialog').length, 1,
+ assert.strictEqual($('[role="dialog"]').length, 1,
'The modal should have opened');
assert.verifySteps([
"The view's image should have been fetched",
@@ -2570,7 +2570,7 @@ QUnit.module('basic_fields', {
// switch to form view
form.$('.o_field_widget[name=p] .o_data_row').click();
- assert.strictEqual($('.modal .o_field_date[name=datetime]').text(), '02/07/2017',
+ assert.strictEqual($('[role="dialog"] .o_field_date[name=datetime]').text(), '02/07/2017',
'the datetime (date widget) should be correctly displayed in form view');
form.destroy();
@@ -2614,7 +2614,7 @@ QUnit.module('basic_fields', {
// switch to form view
form.$('.o_field_widget[name=p] .o_data_row').click();
- assert.strictEqual($('.modal .o_field_date[name=datetime]').text(), '02/08/2017',
+ assert.strictEqual($('[role="dialog"] .o_field_date[name=datetime]').text(), '02/08/2017',
'the datetime (date widget) should be correctly displayed in form view');
form.destroy();
@@ -4602,13 +4602,13 @@ QUnit.module('basic_fields', {
// open the selection
form.$(".o_domain_show_selection_button").click();
- assert.strictEqual($('.modal .o_list_view .o_data_row').length, 2,
+ assert.strictEqual($('[role="dialog"] .o_list_view .o_data_row').length, 2,
"should have open a list view with 2 records in a dialog");
// click on a record -> should not open the record
// we don't actually check that it doesn't open the record because even
// if it tries to, it will crash as we don't define an arch in this test
- $('.modal .o_list_view .o_data_row:first .o_data_cell').click();
+ $('[role="dialog"] .o_list_view .o_data_row:first .o_data_cell').click();
form.destroy();
});
diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js
index 9b42c518891..95b12e9137d 100644
--- a/addons/web/static/tests/fields/relational_fields_tests.js
+++ b/addons/web/static/tests/fields/relational_fields_tests.js
@@ -206,7 +206,7 @@ QUnit.module('relational_fields', {
form.$('.o_external_button').click(); // click on the external button (should do an RPC)
- assert.strictEqual($('.modal .modal-title').text().trim(), 'Open: custom label',
+ assert.strictEqual($('[role="dialog"] .modal-title').text().trim(), 'Open: custom label',
"dialog title should display the custom string label");
// TODO: test that we can edit the record in the dialog, and that the value is correctly
@@ -246,7 +246,7 @@ QUnit.module('relational_fields', {
// click on the external button (should do an RPC)
form.$('.o_external_button').click();
// save and close modal
- $('.modal .modal-footer .btn-primary:first').click();
+ $('footer.modal-footer .btn-primary:first').click();
// save form
form.$buttons.find('.o_form_button_save').click();
// click next on pager
@@ -315,10 +315,10 @@ QUnit.module('relational_fields', {
// click on the external button (should do an RPC)
form.$('.o_external_button').click();
- $('.modal input[name="foo"]').val('brandon').trigger('input');
+ $('[role="dialog"] input[name="foo"]').val('brandon').trigger('input');
// save and close modal
- $('.modal .modal-footer .btn-primary:first').click();
+ $('footer.modal-footer .btn-primary:first').click();
// save form
form.$buttons.find('.o_form_button_save').click();
// click next on pager
@@ -372,7 +372,7 @@ QUnit.module('relational_fields', {
assert.strictEqual(form.$('button.o_external_button:visible').length, 1,
"should still have an open record button");
form.$('input.o_input').trigger('focusout');
- assert.strictEqual($('.modal button:contains(Create and edit)').length, 0,
+ assert.strictEqual($('[role="dialog"] button:contains(Create and edit)').length, 0,
"there should not be a quick create modal");
form.destroy();
@@ -471,10 +471,10 @@ QUnit.module('relational_fields', {
// open the many2one in form view and change something
form.$buttons.find('.o_form_button_edit').click();
form.$('.o_external_button').click();
- $('.modal-body input[name="other_field"]').val('wood').trigger('input');
+ $('main.modal-body input[name="other_field"]').val('wood').trigger('input');
// save the modal and make sure an onchange is triggered
- $('.modal .modal-footer .btn-primary').first().click();
+ $('footer.modal-footer .btn-primary').first().click();
assert.verifySteps(['read', 'get_formview_id', 'load_views', 'read', 'write', 'onchange', 'read']);
// save the main record, and check that no extra rpcs are done (record
@@ -572,20 +572,20 @@ QUnit.module('relational_fields', {
form.$('.o_field_many2one input').click();
// click on 'Search More' (mouseenter required by ui-autocomplete)
$dropdown.find('.o_m2o_dropdown_option:contains(Search)').mouseenter().click();
- assert.ok($('.modal .o_list_view').length, "should have opened a list view in a modal");
- assert.ok(!$('.modal .o_list_view .o_list_record_selector').length,
+ assert.ok($('[role="dialog"] .o_list_view').length, "should have opened a list view in a modal");
+ assert.ok(!$('[role="dialog"] .o_list_view .o_list_record_selector').length,
"there should be no record selector in the list view");
- assert.ok(!$('.modal .modal-footer .o_select_button').length,
+ assert.ok(!$('footer.modal-footer .o_select_button').length,
"there should be no 'Select' button in the footer");
- assert.ok($('.modal tbody tr').length > 10, "list should contain more than 10 records");
+ assert.ok($('[role="dialog"] tbody tr').length > 10, "list should contain more than 10 records");
// filter the list using the searchview
- $('.modal .o_searchview_input').trigger({type: 'keypress', which: 80}); // P
- $('.modal .o_searchview_input').trigger({type: 'keydown', which: 13}); // enter
- assert.strictEqual($('.modal tbody tr').length, 10,
+ $('[role="dialog"] .o_searchview_input').trigger({type: 'keypress', which: 80}); // P
+ $('[role="dialog"] .o_searchview_input').trigger({type: 'keydown', which: 13}); // enter
+ assert.strictEqual($('[role="dialog"] tbody tr').length, 10,
"list should be restricted to records containing a P (10 records)");
// choose a record
- $('.modal tbody tr:contains(Partner 20)').click(); // choose record 'Partner 20'
- assert.ok(!$('.modal').length, "should have closed the modal");
+ $('[role="dialog"] tbody tr:contains(Partner 20)').click(); // choose record 'Partner 20'
+ assert.ok(!$('[role="dialog"]').length, "should have closed the modal");
assert.ok(!$dropdown.is(':visible'), 'should have closed the dropdown');
assert.strictEqual(form.$('.o_field_many2one input').val(), 'Partner 20',
'value of the m2o should have been correctly updated');
@@ -1690,7 +1690,7 @@ QUnit.module('relational_fields', {
form.$('.o_data_cell').click();
form.$('.o_external_button').click();
- var $modal = $('.modal-dialog');
+ var $modal = $('[role="dialog"]');
assert.equal($modal.length, 1,
'There should be 1 modal opened');
@@ -1702,7 +1702,7 @@ QUnit.module('relational_fields', {
{position: 'top'});
// Saving the modal and then the original model
- $modal.find('.modal-footer .btn-primary').click();
+ $modal.find('footer.modal-footer .btn-primary').click();
form.$buttons.find('.o_form_button_save').click();
assert.verifySteps(['onchange sequence', 'partner_type write']);
@@ -1900,7 +1900,7 @@ QUnit.module('relational_fields', {
form.$('.o_field_many2one input').focus();
form.$('.o_field_many2one input').val('new partner').trigger('keyup').trigger('focusout');
- $('.modal .modal-footer .btn-primary').first().click();
+ $('footer.modal-footer .btn-primary').first().click();
form.destroy();
});
@@ -1928,14 +1928,14 @@ QUnit.module('relational_fields', {
// cancel the many2one creation with Cancel button
form.$('.o_field_many2one input').focus().val('new product').trigger('keyup').trigger('blur');
- assert.strictEqual($('.modal').length, 1, "there should be one opened modal");
- $('.modal .modal-footer .btn:contains(Cancel)').click();
+ assert.strictEqual($('[role="dialog"]').length, 1, "there should be one opened modal");
+ $('footer.modal-footer .btn:contains(Cancel)').click();
assert.strictEqual(form.$('.o_field_many2one input').val(), "",
'the many2one should not set a value as its creation has been cancelled (with Cancel button)');
// cancel the many2one creation with Close button
form.$('.o_field_many2one input').focus().val('new product').trigger('keyup').trigger('blur');
- $('.modal .modal-header button').click();
+ $('header.modal-header button').click();
assert.strictEqual(form.$('.o_field_many2one input').val(), "",
'the many2one should not set a value as its creation has been cancelled (with Close button)');
@@ -1944,16 +1944,16 @@ QUnit.module('relational_fields', {
form.$('.o_field_many2one input').autocomplete('widget').find('a').first().click();
assert.strictEqual(form.$('input').val(), "xphone", "should have selected xphone");
form.$('.o_field_many2one input').focus().val('new product').trigger('keyup').trigger('blur');
- assert.strictEqual($('.modal').length, 1, "there should be one opened modal");
- $('.modal .modal-footer .btn:contains(Cancel)').click();
+ assert.strictEqual($('[role="dialog"]').length, 1, "there should be one opened modal");
+ $('footer.modal-footer .btn:contains(Cancel)').click();
assert.strictEqual(form.$('.o_field_many2one input').val(), "xphone",
'should have restored the many2one with its previous selected value (xphone)');
// confirm the many2one creation
form.$('.o_field_many2one input').focus();
form.$('.o_field_many2one input').val('new partner').trigger('keyup').trigger('blur');
- $('.modal .modal-footer .btn-primary').click();
- assert.strictEqual($('.modal .o_form_view').length, 1,
+ $('footer.modal-footer .btn-primary').click();
+ assert.strictEqual($('[role="dialog"] .o_form_view').length, 1,
'a new modal should be opened and contain a form view');
form.destroy();
@@ -1976,7 +1976,7 @@ QUnit.module('relational_fields', {
form.$('.o_field_many2one input').focus();
form.$('.o_field_many2one input').val('new partner').trigger('keyup').trigger('focusout');
- assert.strictEqual($('.modal').length, 0, "should not display the create modal");
+ assert.strictEqual($('[role="dialog"]').length, 0, "should not display the create modal");
form.destroy();
});
@@ -2019,14 +2019,14 @@ QUnit.module('relational_fields', {
"there should be an external button displayed");
form.$('.o_field_many2one .o_external_button').click();
- assert.strictEqual($('.modal .o_form_view.o_form_readonly').length, 1,
+ assert.strictEqual($('[role="dialog"] .o_form_view.o_form_readonly').length, 1,
"there should be a readonly form view opened");
- $('.modal .o_form_button_cancel').click();
+ $('[role="dialog"] .o_form_button_cancel').click();
form.$('.o_field_many2one input').val('new product').trigger('keyup').trigger('focusout');
- assert.strictEqual($('.modal').length, 0, "should not display the create modal");
+ assert.strictEqual($('[role="dialog"]').length, 0, "should not display the create modal");
form.destroy();
});
@@ -2143,10 +2143,10 @@ QUnit.module('relational_fields', {
}));
concurrency.delay(0).then(function() {
$input.blur();
- assert.strictEqual($('.modal').length, 1,
+ assert.strictEqual($('[role="dialog"]').length, 1,
"should have one modal in body");
// Check that discarding clears $input
- $('.modal .o_form_button_cancel').click();
+ $('[role="dialog"] .o_form_button_cancel').click();
assert.strictEqual($input.val(), '',
"the field should be empty");
form.destroy();
@@ -2202,7 +2202,7 @@ QUnit.module('relational_fields', {
which: $.ui.keyCode.ENTER,
keyCode: $.ui.keyCode.ENTER,
}));
- assert.strictEqual($('div.modal').length, 0, "should not have any modal in DOM");
+ assert.strictEqual($('[role="dialog"]').length, 0, "should not have any modal in DOM");
assert.verifySteps(['name_search', 'onchange', 'write', 'read']);
list.destroy();
relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY = M2O_DELAY;
@@ -2258,7 +2258,7 @@ QUnit.module('relational_fields', {
keyCode: $.ui.keyCode.ENTER,
}));
def.resolve();
- assert.strictEqual($('div.modal').length, 0, "should not have any modal in DOM");
+ assert.strictEqual($('[role="dialog"]').length, 0, "should not have any modal in DOM");
assert.verifySteps(['name_search', 'onchange', 'write', 'read']);
list.destroy();
relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY = M2O_DELAY;
@@ -2409,15 +2409,15 @@ QUnit.module('relational_fields', {
form.$buttons.find('.o_form_button_edit').click();
form.$('.o_data_row td:contains(first record)').click();
form.$('.o_external_button').click();
- assert.strictEqual($('.modal').length, 1,
+ assert.strictEqual($('[role="dialog"]').length, 1,
"should have one modal in body");
// Changing the 'trululu' value
- $('.modal input[name="display_name"]').val('test').trigger('input');
- $('.modal button.btn-primary').click();
+ $('[role="dialog"] input[name="display_name"]').val('test').trigger('input');
+ $('[role="dialog"] button.btn-primary').click();
// Test whether the value has changed
- assert.strictEqual($('.modal').length, 0,
+ assert.strictEqual($('[role="dialog"]').length, 0,
"the modal should be closed");
assert.equal(form.$('.o_data_cell:contains(test)').text(), 'test',
"the partner name should have been updated to 'test'");
@@ -2504,7 +2504,7 @@ QUnit.module('relational_fields', {
form.$buttons.find('.btn.btn-primary.btn-sm.o_form_button_edit').click();
form.$('.o_field_x2many_list_row_add a').click();
- var $modal = $('.modal-dialog.modal-lg');
+ var $modal = $('.modal-lg');
assert.equal($modal.length, 1,
'There should be one modal');
@@ -2532,7 +2532,7 @@ QUnit.module('relational_fields', {
// Third round: re-add 1 records
form.$('.o_field_x2many_list_row_add a').click();
- $modal = $('.modal-dialog.modal-lg');
+ $modal = $('.modal-lg');
assert.equal($modal.length, 1,
'There should be one modal');
@@ -3370,7 +3370,7 @@ QUnit.module('relational_fields', {
assert.equal(form.$('.o_field_one2many td[class="o_data_cell"]').text(), "blurp#21#22#23#24#25#26#27#28#29",
"should display the records in order with the changes");
- $('.modal .modal-footer button:first').click();
+ $('footer.modal-footer button:first').click();
assert.equal(form.$('.o_field_one2many td[class="o_data_cell"]').text(), "#20#21#22#23#24#25#26#27#28#29",
"should cancel changes and display the records in order");
@@ -3402,7 +3402,7 @@ QUnit.module('relational_fields', {
assert.equal(form.$('.o_field_one2many td[class="o_data_cell"]').text(), "#20#39#40#41#42#43#44#45#46#47",
"should display the records in order after resequence");
- $('.modal .modal-footer button:first').click();
+ $('footer.modal-footer button:first').click();
assert.equal(form.$('.o_field_one2many td[class="o_data_cell"]').text(), "#20#21#22#23#24#25#26#27#28#29",
"should cancel changes and display the records in order");
@@ -3750,62 +3750,62 @@ QUnit.module('relational_fields', {
// in its relation
form.$('.o_data_row').click();
- assert.strictEqual($('.modal').length, 1, "should have opened a dialog");
- assert.strictEqual($('.modal .o_data_row').length, 0,
+ assert.strictEqual($('[role="dialog"]').length, 1, "should have opened a dialog");
+ assert.strictEqual($('[role="dialog"] .o_data_row').length, 0,
"there should be no record in the one2many in the dialog");
// add a many2many subrecord
- $('.modal .o_field_x2many_list_row_add a').click();
+ $('[role="dialog"] .o_field_x2many_list_row_add a').click();
- assert.strictEqual($('.modal').length, 2,
+ assert.strictEqual($('[role="dialog"]').length, 2,
"should have opened a second dialog");
// select a many2many subrecord
- $('.modal:nth(1) .o_list_view .o_data_cell:first').click();
+ $('[role="dialog"]:nth(1) .o_list_view .o_data_cell:first').click();
- assert.strictEqual($('.modal').length, 1,
+ assert.strictEqual($('[role="dialog"]').length, 1,
"second dialog should be closed");
- assert.strictEqual($('.modal .o_data_row').length, 1,
+ assert.strictEqual($('[role="dialog"] .o_data_row').length, 1,
"there should be one record in the one2many in the dialog");
- assert.notOk($('.modal .o_x2m_control_panel .o_cp_pager div').is(':visible'),
+ assert.notOk($('[role="dialog"] .o_x2m_control_panel .o_cp_pager div').is(':visible'),
'm2m pager should be hidden');
// click on 'Save & Close'
- $('.modal .modal-footer .btn-primary:first').click();
+ $('footer.modal-footer .btn-primary:first').click();
- assert.strictEqual($('.modal').length, 0, "dialog should be closed");
+ assert.strictEqual($('[role="dialog"]').length, 0, "dialog should be closed");
// reopen o2m record, and another m2m subrecord in its relation, but
// discard the changes
form.$('.o_data_row').click();
- assert.strictEqual($('.modal').length, 1, "should have opened a dialog");
- assert.strictEqual($('.modal .o_data_row').length, 1,
+ assert.strictEqual($('[role="dialog"]').length, 1, "should have opened a dialog");
+ assert.strictEqual($('[role="dialog"] .o_data_row').length, 1,
"there should be one record in the one2many in the dialog");
// add another m2m subrecord
- $('.modal .o_field_x2many_list_row_add a').click();
+ $('[role="dialog"] .o_field_x2many_list_row_add a').click();
- assert.strictEqual($('.modal').length, 2,
+ assert.strictEqual($('[role="dialog"]').length, 2,
"should have opened a second dialog");
- $('.modal:nth(1) .o_list_view .o_data_cell:first').click();
+ $('[role="dialog"]:nth(1) .o_list_view .o_data_cell:first').click();
- assert.strictEqual($('.modal').length, 1,
+ assert.strictEqual($('[role="dialog"]').length, 1,
"second dialog should be closed");
- assert.strictEqual($('.modal .o_data_row').length, 2,
+ assert.strictEqual($('[role="dialog"] .o_data_row').length, 2,
"there should be two records in the one2many in the dialog");
// click on 'Discard'
- $('.modal .modal-footer .btn-default').click();
+ $('footer.modal-footer .btn-default').click();
- assert.strictEqual($('.modal').length, 0, "dialog should be closed");
+ assert.strictEqual($('[role="dialog"]').length, 0, "dialog should be closed");
// reopen o2m record to check that second changes have properly been discarded
form.$('.o_data_row').click();
- assert.strictEqual($('.modal').length, 1, "should have opened a dialog");
- assert.strictEqual($('.modal .o_data_row').length, 1,
+ assert.strictEqual($('[role="dialog"]').length, 1, "should have opened a dialog");
+ assert.strictEqual($('[role="dialog"] .o_data_row').length, 1,
"there should be one record in the one2many in the dialog");
form.destroy();
@@ -4158,8 +4158,8 @@ QUnit.module('relational_fields', {
checkRead = true;
form.$buttons.find('.o_form_button_edit').click();
form.$('.o-kanban-button-new').click();
- $('.modal input').val('new record').trigger('input');
- $('.modal .modal-footer .btn-primary:first').click(); // save and close
+ $('[role="dialog"] input').val('new record').trigger('input');
+ $('footer.modal-footer .btn-primary:first').click(); // save and close
// checks
assert.strictEqual(readIDs, undefined, "should not have read any record");
assert.strictEqual(form.$('span:contains(new record)').length, 0,
@@ -4195,8 +4195,8 @@ QUnit.module('relational_fields', {
readIDs = undefined;
// add and delete a record in page 1
form.$('.o-kanban-button-new').click();
- $('.modal input').val('new record page 1').trigger('input');
- $('.modal .modal-footer .btn-primary:first').click(); // save and close
+ $('[role="dialog"] input').val('new record page 1').trigger('input');
+ $('footer.modal-footer .btn-primary:first').click(); // save and close
assert.strictEqual(form.$('.o_kanban_record:first span:contains(relational record 11)').length,
1, 'first record should be the one with id 11 (next checks rely on that)');
form.$('.delete_icon:first').click();
@@ -4210,8 +4210,8 @@ QUnit.module('relational_fields', {
readIDs = undefined;
form.$('.delete_icon:first').click();
form.$('.o-kanban-button-new').click();
- $('.modal input').val('new record page 2').trigger('input');
- $('.modal .modal-footer .btn-primary:first').click(); // save and close
+ $('[role="dialog"] input').val('new record page 2').trigger('input');
+ $('footer.modal-footer .btn-primary:first').click(); // save and close
assert.strictEqual(readIDs, undefined, "should not have read any record");
// checks
assert.strictEqual(form.$('.o_kanban_record:not(".o_kanban_ghost")').length, 5,
@@ -4255,13 +4255,13 @@ QUnit.module('relational_fields', {
form.$buttons.find('.o_form_button_edit').click();
form.$('.o_field_x2many_list_row_add a').click();
$('input[name="turtle_int"]').val('5').trigger('input');
- $('.modal-footer button.btn-primary').first().click();
+ $('footer.modal-footer button.btn-primary').first().click();
assert.strictEqual(form.$('tbody tr:eq(1) td.o_data_cell').text(), '5',
'should display 5 in the foo field');
form.$('tbody tr:eq(1) td.o_data_cell').click();
$('input[name="turtle_int"]').val('3').trigger('input');
- $('.modal-footer button.btn-primary').first().click();
+ $('footer.modal-footer button.btn-primary').first().click();
assert.strictEqual(form.$('tbody tr:eq(1) td.o_data_cell').text(), '3',
'should now display 3 in the foo field');
form.destroy();
@@ -4358,7 +4358,7 @@ QUnit.module('relational_fields', {
form.$buttons.find('.o_form_button_cancel').click();
assert.strictEqual(form.$('.o_field_one2many tbody td').first().text(), 'new value',
"changes shouldn't have been discarded yet, waiting for user confirmation");
- $('.modal .modal-footer .btn-primary').click();
+ $('footer.modal-footer .btn-primary').click();
assert.strictEqual(form.$('.o_field_one2many tbody td').first().text(), 'relational record 1',
"display name of first record in o2m list should be 'relational record 1'");
@@ -4557,15 +4557,15 @@ QUnit.module('relational_fields', {
// edit existing subrecord
form.$('.oe_kanban_global_click').click();
- $('.modal .o_form_view input').val('new name').trigger('input');
- $('.modal .modal-footer .btn-primary').click(); // save
+ $('[role="dialog"] .o_form_view input').val('new name').trigger('input');
+ $('footer.modal-footer .btn-primary').click(); // save
assert.strictEqual(form.$('.o_kanban_record span:first').text(), 'new name',
'value of subrecord should have been updated');
// create a new subrecord
form.$('.o-kanban-button-new').click();
- $('.modal .o_form_view input').val('new subrecord 1').trigger('input');
- $('.modal .modal-footer .btn-primary').click(); // save and close
+ $('[role="dialog"] .o_form_view input').val('new subrecord 1').trigger('input');
+ $('footer.modal-footer .btn-primary').click(); // save and close
assert.strictEqual(form.$('.o_kanban_record:not(.o_kanban_ghost)').length, 2,
'should contain 2 records');
assert.strictEqual(form.$('.o_kanban_record:nth(1) span').text(), 'new subrecord 1',
@@ -4573,10 +4573,10 @@ QUnit.module('relational_fields', {
// create two new subrecords
form.$('.o-kanban-button-new').click();
- $('.modal .o_form_view input').val('new subrecord 2').trigger('input');
- $('.modal .modal-footer .btn-primary:nth(1)').click(); // save and new
- $('.modal .o_form_view input').val('new subrecord 3').trigger('input');
- $('.modal .modal-footer .btn-primary').click(); // save and close
+ $('[role="dialog"] .o_form_view input').val('new subrecord 2').trigger('input');
+ $('footer.modal-footer .btn-primary:nth(1)').click(); // save and new
+ $('[role="dialog"] .o_form_view input').val('new subrecord 3').trigger('input');
+ $('footer.modal-footer .btn-primary').click(); // save and close
assert.strictEqual(form.$('.o_kanban_record:not(.o_kanban_ghost)').length, 4,
'should contain 4 records');
@@ -4742,8 +4742,8 @@ QUnit.module('relational_fields', {
// edit existing subrecord
form.$('.o_list_view tbody tr:first() td:eq(1)').click();
- $('.modal .o_form_view input').val('new name').trigger('input');
- $('.modal .modal-footer .btn-primary').click(); // save
+ $('[role="dialog"] .o_form_view input').val('new name').trigger('input');
+ $('footer.modal-footer .btn-primary').click(); // save
assert.strictEqual(form.$('.o_list_view tbody td:first()').text(), 'new name',
'value of subrecord should have been updated');
assert.strictEqual(nbWrite, 0, "should not have write anything in DB");
@@ -4789,9 +4789,9 @@ QUnit.module('relational_fields', {
'"Add an item" link should not be available in readonly');
form.$('.o_list_view tbody td:first()').click();
- assert.ok($('.modal .o_form_readonly').length,
+ assert.ok($('[role="dialog"] .o_form_readonly').length,
'in readonly, clicking on a subrecord should open it in readonly in a dialog');
- $('.modal .o_form_button_cancel').click(); // close the dialog
+ $('[role="dialog"] .o_form_button_cancel').click(); // close the dialog
form.$buttons.find('.o_form_button_edit').click();
@@ -4800,7 +4800,7 @@ QUnit.module('relational_fields', {
// edit existing subrecord
form.$('.o_list_view tbody td:first()').click();
- assert.strictEqual($('.modal').length, 0,
+ assert.strictEqual($('[role="dialog"]').length, 0,
'in edit, clicking on a subrecord should not open a dialog');
assert.ok(form.$('.o_list_view tbody tr:first()').hasClass('o_selected_row'),
'first row should be in edition');
@@ -4898,7 +4898,7 @@ QUnit.module('relational_fields', {
// cancel the edition
form.$buttons.find('.o_form_button_cancel').click();
- $('.modal-footer button.btn-primary').first().click();
+ $('footer.modal-footer button.btn-primary').first().click();
assert.strictEqual(form.$('tr.o_data_row').length, 1,
"should have 1 data rows");
@@ -5054,7 +5054,7 @@ QUnit.module('relational_fields', {
form.$buttons.find('.o_form_button_cancel').click();
// confirm the discard operation
- $('.modal .modal-footer .btn-primary').click(); // click on confirm
+ $('footer.modal-footer .btn-primary').click(); // click on confirm
assert.strictEqual(form.$('.o_cp_pager').text().trim(), '1-3 / 4',
"pager should still be visible");
@@ -5129,7 +5129,7 @@ QUnit.module('relational_fields', {
"pager should still display the correct total");
// click on cancel
- $('.modal .modal-footer .btn-default').click(); // click on cancel
+ $('footer.modal-footer .btn-default').click(); // click on cancel
assert.strictEqual(form.$('.o_cp_pager').text().trim(), '1-3 / 4',
"pager should again display the correct total");
@@ -5169,7 +5169,7 @@ QUnit.module('relational_fields', {
// discard
form.$buttons.find('.o_form_button_cancel').click();
- $('.modal .modal-footer .btn-primary').click();
+ $('footer.modal-footer .btn-primary').click();
assert.strictEqual(form.$('tr.o_data_row').length, 1,
"should have 1 data row");
@@ -5216,17 +5216,17 @@ QUnit.module('relational_fields', {
// click elsewhere,
form.$('label.o_form_label').click();
- assert.strictEqual($('.modal').length, 1,
+ assert.strictEqual($('[role="dialog"]').length, 1,
'a confirmation model should be opened');
// click on cancel, the line should still be selected
- $('.modal .modal-footer button.btn-default').click();
+ $('footer.modal-footer button.btn-default').click();
assert.strictEqual(form.$('tr.o_data_row.o_selected_row').length, 1,
"should still have 1 selected data row");
// click elsewhere, and click on ok (on the confirmation dialog)
form.$('label.o_form_label').click();
- $('.modal .modal-footer button.btn-primary').click();
+ $('footer.modal-footer button.btn-primary').click();
assert.strictEqual(form.$('tr.o_data_row').length, 0,
"should have 0 data rows (invalid line has been discarded");
@@ -5427,17 +5427,17 @@ QUnit.module('relational_fields', {
});
form.$('.o_field_x2many_list_row_add a').click();
- $('.modal .o_field_x2many_list_row_add a').click();
+ $('[role="dialog"] .o_field_x2many_list_row_add a').click();
- assert.strictEqual($('.modal .o_data_row.o_selected_row').length, 1,
+ assert.strictEqual($('[role="dialog"] .o_data_row.o_selected_row').length, 1,
"there should be a row in edition in the dialog");
// trigger keydown ESCAPE in the edited row
- $('.modal .o_data_cell input').trigger({type: 'keydown', which: $.ui.keyCode.ESCAPE});
+ $('[role="dialog"] .o_data_cell input').trigger({type: 'keydown', which: $.ui.keyCode.ESCAPE});
- assert.strictEqual($('.modal').length, 1,
+ assert.strictEqual($('[role="dialog"]').length, 1,
"dialog should still be open");
- assert.strictEqual($('.modal .o_data_row').length, 0,
+ assert.strictEqual($('[role="dialog"] .o_data_row').length, 0,
"the row should have been removed");
form.destroy();
@@ -6042,8 +6042,8 @@ QUnit.module('relational_fields', {
// click to create a subrecord
form.$('tbody td.o_field_x2many_list_row_add a').click();
- $('.modal input').val('new record').trigger('input');
- $('.modal .modal-footer button:eq(0)').click(); // save and close
+ $('[role="dialog"] input').val('new record').trigger('input');
+ $('footer.modal-footer button:eq(0)').click(); // save and close
assert.ok(!form.$('.o_x2m_control_panel .o_cp_pager div').is(':visible'),
'o2m pager should be hidden');
@@ -6098,14 +6098,14 @@ QUnit.module('relational_fields', {
form.$('tbody td.o_field_x2many_list_row_add a').click();
- $('.modal .o_field_many2one input').click();
+ $('[role="dialog"] .o_field_many2one input').click();
- var $dropdown = $('.modal .o_field_many2one input').autocomplete('widget');
+ var $dropdown = $('[role="dialog"] .o_field_many2one input').autocomplete('widget');
$dropdown.find('li:eq(1) a').mouseenter();
$dropdown.find('li:eq(1) a').click();
- $('.modal .modal-footer button:eq(0)').click(); // save and close
+ $('footer.modal-footer button:eq(0)').click(); // save and close
assert.strictEqual(form.$('tbody td:contains(xpad)').length, 1,
"should display 'xpad' on a td");
@@ -6157,16 +6157,16 @@ QUnit.module('relational_fields', {
form.$('tbody td.o_field_x2many_list_row_add a').click();
// write in the many2one field, value = 37 (xphone)
- $('.modal .o_field_many2one input').click();
- var $dropdown = $('.modal .o_field_many2one input').autocomplete('widget');
+ $('[role="dialog"] .o_field_many2one input').click();
+ var $dropdown = $('[role="dialog"] .o_field_many2one input').autocomplete('widget');
$dropdown.find('li:eq(0) a').mouseenter();
$dropdown.find('li:eq(0) a').click();
// write in the integer field
- $('.modal .modal-body input.o_field_widget').val('123').trigger('input');
+ $('main.modal-body input.o_field_widget').val('123').trigger('input');
// save and close
- $('.modal .modal-footer button:eq(0)').click();
+ $('footer.modal-footer button:eq(0)').click();
assert.strictEqual(form.$('tbody td:contains(xphone)').length, 1,
"should display 'xphone' in a td");
@@ -6174,28 +6174,28 @@ QUnit.module('relational_fields', {
// reopen the record in form view
form.$('tbody td:contains(xphone)').click();
- assert.strictEqual($('.modal .modal-body input').val(), "xphone",
+ assert.strictEqual($('main.modal-body input').val(), "xphone",
"should display 'xphone' in an input");
- $('.modal .modal-body input.o_field_widget').val('456').trigger('input');
+ $('main.modal-body input.o_field_widget').val('456').trigger('input');
// discard
- $('.modal .modal-footer span:contains(Discard)').click();
+ $('footer.modal-footer span:contains(Discard)').click();
// reopen the record in form view
form.$('tbody td:contains(xphone)').click();
- assert.strictEqual($('.modal .modal-body input.o_field_widget').val(), "123",
+ assert.strictEqual($('main.modal-body input.o_field_widget').val(), "123",
"should display 123 (previous change has been discarded)");
// write in the many2one field, value = 41 (xpad)
- $('.modal .o_field_many2one input').click();
- $dropdown = $('.modal .o_field_many2one input').autocomplete('widget');
+ $('[role="dialog"] .o_field_many2one input').click();
+ $dropdown = $('[role="dialog"] .o_field_many2one input').autocomplete('widget');
$dropdown.find('li:eq(1) a').mouseenter();
$dropdown.find('li:eq(1) a').click();
// save and close
- $('.modal .modal-footer button:eq(0)').click();
+ $('footer.modal-footer button:eq(0)').click();
assert.strictEqual(form.$('tbody td:contains(xpad)').length, 1,
"should display 'xpad' in a td");
@@ -6245,7 +6245,7 @@ QUnit.module('relational_fields', {
form.$('tr.o_data_row:eq(0) td:contains(xphone)').click();
// write in the many2one field
- $('.modal .o_field_many2one input').click();
+ $('[role="dialog"] .o_field_many2one input').click();
form.destroy();
});
@@ -6513,29 +6513,29 @@ QUnit.module('relational_fields', {
form.$buttons.find('.o_form_button_edit').click();
form.$('.o_field_x2many_list_row_add a').click();
- assert.strictEqual($('.modal .o_data_row').length, 2,
+ assert.strictEqual($('[role="dialog"] .o_data_row').length, 2,
"sould have 2 records in the select view (the last one is not displayed because it is already selected)");
- $('.modal .o_data_row:first .o_list_record_selector input').click();
- $('.modal .o_select_button').click();
+ $('[role="dialog"] .o_data_row:first .o_list_record_selector input').click();
+ $('[role="dialog"] .o_select_button').click();
$('.o_form_button_save').click();
form.$buttons.find('.o_form_button_edit').click();
form.$('.o_field_x2many_list_row_add a').click();
- assert.strictEqual($('.modal .o_data_row').length, 1,
+ assert.strictEqual($('[role="dialog"] .o_data_row').length, 1,
"sould have 1 record in the select view");
- $('.modal-footer button:eq(1)').click();
- $('.modal input.o_field_widget[name="turtle_foo"]').val('tototo').trigger('input');
- $('.modal input.o_field_widget[name="turtle_int"]').val(50).trigger('input');
- var $many2one = $('.modal [name="product_id"] input').click();
+ $('footer.modal-footer button:eq(1)').click();
+ $('[role="dialog"] input.o_field_widget[name="turtle_foo"]').val('tototo').trigger('input');
+ $('[role="dialog"] input.o_field_widget[name="turtle_int"]').val(50).trigger('input');
+ var $many2one = $('[role="dialog"] [name="product_id"] input').click();
var $dropdown = $many2one.autocomplete('widget');
$dropdown.find('li:first a').mouseenter();
$dropdown.find('li:first a').click();
- $('.modal-footer button:contains(&):first').click();
+ $('footer.modal-footer button:contains(&):first').click();
- assert.strictEqual($('.modal').length, 0, "sould close the modals");
+ assert.strictEqual($('[role="dialog"]').length, 0, "sould close the modals");
assert.strictEqual(form.$('.o_data_row').length, 3,
"sould have 3 records in one2many list");
@@ -6593,34 +6593,34 @@ QUnit.module('relational_fields', {
});
form.$('.o_data_row:first').click();
- assert.strictEqual($('.modal .modal-title').first().text().trim(), 'Open: one2many turtle field',
+ assert.strictEqual($('[role="dialog"] .modal-title').first().text().trim(), 'Open: one2many turtle field',
"modal should use the python field string as title");
- $('.modal .o_form_button_cancel').click();
+ $('[role="dialog"] .o_form_button_cancel').click();
form.$buttons.find('.o_form_button_edit').click();
// edit the first one2many record
form.$('.o_data_row:first').click();
- var $many2one = $('.modal [name="product_id"] input').click();
+ var $many2one = $('[role="dialog"] [name="product_id"] input').click();
var $dropdown = $many2one.autocomplete('widget');
$dropdown.find('li:first a').mouseenter();
$dropdown.find('li:first a').click();
- $('.modal-footer button:first').click();
+ $('footer.modal-footer button:first').click();
$('.o_form_button_save').click(); // don't save anything because the one2many does not change
// add a one2many record
form.$buttons.find('.o_form_button_edit').click();
form.$('.o_field_x2many_list_row_add a').click();
- $('.modal .o_data_row:first .o_list_record_selector input').click();
- $('.modal .o_select_button').click();
+ $('[role="dialog"] .o_data_row:first .o_list_record_selector input').click();
+ $('[role="dialog"] .o_select_button').click();
// edit the second one2many record
form.$('.o_data_row:eq(1)').click();
- $many2one = $('.modal [name="product_id"] input').click();
+ $many2one = $('[role="dialog"] [name="product_id"] input').click();
$dropdown = $many2one.autocomplete('widget');
$dropdown.find('li:first a').mouseenter();
$dropdown.find('li:first a').click();
- $('.modal-footer button:first').click();
+ $('footer.modal-footer button:first').click();
$('.o_form_button_save').click();
@@ -6812,10 +6812,10 @@ QUnit.module('relational_fields', {
form.$buttons.find('.o_form_button_edit').click();
form.$('tbody td.o_field_x2many_list_row_add a').click();
- $('.modal-footer button.btn-primary').first().click();
+ $('footer.modal-footer button.btn-primary').first().click();
- assert.strictEqual($('.modal').length, 1, "should still have an open modal");
- assert.strictEqual($('.modal tbody label.o_field_invalid').length, 1,
+ assert.strictEqual($('[role="dialog"]').length, 1, "should still have an open modal");
+ assert.strictEqual($('[role="dialog"] tbody label.o_field_invalid').length, 1,
"should have displayed invalid fields");
form.destroy();
});
@@ -7011,9 +7011,9 @@ QUnit.module('relational_fields', {
});
form.$('.o_field_one2many .o_field_x2many_list_row_add a').click();
- assert.strictEqual($('.modal').length, 1, "form view dialog should be opened");
- $('.modal-body input[name="model_name"]').val('partner').trigger('input');
- $('.modal-footer button:first').click();
+ assert.strictEqual($('[role="dialog"]').length, 1, "form view dialog should be opened");
+ $('main.modal-body input[name="model_name"]').val('partner').trigger('input');
+ $('footer.modal-footer button:first').click();
assert.strictEqual(form.$('.o_field_one2many tbody tr:first').text(), "coucouhas changed[]",
"the onchange should create one new record and remove the existing");
@@ -7112,9 +7112,9 @@ QUnit.module('relational_fields', {
form.$('.o_data_row:first').click();
- assert.strictEqual($('.modal .o_field_widget').text(), "xphone",
+ assert.strictEqual($('[role="dialog"] .o_field_widget').text(), "xphone",
'should display the form view dialog with the many2one value');
- $('.modal-footer button').click(); // close the modal
+ $('footer.modal-footer button').click(); // close the modal
form.$buttons.find('.o_form_button_edit').click();
@@ -7368,7 +7368,7 @@ QUnit.module('relational_fields', {
form.$('.o_data_row:first').click();
- assert.strictEqual($('.modal .o_field_widget[name="partner_ids"] .o_list_view').length, 1,
+ assert.strictEqual($('[role="dialog"] .o_field_widget[name="partner_ids"] .o_list_view').length, 1,
'should display many2many list view in the modal');
form.destroy();
@@ -7400,7 +7400,7 @@ QUnit.module('relational_fields', {
form.$('.o_data_row:first').click();
- assert.strictEqual($('.modal .o_field_widget[name="turtle_foo"]').text(), 'blip',
+ assert.strictEqual($('[role="dialog"] .o_field_widget[name="turtle_foo"]').text(), 'blip',
'should open the modal and display the form field');
form.destroy();
@@ -7449,14 +7449,14 @@ QUnit.module('relational_fields', {
form.$('.o_data_row:first').click(); // edit first record
- var $input = $('.modal .o_field_many2manytags input');
+ var $input = $('[role="dialog"] .o_field_many2manytags input');
$input.click(); // opens the dropdown
$input.autocomplete('widget').find('li').click(); // add 'first record'
// add a many2many tag and save
- $('.modal .o_field_x2many_list_row_add a').click();
- $('.modal .o_field_widget[name=name]').val('test').trigger('input');
- $('.modal .modal-footer .btn-primary').click(); // save
+ $('[role="dialog"] .o_field_x2many_list_row_add a').click();
+ $('[role="dialog"] .o_field_widget[name=name]').val('test').trigger('input');
+ $('footer.modal-footer .btn-primary').click(); // save
form.$buttons.find('.o_form_button_save').click();
@@ -7510,13 +7510,13 @@ QUnit.module('relational_fields', {
form.$('.o_external_button').click();
// click on add, to add a new partner in the m2m
- $('.modal .o_field_x2many_list_row_add a').click();
+ $('[role="dialog"] .o_field_x2many_list_row_add a').click();
// select the partner_type 'gold' (this closes the 2nd modal)
- $('.modal td:contains(gold)').click();
+ $('[role="dialog"] td:contains(gold)').click();
// confirm the changes in the modal
- $('.modal .modal-footer .btn-primary').click();
+ $('footer.modal-footer .btn-primary').click();
form.$buttons.find('.o_form_button_save').click();
form.destroy();
@@ -7585,7 +7585,7 @@ QUnit.module('relational_fields', {
form.$('.o_data_row:first').click();
- assert.strictEqual($('.modal .o_field_widget[name="turtle_foo"]').text(), 'blip',
+ assert.strictEqual($('[role="dialog"] .o_field_widget[name="turtle_foo"]').text(), 'blip',
'should open the modal and display the form field');
form.destroy();
@@ -8080,7 +8080,7 @@ QUnit.module('relational_fields', {
// try to add a second line and check that it is correctly waiting
// for the onchange to return
form.$('.o_field_x2many_list_row_add a').click();
- assert.strictEqual($('.modal').length, 0, "no modal should be displayed");
+ assert.strictEqual($('[role="dialog"]').length, 0, "no modal should be displayed");
assert.strictEqual($('.o_field_invalid').length, 0,
"no field should be marked as invalid");
assert.strictEqual(form.$('.o_data_row').length, 1,
@@ -8384,11 +8384,11 @@ QUnit.module('relational_fields', {
form.$('.o_data_row td:first').click(); // open the record
- assert.strictEqual($('.modal .o_field_many2manytags[name="timmy"]').length, 1,
+ assert.strictEqual($('[role="dialog"] .o_field_many2manytags[name="timmy"]').length, 1,
"timmy should be displayed in the form view");
- assert.strictEqual($('.modal .o_field_many2manytags[name="timmy"] .badge').length, 1,
+ assert.strictEqual($('[role="dialog"] .o_field_many2manytags[name="timmy"] .badge').length, 1,
"m2mtags should contain one tag");
- assert.strictEqual($('.modal .o_field_many2manytags[name="timmy"] .o_badge_text').text(),
+ assert.strictEqual($('[role="dialog"] .o_field_many2manytags[name="timmy"] .o_badge_text').text(),
'gold', "tag name should have been correctly loaded");
form.destroy();
@@ -8464,10 +8464,10 @@ QUnit.module('relational_fields', {
});
form.$('.o_field_x2many_list_row_add a').click();
- $('.modal .o_field_x2many_list_row_add a').click();
- $('.modal .o_data_cell input').val('new record').trigger('input');
+ $('[role="dialog"] .o_field_x2many_list_row_add a').click();
+ $('[role="dialog"] .o_data_cell input').val('new record').trigger('input');
checkOnchange = true;
- $('.modal .modal-footer .btn-primary').click(); // save (should trigger the onchange)
+ $('footer.modal-footer .btn-primary').click(); // save (should trigger the onchange)
form.destroy();
});
@@ -8788,7 +8788,7 @@ QUnit.module('relational_fields', {
assert.strictEqual(form.$('.o_data_row.o_selected_row').length, 1,
"line should not have been removed and should still be in edition");
- assert.strictEqual($('.modal').length, 1,
+ assert.strictEqual($('[role="dialog"]').length, 1,
"a confirmation dialog should be opened");
assert.ok(form.$('.o_field_widget[name=int_field]').hasClass('o_field_invalid'),
"should indicate that int_field is invalid");
@@ -8986,9 +8986,9 @@ QUnit.module('relational_fields', {
// open the new subrecord to check the value of the nested o2m, and to
// ensure that it will be saved
form.$('.o_data_cell:first').click();
- assert.strictEqual($('.modal .o_data_cell').text(), 'test nested',
+ assert.strictEqual($('[role="dialog"] .o_data_cell').text(), 'test nested',
"should have added the new row to the nested one2many");
- $('.modal .modal-footer .btn-primary').click();
+ $('footer.modal-footer .btn-primary').click();
form.$buttons.find('.o_form_button_save').click();
@@ -9276,26 +9276,26 @@ QUnit.module('relational_fields', {
// add a new record
form.$('.o_field_x2many_list_row_add a').click();
- $('.modal .o_field_widget').val('new record').trigger('input');
- $('.modal .modal-footer .btn-primary:first').click(); // Save & Close
+ $('[role="dialog"] .o_field_widget').val('new record').trigger('input');
+ $('footer.modal-footer .btn-primary:first').click(); // Save & Close
assert.strictEqual(form.$('.o_data_row .o_data_cell').text(), 'new record',
"should display the new record");
// reopen freshly added record and edit it
form.$('.o_data_row .o_data_cell').click();
- $('.modal .o_field_widget').val('new record edited').trigger('input');
+ $('[role="dialog"] .o_field_widget').val('new record edited').trigger('input');
// save it, and choose to directly create another record
- $('.modal .modal-footer .btn-primary:nth(1)').click(); // Save & New
+ $('footer.modal-footer .btn-primary:nth(1)').click(); // Save & New
- assert.strictEqual($('.modal').length, 1,
+ assert.strictEqual($('[role="dialog"]').length, 1,
"the model should still be open");
- assert.strictEqual($('.modal .o_field_widget').text(), '',
+ assert.strictEqual($('[role="dialog"] .o_field_widget').text(), '',
"should have cleared the input");
- $('.modal .o_field_widget').val('another new record').trigger('input');
- $('.modal .modal-footer .btn-primary:first').click(); // Save & Close
+ $('[role="dialog"] .o_field_widget').val('another new record').trigger('input');
+ $('footer.modal-footer .btn-primary:first').click(); // Save & Close
assert.strictEqual(form.$('.o_data_row .o_data_cell').text(),
'new record editedanother new record', "should display the two records");
@@ -9461,19 +9461,19 @@ QUnit.module('relational_fields', {
// edit existing subrecord
form.$('.oe_kanban_global_click:first()').click();
- $('.modal .o_form_view input').val('new name').trigger('input');
- $('.modal .modal-footer .btn-primary').click(); // save
+ $('[role="dialog"] .o_form_view input').val('new name').trigger('input');
+ $('footer.modal-footer .btn-primary').click(); // save
assert.strictEqual(form.$('.o_kanban_record:first() span').text(), 'new name',
'value of subrecord should have been updated');
// add subrecords
// -> single select
form.$('.o_field_many2many .o-kanban-button-new').click();
- assert.ok($('.modal .o_list_view').length, "should have opened a list view in a modal");
- assert.strictEqual($('.modal .o_list_view tbody .o_list_record_selector').length, 3,
+ assert.ok($('[role="dialog"] .o_list_view').length, "should have opened a list view in a modal");
+ assert.strictEqual($('[role="dialog"] .o_list_view tbody .o_list_record_selector').length, 3,
"list view should contain 3 records");
- $('.modal .o_list_view tbody tr:contains(red)').click(); // select red
- assert.ok(!$('.modal .o_list_view').length, "should have closed the modal");
+ $('[role="dialog"] .o_list_view tbody tr:contains(red)').click(); // select red
+ assert.ok(!$('[role="dialog"] .o_list_view').length, "should have closed the modal");
assert.strictEqual(form.$('.o_kanban_record:not(.o_kanban_ghost)').length, 3,
'kanban should now contain 3 records');
assert.ok(form.$('.o_kanban_record:contains(red)').length,
@@ -9481,23 +9481,23 @@ QUnit.module('relational_fields', {
// -> multiple select
form.$('.o_field_many2many .o-kanban-button-new').click();
- assert.ok($('.modal .o_select_button').prop('disabled'), "select button should be disabled");
- assert.strictEqual($('.modal .o_list_view tbody .o_list_record_selector').length, 2,
+ assert.ok($('[role="dialog"] .o_select_button').prop('disabled'), "select button should be disabled");
+ assert.strictEqual($('[role="dialog"] .o_list_view tbody .o_list_record_selector').length, 2,
"list view should contain 2 records");
- $('.modal .o_list_view thead .o_list_record_selector input').click(); // select all
- $('.modal .o_select_button').click(); // validate selection
- assert.ok(!$('.modal .o_select_button').prop('disabled'), "select button should be enabled");
- assert.ok(!$('.modal .o_list_view').length, "should have closed the modal");
+ $('[role="dialog"] .o_list_view thead .o_list_record_selector input').click(); // select all
+ $('[role="dialog"] .o_select_button').click(); // validate selection
+ assert.ok(!$('[role="dialog"] .o_select_button').prop('disabled'), "select button should be enabled");
+ assert.ok(!$('[role="dialog"] .o_list_view').length, "should have closed the modal");
assert.strictEqual(form.$('.o_kanban_record:not(.o_kanban_ghost)').length, 5,
'kanban should now contain 5 records');
// -> created record
form.$('.o_field_many2many .o-kanban-button-new').click();
- $('.modal .modal-footer .btn-primary:nth(1)').click(); // click on 'Create'
- assert.ok($('.modal .o_form_view.o_form_editable').length,
+ $('footer.modal-footer .btn-primary:nth(1)').click(); // click on 'Create'
+ assert.ok($('[role="dialog"] .o_form_view.o_form_editable').length,
"should have opened a form view in edit mode, in a modal");
- $('.modal .o_form_view input').val('A new type').trigger('input');
- $('.modal:nth(1) .modal-footer .btn-primary:first()').click(); // click on 'Save & Close'
- assert.ok(!$('.modal').length, "should have closed both modals");
+ $('[role="dialog"] .o_form_view input').val('A new type').trigger('input');
+ $('[role="dialog"]:nth(1) footer .btn-primary:first()').click(); // click on 'Save & Close'
+ assert.ok(!$('[role="dialog"]').length, "should have closed both modals");
assert.strictEqual(form.$('.o_kanban_record:not(.o_kanban_ghost)').length, 6,
'kanban should now contain 6 records');
assert.ok(form.$('.o_kanban_record:contains(A new type)').length,
@@ -9592,7 +9592,7 @@ QUnit.module('relational_fields', {
'delete icon should be visible in edit');
form.$('.o-kanban-button-new').click(); // click on 'Add'
- assert.strictEqual($('.modal .modal-footer .btn-primary').length, 1, // only button 'Select'
+ assert.strictEqual($('footer.modal-footer .btn-primary').length, 1, // only button 'Select'
'"Create" button should not be available in the modal');
form.destroy();
@@ -9655,19 +9655,19 @@ QUnit.module('relational_fields', {
// edit existing subrecord
form.$('.o_list_view tbody tr:first()').click();
- $('.modal .o_form_view input').val('new name').trigger('input');
- $('.modal .modal-footer .btn-primary').click(); // save
+ $('[role="dialog"] .o_form_view input').val('new name').trigger('input');
+ $('footer.modal-footer .btn-primary').click(); // save
assert.strictEqual(form.$('.o_list_view tbody td:first()').text(), 'new name',
'value of subrecord should have been updated');
// add new subrecords
form.$('.o_field_x2many_list_row_add a').click();
- assert.strictEqual($('.modal .o_list_view').length, 1,
+ assert.strictEqual($('[role="dialog"] .o_list_view').length, 1,
"a modal should be open");
- assert.strictEqual($('.modal .o_list_view .o_data_row').length, 1,
+ assert.strictEqual($('[role="dialog"] .o_list_view .o_data_row').length, 1,
"the list should contain one row");
- $('.modal .o_list_view .o_data_row').click(); // select a record
- assert.strictEqual($('.modal .o_list_view').length, 0,
+ $('[role="dialog"] .o_list_view .o_data_row').click(); // select a record
+ assert.strictEqual($('[role="dialog"] .o_list_view').length, 0,
"the modal should be closed");
assert.strictEqual(form.$('.o_list_view td.o_list_number').length, 3,
'should contain 3 subrecords');
@@ -9758,7 +9758,7 @@ QUnit.module('relational_fields', {
// edit existing subrecord
form.$('.o_list_view tbody td:first()').click();
- assert.ok(!$('.modal').length,
+ assert.ok(!$('[role="dialog"]').length,
'in edit, clicking on a subrecord should not open a dialog');
assert.ok(form.$('.o_list_view tbody tr:first()').hasClass('o_selected_row'),
'first row should be in edition');
@@ -9776,12 +9776,12 @@ QUnit.module('relational_fields', {
// add new subrecords
form.$('.o_field_x2many_list_row_add a').click();
- assert.strictEqual($('.modal .o_list_view').length, 1,
+ assert.strictEqual($('[role="dialog"] .o_list_view').length, 1,
"a modal should be open");
- assert.strictEqual($('.modal .o_list_view .o_data_row').length, 1,
+ assert.strictEqual($('[role="dialog"] .o_list_view .o_data_row').length, 1,
"the list should contain one row");
- $('.modal .o_list_view .o_data_row').click(); // select a record
- assert.strictEqual($('.modal .o_list_view').length, 0,
+ $('[role="dialog"] .o_list_view .o_data_row').click(); // select a record
+ assert.strictEqual($('[role="dialog"] .o_list_view').length, 0,
"the modal should be closed");
assert.strictEqual(form.$('.o_list_view td.o_list_number').length, 3,
'should contain 3 subrecords');
@@ -10048,7 +10048,7 @@ QUnit.module('relational_fields', {
});
form.$('.o_field_x2many_list_row_add a').click();
- $('.modal .o_data_row:first').click(); // add a first record to the relation
+ $('[role="dialog"] .o_data_row:first').click(); // add a first record to the relation
assert.strictEqual(form.$('.o_data_row').length, 1,
"the record should have been added to the relation");
@@ -10056,7 +10056,7 @@ QUnit.module('relational_fields', {
"inner m2m should have been fetched and correctly displayed");
form.$('.o_field_x2many_list_row_add a').click();
- $('.modal .o_data_row:first').click(); // add a second record to the relation
+ $('[role="dialog"] .o_data_row:first').click(); // add a second record to the relation
assert.strictEqual(form.$('.o_data_row').length, 2,
"the second record should have been added to the relation");
@@ -10109,13 +10109,13 @@ QUnit.module('relational_fields', {
});
form.$('.o_field_x2many_list_row_add a').click();
- assert.strictEqual($('.modal .o_data_row').length, 1,
+ assert.strictEqual($('[role="dialog"] .o_data_row').length, 1,
"should contain only one row (gold)");
- $('.modal .o_searchview_input').trigger({type: 'keypress', which: 115}); // s
- $('.modal .o_searchview_input').trigger({type: 'keydown', which: 13}); // enter
+ $('[role="dialog"] .o_searchview_input').trigger({type: 'keypress', which: 115}); // s
+ $('[role="dialog"] .o_searchview_input').trigger({type: 'keydown', which: 13}); // enter
- assert.strictEqual($('.modal .o_data_row').length, 0, "should contain no row");
+ assert.strictEqual($('[role="dialog"] .o_data_row').length, 0, "should contain no row");
form.destroy();
});
@@ -10149,8 +10149,8 @@ QUnit.module('relational_fields', {
form.$buttons.find('.o_form_button_edit').click();
form.$('td.o_data_cell:first').click();
- $('.modal-body input[type="checkbox"]').click();
- $('.modal .modal-footer .btn-primary').first().click();
+ $('main.modal-body input[type="checkbox"]').click();
+ $('footer.modal-footer .btn-primary').first().click();
// there is nothing left to save -> should not do a 'write' RPC
form.$buttons.find('.o_form_button_save').click();
@@ -10806,9 +10806,9 @@ QUnit.module('relational_fields', {
assert.strictEqual($input.autocomplete('widget').find('li').length, 3,
"autocomplete dropdown should have 3 entries (2 values + 'Search and Edit...')");
$input.autocomplete('widget').find('li:first()').click(); // adds a tag
- assert.strictEqual(form.$('.o_field_many2manytags[name="timmy"] > span').length, 1,
+ assert.strictEqual(form.$('.o_field_many2manytags[name="timmy"] > button').length, 1,
"should contain 1 tag");
- assert.ok(form.$('.o_field_many2manytags[name="timmy"] > span:contains("gold")').length,
+ assert.ok(form.$('.o_field_many2manytags[name="timmy"] > button:contains("gold")').length,
"should contain newly added tag 'gold'");
form.destroy();
@@ -10841,18 +10841,18 @@ QUnit.module('relational_fields', {
return this._super.apply(this, arguments);
},
});
- assert.strictEqual(form.$('.o_field_many2manytags > span').length, 2,
+ assert.strictEqual(form.$('.o_field_many2manytags > button').length, 2,
"should contain 2 tags");
- assert.ok(form.$('span:contains(gold)').length,
+ assert.ok(form.$('button:contains(gold)').length,
'should have fetched and rendered gold partner tag');
- assert.ok(form.$('span:contains(silver)').length,
+ assert.ok(form.$('button:contains(silver)').length,
'should have fetched and rendered silver partner tag');
- assert.strictEqual(form.$('span:first()').data('color'), 2,
+ assert.strictEqual(form.$('button:first()').data('color'), 2,
'should have correctly fetched the color');
form.$buttons.find('.o_form_button_edit').click();
- assert.strictEqual(form.$('.o_field_many2manytags > span').length, 2,
+ assert.strictEqual(form.$('.o_field_many2manytags > button').length, 2,
"should still contain 2 tags in edit mode");
assert.ok(form.$('.o_tag_color_2 .o_badge_text:contains(gold)').length,
'first tag should still contain "gold" and be color 2 in edit mode');
@@ -10867,46 +10867,46 @@ QUnit.module('relational_fields', {
assert.strictEqual($input.autocomplete('widget').find('li a:contains("red")').length, 1,
"autocomplete dropdown should contain 'red'");
$input.autocomplete('widget').find('li').click(); // add 'red'
- assert.strictEqual(form.$('.o_field_many2manytags > span').length, 3,
+ assert.strictEqual(form.$('.o_field_many2manytags > button').length, 3,
"should contain 3 tags");
- assert.ok(form.$('.o_field_many2manytags > span:contains("red")').length,
+ assert.ok(form.$('.o_field_many2manytags > button:contains("red")').length,
"should contain newly added tag 'red'");
- assert.ok(form.$('.o_field_many2manytags > span[data-color=8]:contains("red")').length,
+ assert.ok(form.$('.o_field_many2manytags > button[data-color=8]:contains("red")').length,
"should have fetched the color of added tag");
// remove tag with id 14
- form.$('.o_field_many2manytags span[data-id=14] .o_delete').click();
- assert.strictEqual(form.$('.o_field_many2manytags > span').length, 2,
+ form.$('.o_field_many2manytags button[data-id=14] .o_delete').click();
+ assert.strictEqual(form.$('.o_field_many2manytags > button').length, 2,
"should contain 2 tags");
- assert.ok(!form.$('.o_field_many2manytags > span:contains("silver")').length,
+ assert.ok(!form.$('.o_field_many2manytags > button:contains("silver")').length,
"should not contain tag 'silver' anymore");
// save the record (should do the write RPC with the correct commands)
form.$buttons.find('.o_form_button_save').click();
// checkbox 'Hide in Kanban'
- $input = form.$('.o_field_many2manytags span[data-id=13]'); // selects 'red' tag
+ $input = form.$('.o_field_many2manytags button[data-id=13]'); // selects 'red' tag
$input.click(); // opens the colorpicker dropdown
- assert.ok(form.$('.o_field_many2manytags span[data-id=13] .o_colorpicker .o_checkbox'), "should have a checkbox in the colorpicker dropdown menu");
+ assert.ok(form.$('.o_field_many2manytags button[data-id=13] .o_colorpicker .o_checkbox'), "should have a checkbox in the colorpicker dropdown menu");
$input.click();
- var $checkBox = form.$('.o_field_many2manytags span[data-id=13] .o_colorpicker .o_checkbox input');
+ var $checkBox = form.$('.o_field_many2manytags button[data-id=13] .o_colorpicker .o_checkbox input');
assert.notOk($checkBox.is(':checked'), "should have unticked checkbox in colorpicker dropdown menu");
$checkBox.mousedown();
- $input = form.$('.o_field_many2manytags span[data-id=13]'); // refresh
+ $input = form.$('.o_field_many2manytags button[data-id=13]'); // refresh
assert.equal($input.data('color'), "0", "should become transparent when toggling on checkbox");
$input.click();
- $checkBox = form.$('.o_field_many2manytags span[data-id=13] .o_colorpicker .o_checkbox input'); // refresh
+ $checkBox = form.$('.o_field_many2manytags button[data-id=13] .o_colorpicker .o_checkbox input'); // refresh
assert.ok($checkBox.is(':checked'), "should have a ticked checkbox in colorpicker dropdown menu after mousedown");
$checkBox.mousedown();
- $input = form.$('.o_field_many2manytags span[data-id=13]'); // refresh
+ $input = form.$('.o_field_many2manytags button[data-id=13]'); // refresh
assert.equal($input.data('color'), "8", "should revert to old color when toggling off checkbox");
$input.click();
- $checkBox = form.$('.o_field_many2manytags span[data-id=13] .o_colorpicker .o_checkbox input'); // refresh
+ $checkBox = form.$('.o_field_many2manytags button[data-id=13] .o_colorpicker .o_checkbox input'); // refresh
assert.notOk($checkBox.is(':checked'), "should have an unticked checkbox in colorpicker dropdown menu after 2nd click");
// TODO: it would be nice to test the behaviors of the autocomplete dropdown
@@ -10939,9 +10939,9 @@ QUnit.module('relational_fields', {
return this._super.apply(this, arguments);
}
});
- assert.strictEqual(form.$('.o_field_many2manytags > span').length, 1,
+ assert.strictEqual(form.$('.o_field_many2manytags > button').length, 1,
"should contain 1 tag");
- assert.ok(form.$('span:contains(gold)').length,
+ assert.ok(form.$('button:contains(gold)').length,
'should have fetched and rendered gold partner tag');
form.$buttons.find('.o_form_button_edit').click();
@@ -10954,9 +10954,9 @@ QUnit.module('relational_fields', {
assert.strictEqual($input.autocomplete('widget').find('li a:contains("silver")').length, 1,
"autocomplete dropdown should contain 'silver'");
$input.autocomplete('widget').find('li').click(); // add 'silver'
- assert.strictEqual(form.$('.o_field_many2manytags > span').length, 2,
+ assert.strictEqual(form.$('.o_field_many2manytags > button').length, 2,
"should contain 2 tags");
- assert.ok(form.$('.o_field_many2manytags > span:contains("silver")').length,
+ assert.ok(form.$('.o_field_many2manytags > button:contains("silver")').length,
"should contain newly added tag 'silver'");
form.destroy();
@@ -10989,9 +10989,9 @@ QUnit.module('relational_fields', {
assert.strictEqual($input.autocomplete('widget').find('li').length, 3,
"autocomplete dropdown should have 3 entries (2 values + 'Search and Edit...')");
$input.autocomplete('widget').find('li:first()').click(); // adds a tag
- assert.strictEqual(form.$('.o_field_many2manytags > span').length, 1,
+ assert.strictEqual(form.$('.o_field_many2manytags > button').length, 1,
"should contain 1 tag");
- assert.ok(form.$('.o_field_many2manytags > span:contains("gold")').length,
+ assert.ok(form.$('.o_field_many2manytags > button:contains("gold")').length,
"should contain newly added tag 'gold'");
// save the record (should do the write RPC with the correct commands)
@@ -11016,20 +11016,20 @@ QUnit.module('relational_fields', {
});
// First checks that default color 0 is rendered as 0 color
- assert.ok(form.$('span:first()').is('.o_tag_color_0'),
+ assert.ok(form.$('button:first()').is('.o_tag_color_0'),
'first tag color should be 0');
// Update the color in readonly
- form.$('span:first()').click();
+ form.$('button:first()').click();
$('.o_colorpicker a[data-color="1"]').trigger('mousedown'); // choose color 1
- assert.strictEqual(form.$('span:first()').data('color'), 1,
+ assert.strictEqual(form.$('button:first()').data('color'), 1,
'should have correctly updated the color (in readonly)');
// Update the color in edit
form.$buttons.find('.o_form_button_edit').click();
- form.$('span:first()').click();
+ form.$('button:first()').click();
$('.o_colorpicker a[data-color="6"]').trigger('mousedown'); // choose color 6
- assert.strictEqual(form.$('span:first()').data('color'), 6,
+ assert.strictEqual(form.$('button:first()').data('color'), 6,
'should have correctly updated the color (in edit)');
form.destroy();
});
@@ -11136,14 +11136,14 @@ QUnit.module('relational_fields', {
form.$('.o_field_many2one input').click();
$dropdown.find('.o_m2o_dropdown_option:contains(Search)').mouseenter().click(); // Open Search More
- $('.modal .o_search_options .fa-bars').click();
- $('.modal .o_search_options .o_group_by_menu a:contains(Bar)').click(); // group by 'Bar'
+ $('[role="dialog"] .o_search_options .fa-bars').click();
+ $('[role="dialog"] .o_search_options .o_group_by_menu a:contains(Bar)').click(); // group by 'Bar'
- $('.modal .o_group_header:first').click();
- assert.strictEqual($('.modal tbody:nth(1) .o_data_row').length, 7,
+ $('[role="dialog"] .o_group_header:first').click();
+ assert.strictEqual($('[role="dialog"] tbody:nth(1) .o_data_row').length, 7,
"should display 7 records in the first page");
- $('.modal .o_group_header:first .o_pager_next').click();
- assert.strictEqual($('.modal tbody:nth(1) .o_data_row').length, 1,
+ $('[role="dialog"] .o_group_header:first .o_pager_next').click();
+ assert.strictEqual($('[role="dialog"] tbody:nth(1) .o_data_row').length, 1,
"should display 1 record in the second page");
form.destroy();
@@ -11167,7 +11167,7 @@ QUnit.module('relational_fields', {
'</form>',
res_id: 1,
});
- assert.strictEqual(form.$('.o_field_widget[name="partner_ids"] > span').length, 100,
+ assert.strictEqual(form.$('.o_field_widget[name="partner_ids"] > button').length, 100,
'should have rendered 100 tags');
form.destroy();
});
@@ -11192,14 +11192,14 @@ QUnit.module('relational_fields', {
});
form.$buttons.find('.o_form_button_edit').click();
- assert.strictEqual(form.$('.o_field_many2manytags > span').length, 1,
+ assert.strictEqual(form.$('.o_field_many2manytags > button').length, 1,
"should contain one tag");
// update foo, which will trigger an onchange and update timmy
// -> m2mtags input should not have taken the focus
form.$('input:first').focus();
form.$('input:first').val('trigger onchange').trigger('input');
- assert.strictEqual(form.$('.o_field_many2manytags > span').length, 0,
+ assert.strictEqual(form.$('.o_field_many2manytags > button').length, 0,
"should contain no tags");
assert.strictEqual(form.$('input:first').get(0), document.activeElement,
"foo input should have kept the focus");
@@ -11207,14 +11207,14 @@ QUnit.module('relational_fields', {
// add a tag -> m2mtags input should still have the focus
form.$('.o_field_many2manytags input').click(); // opens the dropdown
form.$('.o_field_many2manytags input').autocomplete('widget').find('li:first').click();
- assert.strictEqual(form.$('.o_field_many2manytags > span').length, 1,
+ assert.strictEqual(form.$('.o_field_many2manytags > button').length, 1,
"should contain a tag");
assert.strictEqual(form.$('.o_field_many2manytags input').get(0), document.activeElement,
"m2m tags input should have kept the focus");
// remove a tag -> m2mtags input should still have the focus
form.$('.o_field_many2manytags .o_delete').click();
- assert.strictEqual(form.$('.o_field_many2manytags > span').length, 0,
+ assert.strictEqual(form.$('.o_field_many2manytags > button').length, 0,
"should contain no tags");
assert.strictEqual(form.$('.o_field_many2manytags input').get(0), document.activeElement,
"m2m tags input should have kept the focus");
@@ -11700,8 +11700,8 @@ QUnit.module('relational_fields', {
form.$buttons.find('.o_form_button_edit').click();
form.$('.o_field_x2many_list_row_add a').click();
- var $dropdown = $('.modal .o_field_many2one input').autocomplete('widget');
- $('.modal input').val('new record').trigger('keydown');
+ var $dropdown = $('[role="dialog"] .o_field_many2one input').autocomplete('widget');
+ $('[role="dialog"] input').val('new record').trigger('keydown');
concurrency.delay(0).then(function () {
$dropdown.find('li:first()').click(); // quick create 'new record'
assert.verifySteps(['name_create']);
@@ -11747,7 +11747,7 @@ QUnit.module('relational_fields', {
$cell_o2m.click();
// In modal
- var $modal = $('.modal-dialog.modal-lg');
+ var $modal = $('.modal-lg');
assert.equal($modal.length, 1,
'there should be one modal opened');
@@ -11793,7 +11793,7 @@ QUnit.module('relational_fields', {
form.$('.o_data_cell').click();
// In modal
- var $modal = $('.modal-dialog.modal-lg');
+ var $modal = $('.modal-lg');
assert.equal($modal.length, 1,
'there should be one modal opened');
@@ -11881,9 +11881,9 @@ QUnit.module('relational_fields', {
form.$('.o_external_button').click(); // click on the external button (should do an RPC)
- assert.strictEqual($('.modal .modal-title').text().trim(), 'Open: custom label',
+ assert.strictEqual($('[role="dialog"] .modal-title').text().trim(), 'Open: custom label',
"dialog title should display the custom string label");
- $('.modal .o_form_button_cancel').click();
+ $('[role="dialog"] .o_form_button_cancel').click();
form.$('.o_field_widget select').val('partner_type').trigger('change');
assert.strictEqual(form.$('.o_field_widget input').val(), "",
@@ -12096,7 +12096,7 @@ QUnit.module('relational_fields', {
// Add a record in the list
x2mList.find('.o_field_x2many_list_row_add a').click();
- var modal = $('.modal-dialog.modal-lg');
+ var modal = $('.modal-lg');
var nameInput = modal.find('input.o_input[name=display_name]');
nameInput.val('michelangelo').trigger('input');
diff --git a/addons/web/static/tests/fields/upgrade_fields_tests.js b/addons/web/static/tests/fields/upgrade_fields_tests.js
index 427ec7b54e3..14f20bba8ad 100644
--- a/addons/web/static/tests/fields/upgrade_fields_tests.js
+++ b/addons/web/static/tests/fields/upgrade_fields_tests.js
@@ -33,7 +33,7 @@ QUnit.module('upgrade_fields', {
});
form.$('input:checkbox').click();
- assert.strictEqual($('.modal').length, 1,
+ assert.strictEqual($('[role="dialog"]').length, 1,
"the 'Upgrade to Enterprise' dialog should be opened");
form.destroy();
diff --git a/addons/web/static/tests/services/notification_service_tests.js b/addons/web/static/tests/services/notification_service_tests.js
index 68227093cea..feb3a600550 100644
--- a/addons/web/static/tests/services/notification_service_tests.js
+++ b/addons/web/static/tests/services/notification_service_tests.js
@@ -45,7 +45,7 @@ QUnit.module('Services', {
});
var $notification = $('body .o_notification_manager .o_notification');
assert.strictEqual(_.str.trim($notification.html().replace(/\s+/g, ' ')),
- "<div class=\"o_notification_title\"> <span class=\"o_icon fa fa-3x fa-lightbulb-o\"></span> a </div> <div class=\"o_notification_content\">b</div>",
+ "<div class=\"o_notification_title\"> <span role=\"img\" aria-label=\"Notification undefined\" class=\"o_icon fa fa-3x fa-lightbulb-o\" title=\"Notification undefined\"></span> a </div> <div class=\"o_notification_content\">b</div>",
"should display notification");
assert.strictEqual($notification.find('.o_close').length, 0, "should not display the close button in ");
setTimeout(function () {
@@ -68,7 +68,7 @@ QUnit.module('Services', {
});
var $notification = $('body .o_notification_manager .o_notification');
assert.strictEqual(_.str.trim($notification.html().replace(/\s+/g, ' ')),
- "<div class=\"o_notification_title\"> <span class=\"o_icon fa fa-3x fa-exclamation\"></span> a </div> <div class=\"o_notification_content\">b</div>",
+ "<div class=\"o_notification_title\"> <span role=\"img\" aria-label=\"Notification undefined\" class=\"o_icon fa fa-3x fa-exclamation\" title=\"Notification undefined\"></span> a </div> <div class=\"o_notification_content\">b</div>",
"should display notification");
view.destroy();
setTimeout(done);
@@ -184,7 +184,7 @@ QUnit.module('Services', {
var $notification = $('body .o_notification_manager .o_notification');
assert.strictEqual($notification.eq(0).find('.o_close').length, 1, "should display the close button in notification");
assert.strictEqual(_.str.trim($notification.eq(0).html().replace(/\s+/g, ' ')),
- "<a class=\"fa fa-times o_close\" href=\"#\"></a> <div class=\"o_notification_title\"> <span class=\"o_icon fa fa-3x fa-question-circle-o\"></span> a0 </div> <div class=\"o_notification_content\">b0</div> <div class=\"o_buttons\"> <button type=\"button\" class=\"btn btn-sm btn-primary\"> <span>accept0</span> </button><button type=\"button\" class=\"btn btn-sm btn-default\"> <span>refuse0</span> </button> </div>",
+ "<a aria-label=\"Close\" class=\"fa fa-times o_close\" href=\"#\" title=\"Close\"></a> <div class=\"o_notification_title\"> <span role=\"img\" aria-label=\"Notification undefined\" class=\"o_icon fa fa-3x fa-question-circle-o\" title=\"Notification undefined\"></span> a0 </div> <div class=\"o_notification_content\">b0</div> <div class=\"o_buttons\"> <button type=\"button\" class=\"btn btn-sm btn-primary\"> <span>accept0</span> </button><button type=\"button\" class=\"btn btn-sm btn-default\"> <span>refuse0</span> </button> </div>",
"should display notification");
$notification.find('.o_buttons button:contains(accept0)').click();
diff --git a/addons/web/static/tests/views/calendar_tests.js b/addons/web/static/tests/views/calendar_tests.js
index 0a7ca319c2f..5a07924413e 100644
--- a/addons/web/static/tests/views/calendar_tests.js
+++ b/addons/web/static/tests/views/calendar_tests.js
@@ -213,8 +213,8 @@ QUnit.module('Views', {
$sidebar.find('input[type="text"]').trigger('click');
assert.strictEqual($('ul.ui-autocomplete li:not(.o_m2o_dropdown_option)').text(), "partner 4", "should display the last choice in one2many autocomplete"); // TODO: remove :not(.o_m2o_dropdown_option) because can't have "create & edit" choice
$sidebar.find('.o_calendar_filter_item .o_remove').first().trigger('click');
- assert.ok($('.modal button.btn:contains(Ok)').length, "should display the confirm message");
- $('.modal button.btn:contains(Ok)').trigger('click');
+ assert.ok($('footer.modal-footer button.btn:contains(Ok)').length, "should display the confirm message");
+ $('footer.modal-footer button.btn:contains(Ok)').trigger('click');
assert.strictEqual($sidebar.find('.o_calendar_filter:has(h3:contains(attendees)) .o_calendar_filter_item').length, 3, "click on remove then should display 3 filter items for 'attendees'");
calendar.destroy();
});
@@ -295,19 +295,19 @@ QUnit.module('Views', {
calendar.$('.fc-event:contains(event 4) .fc-content').trigger('click');
- assert.ok($('.modal-body').length, "should open the form view in dialog when click on event");
- assert.ok($('.modal button.btn:contains(Edit)').length, "formViewDialog should be in readonly mode");
- assert.ok($('.modal button.btn:contains(Delete)').length, "formViewDialog should have a delete button");
+ assert.ok($('main.modal-body').length, "should open the form view in dialog when click on event");
+ assert.ok($('footer.modal-footer button.btn:contains(Edit)').length, "formViewDialog should be in readonly mode");
+ assert.ok($('footer.modal-footer button.btn:contains(Delete)').length, "formViewDialog should have a delete button");
- $('.modal button.btn:contains(Edit)').trigger('click');
+ $('footer.modal-footer button.btn:contains(Edit)').trigger('click');
- assert.ok($('.modal-body').length, "should switch the modal in edit mode");
- assert.notOk($('.modal button.btn:contains(Delete)').length, "formViewDialog should not have a delete button in edit mode");
+ assert.ok($('main.modal-body').length, "should switch the modal in edit mode");
+ assert.notOk($('footer.modal-footer button.btn:contains(Delete)').length, "formViewDialog should not have a delete button in edit mode");
- $('.modal-body input:first').val('event 4 modified').trigger('input');
- $('.modal button.btn:contains(Save)').trigger('click');
+ $('main.modal-body input:first').val('event 4 modified').trigger('input');
+ $('footer.modal-footer button.btn:contains(Save)').trigger('click');
- assert.notOk($('.modal-body').length, "save button should close the modal");
+ assert.notOk($('main.modal-body').length, "save button should close the modal");
assert.ok(calendar.$('.fc-event:contains(event 4 modified)').length, "should display the updated records");
// create a new event, quick create only
@@ -317,12 +317,12 @@ QUnit.module('Views', {
testUtils.triggerMouseEvent($cell, "mousedown");
testUtils.triggerMouseEvent($cell, "mouseup");
- assert.ok($('.modal-dialog.modal-sm').length, "should open the quick create dialog");
+ assert.ok($('.modal-sm').length, "should open the quick create dialog");
- $('.modal-body input:first').val('new event in quick create').trigger('input');
- $('.modal button.btn:contains(Create)').trigger('click').trigger('click');
+ $('main.modal-body input:first').val('new event in quick create').trigger('input');
+ $('footer.modal-footer button.btn:contains(Create)').trigger('click').trigger('click');
- assert.strictEqual(calendar.$('.fc-event:contains(new event in quick create)').length, 1, "should display the new record");
+ assert.strictEqual(calendar.$('.fc-event:contains(new event in quick create)').length, 1, "should display the new record after quick create");
assert.strictEqual(calendar.$('td.fc-event-container[colspan]').length, 2, "should the new record have only one day");
// create a new event, quick create only (validated by pressing enter key)
@@ -330,9 +330,9 @@ QUnit.module('Views', {
testUtils.triggerMouseEvent($cell, "mousedown");
testUtils.triggerMouseEvent($cell, "mouseup");
- assert.ok($('.modal-dialog.modal-sm').length, "should open the quick create dialog");
+ assert.ok($('.modal-sm').length, "should open the quick create dialog");
- $('.modal-body input:first')
+ $('main.modal-body input:first')
.val('new event in quick create validated by pressing enter key.')
.trigger($.Event('keyup', {keyCode: $.ui.keyCode.ENTER}))
.trigger($.Event('keyup', {keyCode: $.ui.keyCode.ENTER}));
@@ -347,20 +347,20 @@ QUnit.module('Views', {
testUtils.triggerMouseEvent($cell, "mousedown");
testUtils.triggerMouseEvent($cell, "mouseup");
- assert.strictEqual($('.modal-dialog.modal-sm').length, 1, "should open the quick create dialog");
+ assert.strictEqual($('.modal-sm').length, 1, "should open the quick create dialog");
- $('.modal-body input:first').val('coucou').trigger('input');
- $('.modal button.btn:contains(Edit)').trigger('click');
+ $('main.modal-body input:first').val('coucou').trigger('input');
+ $('footer.modal-footer button.btn:contains(Edit)').trigger('click');
- assert.strictEqual($('.modal-dialog.modal-lg .o_form_view').length, 1, "should open the slow create dialog");
- assert.strictEqual($('.modal-dialog.modal-lg .modal-title').text(), "Create: Events",
+ assert.strictEqual($('.modal-lg .o_form_view').length, 1, "should open the slow create dialog");
+ assert.strictEqual($('.modal-lg .modal-title').text(), "Create: Events",
"should use the string attribute as modal title");
- assert.strictEqual($('.modal-dialog.modal-lg .o_form_view input[name="name"]').val(), "coucou",
+ assert.strictEqual($('.modal-lg .o_form_view input[name="name"]').val(), "coucou",
"should have set the name from the quick create dialog");
$('.modal-lg button.btn:contains(Save)').trigger('click');
- assert.strictEqual(calendar.$('.fc-event:contains(coucou)').length, 1, "should display the new record");
+ assert.strictEqual(calendar.$('.fc-event:contains(coucou)').length, 1, "should display the new record with string attribute");
// create a new event with 2 days
@@ -370,14 +370,14 @@ QUnit.module('Views', {
testUtils.triggerMouseEvent($cell.next(), "mousemove");
testUtils.triggerMouseEvent($cell.next(), "mouseup");
- $('.modal input:first').val('new event in quick create 2').trigger('input');
- $('.modal button.btn:contains(Edit)').trigger('click');
+ $('.modal-dialog input:first').val('new event in quick create 2').trigger('input');
+ $('footer.modal-footer button.btn:contains(Edit)').trigger('click');
assert.strictEqual($('.modal-lg input:first').val(), 'new event in quick create 2', "should open the formViewDialog with default values");
$('.modal-lg button.btn:contains(Save)').trigger('click');
- assert.notOk($('.modal').length, "should close dialogs");
+ assert.notOk($('[role="dialog"]').length, "should close dialogs");
var $newevent2 = calendar.$('.fc-event:contains(new event in quick create 2)');
assert.ok($newevent2.length, "should display the 2 days new record");
assert.strictEqual($newevent2.closest('.fc-event-container').attr('colspan'), "2", "the new record should have 2 days");
@@ -385,9 +385,9 @@ QUnit.module('Views', {
// delete the a record
calendar.$('.fc-event:contains(event 4) .fc-content').trigger('click');
- $('.modal button.btn:contains(Delete)').trigger('click');
- assert.ok($('.modal button.btn:contains(Ok)').length, "should display the confirm message");
- $('.modal button.btn:contains(Ok)').trigger('click');
+ $('footer.modal-footer button.btn:contains(Delete)').trigger('click');
+ assert.ok($('footer.modal-footer button.btn:contains(Ok)').length, "should display the confirm message");
+ $('footer.modal-footer button.btn:contains(Ok)').trigger('click');
assert.notOk(calendar.$('.fc-event:contains(event 4) .fc-content').length, "the record should be deleted");
assert.strictEqual(calendar.$('.fc-event-container .fc-event').length, 10, "should display 10 events");
@@ -439,18 +439,18 @@ QUnit.module('Views', {
testUtils.triggerMouseEvent($cell, "mousedown");
testUtils.triggerMouseEvent($cell, "mouseup");
- assert.strictEqual($('.modal-dialog.modal-sm .modal-title').text(), 'Create: Events',
+ assert.strictEqual($('.modal-sm .modal-title').text(), 'Create: Events',
"should open the quick create dialog");
- $('.modal-body input:first').val('new event in quick create').trigger('input');
- $('.modal button.btn:contains(Create)').trigger('click').trigger('click');
+ $('main.modal-body input:first').val('new event in quick create').trigger('input');
+ $('footer.modal-footer button.btn:contains(Create)').trigger('click').trigger('click');
// If the event is not default-prevented, a traceback will be raised, which we do not want
assert.ok(event.isDefaultPrevented(), "fail deferred event should have been default-prevented");
- assert.strictEqual($('.modal-dialog.modal-lg .modal-title').text(), 'Create: Events',
+ assert.strictEqual($('.modal-lg .modal-title').text(), 'Create: Events',
"should have switched to a bigger modal for an actual create rather than quickcreate");
- assert.strictEqual($('.modal-dialog.modal-lg .modal-body .o_form_view.o_form_editable').length, 1,
+ assert.strictEqual($('.modal-lg main .o_form_view.o_form_editable').length, 1,
"should open the full event form view in a dialog");
calendar.destroy();
@@ -1328,7 +1328,7 @@ QUnit.module('Views', {
var $cell = calendar.$('.fc-day-grid .fc-row:eq(4) .fc-day:eq(2)');
testUtils.triggerMouseEvent($cell, "mousedown");
testUtils.triggerMouseEvent($cell, "mouseup");
- $('.modal-body input:first').val('coucou').trigger('input');
+ $('main.modal-body input:first').val('coucou').trigger('input');
testUtils.intercept(calendar, 'do_action', function (event) {
assert.deepEqual(event.data.action,
@@ -1385,7 +1385,7 @@ QUnit.module('Views', {
var $cell = calendar.$('.fc-day-grid .fc-row:eq(4) .fc-day:eq(2)');
testUtils.triggerMouseEvent($cell, "mousedown");
testUtils.triggerMouseEvent($cell, "mouseup");
- $('.modal-body input:first').val('coucou').trigger('input');
+ $('main.modal-body input:first').val('coucou').trigger('input');
testUtils.intercept(calendar, 'do_action', function (event) {
assert.deepEqual(event.data.action,
@@ -1463,7 +1463,7 @@ QUnit.module('Views', {
var $cell = calendar.$('.fc-day-grid .fc-row:eq(4) .fc-day:eq(2)');
testUtils.triggerMouseEvent($cell, "mousedown");
testUtils.triggerMouseEvent($cell, "mouseup");
- $('.modal-body input:first').val('coucou').trigger('input');
+ $('main.modal-body input:first').val('coucou').trigger('input');
testUtils.intercept(calendar, 'do_action', function (event) {
assert.deepEqual(event.data.action,
@@ -1600,8 +1600,8 @@ QUnit.module('Views', {
testUtils.triggerPositionalMouseEvent(left, top + 200, "mousemove");
testUtils.triggerPositionalMouseEvent(left, top + 200, "mouseup");
- $('.modal-body input:first').val('coucou').trigger('input');
- $('.modal button.btn:contains(Create)').trigger('click');
+ $('main.modal-body input:first').val('coucou').trigger('input');
+ $('footer.modal-footer button.btn:contains(Create)').trigger('click');
assert.strictEqual(calendar.$('.o_calendar_filter_item').length, 6, "should add the missing filter (active)");
assert.strictEqual(calendar.$('.fc-event').length, 4, "should display the created item");
@@ -1617,8 +1617,8 @@ QUnit.module('Views', {
testUtils.triggerPositionalMouseEvent(left, top + 200, "mousemove");
testUtils.triggerPositionalMouseEvent(left, top + 200, "mouseup");
- $('.modal-body input:first').val('coucou 2').trigger('input');
- $('.modal button.btn:contains(Create)').trigger('click');
+ $('main.modal-body input:first').val('coucou 2').trigger('input');
+ $('footer.modal-footer button.btn:contains(Create)').trigger('click');
assert.strictEqual(calendar.$('.o_calendar_filter_item').length, 6, "should have the same filters");
assert.strictEqual(calendar.$('.fc-event').length, 4, "should not display the created item");
@@ -1697,10 +1697,10 @@ QUnit.module('Views', {
testUtils.triggerPositionalMouseEvent(left, top + 200, "mousemove");
testUtils.triggerPositionalMouseEvent(left, top + 200, "mouseup");
- $('.modal-body input:first').val('coucou').trigger('input');
+ $('main.modal-body input:first').val('coucou').trigger('input');
- $('.modal button.btn:contains(Edit)').trigger('click');
- $('.modal button.btn:contains(Save)').trigger('click');
+ $('footer.modal-footer button.btn:contains(Edit)').trigger('click');
+ $('footer.modal-footer button.btn:contains(Save)').trigger('click');
assert.strictEqual(calendar.$('.o_calendar_filter_item').length, 6, "should add the missing filter (active)");
assert.strictEqual(calendar.$('.fc-event').length, 4, "should display the created item");
@@ -1906,10 +1906,10 @@ QUnit.module('Views', {
"should open the quick create dialog");
// Creating the event
- $('.modal-body input:first').val('new event in quick create').trigger('input');
- $('.modal button.btn:contains(Create)').trigger('click').trigger('click');
+ $('main.modal-body input:first').val('new event in quick create').trigger('input');
+ $('footer.modal-footer button.btn:contains(Create)').trigger('click').trigger('click');
assert.strictEqual(calendar.$('.fc-event:contains(new event in quick create)').length, 1,
- "should display the new record");
+ "should display the new record after quick create dialog");
calendar.destroy();
$view.remove();
@@ -1989,14 +1989,14 @@ QUnit.module('Views', {
var $cell = calendar.$('.fc-day-grid .fc-row:eq(2) .fc-day:eq(2)');
testUtils.triggerMouseEvent($cell, "mousedown");
testUtils.triggerMouseEvent($cell, "mouseup");
- var $input = $('.modal-body input:first');
+ var $input = $('[role="dialog"] input:first');
$input.val('new event in quick create').trigger('input');
// Simulate ENTER pressed on Create button (after a TAB)
$input.trigger($.Event('keyup', {
which: $.ui.keyCode.ENTER,
keyCode: $.ui.keyCode.ENTER,
}));
- $('.modal-footer button:first').click();
+ $('footer.modal-footer button:first').click();
def.resolve();
assert.strictEqual(createCount, 1,
"should create only one event");
@@ -2221,7 +2221,7 @@ QUnit.module('Views', {
var $cell = calendar.$('.fc-day-grid .fc-row:eq(2) .fc-day:eq(2)');
testUtils.triggerMouseEvent($cell, "mousedown");
testUtils.triggerMouseEvent($cell, "mouseup");
- var $input = $('.modal-body input:first');
+ var $input = $('main.modal-body input:first');
$input.val("It's just a fleshwound").trigger('input');
$('.modal button.btn:contains(Create)').trigger('click');
@@ -2268,7 +2268,7 @@ QUnit.module('Views', {
var $cell = calendar.$('.fc-day-grid .fc-row:eq(2) .fc-day:eq(2)');
testUtils.triggerMouseEvent($cell, "mousedown");
testUtils.triggerMouseEvent($cell, "mouseup");
- var $input = $('.modal-body input:first');
+ var $input = $('main.modal-body input:first');
$input.val("It's just a fleshwound").trigger('input');
$('.modal button.btn:contains(Create)').trigger('click');
diff --git a/addons/web/static/tests/views/form_tests.js b/addons/web/static/tests/views/form_tests.js
index 18c82adb0a5..44ac724995b 100644
--- a/addons/web/static/tests/views/form_tests.js
+++ b/addons/web/static/tests/views/form_tests.js
@@ -2131,13 +2131,13 @@ QUnit.module('Views', {
"should display the name of the many2many on the original form");
form.$('td:contains(gold)').click();
- assert.strictEqual($('.modal-dialog').length, 1,
+ assert.strictEqual($('[role="dialog"]').length, 1,
'The partner_type modal should have opened');
- assert.strictEqual($('.modal-dialog').find('td:contains(first record)').length, 1,
+ assert.strictEqual($('[role="dialog"]').find('td:contains(first record)').length, 1,
"should display the name of the many2many on the modal form");
- $('.modal-dialog').find('td:contains(first record)').click();
- assert.strictEqual($('.modal-dialog').length, 2,
+ $('[role="dialog"]').find('td:contains(first record)').click();
+ assert.strictEqual($('[role="dialog"]').length, 2,
'There should be 2 modals (partner on top of partner_type) opened');
form.destroy();
@@ -2167,7 +2167,7 @@ QUnit.module('Views', {
// click on discard
form.$buttons.find('.o_form_button_cancel').click();
- assert.ok(!$('.modal:visible').length, 'no confirm modal should be displayed');
+ assert.ok(!$('[role="dialog"]:visible').length, 'no confirm modal should be displayed');
assert.strictEqual(form.$('.o_field_widget').text(), 'yop', 'field in readonly should display yop');
assert.strictEqual(nbWrite, 0, 'no write RPC should have been done');
@@ -2200,14 +2200,14 @@ QUnit.module('Views', {
// click on discard and cancel the confirm request
form.$buttons.find('.o_form_button_cancel').click();
- assert.ok($('.modal').length, 'a confirm modal should be displayed');
- $('.modal .modal-footer .btn-default').click(); // click on cancel
+ assert.ok($('[role="dialog"]').length, 'a confirm modal should be displayed');
+ $('footer.modal-footer .btn-default').click(); // click on cancel
assert.strictEqual(form.$('input').val(), 'new value', 'input should still contain new value');
// click on discard and confirm
form.$buttons.find('.o_form_button_cancel').click();
- assert.ok($('.modal').length, 'a confirm modal should be displayed');
- $('.modal .modal-footer .btn-primary').click(); // click on confirm
+ assert.ok($('[role="dialog"]').length, 'a confirm modal should be displayed');
+ $('footer.modal-footer .btn-primary').click(); // click on confirm
assert.strictEqual(form.$('.o_field_widget').text(), 'yop', 'field in readonly should display yop');
assert.strictEqual(nbWrite, 0, 'no write RPC should have been done');
@@ -2287,7 +2287,7 @@ QUnit.module('Views', {
// click on discard and confirm
form.$buttons.find('.o_form_button_cancel').click();
- $('.modal .modal-footer .btn-primary').click(); // click on confirm
+ $('footer.modal-footer .btn-primary').click(); // click on confirm
assert.notOk(form.$el.prop('outerHTML').match('xphone'),
"the string xphone should not be present after discarding");
@@ -2317,7 +2317,7 @@ QUnit.module('Views', {
form.$buttons.find('.o_form_button_cancel').click();
- assert.strictEqual($('.modal').length, 0,
+ assert.strictEqual($('[role="dialog"]').length, 0,
'there should not be a confirm modal');
form.destroy();
@@ -2351,17 +2351,17 @@ QUnit.module('Views', {
// discard the changes and check it has properly been discarded
form.$buttons.find('.o_form_button_cancel').click();
- assert.strictEqual($('.modal').length, 1,
+ assert.strictEqual($('[role="dialog"]').length, 1,
'there should be a confirm modal');
assert.strictEqual(form.$('input').val(), 'DEF', 'input should be DEF');
- $('.modal .modal-footer .btn-primary').click(); // click on confirm
+ $('footer.modal-footer .btn-primary').click(); // click on confirm
assert.strictEqual(form.$('input').val(), 'ABC', 'input should now be ABC');
// redirty and discard the field foo (to make sure initial changes haven't been lost)
form.$('input').val('GHI').trigger('input');
form.$buttons.find('.o_form_button_cancel').click();
assert.strictEqual(form.$('input').val(), 'GHI', 'input should be GHI');
- $('.modal .modal-footer .btn-primary').click(); // click on confirm
+ $('footer.modal-footer .btn-primary').click(); // click on confirm
assert.strictEqual(form.$('input').val(), 'ABC', 'input should now be ABC');
form.destroy();
@@ -2395,7 +2395,7 @@ QUnit.module('Views', {
form.$buttons.find('.o_form_button_cancel').click();
- assert.strictEqual($('.modal').length, 0,
+ assert.strictEqual($('[role="dialog"]').length, 0,
'there should not be a confirm modal');
form.destroy();
@@ -2436,15 +2436,15 @@ QUnit.module('Views', {
// click on the pager to switch to the next record and cancel the confirm request
form.pager.$('.o_pager_next').click(); // click on next
- assert.ok($('.modal').length, 'a confirm modal should be displayed');
- $('.modal .modal-footer .btn-default').click(); // click on cancel
+ assert.ok($('[role="dialog"]').length, 'a confirm modal should be displayed');
+ $('footer.modal-footer .btn-default').click(); // click on cancel
assert.strictEqual(form.$('input').val(), 'new value', 'input should still contain new value');
assert.strictEqual(form.pager.$('.o_pager_value').text(), "1", 'pager value should still be 1');
// click on the pager to switch to the next record and confirm
form.pager.$('.o_pager_next').click(); // click on next
- assert.ok($('.modal').length, 'a confirm modal should be displayed');
- $('.modal .modal-footer .btn-primary').click(); // click on confirm
+ assert.ok($('[role="dialog"]').length, 'a confirm modal should be displayed');
+ $('footer.modal-footer .btn-primary').click(); // click on confirm
assert.strictEqual(form.$('input').val(), 'blip', 'input should contain blip');
assert.strictEqual(form.pager.$('.o_pager_value').text(), "2", 'pager value should be 2');
@@ -2483,7 +2483,7 @@ QUnit.module('Views', {
// click on the pager to switch to the next record and cancel the confirm request
form.pager.$('.o_pager_next').click(); // click on next
- assert.strictEqual($('.modal:visible').length, 0, 'no confirm modal should be displayed');
+ assert.strictEqual($('[role="dialog"]:visible').length, 0, 'no confirm modal should be displayed');
assert.strictEqual(form.pager.$('.o_pager_value').text(), "2", 'pager value should be 2');
assert.strictEqual(form.$('.o_priority .fa-star-o').length, 2,
@@ -2495,7 +2495,7 @@ QUnit.module('Views', {
'priority widget should have been updated');
form.pager.$('.o_pager_next').click(); // click on next
- assert.strictEqual($('.modal:visible').length, 0, 'no confirm modal should be displayed');
+ assert.strictEqual($('[role="dialog"]:visible').length, 0, 'no confirm modal should be displayed');
assert.strictEqual(form.pager.$('.o_pager_value').text(), "1", 'pager value should be 1');
// switch to edit mode
@@ -2506,8 +2506,8 @@ QUnit.module('Views', {
form.$('input').val('wrong value').trigger('input');
form.$buttons.find('.o_form_button_cancel').click();
- assert.strictEqual($('.modal').length, 1, 'a confirm modal should be displayed');
- $('.modal .modal-footer .btn-primary').click(); // click on confirm
+ assert.strictEqual($('[role="dialog"]').length, 1, 'a confirm modal should be displayed');
+ $('footer.modal-footer .btn-primary').click(); // click on confirm
form.pager.$('.o_pager_next').click(); // click on next
assert.strictEqual(form.pager.$('.o_pager_value').text(), "2", 'pager value should be 2');
form.destroy();
@@ -2736,16 +2736,16 @@ QUnit.module('Views', {
assert.strictEqual(form.pager.$('.o_pager_limit').text(), "3", 'pager limit should be 3');
assert.strictEqual(form.$('span:contains(yop)').length, 1,
'should have a field with foo value for record 1');
- assert.ok(!$('.modal:visible').length, 'no confirm modal should be displayed');
+ assert.ok(!$('[role="dialog"]:visible').length, 'no confirm modal should be displayed');
// open sidebar
form.sidebar.$('button.o_dropdown_toggler_btn').click();
form.sidebar.$('a:contains(Delete)').click();
- assert.ok($('.modal').length, 'a confirm modal should be displayed');
+ assert.ok($('[role="dialog"]').length, 'a confirm modal should be displayed');
// confirm the delete
- $('.modal .modal-footer button.btn-primary').click();
+ $('footer.modal-footer button.btn-primary').click();
assert.strictEqual(form.pager.$('.o_pager_value').text(), "1", 'pager value should be 1');
assert.strictEqual(form.pager.$('.o_pager_limit').text(), "2", 'pager limit should be 2');
@@ -2784,9 +2784,9 @@ QUnit.module('Views', {
testUtils.intercept(form, 'history_back', function () {
assert.step('history_back');
});
- assert.strictEqual($('.modal').length, 1, 'a confirm modal should be displayed');
- $('.modal .modal-footer button.btn-primary').click();
- assert.strictEqual($('.modal').length, 0, 'no confirm modal should be displayed');
+ assert.strictEqual($('[role="dialog"]').length, 1, 'a confirm modal should be displayed');
+ $('footer.modal-footer button.btn-primary').click();
+ assert.strictEqual($('[role="dialog"]').length, 0, 'no confirm modal should be displayed');
assert.verifySteps(['read', 'unlink', 'history_back']);
form.destroy();
@@ -3354,7 +3354,7 @@ QUnit.module('Views', {
// open a subrecord and trigger an onchange
readInModal = true;
form.$('.o_data_row .o_data_cell:first').click();
- $('.modal .o_field_widget[name=name]').val("new name").trigger('input');
+ $('[role="dialog"] .o_field_widget[name=name]').val("new name").trigger('input');
form.destroy();
});
@@ -3582,7 +3582,7 @@ QUnit.module('Views', {
// discard changes
form.$buttons.find('.o_form_button_cancel').click();
- $('.modal .modal-footer .btn-primary').click();
+ $('footer.modal-footer .btn-primary').click();
assert.strictEqual(form.$('span[name="foo"]').text(), "blip",
"field foo should still be displayed to initial value");
@@ -3627,13 +3627,13 @@ QUnit.module('Views', {
assert.strictEqual(form.$('.o_field_widget[name="foo"]').val(), "1234",
"field foo should still contain new value");
- assert.strictEqual($('.modal').length, 0,
+ assert.strictEqual($('[role="dialog"]').length, 0,
"Confirm dialog should not be displayed");
// complete the write
def.resolve();
- assert.strictEqual($('.modal').length, 0,
+ assert.strictEqual($('[role="dialog"]').length, 0,
"Confirm dialog should not be displayed");
assert.strictEqual(form.$('.o_field_widget[name="foo"]').text(), "1234",
"value should have been saved and rerendered in readonly");
@@ -3804,9 +3804,9 @@ QUnit.module('Views', {
"onchange should have been correctly applied on field in o2m list");
form.$('.o_data_row').click(); // edit the o2m in the dialog
- assert.strictEqual($('.modal .modal-title').text().trim(), 'Open: one2many field',
+ assert.strictEqual($('[role="dialog"] .modal-title').text().trim(), 'Open: one2many field',
"the field string is displayed in the modal title");
- assert.strictEqual($('.modal .o_field_widget').val(), 'foo changed',
+ assert.strictEqual($('[role="dialog"] .o_field_widget').val(), 'foo changed',
"the onchange value hasn't been discarded when opening the o2m");
form.destroy();
@@ -3898,20 +3898,20 @@ QUnit.module('Views', {
"the initial value should be the default one");
form.$('.o_data_row td:first').click(); // edit the o2m in a dialog
- $('.modal input:nth(1)').val(77).trigger('input');
- assert.strictEqual($('.modal input:first').val(), '[blip] 77',
+ $('[role="dialog"] input:nth(1)').val(77).trigger('input');
+ assert.strictEqual($('[role="dialog"] input:first').val(), '[blip] 77',
"onchange should have been correctly applied");
- $('.modal .modal-footer .btn-primary').click(); // save the dialog
+ $('footer.modal-footer .btn-primary').click(); // save the dialog
assert.strictEqual(form.$('.o_data_row td:first').text(), '[blip] 77',
"onchange should have been correctly applied");
// create a new o2m record
form.$('.o_field_x2many_list_row_add a').click();
- assert.strictEqual($('.modal .modal-title').text().trim(), 'Create custom label',
+ assert.strictEqual($('[role="dialog"] .modal-title').text().trim(), 'Create custom label',
"the custom field label is applied in the modal title");
- assert.strictEqual($('.modal input:first').val(), '[blip] 14',
+ assert.strictEqual($('[role="dialog"] input:first').val(), '[blip] 14',
"onchange should have been correctly applied after default get");
- $('.modal .modal-footer .btn-primary').click(); // save the dialog
+ $('footer.modal-footer .btn-primary').click(); // save the dialog
assert.strictEqual(form.$('.o_data_row:nth(1) td:first').text(), '[blip] 14',
"onchange should have been correctly applied after default get");
@@ -4340,10 +4340,10 @@ QUnit.module('Views', {
// add a o2m subrecord
form.$('.o_field_x2many_list_row_add a').click();
- $('.modal-body .o_field_one2many .o_field_x2many_list_row_add a').click();
- $('.modal-body input').val('xtv').trigger('input');
- $('.modal-footer button:first').click(); // save & close
- assert.strictEqual($('.modal').length, 0,
+ $('main.modal-body .o_field_one2many .o_field_x2many_list_row_add a').click();
+ $('main.modal-body input').val('xtv').trigger('input');
+ $('footer.modal-footer button:first').click(); // save & close
+ assert.strictEqual($('[role="dialog"]').length, 0,
"dialog should be closed");
var row = form.$('.o_field_one2many .o_list_view .o_data_row');
@@ -4639,7 +4639,7 @@ QUnit.module('Views', {
assert.strictEqual(row.children()[1].textContent, '1 record',
"the cell should contains the number of record: 1");
row.click();
- var modal_row = $('.modal-body .o_form_sheet .o_field_one2many .o_list_view .o_data_row');
+ var modal_row = $('main.modal-body .o_form_sheet .o_field_one2many .o_list_view .o_data_row');
assert.strictEqual(modal_row.children().length, 2,
"the row should contains the 2 fields defined in the form view");
assert.strictEqual($(modal_row).text(), "gold2",
@@ -4986,29 +4986,29 @@ QUnit.module('Views', {
form.$buttons.find('.o_form_button_edit').click();
// Open first dialog
form.$('.o_external_button').click();
- assert.strictEqual($('.modal').length, 1,
+ assert.strictEqual($('[role="dialog"]').length, 1,
"One FormViewDialog should be opened");
- var $firstModal = $('.modal');
- assert.strictEqual($('.modal .modal-title').first().text().trim(), 'Open: Product',
+ var $firstModal = $('[role="dialog"]');
+ assert.strictEqual($('[role="dialog"] .modal-title').first().text().trim(), 'Open: Product',
"dialog title should display the python field string as label");
assert.strictEqual($firstModal.find('input').val(), 'xphone',
"display_name should be correctly displayed");
// Open second dialog
$firstModal.find('.o_field_x2many_list_row_add a').click();
- assert.strictEqual($('.modal').length, 2,
+ assert.strictEqual($('[role="dialog"]').length, 2,
"two FormViewDialogs should be opened");
- var $secondModal = $('.modal:nth(1)');
+ var $secondModal = $('[role="dialog"]:nth(1)');
// Add new value
$secondModal.find('input').val('xtv').trigger('input');
- $secondModal.find('.modal-footer button:first').click(); // Save & close
- assert.strictEqual($('.modal').length, 1,
+ $secondModal.find('footer.modal-footer button:first').click(); // Save & close
+ assert.strictEqual($('[role="dialog"]').length, 1,
"last opened dialog should be closed");
// Check that data in first dialog is correctly updated
assert.strictEqual($firstModal.find('tr.o_data_row td').text(), 'xtv',
"should have added a line with xtv as new record");
- $firstModal.find('.modal-footer button:first').click(); // Save & close
+ $firstModal.find('footer.modal-footer button:first').click(); // Save & close
form.destroy();
});
@@ -5210,12 +5210,12 @@ QUnit.module('Views', {
"record 1 should be first");
form.$buttons.find('.o_form_button_edit').click();
form.$('.o_field_x2many_list_row_add a').click();
- assert.strictEqual($('.modal').length, 1,
+ assert.strictEqual($('[role="dialog"]').length, 1,
"FormViewDialog should be opened");
- $('.modal input[name="foo"]').val('xop').trigger("input");
- $('.modal .modal-footer button:eq(1)').click(); // Save & new
- $('.modal input[name="foo"]').val('zop').trigger("input");
- $('.modal .modal-footer button:first').click(); // Save & close
+ $('[role="dialog"] input[name="foo"]').val('xop').trigger("input");
+ $('footer.modal-footer button:eq(1)').click(); // Save & new
+ $('[role="dialog"] input[name="foo"]').val('zop').trigger("input");
+ $('footer.modal-footer button:first').click(); // Save & close
// client-side sort
assert.ok(form.$('.o_field_one2many tbody tr:eq(0) td:contains(zop)').length,
@@ -5237,8 +5237,8 @@ QUnit.module('Views', {
// client-side sort on edit
form.$buttons.find('.o_form_button_edit').click();
form.$('.o_field_one2many tbody tr:eq(1) td:contains(yop)').click();
- $('.modal input[name="foo"]').val('zzz').trigger("input");
- $('.modal .modal-footer button:first').click(); // Save
+ $('[role="dialog"] input[name="foo"]').val('zzz').trigger("input");
+ $('footer.modal-footer button:first').click(); // Save
assert.ok(form.$('.o_field_one2many tbody tr:eq(0) td:contains(zzz)').length,
"record zzz should be first");
assert.ok(form.$('.o_field_one2many tbody tr:eq(1) td:contains(zop)').length,
@@ -5524,7 +5524,7 @@ QUnit.module('Views', {
});
form.$('.o_data_row:first').click();
- assert.strictEqual($('.modal .modal-title').first().text().trim(), 'Open: custom label',
+ assert.strictEqual($('[role="dialog"] .modal-title').first().text().trim(), 'Open: custom label',
"modal should use the python field string as title");
form.destroy();
@@ -5610,10 +5610,10 @@ QUnit.module('Views', {
form.$buttons.find('.o_form_button_edit').click();
form.$('[name="product_id"] .o_external_button').click();
- assert.strictEqual($('.modal-body .o_field_translate').length, 1,
+ assert.strictEqual($('main.modal-body .o_field_translate').length, 1,
"there should be a translate button in the modal");
- $('.modal-body .o_field_translate').click();
+ $('main.modal-body .o_field_translate').click();
assert.strictEqual(nbTranslateCalls, 1, "should call_button translate once");
@@ -5780,7 +5780,7 @@ QUnit.module('Views', {
form.$('.o_statusbar_buttons button').click();
assert.ok(form.$('.o_statusbar_buttons button').prop('disabled'),
'button should be disabled');
- $('.modal .modal-footer button.btn-default').click();
+ $('footer.modal-footer button.btn-default').click();
assert.ok(!form.$('.o_statusbar_buttons button').prop('disabled'),
'button should no longer be disabled');
@@ -5789,7 +5789,7 @@ QUnit.module('Views', {
// click on button, and click on ok in confirm dialog
form.$('.o_statusbar_buttons button').click();
assert.verifySteps(['default_get']);
- $('.modal .modal-footer button.btn-primary').click();
+ $('footer.modal-footer button.btn-primary').click();
assert.verifySteps(['default_get', 'create', 'read', 'execute_action']);
@@ -5845,17 +5845,17 @@ QUnit.module('Views', {
form.$('.o_external_button').click();
- assert.notOk($('.modal .oe_button_box button').attr('disabled'),
+ assert.notOk($('[role="dialog"] .oe_button_box button').attr('disabled'),
"stat buttons should be enabled");
- $('.modal .oe_button_box button').click();
+ $('[role="dialog"] .oe_button_box button').click();
- assert.ok($('.modal .oe_button_box button').attr('disabled'),
+ assert.ok($('[role="dialog"] .oe_button_box button').attr('disabled'),
"stat buttons should be disabled");
def.resolve();
- assert.notOk($('.modal .oe_button_box button').attr('disabled'),
+ assert.notOk($('[role="dialog"] .oe_button_box button').attr('disabled'),
"stat buttons should be enabled");
form.destroy();
@@ -6034,16 +6034,16 @@ QUnit.module('Views', {
form.$buttons.find('.o_form_button_edit').click();
form.$('.o_field_x2many_list_row_add a').click();
- assert.strictEqual($('.modal').length, 1,
+ assert.strictEqual($('[role="dialog"]').length, 1,
"One FormViewDialog should be opened");
// set a value on the m2o
var $dropdown = form.$('.o_field_many2one input').autocomplete('widget');
- $('.modal .o_field_many2one input').click();
+ $('[role="dialog"] .o_field_many2one input').click();
$dropdown.find('li:first()').click();
- $('.modal .modal-footer button:eq(1)').click(); // Save & new
- $('.modal .o_field_many2one input').click();
- $('.modal .modal-footer button:first').click(); // Save & close
+ $('footer.modal-footer button:eq(1)').click(); // Save & new
+ $('[role="dialog"] .o_field_many2one input').click();
+ $('footer.modal-footer button:first').click(); // Save & close
form.destroy();
});
@@ -6133,20 +6133,20 @@ QUnit.module('Views', {
});
form.$('.o_field_x2many_list_row_add a').click();
- assert.strictEqual($('.modal input.o_field_widget[name=foo]').length, 1,
+ assert.strictEqual($('[role="dialog"] input.o_field_widget[name=foo]').length, 1,
'foo should be editable');
checkOnchange = true;
- $('.modal .o_field_widget[name=foo]').val('foo value').trigger('input');
- $('.modal .o_field_widget[name=display_name]').val('readonly').trigger('input');
- assert.strictEqual($('.modal span.o_field_widget[name=foo]').length, 1,
+ $('[role="dialog"] .o_field_widget[name=foo]').val('foo value').trigger('input');
+ $('[role="dialog"] .o_field_widget[name=display_name]').val('readonly').trigger('input');
+ assert.strictEqual($('[role="dialog"] span.o_field_widget[name=foo]').length, 1,
'foo should be readonly');
- $('.modal .modal-footer .btn-primary').click(); // close the modal
+ $('footer.modal-footer .btn-primary').click(); // close the modal
checkOnchange = false;
form.$('.o_data_row').click(); // re-open previous record
- assert.strictEqual($('.modal .o_field_widget[name=foo]').text(), 'foo value',
+ assert.strictEqual($('[role="dialog"] .o_field_widget[name=foo]').text(), 'foo value',
"the edited value should have been kept");
- $('.modal .modal-footer .btn-primary').click(); // close the modal
+ $('footer.modal-footer .btn-primary').click(); // close the modal
form.$buttons.find('.o_form_button_save').click(); // save the record
@@ -6219,8 +6219,8 @@ QUnit.module('Views', {
form.$buttons.find('.o_form_button_save').click(); // save duplicated record
form.sidebar.$('a:contains(Delete)').click(); // delete duplicated record
- assert.strictEqual($('.modal').length, 1, "should have opened a confirm dialog");
- $('.modal .modal-footer .btn-primary').click(); // confirm
+ assert.strictEqual($('[role="dialog"]').length, 1, "should have opened a confirm dialog");
+ $('footer.modal-footer .btn-primary').click(); // confirm
assert.strictEqual(form.$('.o_field_widget').text(), 'first record',
"should have come back to previous record");
@@ -6301,12 +6301,12 @@ QUnit.module('Views', {
});
form.$('.o_external_button').click(); // open the many2one record in a modal
- $('.modal .o_external_button').click(); // in the modal, open the many2one record in another modal
+ $('[role="dialog"] .o_external_button').click(); // in the modal, open the many2one record in another modal
- $('.modal:nth(1) .o_field_widget[name=display_name]').val('new name').trigger('input');
- $('.modal:nth(1) .modal-footer .btn-primary').first().click(); // save changes
+ $('[role="dialog"]:nth(1) .o_field_widget[name=display_name]').val('new name').trigger('input');
+ $('[role="dialog"]:nth(1) footer .btn-primary').first().click(); // save changes
- assert.strictEqual($('.modal .o_field_widget[name=trululu] input').val(), 'new name',
+ assert.strictEqual($('[role="dialog"] .o_field_widget[name=trululu] input').val(), 'new name',
"record should have been reloaded");
assert.verifySteps([
"read", // main record
@@ -6409,11 +6409,11 @@ QUnit.module('Views', {
viewOptions: {mode: 'edit'},
});
form.$('.o_external_button').click();
- assert.strictEqual($('.modal-body .o_form_view .o_list_view .o_data_cell').text(), "yopblip",
+ assert.strictEqual($('main.modal-body .o_form_view .o_list_view .o_data_cell').text(), "yopblip",
"table has some initial order");
- $('.modal-body .o_form_view .o_list_view th').click();
- assert.strictEqual($('.modal-body .o_form_view .o_list_view .o_data_cell').text(), "blipyop",
+ $('main.modal-body .o_form_view .o_list_view th').click();
+ assert.strictEqual($('main.modal-body .o_form_view .o_list_view .o_data_cell').text(), "blipyop",
"table is now sorted");
form.destroy();
});
@@ -6849,7 +6849,7 @@ QUnit.module('Views', {
core.bus.on('clear_cache', form, assert.step.bind(assert, 'clear_cache'));
form.sidebar.$('a:contains(Delete)').click();
- $('.modal .modal-footer .btn-primary').click(); // confirm
+ $('footer.modal-footer .btn-primary').click(); // confirm
assert.verifySteps(['unlink', 'clear_cache']);
diff --git a/addons/web/static/tests/views/kanban_tests.js b/addons/web/static/tests/views/kanban_tests.js
index 04c1c603118..2cc7a2431ef 100644
--- a/addons/web/static/tests/views/kanban_tests.js
+++ b/addons/web/static/tests/views/kanban_tests.js
@@ -182,12 +182,12 @@ QUnit.module('Views', {
"last column should contain 3 records");
envIDs = [4];
kanban.$('.o_kanban_group:last .o_column_archive_records').click(); // Click on 'Archive All'
- assert.ok($('.modal').length, 'a confirm modal should be displayed');
- $('.modal .modal-footer .btn-default').click(); // Click on 'Cancel'
+ assert.ok($('[role="dialog"]').length, 'a confirm modal should be displayed');
+ $('footer.modal-footer .btn-default').click(); // Click on 'Cancel'
assert.strictEqual(kanban.$('.o_kanban_group:last .o_kanban_record').length, 3, "still last column should contain 3 records");
kanban.$('.o_kanban_group:last .o_column_archive_records').click();
- assert.ok($('.modal').length, 'a confirm modal should be displayed');
- $('.modal .modal-footer .btn-primary').click(); // Click on 'Ok'
+ assert.ok($('[role="dialog"]').length, 'a confirm modal should be displayed');
+ $('footer.modal-footer .btn-primary').click(); // Click on 'Ok'
assert.strictEqual(kanban.$('.o_kanban_group:last .o_kanban_record').length, 0, "last column should not contain any records");
kanban.destroy();
});
@@ -1204,16 +1204,16 @@ QUnit.module('Views', {
which: $.ui.keyCode.ENTER,
}));
- assert.strictEqual($('.modal .o_form_view.o_form_editable').length, 1,
+ assert.strictEqual($('[role="dialog"] .o_form_view.o_form_editable').length, 1,
"a form view dialog should have been opened (in edit)");
- assert.strictEqual($('.modal .o_field_many2one input').val(), 'hello',
+ assert.strictEqual($('[role="dialog"] .o_field_many2one input').val(), 'hello',
"the correct product_id should already be set");
// specify a name and save
- $('.modal input[name=foo]').val('test').trigger('input');
- $('.modal-footer .btn-primary').click();
+ $('[role="dialog"] input[name=foo]').val('test').trigger('input');
+ $('footer.modal-footer .btn-primary').click();
- assert.strictEqual($('.modal').length, 0, "the modal should be closed");
+ assert.strictEqual($('[role="dialog"]').length, 0, "the modal should be closed");
assert.strictEqual(kanban.$('.o_kanban_group:first .o_kanban_record').length, 3,
"there should be 3 records in first column");
var $firstRecord = kanban.$('.o_kanban_group:first .o_kanban_record:first');
@@ -1793,13 +1793,13 @@ QUnit.module('Views', {
// delete second column (first cancel the confirm request, then confirm)
kanban.$('.o_kanban_group:last .o_column_delete').click(); // click on delete
- assert.ok($('.modal').length, 'a confirm modal should be displayed');
- $('.modal .modal-footer .btn-default').click(); // click on cancel
+ assert.ok($('[role="dialog"]').length, 'a confirm modal should be displayed');
+ $('footer.modal-footer .btn-default').click(); // click on cancel
assert.strictEqual(kanban.$('.o_kanban_group:last').data('id'), 5,
'column [5, "xmo"] should still be there');
kanban.$('.o_kanban_group:last .o_column_delete').click(); // click on delete
- assert.ok($('.modal').length, 'a confirm modal should be displayed');
- $('.modal .modal-footer .btn-primary').click(); // click on confirm
+ assert.ok($('[role="dialog"]').length, 'a confirm modal should be displayed');
+ $('footer.modal-footer .btn-primary').click(); // click on confirm
assert.strictEqual(kanban.$('.o_kanban_group:last').data('id'), 3,
'last column should now be [3, "hello"]');
assert.strictEqual(kanban.$('.o_kanban_group').length, 2, "should still have two columns");
@@ -1870,7 +1870,7 @@ QUnit.module('Views', {
assert.strictEqual(kanban.$('.o_kanban_group').length, 3, "should have two columns");
kanban.$('.o_kanban_group:last .o_column_delete').click();
- $('.modal .modal-footer .btn-primary').click();
+ $('footer.modal-footer .btn-primary').click();
assert.strictEqual(kanban.$('.o_kanban_group').length, 2, "should have twos columns");
@@ -1912,33 +1912,33 @@ QUnit.module('Views', {
// edit the title of column [5, 'xmo'] and close without saving
kanban.$('.o_kanban_group[data-id=5] .o_column_edit').click(); // click on 'Edit'
- assert.ok($('.modal .o_form_editable').length, 'a form view should be open in a modal');
- assert.strictEqual($('.modal .o_form_editable input').val(), 'xmo',
+ assert.ok($('[role="dialog"] .o_form_editable').length, 'a form view should be open in a modal');
+ assert.strictEqual($('[role="dialog"] .o_form_editable input').val(), 'xmo',
'the name should be "xmo"');
- $('.modal .o_form_editable input').val('ged').trigger('input'); // change the value
+ $('[role="dialog"] .o_form_editable input').val('ged').trigger('input'); // change the value
nbRPCs = 0;
- $('.modal .modal-header .close').click(); // click on the cross to close the modal
- assert.ok(!$('.modal').length, 'the modal should be closed');
+ $('header.modal-header .close').click(); // click on the cross to close the modal
+ assert.ok(!$('[role="dialog"]').length, 'the modal should be closed');
assert.strictEqual(kanban.$('.o_kanban_group[data-id=5] .o_column_title').text(), 'xmo',
'title of the column should still be "xmo"');
assert.strictEqual(nbRPCs, 0, 'no RPC should have been done');
// edit the title of column [5, 'xmo'] and discard
kanban.$('.o_kanban_group[data-id=5] .o_column_edit').click(); // click on 'Edit'
- $('.modal .o_form_editable input').val('ged').trigger('input'); // change the value
+ $('[role="dialog"] .o_form_editable input').val('ged').trigger('input'); // change the value
nbRPCs = 0;
- $('.modal .modal-footer .btn-default').click(); // click on discard
- assert.ok(!$('.modal').length, 'the modal should be closed');
+ $('footer.modal-footer .btn-default').click(); // click on discard
+ assert.ok(!$('[role="dialog"]').length, 'the modal should be closed');
assert.strictEqual(kanban.$('.o_kanban_group[data-id=5] .o_column_title').text(), 'xmo',
'title of the column should still be "xmo"');
assert.strictEqual(nbRPCs, 0, 'no RPC should have been done');
// edit the title of column [5, 'xmo'] and save
kanban.$('.o_kanban_group[data-id=5] .o_column_edit').click(); // click on 'Edit'
- $('.modal .o_form_editable input').val('ged').trigger('input'); // change the value
+ $('[role="dialog"] .o_form_editable input').val('ged').trigger('input'); // change the value
nbRPCs = 0;
- $('.modal .modal-footer .btn-primary').click(); // click on save
- assert.ok(!$('.modal').length, 'the modal should be closed');
+ $('footer.modal-footer .btn-primary').click(); // click on save
+ assert.ok(!$('[role="dialog"]').length, 'the modal should be closed');
assert.strictEqual(kanban.$('.o_kanban_group[data-id=5] .o_column_title').text(), 'ged',
'title of the column should be "ged"');
assert.strictEqual(nbRPCs, 4, 'should have done 1 write, 1 read_group and 2 search_read');
@@ -2058,16 +2058,16 @@ QUnit.module('Views', {
// click to see the examples
kanban.$('.o_column_quick_create .o_kanban_examples').click();
- assert.strictEqual($('.modal .o_kanban_examples_dialog').length, 1,
+ assert.strictEqual($('[role="dialog"] .o_kanban_examples_dialog').length, 1,
"should have open the examples dialog");
- assert.strictEqual($('.modal .o_kanban_examples_dialog_nav li').length, 2,
+ assert.strictEqual($('[role="dialog"] .o_kanban_examples_dialog_nav li').length, 2,
"should have two examples (in the menu)");
- assert.strictEqual($('.modal .o_kanban_examples_dialog_nav a').text(),
+ assert.strictEqual($('[role="dialog"] .o_kanban_examples_dialog_nav a').text(),
' A first example A second example ', "example names should be correct");
- assert.strictEqual($('.modal .o_kanban_examples_dialog_content .tab-pane').length, 2,
+ assert.strictEqual($('[role="dialog"] .o_kanban_examples_dialog_content .tab-pane').length, 2,
"should have two examples");
- var $firstPane = $('.modal .o_kanban_examples_dialog_content .tab-pane:first');
+ var $firstPane = $('[role="dialog"] .o_kanban_examples_dialog_content .tab-pane:first');
assert.strictEqual($firstPane.find('.o_kanban_examples_group').length, 3,
"there should be 3 stages in the first example");
assert.strictEqual($firstPane.find('h6').text(), 'Column 1Column 2Column 3',
@@ -2075,7 +2075,7 @@ QUnit.module('Views', {
assert.strictEqual($firstPane.find('.o_kanban_examples_description').text().trim(),
"Some description", "the correct description should be displayed");
- var $secondPane = $('.modal .o_kanban_examples_dialog_content .tab-pane:nth(1)');
+ var $secondPane = $('[role="dialog"] .o_kanban_examples_dialog_content .tab-pane:nth(1)');
assert.strictEqual($secondPane.find('.o_kanban_examples_group').length, 2,
"there should be 2 stages in the second example");
assert.strictEqual($secondPane.find('h6').text(), 'Col 1Col 2',
diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js
index 8aa70c1c099..ed57c6968ac 100644
--- a/addons/web/static/tests/views/list_tests.js
+++ b/addons/web/static/tests/views/list_tests.js
@@ -880,7 +880,7 @@ QUnit.module('Views', {
list.sidebar.$('a:contains(Delete)').click();
assert.ok($('body').hasClass('modal-open'), 'body should have modal-open clsss');
- $('body .modal-dialog button span:contains(Ok)').click();
+ $('body [role="dialog"] button span:contains(Ok)').click();
assert.strictEqual(list.$('tbody td.o_list_record_selector').length, 3, "should have 3 records");
list.destroy();
@@ -916,13 +916,13 @@ QUnit.module('Views', {
assert.verifySteps(['/web/dataset/search_read']);
list.sidebar.$('a:contains(Archive)').click();
- assert.strictEqual($('.modal').length, 1, 'a confirm modal should be displayed');
- $('.modal .modal-footer .btn-default').click(); // Click on 'Cancel'
+ assert.strictEqual($('[role="dialog"]').length, 1, 'a confirm modal should be displayed');
+ $('footer.modal-footer .btn-default').click(); // Click on 'Cancel'
assert.strictEqual(list.$('tbody td.o_list_record_selector').length, 4, "still should have 4 records");
list.sidebar.$('a:contains(Archive)').click();
- assert.strictEqual($('.modal').length, 1, 'a confirm modal should be displayed');
- $('.modal .modal-footer .btn-primary').click(); // Click on 'Ok'
+ assert.strictEqual($('[role="dialog"]').length, 1, 'a confirm modal should be displayed');
+ $('footer.modal-footer .btn-primary').click(); // Click on 'Ok'
assert.strictEqual(list.$('tbody td.o_list_record_selector').length, 3, "should have 3 records");
assert.verifySteps(['/web/dataset/search_read', '/web/dataset/call_kw/foo/write', '/web/dataset/search_read']);
list.destroy();
@@ -2759,10 +2759,10 @@ QUnit.module('Views', {
list.$('.o_data_cell:first').click();
list.$('input[name="foo"]').val("hello").trigger('input');
list.$buttons.find('.o_list_button_discard').click();
- assert.strictEqual($('.modal:visible').length, 1,
+ assert.strictEqual($('[role="dialog"]:visible').length, 1,
"a modal to ask for discard should be visible");
- $('.modal:visible .btn-primary').click();
+ $('[role="dialog"]:visible .btn-primary').click();
assert.strictEqual(list.$('.o_data_cell:first').text(), "yop",
"first cell should still contain 'yop'");
@@ -3555,7 +3555,7 @@ QUnit.module('Views', {
// delete a record
list.$('.o_data_row:first .o_list_record_selector input').click();
list.sidebar.$('a:contains(Delete)').click();
- $('.modal .modal-footer .btn-primary').click(); // confirm
+ $('footer.modal-footer .btn-primary').click(); // confirm
assert.verifySteps([
'create',
diff --git a/addons/web/static/tests/views/view_dialogs_tests.js b/addons/web/static/tests/views/view_dialogs_tests.js
index 39484795f5a..2d6af1b5e14 100644
--- a/addons/web/static/tests/views/view_dialogs_tests.js
+++ b/addons/web/static/tests/views/view_dialogs_tests.js
@@ -90,9 +90,9 @@ QUnit.module('Views', {
res_id: 1,
}).open();
- assert.notOk($('div.modal .modal-body button').length,
+ assert.notOk($('main.modal-body button').length,
"should not have any button in body");
- assert.strictEqual($('div.modal .modal-footer button').length, 1,
+ assert.strictEqual($('footer.modal-footer button').length, 1,
"should have only one button in footer");
parent.destroy();
});
@@ -118,7 +118,7 @@ QUnit.module('Views', {
res_id: 1,
}).open();
- assert.strictEqual($('div.modal button.btn-primary').length, 1,
+ assert.strictEqual($('[role="dialog"] button.btn-primary').length, 1,
"should have 1 buttons in modal");
$('.o_field_x2many_list_row_add a').click();
@@ -127,7 +127,7 @@ QUnit.module('Views', {
keyCode: $.ui.keyCode.ESCAPE,
}));
- assert.strictEqual($('div.modal button.btn-primary').length, 1,
+ assert.strictEqual($('[role="dialog"] button.btn-primary').length, 1,
"should still have 1 buttons in modal");
parent.destroy();
});
@@ -334,14 +334,14 @@ QUnit.module('Views', {
form.$('.o_field_widget .o_field_many2one[name=instrument] input').click();
$('ul.ui-autocomplete.ui-front.ui-menu.ui-widget.ui-widget-content li.o_m2o_dropdown_option').first().click();
- var $modal = $('.modal-dialog.modal-lg');
+ var $modal = $('.modal-lg');
assert.equal($modal.length, 1,
'There should be one modal');
$modal.find('.o_field_x2many_list_row_add a').click();
- var $modals = $('.modal-dialog.modal-lg');
+ var $modals = $('.modal-lg');
assert.equal($modals.length, 2,
'There should be two modals');
@@ -351,7 +351,7 @@ QUnit.module('Views', {
$second_modal.find('.o_select_button').click();
- $modal = $('.modal-dialog.modal-lg');
+ $modal = $('.modal-lg');
assert.equal($modal.length, 1,
'There should be one modal');
diff --git a/addons/web/static/tests/widgets/data_export_tests.js b/addons/web/static/tests/widgets/data_export_tests.js
index 4569a5a6290..4948db07474 100644
--- a/addons/web/static/tests/widgets/data_export_tests.js
+++ b/addons/web/static/tests/widgets/data_export_tests.js
@@ -107,15 +107,15 @@ QUnit.module('widgets', {
list.$('thead th.o_list_record_selector input').click();
list.sidebar.$('a:contains(Export)').click();
- assert.strictEqual($('.modal').length, 1, "a modal dialog should be open");
+ assert.strictEqual($('[role="dialog"]').length, 1, "a modal dialog should be open");
assert.strictEqual($('span.o_tree_column:contains(Activities)').length, 1,
"the Activities field should be in the list of exportable fields");
// select the field Description, click on add, then export and close
- $('.modal span:contains(Description)').click();
- $('.modal .o_add_field').click();
- $('.modal span:contains(Export To File)').click();
- $('.modal span:contains(Close)').click();
+ $('[role="dialog"] span:contains(Description)').click();
+ $('[role="dialog"] .o_add_field').click();
+ $('[role="dialog"] span:contains(Export To File)').click();
+ $('[role="dialog"] span:contains(Close)').click();
list.destroy();
framework.blockUI = blockUI;
@@ -185,26 +185,26 @@ QUnit.module('widgets', {
// Open the export modal
list.$('thead th.o_list_record_selector input').click();
list.sidebar.$('a:contains(Export)').click();
- assert.strictEqual($('.modal').length, 1,
+ assert.strictEqual($('[role="dialog"]').length, 1,
"a modal dialog should be open");
// Select 'Activities' in fields to export
- assert.strictEqual($('.modal select.o_fields_list option').length, 0,
+ assert.strictEqual($('[role="dialog"] select.o_fields_list option').length, 0,
"the fields list should be empty");
- $('.modal .o_export_tree_item:contains(Activities)').click();
- $('.modal button:contains(Add)').click();
- assert.strictEqual($('.modal select.o_fields_list option').length, 1,
+ $('[role="dialog"] .o_export_tree_item:contains(Activities)').click();
+ $('[role="dialog"] button:contains(Add)').click();
+ assert.strictEqual($('[role="dialog"] select.o_fields_list option').length, 1,
"there should be one item in the fields list");
// Save fields list
- $('.modal a:contains(Save fields list)').click();
- $('.modal .o_save_list > input').val('fields list').trigger('input');
- $('.modal .o_save_list > button').click();
+ $('[role="dialog"] a:contains(Save fields list)').click();
+ $('[role="dialog"] .o_save_list > input').val('fields list').trigger('input');
+ $('[role="dialog"] .o_save_list > button').click();
assert.verifySteps(['build_search_data', 'create'],
"create should have been called");
// Close the modal and destroy list
- $('.modal button span:contains(Close)').click();
+ $('[role="dialog"] button span:contains(Close)').click();
list.destroy();
// restore create function