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