Commit 5a6fcca5 authored by Akihiro Ota's avatar Akihiro Ota Committed by Commit Bot

ChromeVox: Interactive tutorial CSS and clean up.

This change does the following:
1. Adds CSS according to initial UI mocks
2. Adds JSDoc to member functions
3. Removes quotes from object keys
4. Removes underscores from function names.
5. Changes instances of "test" or "test area" to "practice" according
to UX feedback. We also make these changes in the code for consistency.
6. Changes many instances of "container" to "screen".
7. Rename "routing page" to "main menu" per UI feedback.
8. General clean up and use less verbose language.

--enable-experimental-accessibility-chromevox-tutorial flag.
Manually verify that tutorial loads with new UI. Move through the
tutorial using ChromeVox and verify no behavior change.

Bug: 1075752
Change-Id: I4895d7ad153a5adf1fc1471b637ca0156cea6b99
AX-Relnotes: N/A
Test: Run Chrome with the
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2304443Reviewed-by: default avatarAnastasia Helfinstein <anastasi@google.com>
Commit-Queue: Akihiro Ota <akihiroota@chromium.org>
Cr-Commit-Position: refs/heads/master@{#790102}
parent de5b316d
...@@ -2,110 +2,171 @@ ...@@ -2,110 +2,171 @@
Use of this source code is governed by a BSD-style license that can be Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. --> found in the LICENSE file. -->
<style> <style>
#tutorialContainer { #tutorial {
background: linear-gradient(to bottom,rgba(255, 255, 255, 1) 40%, background: #FFF;
rgba(215, 215, 215, 1) 100%); bottom: 0;
bottom: 0; left: 0;
left: 0; margin-bottom: 30px;
position: fixed; margin-top: 30px;
right: 0; position: fixed;
top: 0; right: 0;
top: 0;
}
#mainMenu {
margin: auto;
text-align: center;
width: 50%;
} }
#routingPageContainer { #lessonMenu {
margin: auto; margin: auto;
text-align: center; text-align: center;
width: 50%; width: 50%;
} }
#mainMenuContainer { #lessonShortcuts {
margin: auto; background: #FFF;
text-align: center; border-radius: 4px;
width: 50%; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.3);
display: flex;
flex-direction: column;
} }
#lessonContainer { #lessonContainer {
margin: auto; margin: auto;
text-align: center; text-align: center;
width: 50%; width: 50%;
}
#nav {
margin: auto;
text-align: center;
width: 50%;
}
h1 {
display: flex;
flex-direction: column;
font-family: Google Sans;
font-size: 24px;
line-height: 32px;
padding: 8px;
} }
#navigationContainer { #mainMenuButtons {
margin: auto; background: #FFF;
text-align: center; border-radius: 4px;
width: 50%; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.3);
display: flex;
flex-direction: column;
}
#navSeparator {
background: rgb(232, 234, 237);
height: 1px;
margin: 10px;
}
#mainMenu cr-button,
#lessonMenu cr-button {
color: rgb(32, 33, 36);
font-family: Roboto;
font-size: 18px;
font-style: normal;
font-weight: normal;
line-height: 20px;
padding: 30px;
}
#navButtons cr-button {
color: rgb(26, 115, 232);
font-family: Roboto;
font-size: 13px;
font-style: normal;
font-weight: 500;
line-height: 20px;
margin-bottom: 10px;
margin-inline-end: 10px;
margin-inline-start: 10px;
margin-top: 10px;
} }
</style> </style>
<div id="tutorialContainer"> <div id="tutorial">
<div id="routingPageContainer" <div id="mainMenu"
hidden$="[[ shouldHideRoutingContainer_(activeContainer) ]]"> hidden$="[[ shouldHideMainMenu(activeScreen) ]]">
<h1 id="welcomeRoutingPage" tabindex="-1">[[ routingPageWelcome ]]</h1> <h1 id="mainMenuHeader" tabindex="-1">[[ chooseYourExperience ]]</h1>
<h2>[[ chooseYourExperience ]]</h2> <div id="mainMenuButtons">
<cr-button id="newUserButton" on-click="chooseCurriculum_"> <cr-button id="newUserButton" on-click="chooseCurriculum">
[[ newUser ]] [[ newUser ]]
</cr-button> </cr-button>
<cr-button id="experiencedUserButton" on-click="chooseCurriculum_"> <cr-button id="experiencedUserButton" on-click="chooseCurriculum">
[[ experiencedUser ]] [[ experiencedUser ]]
</cr-button> </cr-button>
<cr-button id="developerButton" on-click="chooseCurriculum_"> <cr-button id="developerButton" on-click="chooseCurriculum">
[[ developer ]] [[ developer ]]
</cr-button> </cr-button>
</div>
</div> </div>
<div id="mainMenuContainer" <div id="lessonMenu"
hidden$="[[ shouldHideMainMenuContainer_(activeContainer) ]]"> hidden$="[[ shouldHideLessonMenu(activeScreen) ]]">
<h1 id="welcomeMainMenu" <h1 id="lessonMenuHeader"
tabindex="-1">[[ computeMainMenuHeader_(curriculum, medium) ]]</h1> tabindex="-1">[[ computeLessonMenuHeader(curriculum, medium) ]]</h1>
<div id="lessonShortcuts"></div> <div id="lessonShortcuts"></div>
</div> </div>
<div id="lessonContainer" <div id="lessonContainer"
hidden$="[[ shouldHideLessonContainer_(activeContainer) ]]"> hidden$="[[ shouldHideLessonContainer(activeScreen) ]]">
<!-- Use lessonData object to create all lessons --> <!-- Use lessonData object to create all lessons -->
<template is="dom-repeat" items="[[ lessonData ]]" as="lesson" <template is="dom-repeat" items="[[ lessonData ]]" as="lesson"
index-as="index"> index-as="index">
<tutorial-lesson <tutorial-lesson
lesson-num="[[ index ]]" lesson-num="[[ index ]]"
title="[[ lesson.title ]]" title="[[ lesson.title ]]"
content="[[ lesson.content ]]" content="[[ lesson.content ]]"
medium="[[ lesson.medium ]]" medium="[[ lesson.medium ]]"
curriculums="[[ lesson.curriculums ]]" curriculums="[[ lesson.curriculums ]]"
test-area-title="[[ lesson.testAreaTitle ]]" practice-title="[[ lesson.practiceTitle ]]"
test-area-instructions="[[ lesson.testAreaInstructions ]]" practice-instructions="[[ lesson.practiceInstructions ]]"
test-area-file="[[ lesson.testAreaFile ]]" practice-file="[[ lesson.practiceFile ]]"
test-area-state="[[ lesson.testAreaState ]]" practice-state="[[ lesson.practiceState ]]"
hints="[[ lesson.hints ]]" hints="[[ lesson.hints ]]"
events="[[ lesson.events ]]" events="[[ lesson.events ]]"
active-lesson-num="[[ activeLessonNum ]]"> active-lesson-num="[[ activeLessonNum ]]">
</tutorial-lesson> </tutorial-lesson>
</template> </template>
</div> </div>
<div id="navigationContainer"> <div id="nav">
<cr-button id="previousLesson" on-click="previousLesson_" <div id="navSeparator">
hidden$="[[ </div>
shouldHidePreviousLessonButton_(activeLessonIndex, activeContainer) ]]"> <div id="navButtons">
[[ previousLesson ]] <cr-button on-click="showPreviousLesson"
</cr-button> hidden$="[[
<cr-button id="nextLesson" on-click="nextLesson_" shouldHidePreviousLessonButton(activeLessonIndex, activeScreen) ]]">
hidden$="[[ [[ previousLesson ]]
shouldHideNextLessonButton_(activeLessonIndex, activeContainer) ]]"> </cr-button>
[[ nextLesson ]] <cr-button on-click="showNextLesson"
</cr-button> hidden$="[[
<cr-button id="showRoutingPage" on-click="showRoutingContainer_" shouldHideNextLessonButton(activeLessonIndex, activeScreen) ]]">
hidden$="[[ !shouldHideRoutingContainer_(activeContainer) ]]"> [[ nextLesson ]]
[[ showRoutingPage ]] </cr-button>
</cr-button> <cr-button on-click="showLessonMenu"
<cr-button id="showMainMenu" on-click="showMainMenuContainer_" hidden$="[[ !shouldHideLessonMenu(activeScreen) ]]">
hidden$="[[ !shouldHideMainMenuContainer_(activeContainer) ]]"> [[ lessonMenu ]]
[[ showMainMenu ]] </cr-button>
</cr-button> <cr-button on-click="showMainMenu"
<cr-button id="quit" on-click="quit_">[[ quitTutorial ]]</cr-button> hidden$="[[ !shouldHideMainMenu(activeScreen) ]]">
[[ mainMenu ]]
</cr-button>
<cr-button on-click="exit">[[ exitTutorial ]]</cr-button>
</div>
</div> </div>
</div> </div>
\ No newline at end of file
...@@ -3,37 +3,40 @@ ...@@ -3,37 +3,40 @@
found in the LICENSE file. --> found in the LICENSE file. -->
<style> <style>
#lessonTitle { #content {
font-family: 'Roboto', sans-serif; display: block;
font-size: 30pt; font-family: Google Sans;
font-weight: bold; font-size: 18px;
font-style: normal;
font-weight: 500;
line-height: 24px;
text-align: center;
} }
#lessonContent { #startPractice {
display: block; margin-bottom: 10px;
font-family: 'Roboto', sans-serif; margin-inline-end: 10px;
font-size: 16pt; margin-inline-start: 10px;
line-height: 150%; margin-top: 10px;
} }
</style> </style>
<div id="lessonContainer" hidden> <div id="container" hidden>
<h1 id="lessonTitle" tabindex="-1">[[title]]</h1> <h1 id="title" tabindex="-1">[[ title ]]</h1>
<div id="lessonContent"> <div id="content">
<template is="dom-repeat" items="[[ content ]]" as="text"> <template is="dom-repeat" items="[[ content ]]" as="text">
<p>[[ text ]]</p> <p>[[ text ]]</p>
</template> </template>
</div> </div>
<cr-dialog id="testAreaContainer" close-text="Exit test area" <cr-dialog id="practice" close-text="Exit practice area"
on-close="closeTestArea" show-close-button> on-close="endPractice" show-close-button>
<div id="testAreaTitle" class="title" <div class="title" slot="title">[[ practiceTitle ]]</div>
slot="title">[[testAreaTitle]]</div> <div class="body" slot="body">
<div class="body" slot="body"> <p>[[ practiceInstructions ]]</p>
<p id="testAreaInstructions">[[testAreaInstructions]]</p> <div id="practiceContent"></div>
<div id="testArea"></div> </div>
</div> </cr-dialog>
</cr-dialog> <cr-button id="startPractice" on-click="startPractice"
<cr-button id="showTestArea" on-click="showTestArea" hidden$="[[ shouldHidePracticeButton() ]]">Practice Area</cr-button>
hidden$="[[ shouldHideTestAreaButton_() ]]">Show test area</cr-button>
</div> </div>
\ No newline at end of file
...@@ -23,13 +23,13 @@ export const TutorialLesson = Polymer({ ...@@ -23,13 +23,13 @@ export const TutorialLesson = Polymer({
curriculums: {type: Array}, curriculums: {type: Array},
testAreaTitle: {type: String}, practiceTitle: {type: String},
testAreaInstructions: {type: String}, practiceInstructions: {type: String},
testAreaFile: {type: String}, practiceFile: {type: String},
testAreaState: {type: Object}, practiceState: {type: Object},
events: {type: Array}, events: {type: Array},
...@@ -43,16 +43,16 @@ export const TutorialLesson = Polymer({ ...@@ -43,16 +43,16 @@ export const TutorialLesson = Polymer({
// Observed properties. // Observed properties.
activeLessonNum: {type: Number, observer: '_setVisibility'}, activeLessonNum: {type: Number, observer: 'setVisibility'},
}, },
/** @override */ /** @override */
ready() { ready() {
if (this.testAreaFile) { if (this.practiceFile) {
this.populateTestArea_(); this.populatePracticeContent();
for (const evt of this.events) { for (const evt of this.events) {
this.$.testArea.addEventListener( this.$.practiceContent.addEventListener(
evt, this.onTestAreaEvent.bind(this), true); evt, this.onPracticeEvent.bind(this), true);
} }
} }
}, },
...@@ -60,8 +60,9 @@ export const TutorialLesson = Polymer({ ...@@ -60,8 +60,9 @@ export const TutorialLesson = Polymer({
/** /**
* Updates this lessons visibility whenever the active lesson of the tutorial * Updates this lessons visibility whenever the active lesson of the tutorial
* changes. * changes.
* @private
*/ */
_setVisibility() { setVisibility() {
if (this.lessonNum === this.activeLessonNum) { if (this.lessonNum === this.activeLessonNum) {
this.show(); this.show();
} else { } else {
...@@ -69,63 +70,70 @@ export const TutorialLesson = Polymer({ ...@@ -69,63 +70,70 @@ export const TutorialLesson = Polymer({
} }
}, },
/** @private */
show() { show() {
this.$.lessonContainer.hidden = false; this.$.container.hidden = false;
this.$.lessonTitle.focus(); this.$.title.focus();
}, },
/** @private */
hide() { hide() {
this.$.lessonContainer.hidden = true; this.$.container.hidden = true;
}, },
// Methods for managing the test area. // Methods for managing the practice area.
/** /**
* Asynchronously populates test area. * Asynchronously populates practice area.
* @private * @private
*/ */
populateTestArea_() { populatePracticeContent() {
const path = '../i_tutorial/lessons/' + this.testAreaFile + '.html'; const path = '../i_tutorial/lessons/' + this.practiceFile + '.html';
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('GET', path, true); xhr.open('GET', path, true);
xhr.onload = (evt) => { xhr.onload = (evt) => {
if (xhr.readyState === 4 && xhr.status === 200) { if (xhr.readyState === 4 && xhr.status === 200) {
this.$.testArea.innerHTML = xhr.responseText; this.$.practiceContent.innerHTML = xhr.responseText;
} else { } else {
console.error(xhr.statusText); console.error(xhr.statusText);
} }
}; };
xhr.onerror = function(evt) { xhr.onerror = function(evt) {
console.error('Failed to open test area file: ' + path); console.error('Failed to open practice file: ' + path);
console.error(xhr.statusText); console.error(xhr.statusText);
}; };
xhr.send(null); xhr.send(null);
}, },
showTestArea() { /** @private */
this.$.testAreaContainer.showModal(); startPractice() {
this.$.practice.showModal();
this.startHints(); this.startHints();
}, },
closeTestArea() { /** @private */
endPractice() {
this.stopHints(); this.stopHints();
this.$.showTestArea.focus(); this.$.startPractice.focus();
}, },
// Methods for tracking the state of the test area. // Methods for tracking the state of the practice area.
/** @param {Event} event */ /**
onTestAreaEvent(event) { * @param {Event} event
* @private
*/
onPracticeEvent(event) {
const elt = event.target.id; const elt = event.target.id;
const type = event.type; const type = event.type;
// Maybe update goal state. // Maybe update goal state.
if (elt in this.testAreaState) { if (elt in this.practiceState) {
if (type in this.testAreaState[elt]) { if (type in this.practiceState[elt]) {
this.testAreaState[elt][type] = true; this.practiceState[elt][type] = true;
} }
} }
...@@ -134,13 +142,16 @@ export const TutorialLesson = Polymer({ ...@@ -134,13 +142,16 @@ export const TutorialLesson = Polymer({
} }
}, },
/** @return {boolean} */ /**
* @return {boolean}
* @private
*/
isGoalStateReached() { isGoalStateReached() {
if (this.goalStateReached === true) { if (this.goalStateReached === true) {
return true; return true;
} }
for (const [elt, state] of Object.entries(this.testAreaState)) { for (const [elt, state] of Object.entries(this.practiceState)) {
for (const [evt, performed] of Object.entries(state)) { for (const [evt, performed] of Object.entries(state)) {
if (performed == false) { if (performed == false) {
return false; return false;
...@@ -150,6 +161,7 @@ export const TutorialLesson = Polymer({ ...@@ -150,6 +161,7 @@ export const TutorialLesson = Polymer({
return true; return true;
}, },
/** @private */
onGoalStateReached() { onGoalStateReached() {
const previousState = this.goalStateReached; const previousState = this.goalStateReached;
this.goalStateReached = true; this.goalStateReached = true;
...@@ -157,15 +169,15 @@ export const TutorialLesson = Polymer({ ...@@ -157,15 +169,15 @@ export const TutorialLesson = Polymer({
// Only perform when crossing the threshold from not reached to reached. // Only perform when crossing the threshold from not reached to reached.
this.stopHints(); this.stopHints();
this.requestSpeech( this.requestSpeech(
'You have passed this tutorial lesson. Find and press the close ' + 'You have passed this tutorial lesson. Find and press the exit ' +
'test area button to continue'); 'practice area button to continue');
} }
}, },
// Methods for managing hints. // Methods for managing hints.
/** @private */
startHints() { startHints() {
this.hintCounter = 0; this.hintCounter = 0;
this.hintIntervalId = setInterval(() => { this.hintIntervalId = setInterval(() => {
...@@ -175,9 +187,10 @@ export const TutorialLesson = Polymer({ ...@@ -175,9 +187,10 @@ export const TutorialLesson = Polymer({
} }
this.requestSpeech(this.hints[this.hintCounter]); this.requestSpeech(this.hints[this.hintCounter]);
this.hintCounter += 1; this.hintCounter += 1;
}, 20000); }, 20 * 1000);
}, },
/** @private */
stopHints() { stopHints() {
if (this.hintIntervalId) { if (this.hintIntervalId) {
clearInterval(this.hintIntervalId); clearInterval(this.hintIntervalId);
...@@ -203,15 +216,19 @@ export const TutorialLesson = Polymer({ ...@@ -203,15 +216,19 @@ export const TutorialLesson = Polymer({
/** /**
* Requests speech from the Panel. * Requests speech from the Panel.
* @param {string} text * @param {string} text
* @private
*/ */
requestSpeech(text) { requestSpeech(text) {
this.dispatchEvent( this.dispatchEvent(
new CustomEvent('request-speech', {composed: true, detail: {text}})); new CustomEvent('request-speech', {composed: true, detail: {text}}));
}, },
/** @return {boolean} */ /**
shouldHideTestAreaButton_() { * @return {boolean}
if (!this.testAreaFile) { * @private
*/
shouldHidePracticeButton() {
if (!this.practiceFile) {
return true; return true;
} }
......
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