Commit 83748da3 authored by Scott Chen's avatar Scott Chen Committed by Commit Bot

NUX Onboarding: add navigation mechanism.

Bug: 874146
Change-Id: I2582d5d87c3e8839bdbe1e7939cd656eb76bc4ac
Reviewed-on: https://chromium-review.googlesource.com/1237520
Commit-Queue: Scott Chen <scottchen@chromium.org>
Reviewed-by: default avatarDemetrios Papadopoulos <dpapad@chromium.org>
Cr-Commit-Position: refs/heads/master@{#593645}
parent 2c126005
......@@ -63,6 +63,8 @@
<structure name="IDR_DICE_WELCOME_APP_JS" file="resources\welcome\dice_welcome\welcome_app.js" type="chrome_html" preprocess="true"/>
<structure name="IDR_WELCOME_ONBOARDING_WELCOME_LANDING_VIEW_HTML" file="resources\welcome\onboarding_welcome\landing_view.html" type="chrome_html" preprocess="true"/>
<structure name="IDR_WELCOME_ONBOARDING_WELCOME_LANDING_VIEW_JS" file="resources\welcome\onboarding_welcome\landing_view.js" type="chrome_html" preprocess="true"/>
<structure name="IDR_WELCOME_ONBOARDING_WELCOME_NAVIGATION_BEHAVIOR_HTML" file="resources\welcome\onboarding_welcome\navigation_behavior.html" type="chrome_html" preprocess="true"/>
<structure name="IDR_WELCOME_ONBOARDING_WELCOME_NAVIGATION_BEHAVIOR_JS" file="resources\welcome\onboarding_welcome\navigation_behavior.js" type="chrome_html" preprocess="true"/>
<structure name="IDR_WELCOME_ONBOARDING_WELCOME_WELCOME_APP_HTML" file="resources\welcome\onboarding_welcome\welcome_app.html" type="chrome_html" preprocess="true"/>
<structure name="IDR_WELCOME_ONBOARDING_WELCOME_WELCOME_APP_JS" file="resources\welcome\onboarding_welcome\welcome_app.js" type="chrome_html" preprocess="true"/>
<structure name="IDR_WELCOME_ONBOARDING_WELCOME_WELCOME_CSS" file="resources\welcome\onboarding_welcome\welcome.css" type="chrome_html" preprocess="true"/>
......
......@@ -3,6 +3,7 @@
<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
<link rel="import" href="navigation_behavior.html">
<dom-module id="landing-view">
<template>
......
......@@ -5,13 +5,15 @@
Polymer({
is: 'landing-view',
behaviors: [welcome.NavigationBehavior],
/** @private */
onExistingUserClick_: function() {
// TODO(scottchen): do something.
this.navigateTo(welcome.Routes.RETURNING_USER, 1);
},
/** @private */
onNewUserClick_: function() {
// TODO(scottchen): do something.
this.navigateTo(welcome.Routes.NEW_USER, 1);
}
});
<link rel="import" href="chrome://resources/html/assert.html">
<link rel="import" href="chrome://resources/html/cr.html">
<script src="navigation_behavior.js"></script>
\ No newline at end of file
// Copyright 2018 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 The NavigationBehavior is in charge of manipulating and
* watching window.history.state changes. The page is using the history
* state object to remember state instead of changing the URL directly,
* because the flow requires that users can use browser-back/forward to
* navigate between steps, without being able to go directly or copy an URL
* that points at a specific step. Using history.state object allows adding
* or popping history state without actually changing the path.
*/
cr.define('welcome', function() {
'use strict';
/**
* Valid route pathnames.
* @enum {string}
*/
const Routes = {
LANDING: 'landing',
NEW_USER: 'new-user',
RETURNING_USER: 'returning-user',
};
/**
* Regular expression that captures the leading slash, the content and the
* trailing slash in three different groups.
* @const {!RegExp}
*/
const CANONICAL_PATH_REGEX = /(^\/)([\/-\w]+)(\/$)/;
const path = location.pathname.replace(CANONICAL_PATH_REGEX, '$1$2');
// Sets up history state based on the url path, unless it's already set (e.g.
// when user uses browser-back button to get back on chrome://welcome/...).
if (!history.state || !history.state.route || !history.state.step) {
switch (path) {
case `/${Routes.NEW_USER}`:
history.replaceState({route: Routes.NEW_USER, step: 1}, '', path);
break;
case `/${Routes.RETURNING_USER}`:
history.replaceState({route: Routes.RETURNING_USER, step: 1}, '', path);
break;
default:
history.replaceState(
{route: Routes.LANDING, step: Routes.LANDING}, '', '/');
}
}
/** @type {!Set<!PolymerElement>} */
const routeObservers = new Set();
// Notifies all the elements that extended NavigationBehavior.
function notifyObservers() {
const route = history.state.route;
const step = history.state.step;
routeObservers.forEach((observer) => {
observer.onRouteChange(route, step);
});
}
// Notifies all elements when browser history is popped.
window.addEventListener('popstate', notifyObservers);
/** @polymerBehavior */
const NavigationBehavior = {
/** @override */
attached: function() {
assert(!routeObservers.has(this));
routeObservers.add(this);
// history state was set when page loaded, so when the element first
// attaches, call the route-change handler to initialize first.
this.onRouteChange(history.state.route, history.state.step);
},
/** @override */
detached: function() {
assert(routeObservers.delete(this));
},
/** Elements can override onRouteChange to handle route changes. */
onRouteChange: function() {},
navigateToNextStep: function() {
history.pushState(
{
route: history.state.route,
step: history.state.step + 1,
},
'', `/${history.state.route}`);
notifyObservers();
},
/**
* @param {string} route
* @param {number} step
*/
navigateTo: function(route, step) {
assert([
Routes.LANDING,
Routes.NEW_USER,
Routes.RETURNING_USER,
].includes(route));
history.pushState(
{
route: route,
step: step,
},
'', '/' + (route === Routes.LANDING ? '' : route));
notifyObservers();
},
};
return {
NavigationBehavior: NavigationBehavior,
Routes: Routes,
};
});
\ No newline at end of file
<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/cr_elements/cr_view_manager/cr_view_manager.html">
<link rel="import" href="navigation_behavior.html">
<link rel="import" href="landing_view.html">
<dom-module id="welcome-app">
......
......@@ -2,6 +2,75 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* The strings contained in the arrays should be valid DOM-element tag names.
* @typedef {{
* 'new-user': !Array<string>,
* 'returning-user': !Array<string>
* }}
*/
let NuxOnboardingModules;
Polymer({
is: 'welcome-app',
behaviors: [welcome.NavigationBehavior],
/** @private {?welcome.Routes} */
currentRoute_: null,
// TODO(scottchen): instead of dummy, get data from finch/load time data.
/** @private {NuxOnboardingModules} */
modules_: {
'new-user': ['h1', 'h1', 'h1'],
'returning-user': ['h3', 'h3'],
},
/**
* @param {string} route
* @param {number} step
* @private
*/
onRouteChange: function(route, step) {
// If the route changed, initialize the steps of modules for that route.
if (this.currentRoute_ != route) {
this.currentRoute_ = route;
this.initializeModules(this.modules_[route]);
}
// If the specified step doesn't exist, that means there are no more steps.
// In that case, go to NTP.
if (!this.$$('#step-' + step))
window.location.replace('chrome://newtab');
else // Otherwise, go to the chosen step of that route.
this.$.viewManager.switchView('step-' + step);
},
initializeModules: function(modules) {
assert(this.currentRoute_); // this.currentRoute_ should be set by now.
if (this.currentRoute_ == welcome.Routes.LANDING)
return;
assert(modules); // modules should be defined if on a valid route.
// Remove all views except landing.
this.$.viewManager
.querySelectorAll('[slot="view"]:not([id="step-landing"])')
.forEach(element => {
element.remove();
});
modules.forEach((elementTagName, index) => {
const element = document.createElement(elementTagName);
element.id = 'step-' + (index + 1);
element.setAttribute('slot', 'view');
this.$.viewManager.appendChild(element);
// TODO(scottchen): this is just to test routing works. Actual elements
// will have buttons that are responsible for navigation.
element.textContent = index + 1;
element.addEventListener('click', () => {
this.navigateToNextStep();
});
});
},
});
......@@ -80,6 +80,12 @@ WelcomeUI::WelcomeUI(content::WebUI* web_ui, const GURL& url)
"landing_view.html", IDR_WELCOME_ONBOARDING_WELCOME_LANDING_VIEW_HTML);
html_source->AddResourcePath(
"landing_view.js", IDR_WELCOME_ONBOARDING_WELCOME_LANDING_VIEW_JS);
html_source->AddResourcePath(
"navigation_behavior.html",
IDR_WELCOME_ONBOARDING_WELCOME_NAVIGATION_BEHAVIOR_HTML);
html_source->AddResourcePath(
"navigation_behavior.js",
IDR_WELCOME_ONBOARDING_WELCOME_NAVIGATION_BEHAVIOR_JS);
html_source->AddResourcePath("welcome.css",
IDR_WELCOME_ONBOARDING_WELCOME_WELCOME_CSS);
html_source->AddResourcePath(
......
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