Commit c84225ee authored by hcarmona's avatar hcarmona Committed by Commit bot

MD-Settings: A11y - Fix password list focusability.

BUG=705575
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:closure_compilation

Review-Url: https://codereview.chromium.org/2818283002
Cr-Commit-Position: refs/heads/master@{#469408}
parent 319966b1
...@@ -48,6 +48,14 @@ ...@@ -48,6 +48,14 @@
], ],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
}, },
{
'target_name': 'password_list_item',
'dependencies': [
'../compiled_resources2.gyp:focus_row_behavior',
'<(EXTERNS_GYP):passwords_private',
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
{ {
'target_name': 'passwords_section', 'target_name': 'passwords_section',
'dependencies': [ 'dependencies': [
...@@ -58,6 +66,7 @@ ...@@ -58,6 +66,7 @@
'<(DEPTH)/ui/webui/resources/js/cr/ui/compiled_resources2.gyp:focus_without_ink', '<(DEPTH)/ui/webui/resources/js/cr/ui/compiled_resources2.gyp:focus_without_ink',
'<(EXTERNS_GYP):passwords_private', '<(EXTERNS_GYP):passwords_private',
'password_edit_dialog', 'password_edit_dialog',
'password_list_item',
], ],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
}, },
......
<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/cr_elements/icons.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html">
<link rel="import" href="../focus_row_behavior.html">
<link rel="import" href="../settings_shared_css.html">
<link rel="import" href="passwords_shared_css.html">
<dom-module id="password-list-item">
<template>
<style include="settings-shared passwords-shared">
#password {
background-color: transparent;
border: none;
flex: 1;
height: 20px;
width: 0;
}
#originUrl {
/* The following non-flex directives allow eliding long originUrls from
* the left. Forcing rtl should not cause an issue for right-to-left
* languages in this case, since valid URL characters are restricted to
* ASCII.
*/
direction: rtl;
display: flex;
}
</style>
<div class="list-item" focus-row-container>
<div class="website-column no-min-width"
title="[[item.loginPair.urls.link]]">
<a id="originUrl" target="_blank" class="selectable no-min-width"
href="[[item.loginPair.urls.link]]"
focus-row-control focus-type="originUrl">
<span class="text-elide">
[[item.loginPair.urls.shown]]
</span>
</a>
</div>
<div class="username-column selectable text-elide"
id="username">[[item.loginPair.username]]</div>
<div class="password-column text-elide">
<template is="dom-if" if="[[!item.federationText]]">
<!-- Password type and disabled in order to match mock. -->
<input id="password" type="password" disabled
value="[[getEmptyPassword_(item.numCharactersInPassword)]]">
</input>
</template>
<template is="dom-if" if="[[item.federationText]]">
<span class="selectable text-elide">
[[item.federationText]]
</span>
</template>
</div>
<paper-icon-button id="passwordMenu" icon="cr:more-vert"
on-tap="onPasswordMenuTap_" title="$i18n{moreActions}"
focus-row-control focus-type="passwordMenu">
</paper-icon-button>
</div>
</div>
</template>
<script src="password_list_item.js"></script>
</dom-module>
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview PasswordListItem represents one row in the list of passwords.
* It needs to be its own component because FocusRowBehavior provides good a11y.
*/
Polymer({
is: 'password-list-item',
behaviors: [FocusRowBehavior],
properties: {
/**
* The password whose info should be displayed.
* @type {!chrome.passwordsPrivate.PasswordUiEntry}
*/
item: Array,
},
/**
* Creates an empty password of specified length.
* @param {number} length
* @return {string} password
* @private
*/
getEmptyPassword_: function(length) {
return ' '.repeat(length);
},
/**
* Opens the password action menu.
* @private
*/
onPasswordMenuTap_: function() {
this.fire(
'password-menu-tap', {target: this.$.passwordMenu, item: this.item});
},
});
<link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html"> <link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html">
<link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html">
<link rel="import" href="chrome://resources/html/action_link.html">
<link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/assert.html">
<link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html"> <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
<link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/html/polymer.html">
...@@ -9,55 +8,16 @@ ...@@ -9,55 +8,16 @@
<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html">
<link rel="import" href="../controls/settings_toggle_button.html"> <link rel="import" href="../controls/settings_toggle_button.html">
<link rel="import" href="../controls/extension_controlled_indicator.html"> <link rel="import" href="../controls/extension_controlled_indicator.html">
<link rel="import" href="../controls/settings_toggle_button.html">
<link rel="import" href="../global_scroll_target_behavior.html"> <link rel="import" href="../global_scroll_target_behavior.html">
<link rel="import" href="../prefs/prefs.html"> <link rel="import" href="../prefs/prefs.html">
<link rel="import" href="../settings_shared_css.html"> <link rel="import" href="../settings_shared_css.html">
<link rel="import" href="password_edit_dialog.html"> <link rel="import" href="password_edit_dialog.html">
<link rel="import" href="passwords_shared_css.html"> <link rel="import" href="passwords_shared_css.html">
<link rel="import" href="password_list_item.html">
<dom-module id="passwords-section"> <dom-module id="passwords-section">
<template> <template>
<style include="settings-shared passwords-shared"> <style include="settings-shared passwords-shared"></style>
#password {
background-color: transparent;
border: none;
flex: 1;
height: 20px;
width: 0;
}
.website-column {
display: flex;
flex: 3;
}
#originUrl {
/* The following non-flex directives allow eliding long originUrls from
* the left. Forcing rtl should not cause an issue for right-to-left
* languages in this case, since valid URL characters are restricted to
* ASCII.
*/
direction: rtl;
display: flex;
}
.username-column {
-webkit-margin-end: 8px;
-webkit-margin-start: 8px;
flex: 2;
}
.password-column {
align-items: center;
display: flex;
flex: 2;
}
.selectable {
-webkit-user-select: text;
}
</style>
<div class="settings-box first"> <div class="settings-box first">
<settings-toggle-button id="passwordToggle" class="start primary-toggle" <settings-toggle-button id="passwordToggle" class="start primary-toggle"
aria-label="$i18n{passwords}" no-extension-indicator aria-label="$i18n{passwords}" no-extension-indicator
...@@ -108,36 +68,10 @@ ...@@ -108,36 +68,10 @@
class="vertical-list list-with-header" class="vertical-list list-with-header"
scroll-target="[[subpageScrollTarget]]"> scroll-target="[[subpageScrollTarget]]">
<template> <template>
<div class="list-item"> <password-list-item item="[[item]]" tabindex$="[[tabIndex]]"
<div class="website-column no-min-width" iron-list-tab-index="[[tabIndex]]"
title="[[item.loginPair.urls.link]]"> last-focused="{{lastFocused_}}">
<a id="originUrl" target="_blank" class="selectable no-min-width" <password-list-item>
href="[[item.loginPair.urls.link]]">
<span class="text-elide">
[[item.loginPair.urls.shown]]
</span>
</a>
</div>
<div class="username-column selectable text-elide"
id="username">[[item.loginPair.username]]</div>
<div class="password-column text-elide">
<template is="dom-if" if="[[!item.federationText]]">
<!-- Password type and disabled in order to match mock. -->
<input id="password" type="password" disabled
value="[[getEmptyPassword_(item.numCharactersInPassword)]]">
</input>
</template>
<template is="dom-if" if="[[item.federationText]]">
<span class="selectable text-elide">
[[item.federationText]]
</span>
</template>
</div>
<paper-icon-button id="passwordMenu" icon="cr:more-vert"
on-tap="onPasswordMenuTap_" title="$i18n{moreActions}"
tabindex$="[[tabIndex]]">
</paper-icon-button>
</div>
</template> </template>
</iron-list> </iron-list>
<div id="noPasswordsLabel" class="list-item" <div id="noPasswordsLabel" class="list-item"
......
...@@ -209,10 +209,14 @@ Polymer({ ...@@ -209,10 +209,14 @@ Polymer({
type: String, type: String,
value: '', value: '',
}, },
/** @private {!PasswordManager.PasswordUiEntry} */
lastFocused_: Object,
}, },
listeners: { listeners: {
'show-password': 'showPassword_', 'show-password': 'showPassword_',
'password-menu-tap': 'onPasswordMenuTap_',
}, },
/** /**
...@@ -354,28 +358,18 @@ Polymer({ ...@@ -354,28 +358,18 @@ Polymer({
this.passwordManager_.removeException(e.model.item.urls.origin); this.passwordManager_.removeException(e.model.item.urls.origin);
}, },
/**
* Creates an empty password of specified length.
* @param {number} length
* @return {string} password
* @private
*/
getEmptyPassword_: function(length) {
return ' '.repeat(length);
},
/** /**
* Opens the password action menu. * Opens the password action menu.
* @param {!Event} event
* @private * @private
*/ */
onPasswordMenuTap_: function(e) { onPasswordMenuTap_: function(event) {
var menu = /** @type {!CrActionMenuElement} */(this.$.menu); var menu = /** @type {!CrActionMenuElement} */(this.$.menu);
var target = /** @type {!HTMLElement} */(Polymer.dom(e).localTarget); var target = /** @type {!HTMLElement} */ (event.detail.target);
var passwordUiEntryEvent = /** @type {!PasswordUiEntryEvent} */(e);
this.activePassword = this.activePassword =
/** @type {!chrome.passwordsPrivate.PasswordUiEntry} */ ( /** @type {!chrome.passwordsPrivate.PasswordUiEntry} */ (
passwordUiEntryEvent.model.item); event.detail.item);
menu.showAt(target); menu.showAt(target);
this.activeDialogAnchor_ = target; this.activeDialogAnchor_ = target;
}, },
......
...@@ -15,6 +15,26 @@ ...@@ -15,6 +15,26 @@
.list-with-header > div:first-of-type { .list-with-header > div:first-of-type {
border-top: var(--settings-separator-line); border-top: var(--settings-separator-line);
} }
.website-column {
display: flex;
flex: 3;
}
.username-column {
flex: 2;
margin: 0 8px;
}
.password-column {
align-items: center;
display: flex;
flex: 2;
}
.selectable {
-webkit-user-select: text;
}
</style> </style>
</template> </template>
</dom-module> </dom-module>
...@@ -681,6 +681,12 @@ ...@@ -681,6 +681,12 @@
<structure name="IDR_SETTINGS_ADDRESS_EDIT_DIALOG_JS" <structure name="IDR_SETTINGS_ADDRESS_EDIT_DIALOG_JS"
file="passwords_and_forms_page/address_edit_dialog.js" file="passwords_and_forms_page/address_edit_dialog.js"
type="chrome_html" /> type="chrome_html" />
<structure name="IDR_SETTINGS_PASSWORD_LIST_ITEM_HTML"
file="passwords_and_forms_page/password_list_item.html"
type="chrome_html" />
<structure name="IDR_SETTINGS_PASSWORD_LIST_ITEM_JS"
file="passwords_and_forms_page/password_list_item.js"
type="chrome_html" />
<structure name="IDR_SETTINGS_PASSWORDS_SECTION_HTML" <structure name="IDR_SETTINGS_PASSWORDS_SECTION_HTML"
file="passwords_and_forms_page/passwords_section.html" file="passwords_and_forms_page/passwords_section.html"
type="chrome_html" /> type="chrome_html" />
......
...@@ -61,13 +61,13 @@ TEST_F('SettingsPasswordSectionBrowserTest', 'uiTests', function() { ...@@ -61,13 +61,13 @@ TEST_F('SettingsPasswordSectionBrowserTest', 'uiTests', function() {
assert(node); assert(node);
var passwordInfo = passwordList[0]; var passwordInfo = passwordList[0];
assertEquals(passwordInfo.loginPair.urls.shown, assertEquals(passwordInfo.loginPair.urls.shown,
node.querySelector('#originUrl').textContent.trim()); node.$$('#originUrl').textContent.trim());
assertEquals(passwordInfo.loginPair.urls.link, assertEquals(passwordInfo.loginPair.urls.link,
node.querySelector('#originUrl').href); node.$$('#originUrl').href);
assertEquals(passwordInfo.loginPair.username, assertEquals(passwordInfo.loginPair.username,
node.querySelector('#username').textContent); node.$$('#username').textContent);
assertEquals(passwordInfo.numCharactersInPassword, assertEquals(passwordInfo.numCharactersInPassword,
node.querySelector('#password').value.length); node.$$('#password').value.length);
} }
} }
...@@ -283,7 +283,7 @@ TEST_F('SettingsPasswordSectionBrowserTest', 'uiTests', function() { ...@@ -283,7 +283,7 @@ TEST_F('SettingsPasswordSectionBrowserTest', 'uiTests', function() {
}; };
// Click the remove button on the first password. // Click the remove button on the first password.
MockInteractions.tap(firstNode.querySelector('#passwordMenu')); MockInteractions.tap(firstNode.$$('#passwordMenu'));
MockInteractions.tap(passwordsSection.$.menuRemovePassword); MockInteractions.tap(passwordsSection.$.menuRemovePassword);
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment