Skip to content

Commit

Permalink
load shepherd.js lazily using ember-auto-import (#305)
Browse files Browse the repository at this point in the history
* wip: load shepherd.js lazily using ember-auto-import

* guard fastboot
use ember-cli to import theme css

* remove ember-cli-node-assets

* rename fastboot variable

* await addSteps promise to resolve

* clarify usage. Throw an error when the start method is being invoked and shepherd has not been fully loaded

* use node 8 for tests
  • Loading branch information
st-h authored and RobbieTheWagner committed Apr 29, 2019
1 parent 2c11fc2 commit a95b171
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 88 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module.exports = {
root: true,
parser: 'babel-eslint',
parserOptions: {
ecmaVersion: 2017,
sourceType: 'module'
Expand Down
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ language: node_js
node_js:
# we recommend testing addons with the same minimum supported node version as Ember CLI
# so that your addon works for all apps
- "6"
- "8"

sudo: false
dist: trusty
Expand Down
151 changes: 97 additions & 54 deletions addon/services/tour.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
elementIsHidden
} from '../utils/dom';
import { disableBodyScroll, enableBodyScroll, clearAllBodyScrollLocks } from 'body-scroll-lock';
import { Promise } from 'rsvp';

/**
* Interaction with `ember-shepherd` is done entirely through the Tour service, which you can access from any object using the `Ember.inject` syntax:
Expand Down Expand Up @@ -156,12 +157,20 @@ export default Service.extend(Evented, {
requiredElements: [],
steps: [],

init() {
this._super(...arguments);
let owner = getOwner(this);
const fastboot = owner.lookup('service:fastboot');
this._isFastBoot = fastboot && fastboot.isFastBoot;
},

willDestroy() {
this._cleanup();
},

/**
* Take a set of steps and create a tour object based on the current configuration
* Take a set of steps, create a tour object based on the current configuration and load the shepherd.js dependency.
* This method returns a promise which resolves when the shepherd.js dependency has been loaded and shepherd is ready to use.
*
* You must pass an array of steps to `addSteps`, something like this:
*
Expand Down Expand Up @@ -219,58 +228,63 @@ export default Service.extend(Evented, {
*
* @method addSteps
* @param {array} steps An array of steps
* @returns {Promise} Promise that resolves when everything has been set up and shepherd is ready to use
* @public
*/
addSteps(steps) {
this._initialize();
const tour = get(this, 'tourObject');

// Return nothing if there are no steps
if (isEmpty(steps)) {
return;
}
/* istanbul ignore next: also can't test this due to things attached to root blowing up tests */
if (!this._requiredElementsPresent()) {
tour.addStep('error', {
buttons: [{
text: 'Exit',
action: tour.cancel
}],
title: get(this, 'errorTitle'),
text: [get(this, 'messageForUser')]
});
return;
if (this._isFastBoot) {
return Promise.resolve();
}
return this._initialize().then(() => {
const tour = get(this, 'tourObject');

steps.forEach((step, index) => {
const { id, options } = step;

if (options.buttons) {
options.buttons = options.buttons.map(makeButton.bind(this), this);
// Return nothing if there are no steps
if (isEmpty(steps)) {
return;
}
/* istanbul ignore next: also can't test this due to things attached to root blowing up tests */
if (!this._requiredElementsPresent()) {
tour.addStep('error', {
buttons: [{
text: 'Exit',
action: tour.cancel
}],
title: get(this, 'errorTitle'),
text: [get(this, 'messageForUser')]
});
return;
}

steps.forEach((step, index) => {
const { id, options } = step;

options.attachTo = normalizeAttachTo(options.attachTo);
tour.addStep(id, options);
if (options.buttons) {
options.buttons = options.buttons.map(makeButton.bind(this), this);
}

// Step up events for the current step
const currentStep = tour.steps[index];
options.attachTo = normalizeAttachTo(options.attachTo);
tour.addStep(id, options);

if (!currentStep.options.scrollToHandler) {
currentStep.options.scrollToHandler = (elem) => {
// Allow scrolling so scrollTo works.
enableBodyScroll();
// Step up events for the current step
const currentStep = tour.steps[index];

if (typeof elem !== 'undefined') {
elem.scrollIntoView();
}
if (!currentStep.options.scrollToHandler) {
currentStep.options.scrollToHandler = (elem) => {
// Allow scrolling so scrollTo works.
enableBodyScroll();

later(() => {
if (get(this, 'disableScroll')) {
disableBodyScroll();
if (typeof elem !== 'undefined') {
elem.scrollIntoView();
}
}, 50);
};
}

later(() => {
if (get(this, 'disableScroll')) {
disableBodyScroll();
}
}, 50);
};
}
});
});
},

Expand All @@ -281,6 +295,9 @@ export default Service.extend(Evented, {
* @public
*/
back() {
if (this._isFastBoot) {
return;
}
get(this, 'tourObject').back();
this.trigger('back');
},
Expand All @@ -292,6 +309,9 @@ export default Service.extend(Evented, {
* @public
*/
cancel() {
if (this._isFastBoot) {
return;
}
get(this, 'tourObject').cancel();
},

Expand All @@ -302,6 +322,9 @@ export default Service.extend(Evented, {
* @public
*/
complete() {
if (this._isFastBoot) {
return;
}
get(this, 'tourObject').complete();
},

Expand All @@ -312,6 +335,9 @@ export default Service.extend(Evented, {
* @public
*/
hide() {
if (this._isFastBoot) {
return;
}
get(this, 'tourObject').hide();
},

Expand All @@ -322,6 +348,9 @@ export default Service.extend(Evented, {
* @public
*/
next() {
if (this._isFastBoot) {
return;
}
get(this, 'tourObject').next();
this.trigger('next');
},
Expand All @@ -334,18 +363,28 @@ export default Service.extend(Evented, {
* @public
*/
show(id) {
if (this._isFastBoot) {
return;
}
get(this, 'tourObject').show(id);
},

/**
* Start the tour
* Start the tour. The Promise from addSteps() must be in a resolved state prior to starting the tour!
*
* @method start
* @public
*/
start() {
if (this._isFastBoot) {
return;
}
const tourObject = get(this, 'tourObject');
if (tourObject == undefined) {
throw new Error("the Promise from addSteps must be in a resolved state before the tour can be started");
}
set(this, 'isActive', true);
get(this, 'tourObject').start();
tourObject.start();
},

/**
Expand Down Expand Up @@ -414,19 +453,23 @@ export default Service.extend(Evented, {
defaultStepOptions.tippyOptions.appendTo = rootElement;
}

const tourObject = new Shepherd.Tour({
confirmCancel,
confirmCancelMessage,
defaultStepOptions,
tourName,
useModalOverlay
});

tourObject.on('start', bind(this, '_onTourStart'));
tourObject.on('complete', bind(this, '_onTourFinish', 'complete'));
tourObject.on('cancel', bind(this, '_onTourFinish', 'cancel'));
return import('shepherd.js').then(module => {
const Shepherd = module.default
const tourObject = new Shepherd.Tour({
confirmCancel,
confirmCancelMessage,
defaultStepOptions,
tourName,
useModalOverlay
});

tourObject.on('start', bind(this, '_onTourStart'));
tourObject.on('complete', bind(this, '_onTourFinish', 'complete'));
tourObject.on('cancel', bind(this, '_onTourFinish', 'cancel'));

set(this, 'tourObject', tourObject);
set(this, 'tourObject', tourObject);
})
},

/**
Expand Down
3 changes: 3 additions & 0 deletions ember-cli-build.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ const EmberAddon = require('ember-cli/lib/broccoli/ember-addon');

module.exports = function(defaults) {
const app = new EmberAddon(defaults, {
babel: {
plugins: [ require.resolve('ember-auto-import/babel-plugin') ]
},
sassOptions: {
includePaths: [
'node_modules/shepherd.js/src/scss'
Expand Down
26 changes: 3 additions & 23 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,16 @@
'use strict';

const fastbootTransform = require('fastboot-transform');

module.exports = {
name: require('./package').name,
included(app) {
if (app.options && app.options.shepherd && app.options.shepherd.theme) {
this.theme = `dist/css/shepherd-theme-${app.options.shepherd.theme}.css`;
app.import(`node_modules/shepherd.js/dist/css/shepherd-theme-${app.options.shepherd.theme}.css`);
}

this._super.included.apply(this, arguments);
},
options: {
nodeAssets: {
'shepherd.js'() {
const include = [
'dist/js/shepherd.js'
];

if (this.theme) {
include.push(this.theme);
}

return {
import: {
include,
processTree(tree) {
return fastbootTransform(tree);
}
}
};
}
babel: {
plugins: [ require.resolve('ember-auto-import/babel-plugin') ]
}
}
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@
"body-scroll-lock": "^2.6.1",
"ember-auto-import": "^1.2.21",
"ember-cli-babel": "^7.1.2",
"ember-cli-node-assets": "0.2.2",
"fastboot-transform": "^0.1.3",
"shepherd.js": "^2.6.0"
},
"devDependencies": {
"@ember/optional-features": "^0.7.0",
"babel-eslint": "^10.0.1",
"broccoli-asset-rev": "^3.0.0",
"codeclimate-test-reporter": "^0.5.1",
"ember-cli": "~3.9.0",
Expand Down
14 changes: 7 additions & 7 deletions tests/acceptance/ember-shepherd-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { builtInButtons, steps as defaultSteps } from '../data';

const toggleTour = async (tour, modal) => {
tour.set('modal', modal);
tour.addSteps(defaultSteps);
await tour.addSteps(defaultSteps);
return await tour.start();
};

Expand Down Expand Up @@ -56,7 +56,7 @@ module('Acceptance | Tour functionality tests', function(hooks) {
await visit('/docs/demo');

tour.set('defaultStepOptions', defaultStepOptions);
tour.addSteps(steps);
await tour.addSteps(steps);

tour.start();

Expand Down Expand Up @@ -149,7 +149,7 @@ module('Acceptance | Tour functionality tests', function(hooks) {

await visit('/docs/demo');

tour.addSteps(stepsWithoutClasses);
await tour.addSteps(stepsWithoutClasses);

tour.start();

Expand All @@ -173,7 +173,7 @@ module('Acceptance | Tour functionality tests', function(hooks) {
}
}];

tour.addSteps(steps);
await tour.addSteps(steps);

await visit('/docs/demo');

Expand Down Expand Up @@ -201,7 +201,7 @@ module('Acceptance | Tour functionality tests', function(hooks) {
}
}];

tour.addSteps(steps);
await tour.addSteps(steps);
tour.start();

assert.ok(document.querySelector('.shepherd-element'), 'tour is visible');
Expand Down Expand Up @@ -241,7 +241,7 @@ module('Acceptance | Tour functionality tests', function(hooks) {

await visit('/docs/demo');

tour.addSteps(steps);
await tour.addSteps(steps);

await tour.start();

Expand Down Expand Up @@ -302,7 +302,7 @@ module('Acceptance | Tour functionality tests', function(hooks) {
// Visit route
await visit('/docs/demo');

tour.addSteps(steps);
await tour.addSteps(steps);

document.querySelector('#ember-testing-container').scrollTop = 0;
assert.equal(document.querySelector('#ember-testing-container').scrollTop, 0, 'Scroll is initially 0');
Expand Down
Loading

0 comments on commit a95b171

Please sign in to comment.