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
...@@ -3,68 +3,125 @@ ...@@ -3,68 +3,125 @@
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;
margin-top: 30px;
position: fixed; position: fixed;
right: 0; right: 0;
top: 0; top: 0;
} }
#routingPageContainer { #mainMenu {
margin: auto; margin: auto;
text-align: center; text-align: center;
width: 50%; width: 50%;
} }
#mainMenuContainer { #lessonMenu {
margin: auto; margin: auto;
text-align: center; text-align: center;
width: 50%; width: 50%;
} }
#lessonShortcuts {
background: #FFF;
border-radius: 4px;
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%;
} }
#navigationContainer { #nav {
margin: auto; margin: auto;
text-align: center; text-align: center;
width: 50%; width: 50%;
} }
h1 {
display: flex;
flex-direction: column;
font-family: Google Sans;
font-size: 24px;
line-height: 32px;
padding: 8px;
}
#mainMenuButtons {
background: #FFF;
border-radius: 4px;
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"
...@@ -75,10 +132,10 @@ ...@@ -75,10 +132,10 @@
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 ]]">
...@@ -86,26 +143,30 @@ ...@@ -86,26 +143,30 @@
</template> </template>
</div> </div>
<div id="navigationContainer"> <div id="nav">
<cr-button id="previousLesson" on-click="previousLesson_" <div id="navSeparator">
</div>
<div id="navButtons">
<cr-button on-click="showPreviousLesson"
hidden$="[[ hidden$="[[
shouldHidePreviousLessonButton_(activeLessonIndex, activeContainer) ]]"> shouldHidePreviousLessonButton(activeLessonIndex, activeScreen) ]]">
[[ previousLesson ]] [[ previousLesson ]]
</cr-button> </cr-button>
<cr-button id="nextLesson" on-click="nextLesson_" <cr-button on-click="showNextLesson"
hidden$="[[ hidden$="[[
shouldHideNextLessonButton_(activeLessonIndex, activeContainer) ]]"> shouldHideNextLessonButton(activeLessonIndex, activeScreen) ]]">
[[ nextLesson ]] [[ nextLesson ]]
</cr-button> </cr-button>
<cr-button id="showRoutingPage" on-click="showRoutingContainer_" <cr-button on-click="showLessonMenu"
hidden$="[[ !shouldHideRoutingContainer_(activeContainer) ]]"> hidden$="[[ !shouldHideLessonMenu(activeScreen) ]]">
[[ showRoutingPage ]] [[ lessonMenu ]]
</cr-button> </cr-button>
<cr-button id="showMainMenu" on-click="showMainMenuContainer_" <cr-button on-click="showMainMenu"
hidden$="[[ !shouldHideMainMenuContainer_(activeContainer) ]]"> hidden$="[[ !shouldHideMainMenu(activeScreen) ]]">
[[ showMainMenu ]] [[ mainMenu ]]
</cr-button> </cr-button>
<cr-button id="quit" on-click="quit_">[[ quitTutorial ]]</cr-button> <cr-button on-click="exit">[[ exitTutorial ]]</cr-button>
</div>
</div> </div>
</div> </div>
\ No newline at end of file
...@@ -26,14 +26,13 @@ const InteractionMedium = { ...@@ -26,14 +26,13 @@ const InteractionMedium = {
}; };
/** /**
* The various containers within the tutorial. * The various screens within the tutorial.
* @enum {string} * @enum {string}
*/ */
const Container = { const Screen = {
ROUTING: 'routing',
MAIN_MENU: 'main_menu', MAIN_MENU: 'main_menu',
LESSON_MENU: 'lesson_menu',
LESSON: 'lesson', LESSON: 'lesson',
NAVIGATION: 'navigation',
}; };
Polymer({ Polymer({
...@@ -42,12 +41,12 @@ Polymer({ ...@@ -42,12 +41,12 @@ Polymer({
_template: html`{__html_template__}`, _template: html`{__html_template__}`,
properties: { properties: {
curriculum: {type: String, observer: '_updateIncludedLessons'}, curriculum: {type: String, observer: 'updateIncludedLessons'},
medium: { medium: {
type: String, type: String,
value: InteractionMedium.KEYBOARD, value: InteractionMedium.KEYBOARD,
observer: '_updateIncludedLessons' observer: 'updateIncludedLessons'
}, },
// Bookkeeping variables. // Bookkeeping variables.
...@@ -63,13 +62,10 @@ Polymer({ ...@@ -63,13 +62,10 @@ Polymer({
numLessons: {type: Number, value: 0}, numLessons: {type: Number, value: 0},
activeContainer: {type: String}, activeScreen: {type: String},
// Labels and text content. // Labels and text content.
routingPageWelcome:
{type: String, value: 'Welcome to the ChromeVox Tutorial'},
chooseYourExperience: { chooseYourExperience: {
type: String, type: String,
value: 'Choose your tutorial experience', value: 'Choose your tutorial experience',
...@@ -87,61 +83,61 @@ Polymer({ ...@@ -87,61 +83,61 @@ Polymer({
nextLesson: {type: String, value: 'Next lesson'}, nextLesson: {type: String, value: 'Next lesson'},
showRoutingPage: {type: String, value: 'Show routing page'}, mainMenu: {type: String, value: 'Main menu'},
showMainMenu: {type: String, value: 'Show main menu'}, lessonMenu: {type: String, value: 'All lessons'},
quitTutorial: {type: String, value: 'Quit tutorial'}, exitTutorial: {type: String, value: 'Exit tutorial'},
lessonData: { lessonData: {
type: Array, type: Array,
value: [ value: [
{ {
'title': 'On, Off, and Stop', title: 'On, Off, and Stop',
'content': [ content: [
'To temporarily stop ChromeVox from speaking, press the Control ' + 'To temporarily stop ChromeVox from speaking, press the Control ' +
'key.', 'key.',
'To turn ChromeVox on or off, use Control+Alt+Z.', 'To turn ChromeVox on or off, use Control+Alt+Z.',
], ],
'medium': InteractionMedium.KEYBOARD, medium: InteractionMedium.KEYBOARD,
'curriculums': [Curriculum.OOBE, Curriculum.NEW_USER], curriculums: [Curriculum.OOBE, Curriculum.NEW_USER],
}, },
{ {
'title': 'The ChromeVox Modifier Key', title: 'The ChromeVox Modifier Key',
'content': [ content: [
'In ChromeVox, the Search key is the modifier key. ' + 'In ChromeVox, the Search key is the modifier key. ' +
'Most ChromeVox shortcuts start with the Search key. ' + 'Most ChromeVox shortcuts start with the Search key. ' +
'You’ll also use the arrow keys for navigation.', 'You’ll also use the arrow keys for navigation.',
'On the Chromebook, the Search key is immediately above the ' + 'On the Chromebook, the Search key is immediately above the ' +
'left Shift key.', 'left Shift key.',
], ],
'medium': InteractionMedium.KEYBOARD, medium: InteractionMedium.KEYBOARD,
'curriculums': [Curriculum.OOBE, Curriculum.NEW_USER], curriculums: [Curriculum.OOBE, Curriculum.NEW_USER],
}, },
{ {
'title': 'Basic Navigation', title: 'Basic Navigation',
'content': [ content: [
'To move forward between items on a page, press Search + Right ' + 'To move forward between items on a page, press Search + Right ' +
'Arrow, or Search + Left Arrow to jump back.', 'Arrow, or Search + Left Arrow to jump back.',
'To go to the next line, press Search + Down Arrow. ' + 'To go to the next line, press Search + Down Arrow. ' +
'To get to the previous line, use Search + Up Arrow.', 'To get to the previous line, use Search + Up Arrow.',
'If you reach an item you want to click, press Search + Space.', 'If you reach an item you want to click, press Search + Space.',
], ],
'medium': InteractionMedium.KEYBOARD, medium: InteractionMedium.KEYBOARD,
'curriculums': [Curriculum.OOBE, Curriculum.NEW_USER], curriculums: [Curriculum.OOBE, Curriculum.NEW_USER],
'testAreaTitle': 'Basic Navigation Test Area', practiceTitle: 'Basic Navigation Practice',
'testAreaInstructions': practiceInstructions:
'Try using basic navigation to navigate through the items ' + 'Try using basic navigation to navigate through the items ' +
'below. Find the button titled "Click me" and use Search ' + 'below. Find the button titled "Click me" and use Search ' +
'+ Space to click it. Then move to the next lesson.', '+ Space to click it. Then move to the next lesson.',
'testAreaFile': 'basic_navigation', practiceFile: 'basic_navigation',
'testAreaState': { practiceState: {
'goal': {'click': false}, goal: {click: false},
}, },
'events': ['click'], events: ['click'],
'hints': [ hints: [
'Try pressing Search + left/right arrow. The search key directly ' + 'Try pressing Search + left/right arrow. The search key directly ' +
' above the shift key', ' above the shift key',
'Use search + space to click an item' 'Use search + space to click an item'
...@@ -149,8 +145,8 @@ Polymer({ ...@@ -149,8 +145,8 @@ Polymer({
}, },
{ {
'title': 'Jump Commands', title: 'Jump Commands',
'content': [ content: [
'Use jump commands to skip to specific types of elements.', 'Use jump commands to skip to specific types of elements.',
'To jump forward between headings, press Search + H, or to ' + 'To jump forward between headings, press Search + H, or to ' +
'jump backward, press Search + Shift + H.', 'jump backward, press Search + Shift + H.',
...@@ -159,28 +155,28 @@ Polymer({ ...@@ -159,28 +155,28 @@ Polymer({
'To jump foorward beetween links, press Search + L, or to ' + 'To jump foorward beetween links, press Search + L, or to ' +
'jump backward, press Search + Shift + L' 'jump backward, press Search + Shift + L'
], ],
'medium': InteractionMedium.KEYBOARD, medium: InteractionMedium.KEYBOARD,
'curriculums': [Curriculum.NEW_USER], curriculums: [Curriculum.NEW_USER],
'testAreaTitle': 'Jump Commands Test Area', practiceTitle: 'Jump Commands Practice',
'testAreaInstructions': practiceInstructions:
'Try using what you have learned to navigate by element type. ' + 'Try using what you have learned to navigate by element type. ' +
'Notice that navigation wraps if you are on the first or ' + 'Notice that navigation wraps if you are on the first or ' +
'last element and press previous element or next element, ' + 'last element and press previous element or next element, ' +
'respectively.', 'respectively.',
'testAreaFile': 'jump_commands', practiceFile: 'jump_commands',
'testAreaState': { practiceState: {
'first-heading': {'focus': false}, 'first-heading': {focus: false},
'first-link': {'focus': false}, 'first-link': {focus: false},
'first-button': {'focus': false}, 'first-button': {focus: false},
'second-heading': {'focus': false}, 'second-heading': {focus: false},
'second-link': {'focus': false}, 'second-link': {focus: false},
'second-button': {'focus': false}, 'second-button': {focus: false},
'last-heading': {'focus': false}, 'last-heading': {focus: false},
'last-link': {'focus': false}, 'last-link': {focus: false},
'last-button': {'focus': false}, 'last-button': {focus: false},
}, },
'events': ['focus'], events: ['focus'],
'hints': [ hints: [
'Try using search + h to move by header', 'Try using search + h to move by header',
'Try using search + b to move by button', 'Try using search + b to move by button',
'Try using search + l to move by link' 'Try using search + l to move by link'
...@@ -188,20 +184,20 @@ Polymer({ ...@@ -188,20 +184,20 @@ Polymer({
}, },
{ {
'title': 'The ChromeVox Menu', title: 'The ChromeVox Menu',
'content': [ content: [
'To explore all ChromeVox commands and shortcuts, press ' + 'To explore all ChromeVox commands and shortcuts, press ' +
'Search + Period, then use the Arrow keys to navigate the ' + 'Search + Period, then use the Arrow keys to navigate the ' +
'menus, and Enter to activate a command. Return here by ' + 'menus, and Enter to activate a command. Return here by ' +
'pressing Search+o then t.', 'pressing Search+o then t.',
], ],
'medium': InteractionMedium.KEYBOARD, medium: InteractionMedium.KEYBOARD,
'curriculums': [Curriculum.NEW_USER] curriculums: [Curriculum.NEW_USER]
}, },
{ {
'title': 'Helpful Chrome Shortcuts', title: 'Helpful Chrome Shortcuts',
'content': [ content: [
'The next few shortcuts aren’t ChromeVox commands, but they are ' + 'The next few shortcuts aren’t ChromeVox commands, but they are ' +
'still very useful for getting the most out of Chrome.', 'still very useful for getting the most out of Chrome.',
'To navigate forward through actionable items like buttons and ' + 'To navigate forward through actionable items like buttons and ' +
...@@ -217,13 +213,13 @@ Polymer({ ...@@ -217,13 +213,13 @@ Polymer({
'To open the full list of keyboard shortcuts, press ' + 'To open the full list of keyboard shortcuts, press ' +
'Control + Alt + /' 'Control + Alt + /'
], ],
'medium': InteractionMedium.KEYBOARD, medium: InteractionMedium.KEYBOARD,
'curriculums': [Curriculum.NEW_USER] curriculums: [Curriculum.NEW_USER]
}, },
{ {
'title': 'Sounds', title: 'Sounds',
'content': [ content: [
'ChromeVox uses sounds to give you essential and additional ' + 'ChromeVox uses sounds to give you essential and additional ' +
'information. You can use these sounds to navigate more ' + 'information. You can use these sounds to navigate more ' +
'quickly by learning what each sound means. Once you get ' + 'quickly by learning what each sound means. Once you get ' +
...@@ -231,34 +227,34 @@ Polymer({ ...@@ -231,34 +227,34 @@ Polymer({
'speech and rely on them for essential information about the ' + 'speech and rely on them for essential information about the ' +
'page. Here is a complete list of sounds and what they mean', 'page. Here is a complete list of sounds and what they mean',
], ],
'medium': InteractionMedium.KEYBOARD, medium: InteractionMedium.KEYBOARD,
'curriculums': [Curriculum.NEW_USER] curriculums: [Curriculum.NEW_USER]
}, },
{ {
'title': 'Text fields', title: 'Text fields',
'content': ['Text content for text fields lesson'], content: ['Text content for text fields lesson'],
'medium': InteractionMedium.KEYBOARD, medium: InteractionMedium.KEYBOARD,
'curriculums': [Curriculum.EXPERIENCED_USER], curriculums: [Curriculum.EXPERIENCED_USER],
'testAreaTitle': 'Edit fields Test Area', practiceTitle: 'Edit fields practice',
'testAreaInstructions': practiceInstructions:
'Try using what you have learned about text fields and edit ' + 'Try using what you have learned about text fields and edit ' +
'the text fields below.', 'the text fields below.',
'testAreaFile': 'text_fields', practiceFile: 'text_fields',
'testAreaState': { practiceState: {
'input': {'focus': false, 'input': false}, input: {focus: false, input: false},
'editable': {'focus': false, 'input': false} editable: {focus: false, input: false}
}, },
'events': ['focus', 'input'], events: ['focus', 'input'],
'hints': [ hints: [
'Once you find an editable element, you can type normally.', 'Once you find an editable element, you can type normally.',
'Try editing the content you entered' 'Try editing the content you entered'
] ]
}, },
{ {
'title': 'Congratulations', title: 'Congratulations',
'content': [ content: [
'You’ve learned the essentials to use ChromeVox successfully. ' + 'You’ve learned the essentials to use ChromeVox successfully. ' +
'Remember that you can open the ChromeVox command menu at ' + 'Remember that you can open the ChromeVox command menu at ' +
'any time by pressing Search+Period. To learn even more ' + 'any time by pressing Search+Period. To learn even more ' +
...@@ -266,8 +262,8 @@ Polymer({ ...@@ -266,8 +262,8 @@ Polymer({
'If you are done with the tutorial, use ChromeVox to navigate ' + 'If you are done with the tutorial, use ChromeVox to navigate ' +
'to the Quit button and click it.' 'to the Quit button and click it.'
], ],
'medium': InteractionMedium.KEYBOARD, medium: InteractionMedium.KEYBOARD,
'curriculums': [ curriculums: [
Curriculum.OOBE, Curriculum.NEW_USER, Curriculum.DEVELOPER, Curriculum.OOBE, Curriculum.NEW_USER, Curriculum.DEVELOPER,
Curriculum.EXPERIENCED_USER Curriculum.EXPERIENCED_USER
], ],
...@@ -278,10 +274,14 @@ Polymer({ ...@@ -278,10 +274,14 @@ Polymer({
/** @override */ /** @override */
ready() { ready() {
this.showRoutingContainer_(); this.showMainMenu();
}, },
chooseCurriculum_(evt) { /**
* @param {!MouseEvent} evt
* @private
*/
chooseCurriculum(evt) {
const id = evt.target.id; const id = evt.target.id;
if (id === 'newUserButton') { if (id === 'newUserButton') {
this.curriculum = Curriculum.NEW_USER; this.curriculum = Curriculum.NEW_USER;
...@@ -290,23 +290,26 @@ Polymer({ ...@@ -290,23 +290,26 @@ Polymer({
} else if (id === 'developerButton') { } else if (id === 'developerButton') {
this.curriculum = Curriculum.DEVELOPER; this.curriculum = Curriculum.DEVELOPER;
} else { } else {
throw new Error('Invalid target for chooseCurriculum_: ' + evt.target.id); throw new Error('Invalid target for chooseCurriculum: ' + evt.target.id);
} }
this.showMainMenuContainer_(); this.showLessonMenu();
}, },
nextLesson_() { /** @private */
this.showLesson_(this.activeLessonIndex + 1); showNextLesson() {
this.showLesson(this.activeLessonIndex + 1);
}, },
previousLesson_() { /** @private */
this.showLesson_(this.activeLessonIndex - 1); showPreviousLesson() {
this.showLesson(this.activeLessonIndex - 1);
}, },
/** /**
* @param {number} index * @param {number} index
* @private
*/ */
showLesson_(index) { showLesson(index) {
this.showLessonContainer(); this.showLessonContainer();
if (index < 0 || index >= this.numLessons) { if (index < 0 || index >= this.numLessons) {
return; return;
...@@ -320,25 +323,28 @@ Polymer({ ...@@ -320,25 +323,28 @@ Polymer({
}, },
// Methods for hiding and showing containers. // Methods for hiding and showing screens.
/** @private */
showRoutingContainer_() { showMainMenu() {
this.activeContainer = Container.ROUTING; this.activeScreen = Screen.MAIN_MENU;
this.$.welcomeRoutingPage.focus(); this.$.mainMenuHeader.focus();
}, },
showMainMenuContainer_() { /** @private */
this.activeContainer = Container.MAIN_MENU; showLessonMenu() {
this.activeScreen = Screen.LESSON_MENU;
this.createLessonShortcuts(); this.createLessonShortcuts();
this.$.welcomeMainMenu.focus(); this.$.lessonMenuHeader.focus();
}, },
/** @private */
showLessonContainer() { showLessonContainer() {
this.activeContainer = Container.LESSON; this.activeScreen = Screen.LESSON;
}, },
_updateIncludedLessons() { /** @private */
updateIncludedLessons() {
this.includedLessons = []; this.includedLessons = [];
this.activeLessonNum = -1; this.activeLessonNum = -1;
this.activeLessonIndex = -1; this.activeLessonIndex = -1;
...@@ -359,6 +365,7 @@ Polymer({ ...@@ -359,6 +365,7 @@ Polymer({
this.createLessonShortcuts(); this.createLessonShortcuts();
}, },
/** @private */
createLessonShortcuts() { createLessonShortcuts() {
// Clear previous lesson shortcuts, as the user may have chosen a new // Clear previous lesson shortcuts, as the user may have chosen a new
// curriculum or medium for the tutorial. // curriculum or medium for the tutorial.
...@@ -368,8 +375,8 @@ Polymer({ ...@@ -368,8 +375,8 @@ Polymer({
let count = 1; let count = 1;
for (const lesson of this.includedLessons) { for (const lesson of this.includedLessons) {
const button = document.createElement('cr-button'); const button = document.createElement('cr-button');
button.addEventListener('click', this.showLesson_.bind(this, count - 1)); button.addEventListener('click', this.showLesson.bind(this, count - 1));
button.textContent = count + ': ' + lesson.title; button.textContent = lesson.title;
this.$.lessonShortcuts.appendChild(button); this.$.lessonShortcuts.appendChild(button);
count += 1; count += 1;
} }
...@@ -378,53 +385,86 @@ Polymer({ ...@@ -378,53 +385,86 @@ Polymer({
// Methods for computing attributes and properties. // Methods for computing attributes and properties.
/**
shouldHideNextLessonButton_(activeLessonIndex, activeContainer) { * @param {number} activeLessonIndex
* @param {Screen} activeScreen
* @return {boolean}
* @private
*/
shouldHideNextLessonButton(activeLessonIndex, activeScreen) {
if (activeLessonIndex === this.numLessons - 1 || if (activeLessonIndex === this.numLessons - 1 ||
activeContainer !== Container.LESSON) { activeScreen !== Screen.LESSON) {
return true; return true;
} }
return false; return false;
}, },
shouldHidePreviousLessonButton_(activeLessonIndex, activeContainer) { /**
if (activeLessonIndex === 0 || activeContainer !== Container.LESSON) { * @param {number} activeLessonIndex
* @param {Screen} activeScreen
* @return {boolean}
* @private
*/
shouldHidePreviousLessonButton(activeLessonIndex, activeScreen) {
if (activeLessonIndex === 0 || activeScreen !== Screen.LESSON) {
return true; return true;
} }
return false; return false;
}, },
shouldHideRoutingContainer_(activeContainer) { /**
if (activeContainer === Container.ROUTING) { * @param {Screen} activeScreen
* @return {boolean}
* @private
*/
shouldHideMainMenu(activeScreen) {
if (activeScreen === Screen.MAIN_MENU) {
return false; return false;
} }
return true; return true;
}, },
shouldHideLessonContainer_(activeContainer) { /**
if (activeContainer === Container.LESSON) { * @param {Screen} activeScreen
* @return {boolean}
* @private
*/
shouldHideLessonContainer(activeScreen) {
if (activeScreen === Screen.LESSON) {
return false; return false;
} }
return true; return true;
}, },
shouldHideMainMenuContainer_(activeContainer) { /**
if (activeContainer === Container.MAIN_MENU) { * @param {Screen} activeScreen
* @return {boolean}
* @private
*/
shouldHideLessonMenu(activeScreen) {
if (activeScreen === Screen.LESSON_MENU) {
return false; return false;
} }
return true; return true;
}, },
computeMainMenuHeader_(curriculum, medium) { /**
* @param {Curriculum} curriculum
* @param {InteractionMedium} medium
* @return {string}
* @private
*/
computeLessonMenuHeader(curriculum, medium) {
return 'Lessons for the ' + curriculum + ' ' + medium + ' experience'; return 'Lessons for the ' + curriculum + ' ' + medium + ' experience';
}, },
quit_() { /** @private */
exit() {
this.dispatchEvent(new CustomEvent('tutorial-close', {})); this.dispatchEvent(new CustomEvent('tutorial-close', {}));
}, },
}); });
...@@ -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 id="testAreaInstructions">[[testAreaInstructions]]</p> <p>[[ practiceInstructions ]]</p>
<div id="testArea"></div> <div id="practiceContent"></div>
</div> </div>
</cr-dialog> </cr-dialog>
<cr-button id="showTestArea" on-click="showTestArea" <cr-button id="startPractice" on-click="startPractice"
hidden$="[[ shouldHideTestAreaButton_() ]]">Show test area</cr-button> hidden$="[[ shouldHidePracticeButton() ]]">Practice 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