Skip to content

Commit

Permalink
issue/3186 General bugs and enhancements (#120)
Browse files Browse the repository at this point in the history
* issue/3186 General bugs and enhancements

* issue/3186 Added _showEndOfPage to readme and example

* issue/3186 Defensive code

* issue/3186 Addd branching support

* issue/3186 Switch to optional chaining
  • Loading branch information
oliverfoster committed Jul 8, 2021
1 parent f7e2f6f commit fcfb6cc
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 14 deletions.
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -82,6 +82,8 @@ The attributes listed below are properly formatted as JSON in [*example.json*](h
>>**\_component** (string): Defines the Trickle plug-in which should handle the interaction. At present only `"trickle-button"` is available, but it is possible to create new plug-ins. The default is `"trickle-button"`.
>>**\_showEndOfPage** (boolean): When set to `false`, hides any end-of-page button. The default is `true`.
>**\_stepLocking** (object): Step locking (section hiding) attributes group contains values for **\_isEnabled**, **\_isCompletionRequired**, and **\_isLockedOnRevisit**.
>>**\_isEnabled** (boolean): Will allow Trickle to truncate the page at the step until the user is allowed to move forward. The default is `true`. Note that if **\_isFullWidth** is set to `true` on the **\_button** attribute group (see above), **\_isEnabled** will be forced to `true` regardless of what you set here.
Expand Down
3 changes: 2 additions & 1 deletion example.json
Expand Up @@ -29,7 +29,8 @@
"startAriaLabel": "",
"finalText": "Finish",
"finalAriaLabel": "",
"_component": "trickle-button"
"_component": "trickle-button",
"_showEndOfPage": true
},
"_stepLocking": {
"_isEnabled": true,
Expand Down
3 changes: 3 additions & 0 deletions js/TrickleButtonModel.js
Expand Up @@ -42,6 +42,9 @@ export default class TrickleButtonModel extends ComponentModel {
*/
isStepUnlocked() {
const completionAttribute = getCompletionAttribute();
// Check if completion is blocked by another extension
const isCompletionBlocked = (this.getParent().get('_requireCompletionOf') === Number.POSITIVE_INFINITY);
if (isCompletionBlocked) return;
return this.getSiblings().every(sibling => {
if (sibling === this) {
return true;
Expand Down
1 change: 1 addition & 0 deletions js/TrickleButtonView.js
Expand Up @@ -72,6 +72,7 @@ class TrickleButtonView extends ComponentView {
const parentModel = this.model.getParent();
const completionAttribute = getCompletionAttribute();
this.listenTo(parentModel, {
'change:_requireCompletionOf': this.onStepUnlocked,
[`bubble:change:${completionAttribute}`]: this.onStepUnlocked,
[`change:${completionAttribute}`]: this.onParentComplete
});
Expand Down
7 changes: 4 additions & 3 deletions js/controller.js
Expand Up @@ -17,7 +17,7 @@ class TrickleController extends Backbone.Controller {
this.checkIsFinished = _.debounce(this.checkIsFinished, 1);
this.listenTo(data, {
// Check that the locking is accurate after any completion
'change:_isInteractionComplete change:_isComplete': checkApplyLocks,
'change:_isInteractionComplete change:_isComplete change:_isAvailable add remove': checkApplyLocks,
// Check whether trickle is finished after any locking changes
'change:_isLocked': this.checkIsFinished
});
Expand Down Expand Up @@ -107,15 +107,16 @@ class TrickleController extends Backbone.Controller {
const scrollTo = trickleConfig._scrollTo;
const firstCharacter = scrollTo.substr(0, 1);
switch (firstCharacter) {
case '@':
case '@': {
// NAVIGATE BY RELATIVE TYPE
// Allows trickle to scroll to a sibling / cousin component
// relative to the current trickle item
var relativeModel = fromModel.findRelativeModel(scrollTo, {
const relativeModel = fromModel.findRelativeModel(scrollTo, {
filter: model => model.get('_isAvailable')
});
if (relativeModel === undefined) return;
return relativeModel.get('_id');
}
case '.':
// NAVIGATE BY CLASS
return scrollTo.substr(1, scrollTo.length - 1);
Expand Down
36 changes: 26 additions & 10 deletions js/models.js
Expand Up @@ -20,7 +20,7 @@ export function _deepDefaults(original, ...defaultObjects) {
original[key] = _deepDefaults(original[key] || {}, defaultValue);
return;
}
const isValueAlreadySet = original.hasOwnProperty(key);
const isValueAlreadySet = Object.prototype.hasOwnProperty.call(original, key);
if (isValueAlreadySet) return;
original[key] = defaultValue;
});
Expand Down Expand Up @@ -100,7 +100,15 @@ export function getModelInheritanceChain(configModel) {
if (!data.isReady) throw new Error('Trickle cannot resolve inheritance chains until data is ready');
const type = configModel.get('_type');
if (type === 'block') {
return [ configModel, configModel.getParent() ].filter(ancestor => {
const parentModel = configModel.getParent();
const parentConfig = parentModel.get('_trickle');
const blockConfig = configModel.get('_trickle');
const isParentEnabledNotOnChildren = (parentConfig?._isEnabled && parentConfig._onChildren === false);
const isNoChildConfig = (!blockConfig?._isEnabled);
if (isParentEnabledNotOnChildren && isNoChildConfig) {
return null;
}
return [ configModel, parentModel ].filter(ancestor => {
const config = ancestor.get('_trickle');
// Remove models with no config and models which explicitly require inheritance
return (config && !config._isInherited);
Expand Down Expand Up @@ -158,7 +166,7 @@ export function getCompletionAttribute() {
*/
export function checkApplyLocks(model) {
const completionAttribute = getCompletionAttribute();
if (!model.changed.hasOwnProperty(completionAttribute)) return;
if (!Object.prototype.hasOwnProperty.call(model.changed, completionAttribute)) return;
applyLocks();
}

Expand All @@ -174,7 +182,7 @@ export function applyLocks() {
// Fetch the component model from the store incase it needs overriding by another extension
const TrickleButtonModel = Adapt.getModelClass('trickle-button');
// Check all models for trickle potential
Adapt.course.getAllDescendantModels(true).forEach(siteModel => {
Adapt.course.getAllDescendantModels(true).filter(model => model.get('_isAvailable')).forEach(siteModel => {
const trickleConfig = getModelConfig(siteModel);
if (!trickleConfig || !trickleConfig._isEnabled) return;
const isStepLocked = Boolean(trickleConfig?._stepLocking?._isEnabled);
Expand Down Expand Up @@ -251,7 +259,7 @@ export function addButtonComponents() {
let uid = 0;
data.forEach(buttonModelSite => {
if (buttonModelSite instanceof CourseModel) return;
let trickleConfig = getModelConfig(buttonModelSite);
const trickleConfig = getModelConfig(buttonModelSite);
if (!trickleConfig || !trickleConfig?._isEnabled || buttonModelSite.get('_isTrickleSiteConfigured')) return;
buttonModelSite.set('_isTrickleSiteConfigured', true);
const parentId = buttonModelSite.get('_id');
Expand All @@ -275,13 +283,21 @@ export function addButtonComponents() {
* Pretty print locking state for current page
*/
export function logTrickleState() {
if (!Adapt.parentView?.model?.isTypeGroup('page')) return;
logging.debug(`TRICKLE STATE`);
Adapt.parentView.model.getAllDescendantModels(true).forEach(model => {
if (logging._config?._level !== 'debug') return;
if (!Adapt.parentView?.model?.isTypeGroup('page')) {
logging.debug('TRICKLE GLOBAL STATE');
Adapt.course.getAllDescendantModels(true).filter(model => model.get('_isAvailable')).forEach(model => {
const isLocked = model.get('_isLocked');
const isTrickled = model.get('_isTrickled');
logging.debug(`${' '.repeat(model.getAncestorModels().length)}${model.get('_type')} ${model.get('_id')} isLocked: ${isLocked} isTrickled: ${isTrickled}`);
});
return;
}
logging.debug('TRICKLE STATE');
Adapt.parentView.model.getAllDescendantModels(true).filter(model => model.get('_isAvailable')).forEach(model => {
const isLocked = model.get('_isLocked');
const isTrickled = model.get('_isTrickled');
if (!isTrickled) return;
logging.debug(`${model.get('_type')} ${model.get('_id')} isLocked: ${isLocked} isTrickled: ${isTrickled}`);
logging.debug(`${' '.repeat(model.getAncestorModels().length)}${model.get('_type')} ${model.get('_id')} isLocked: ${isLocked} isTrickled: ${isTrickled}`);
});
}

Expand Down

0 comments on commit fcfb6cc

Please sign in to comment.