Commit cd12c8a7 authored by kristipark's avatar kristipark Committed by Commit Bot

[NTP] Fix delete tile and tile accessibility issues

Fixed tile deletion functionality. The id of the tile was not being
obtained correctly.

Also fixed various accessibility issues:
- Keyboard focus will start on the first input field of the edit
  dialog.
- The iframes now have titles instead of "frame 0".
- The "x" button on a tile will no longer remain visible after clicking
  and releasing with a mouse.
- The button labels for screen readers are now applied correctly.
- "Restore all" is now "Restore default shortcuts" in the shortcut
  removed notification.
- If custom links is enabled, pressing "backspace" or "delete" while
  the shortcut is focused will now delete the shortcut.

Also adjusted variable names to match their CSS name.

Bug: 871663, 864357
Change-Id: I10135be42d9525931077daf0b7c859c60dd59e89
Reviewed-on: https://chromium-review.googlesource.com/1166028Reviewed-by: default avatarMathieu Perreault <mathp@chromium.org>
Commit-Queue: Kristi Park <kristipark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#581374}
parent 880262ad
...@@ -6,6 +6,10 @@ body { ...@@ -6,6 +6,10 @@ body {
overflow: hidden; overflow: hidden;
} }
.mouse-navigation {
outline: none;
}
#edit-link-dialog { #edit-link-dialog {
background-color: #fff; background-color: #fff;
border: none; border: none;
......
...@@ -54,6 +54,7 @@ const CLASSES = { ...@@ -54,6 +54,7 @@ const CLASSES = {
const KEYCODES = { const KEYCODES = {
ENTER: 13, ENTER: 13,
ESC: 27, ESC: 27,
TAB: 9,
}; };
...@@ -104,6 +105,13 @@ let editLinkTitle = ''; ...@@ -104,6 +105,13 @@ let editLinkTitle = '';
let deleteLinkTitle = ''; let deleteLinkTitle = '';
/**
* True if keyboard navigation should start at the first input field.
* @type {boolean}
*/
let startKeyboardAtFirstField = false;
/** /**
* True if the provided url is valid. * True if the provided url is valid.
* @type {string} * @type {string}
...@@ -137,8 +145,8 @@ function prepopulateFields(rid) { ...@@ -137,8 +145,8 @@ function prepopulateFields(rid) {
$(IDS.URL_FIELD).classList.add(CLASSES.TEXT_MODIFIED); $(IDS.URL_FIELD).classList.add(CLASSES.TEXT_MODIFIED);
// Set accessibility names. // Set accessibility names.
$(IDS.DELETE).name = deleteLinkTitle + ' ' + data.title; $(IDS.DELETE).setAttribute('aria-label', deleteLinkTitle + ' ' + data.title);
$(IDS.DONE).name = editLinkTitle + ' ' + data.title; $(IDS.DONE).setAttribute('aria-label', editLinkTitle + ' ' + data.title);
$(IDS.DONE).title = editLinkTitle; $(IDS.DONE).title = editLinkTitle;
} }
...@@ -224,6 +232,7 @@ function closeDialog() { ...@@ -224,6 +232,7 @@ function closeDialog() {
prepopulatedLink.rid = -1; prepopulatedLink.rid = -1;
prepopulatedLink.title = ''; prepopulatedLink.title = '';
prepopulatedLink.url = ''; prepopulatedLink.url = '';
startKeyboardAtFirstField = false;
}, 10); }, 10);
} }
...@@ -236,15 +245,19 @@ function handlePostMessage(event) { ...@@ -236,15 +245,19 @@ function handlePostMessage(event) {
let cmd = event.data.cmd; let cmd = event.data.cmd;
let args = event.data; let args = event.data;
if (cmd === 'linkData') { if (cmd === 'linkData') {
startKeyboardAtFirstField = true;
if (args.tid) { // We are editing a link, prepopulate the link data. if (args.tid) { // We are editing a link, prepopulate the link data.
document.title = editLinkTitle;
$(IDS.DIALOG_TITLE).textContent = editLinkTitle; $(IDS.DIALOG_TITLE).textContent = editLinkTitle;
prepopulateFields(args.tid); prepopulateFields(args.tid);
} else { // We are adding a link, disable the delete button. } else { // We are adding a link, disable the delete button.
document.title = addLinkTitle;
$(IDS.DIALOG_TITLE).textContent = addLinkTitle; $(IDS.DIALOG_TITLE).textContent = addLinkTitle;
$(IDS.DELETE).disabled = true; $(IDS.DELETE).disabled = true;
disableSubmitUntilTextInput(); disableSubmitUntilTextInput();
// Set accessibility names. // Set accessibility names.
$(IDS.DONE).name = $(IDS.DONE).title = addLinkTitle; $(IDS.DONE).setAttribute('aria-label', addLinkTitle);
$(IDS.DONE).title = addLinkTitle;
} }
} }
} }
...@@ -256,12 +269,9 @@ function handlePostMessage(event) { ...@@ -256,12 +269,9 @@ function handlePostMessage(event) {
*/ */
function disableOutlineOnMouseClick(element) { function disableOutlineOnMouseClick(element) {
element.addEventListener('mousedown', (event) => { element.addEventListener('mousedown', (event) => {
element.style.outline = 'none'; element.classList.add('mouse-navigation');
let resetOutline = (event) => { let resetOutline = (event) => {
// Clear current focus to prevent the outline from reappearing when the element.classList.remove('mouse-navigation');
// user switches windows.
document.activeElement.blur();
element.style.outline = '';
element.removeEventListener('blur', resetOutline); element.removeEventListener('blur', resetOutline);
}; };
element.addEventListener('blur', resetOutline); element.addEventListener('blur', resetOutline);
...@@ -283,6 +293,8 @@ function init() { ...@@ -283,6 +293,8 @@ function init() {
queryArgs[decodeURIComponent(val[0])] = decodeURIComponent(val[1]); queryArgs[decodeURIComponent(val[0])] = decodeURIComponent(val[1]);
} }
document.title = queryArgs['editTitle'];
// Enable RTL. // Enable RTL.
// TODO(851293): Add RTL formatting. // TODO(851293): Add RTL formatting.
if (queryArgs['rtl'] == '1') { if (queryArgs['rtl'] == '1') {
...@@ -296,16 +308,29 @@ function init() { ...@@ -296,16 +308,29 @@ function init() {
deleteLinkTitle = queryArgs['linkRemove']; deleteLinkTitle = queryArgs['linkRemove'];
$(IDS.DIALOG_TITLE).textContent = addLinkTitle; $(IDS.DIALOG_TITLE).textContent = addLinkTitle;
$(IDS.TITLE_FIELD_NAME).textContent = queryArgs['nameField']; $(IDS.TITLE_FIELD_NAME).textContent = queryArgs['nameField'];
$(IDS.TITLE_FIELD_NAME).name = queryArgs['nameField']; $(IDS.TITLE_FIELD_NAME).setAttribute('aria-label', queryArgs['nameField']);
$(IDS.URL_FIELD_NAME).textContent = queryArgs['urlField']; $(IDS.URL_FIELD_NAME).textContent = queryArgs['urlField'];
$(IDS.URL_FIELD_NAME).name = queryArgs['urlField']; $(IDS.URL_FIELD_NAME).setAttribute('aria-label', queryArgs['urlField']);
$(IDS.DELETE).textContent = $(IDS.DELETE).title = queryArgs['linkRemove']; $(IDS.DELETE).textContent = $(IDS.DELETE).title = queryArgs['linkRemove'];
$(IDS.CANCEL).textContent = $(IDS.CANCEL).title = $(IDS.CANCEL).name = $(IDS.CANCEL).textContent = $(IDS.CANCEL).title = queryArgs['linkCancel'];
queryArgs['linkCancel']; $(IDS.CANCEL).setAttribute('aria-label', queryArgs['linkCancel']);
$(IDS.DONE).textContent = $(IDS.DONE).title = queryArgs['linkDone']; $(IDS.DONE).textContent = $(IDS.DONE).title = queryArgs['linkDone'];
$(IDS.INVALID_URL).textContent = queryArgs['invalidUrl']; $(IDS.INVALID_URL).textContent = queryArgs['invalidUrl'];
// Set up event listeners. // Set up event listeners.
document.body.onkeydown = (event) => {
if (event.keyCode === KEYCODES.ESC) {
// Close the iframe instead of just this dialog.
event.preventDefault();
closeDialog();
} else if (event.keyCode === KEYCODES.TAB && startKeyboardAtFirstField) {
// Start keyboard navigation at the first input field when the dialog
// opens.
event.preventDefault();
$(IDS.TITLE_FIELD).focus();
startKeyboardAtFirstField = false;
}
};
$(IDS.URL_FIELD).addEventListener('input', (event) => { $(IDS.URL_FIELD).addEventListener('input', (event) => {
if (!$(IDS.URL_FIELD).classList.contains(CLASSES.TEXT_MODIFIED)) if (!$(IDS.URL_FIELD).classList.contains(CLASSES.TEXT_MODIFIED))
$(IDS.URL_FIELD).classList.add(CLASSES.TEXT_MODIFIED); $(IDS.URL_FIELD).classList.add(CLASSES.TEXT_MODIFIED);
......
...@@ -84,6 +84,10 @@ button { ...@@ -84,6 +84,10 @@ button {
padding: 0; padding: 0;
} }
.mouse-navigation {
outline: none;
}
#ntp-contents { #ntp-contents {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
......
...@@ -911,12 +911,9 @@ function addRippleAnimations() { ...@@ -911,12 +911,9 @@ function addRippleAnimations() {
*/ */
function disableOutlineOnMouseClick(element) { function disableOutlineOnMouseClick(element) {
element.addEventListener('mousedown', (event) => { element.addEventListener('mousedown', (event) => {
element.style.outline = 'none'; element.classList.add('mouse-navigation');
let resetOutline = (event) => { let resetOutline = (event) => {
// Clear current focus to prevent the outline from reappearing when the element.classList.remove('mouse-navigation');
// user switches windows.
document.activeElement.blur();
element.style.outline = '';
element.removeEventListener('blur', resetOutline); element.removeEventListener('blur', resetOutline);
}; };
element.addEventListener('blur', resetOutline); element.addEventListener('blur', resetOutline);
...@@ -956,7 +953,9 @@ function init() { ...@@ -956,7 +953,9 @@ function init() {
registerKeyHandler(restoreAllLink, KEYCODE.ENTER, onRestoreAll); registerKeyHandler(restoreAllLink, KEYCODE.ENTER, onRestoreAll);
registerKeyHandler(restoreAllLink, KEYCODE.SPACE, onRestoreAll); registerKeyHandler(restoreAllLink, KEYCODE.SPACE, onRestoreAll);
restoreAllLink.textContent = restoreAllLink.textContent =
configData.translatedStrings.restoreThumbnailsShort; (configData.isCustomLinksEnabled ?
configData.translatedStrings.restoreDefaultLinks :
configData.translatedStrings.restoreThumbnailsShort);
$(IDS.ATTRIBUTION_TEXT).textContent = $(IDS.ATTRIBUTION_TEXT).textContent =
configData.translatedStrings.attributionIntro; configData.translatedStrings.attributionIntro;
...@@ -1114,6 +1113,9 @@ function init() { ...@@ -1114,6 +1113,9 @@ function init() {
if (NTP_DESIGN.numTitleLines > 1) if (NTP_DESIGN.numTitleLines > 1)
args.push('ntl=' + NTP_DESIGN.numTitleLines); args.push('ntl=' + NTP_DESIGN.numTitleLines);
args.push(
'title=' +
encodeURIComponent(configData.translatedStrings.mostVisitedTitle));
args.push('removeTooltip=' + args.push('removeTooltip=' +
encodeURIComponent(configData.translatedStrings.removeThumbnailTooltip)); encodeURIComponent(configData.translatedStrings.removeThumbnailTooltip));
......
...@@ -375,6 +375,10 @@ html[dir=rtl] .mv-favicon { ...@@ -375,6 +375,10 @@ html[dir=rtl] .mv-favicon {
display: none; display: none;
} }
.mouse-navigation {
outline: none;
}
.md-tile-container { .md-tile-container {
border-radius: 4px; border-radius: 4px;
box-sizing: border-box; box-sizing: border-box;
...@@ -522,7 +526,8 @@ body.using-theme .md-title { ...@@ -522,7 +526,8 @@ body.using-theme .md-title {
width: var(--md-menu-size); width: var(--md-menu-size);
} }
.md-menu:focus { .md-menu:active,
.md-menu:focus:not(.mouse-navigation) {
opacity: 1; opacity: 1;
} }
......
...@@ -36,9 +36,9 @@ const CLASSES = { ...@@ -36,9 +36,9 @@ const CLASSES = {
MD_ADD_BACKGROUND: 'md-add-background', MD_ADD_BACKGROUND: 'md-add-background',
MD_MENU: 'md-menu', MD_MENU: 'md-menu',
MD_EDIT_MENU: 'md-edit-menu', MD_EDIT_MENU: 'md-edit-menu',
MD_TILE: 'md-tile-container', MD_TILE: 'md-tile',
MD_TILE_CONTAINER: 'md-tile-container',
MD_TILE_INNER: 'md-tile-inner', MD_TILE_INNER: 'md-tile-inner',
MD_TILE_LINK: 'md-tile',
MD_TITLE: 'md-title', MD_TITLE: 'md-title',
MD_TITLE_CONTAINER: 'md-title-container', MD_TITLE_CONTAINER: 'md-title-container',
}; };
...@@ -426,15 +426,20 @@ var addTile = function(args) { ...@@ -426,15 +426,20 @@ var addTile = function(args) {
* @param {Element} tile DOM node of the tile we want to remove. * @param {Element} tile DOM node of the tile we want to remove.
*/ */
var blacklistTile = function(tile) { var blacklistTile = function(tile) {
tile.classList.add('blacklisted'); let tid = isMDEnabled ? Number(tile.firstChild.getAttribute('data-tid')) :
tile.addEventListener('transitionend', function(ev) { Number(tile.getAttribute('data-tid'));
if (ev.propertyName != 'width')
return;
window.parent.postMessage( if (isCustomLinksEnabled) {
{cmd: 'tileBlacklisted', tid: Number(tile.getAttribute('data-tid'))}, chrome.embeddedSearch.newTabPage.deleteMostVisitedItem(tid);
DOMAIN_ORIGIN); } else {
}); tile.classList.add('blacklisted');
tile.addEventListener('transitionend', function(ev) {
if (ev.propertyName != 'width')
return;
window.parent.postMessage(
{cmd: 'tileBlacklisted', tid: Number(tid)}, DOMAIN_ORIGIN);
});
}
}; };
...@@ -464,12 +469,9 @@ var isSchemeAllowed = function(url) { ...@@ -464,12 +469,9 @@ var isSchemeAllowed = function(url) {
*/ */
function disableOutlineOnMouseClick(element) { function disableOutlineOnMouseClick(element) {
element.addEventListener('mousedown', (event) => { element.addEventListener('mousedown', (event) => {
element.style.outline = 'none'; element.classList.add('mouse-navigation');
let resetOutline = (event) => { let resetOutline = (event) => {
// Clear current focus to prevent the outline from reappearing when the element.classList.remove('mouse-navigation');
// user switches windows.
document.activeElement.blur();
element.style.outline = '';
element.removeEventListener('blur', resetOutline); element.removeEventListener('blur', resetOutline);
}; };
element.addEventListener('blur', resetOutline); element.addEventListener('blur', resetOutline);
...@@ -652,14 +654,14 @@ var renderMostVisitedTile = function(data) { ...@@ -652,14 +654,14 @@ var renderMostVisitedTile = function(data) {
* @return {Element} * @return {Element}
*/ */
function renderMaterialDesignTile(data) { function renderMaterialDesignTile(data) {
let mdTile = document.createElement('div'); let mdTileContainer = document.createElement('div');
mdTile.role = 'none'; mdTileContainer.role = 'none';
if (data == null) { if (data == null) {
mdTile.className = CLASSES.MD_EMPTY_TILE; mdTileContainer.className = CLASSES.MD_EMPTY_TILE;
return mdTile; return mdTileContainer;
} }
mdTile.className = CLASSES.MD_TILE; mdTileContainer.className = CLASSES.MD_TILE_CONTAINER;
if (data.isCustomLink) if (data.isCustomLink)
tilesAreCustomLinks = true; tilesAreCustomLinks = true;
...@@ -669,18 +671,18 @@ function renderMaterialDesignTile(data) { ...@@ -669,18 +671,18 @@ function renderMaterialDesignTile(data) {
// This is set in the load/error event for the favicon image. // This is set in the load/error event for the favicon image.
let tileType = TileVisualType.NONE; let tileType = TileVisualType.NONE;
let mdTileLink = document.createElement('a'); let mdTile = document.createElement('a');
mdTileLink.className = CLASSES.MD_TILE_LINK; mdTile.className = CLASSES.MD_TILE;
mdTileLink.tabIndex = 0; mdTile.tabIndex = 0;
mdTileLink.setAttribute('data-tid', data.tid); mdTile.setAttribute('data-tid', data.tid);
mdTileLink.setAttribute('data-pos', position); mdTile.setAttribute('data-pos', position);
if (isSchemeAllowed(data.url)) { if (isSchemeAllowed(data.url)) {
mdTileLink.href = data.url; mdTile.href = data.url;
} }
mdTileLink.setAttribute('aria-label', data.title); mdTile.setAttribute('aria-label', data.title);
mdTileLink.title = data.title; mdTile.title = data.title;
mdTileLink.addEventListener('click', function(ev) { mdTile.addEventListener('click', function(ev) {
if (data.isAddButton) { if (data.isAddButton) {
editCustomLink(); editCustomLink();
logEvent(LOG_TYPE.NTP_CUSTOMIZE_ADD_SHORTCUT_CLICKED); logEvent(LOG_TYPE.NTP_CUSTOMIZE_ADD_SHORTCUT_CLICKED);
...@@ -690,30 +692,28 @@ function renderMaterialDesignTile(data) { ...@@ -690,30 +692,28 @@ function renderMaterialDesignTile(data) {
data.dataGenerationTime); data.dataGenerationTime);
} }
}); });
mdTileLink.addEventListener('keydown', function(event) { mdTile.addEventListener('keydown', function(event) {
if ((event.keyCode == 46 /* DELETE */ || if ((event.keyCode == 46 /* DELETE */ ||
event.keyCode == 8 /* BACKSPACE */) && event.keyCode == 8 /* BACKSPACE */) &&
!data.isAddButton) { !data.isAddButton) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
blacklistTile(this); blacklistTile(mdTileContainer);
} else if ( } else if (
event.keyCode == 13 /* ENTER */ || event.keyCode == 32 /* SPACE */) { event.keyCode == 13 /* ENTER */ || event.keyCode == 32 /* SPACE */) {
event.preventDefault(); event.preventDefault();
this.click(); this.click();
} else if (event.keyCode == 37 /* LEFT */) { } else if (event.keyCode == 37 /* LEFT */) {
const tiles = const tiles = document.querySelectorAll('#mv-tiles .' + CLASSES.MD_TILE);
document.querySelectorAll('#mv-tiles .' + CLASSES.MD_TILE_LINK);
tiles[Math.max(Number(this.getAttribute('data-pos')) - 1, 0)].focus(); tiles[Math.max(Number(this.getAttribute('data-pos')) - 1, 0)].focus();
} else if (event.keyCode == 39 /* RIGHT */) { } else if (event.keyCode == 39 /* RIGHT */) {
const tiles = const tiles = document.querySelectorAll('#mv-tiles .' + CLASSES.MD_TILE);
document.querySelectorAll('#mv-tiles .' + CLASSES.MD_TILE_LINK);
tiles[Math.min( tiles[Math.min(
Number(this.getAttribute('data-pos')) + 1, tiles.length - 1)] Number(this.getAttribute('data-pos')) + 1, tiles.length - 1)]
.focus(); .focus();
} }
}); });
disableOutlineOnMouseClick(mdTileLink); disableOutlineOnMouseClick(mdTile);
let mdTileInner = document.createElement('div'); let mdTileInner = document.createElement('div');
mdTileInner.className = CLASSES.MD_TILE_INNER; mdTileInner.className = CLASSES.MD_TILE_INNER;
...@@ -787,8 +787,8 @@ function renderMaterialDesignTile(data) { ...@@ -787,8 +787,8 @@ function renderMaterialDesignTile(data) {
mdTitle.style.direction = data.direction || 'ltr'; mdTitle.style.direction = data.direction || 'ltr';
mdTitleContainer.appendChild(mdTitle); mdTitleContainer.appendChild(mdTitle);
mdTileInner.appendChild(mdTitleContainer); mdTileInner.appendChild(mdTitleContainer);
mdTileLink.appendChild(mdTileInner); mdTile.appendChild(mdTileInner);
mdTile.appendChild(mdTileLink); mdTileContainer.appendChild(mdTile);
if (!data.isAddButton) { if (!data.isAddButton) {
let mdMenu = document.createElement('button'); let mdMenu = document.createElement('button');
...@@ -796,7 +796,9 @@ function renderMaterialDesignTile(data) { ...@@ -796,7 +796,9 @@ function renderMaterialDesignTile(data) {
if (isCustomLinksEnabled) { if (isCustomLinksEnabled) {
mdMenu.classList.add(CLASSES.MD_EDIT_MENU); mdMenu.classList.add(CLASSES.MD_EDIT_MENU);
mdMenu.title = queryArgs['editLinkTooltip'] || ''; mdMenu.title = queryArgs['editLinkTooltip'] || '';
mdMenu.name = (queryArgs['editLinkTooltip'] || '') + ' ' + data.title; mdMenu.setAttribute(
'aria-label',
(queryArgs['editLinkTooltip'] || '') + ' ' + data.title);
mdMenu.addEventListener('click', function(ev) { mdMenu.addEventListener('click', function(ev) {
editCustomLink(data.tid); editCustomLink(data.tid);
ev.preventDefault(); ev.preventDefault();
...@@ -805,10 +807,11 @@ function renderMaterialDesignTile(data) { ...@@ -805,10 +807,11 @@ function renderMaterialDesignTile(data) {
}); });
} else { } else {
mdMenu.title = queryArgs['removeTooltip'] || ''; mdMenu.title = queryArgs['removeTooltip'] || '';
mdMenu.name = (queryArgs['removeTooltip'] || '') + ' ' + data.title; mdMenu.setAttribute(
'aria-label', (queryArgs['removeTooltip'] || '') + ' ' + data.title);
mdMenu.addEventListener('click', function(ev) { mdMenu.addEventListener('click', function(ev) {
removeAllOldTiles(); removeAllOldTiles();
blacklistTile(mdTileLink); blacklistTile(mdTileContainer);
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
}); });
...@@ -820,10 +823,10 @@ function renderMaterialDesignTile(data) { ...@@ -820,10 +823,10 @@ function renderMaterialDesignTile(data) {
}); });
disableOutlineOnMouseClick(mdMenu); disableOutlineOnMouseClick(mdMenu);
mdTile.appendChild(mdMenu); mdTileContainer.appendChild(mdMenu);
} }
return mdTile; return mdTileContainer;
} }
...@@ -847,6 +850,8 @@ var init = function() { ...@@ -847,6 +850,8 @@ var init = function() {
queryArgs[decodeURIComponent(val[0])] = decodeURIComponent(val[1]); queryArgs[decodeURIComponent(val[0])] = decodeURIComponent(val[1]);
} }
document.title = queryArgs['title'];
if ('ntl' in queryArgs) { if ('ntl' in queryArgs) {
var ntl = parseInt(queryArgs['ntl'], 10); var ntl = parseInt(queryArgs['ntl'], 10);
if (isFinite(ntl)) if (isFinite(ntl))
......
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