Skip to content

Commit

Permalink
issue/2890 Rewrite utilising ChildView API (#113)
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverfoster authored May 13, 2021
1 parent 2589e11 commit a0f97e8
Show file tree
Hide file tree
Showing 22 changed files with 1,064 additions and 1,535 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ The attributes listed below are properly formatted as JSON in [*example.json*](h
>>**\_isEnabled** (boolean): If set to `false`, no button is displayed, so step-locking is triggered by component completion only. The page will scroll to the specified destination if **\_autoScroll** is set to `true`. The default is `true`.
>>**\_styleBeforeCompletion** (string): Determines whether the Trickle button is visible even while subsequent sections of the page remain inaccessible. Acceptable values are `"hidden"` and `"visible"`. The default is `"hidden"`.
>>**\_styleBeforeCompletion** (string): Determines whether the Trickle button is visible even while subsequent sections of the page remain inaccessible. Acceptable values are `"hidden"`, `"disabled"`, and `"visible"`. The default is `"hidden"`.
>>**\_styleAfterClick** (string): Determines the properties of the Trickle button after it has been clicked. Acceptable values are `"hidden"`, `"disabled"`, and `"scroll"`. `"hidden"` hides the button. `"disabled"` applies the "disabled" CSS class. The value `"scroll"` will cause the button to maintain its visibility allowing the user an alternative method for scrolling down the page by using the button (even after all sections have been revealed). The default is `"hidden"`.
>>**\_styleAfterClick** (string): Determines the properties of the Trickle button after it has been clicked. Acceptable values are `"hidden"`, `"disabled"`, and `"visible"`. `"hidden"` hides the button. `"disabled"` applies the "disabled" CSS class. The value `"visible"` will cause the button to maintain its visibility allowing the user an alternative method for scrolling down the page by using the button (even after all sections have been revealed). The default is `"hidden"`.
>>**\_isFullWidth** (boolean): Will position the button fixed to the bottom of the window. This option will force to `true` **\_isEnabled** in the **\_stepLocking** attribute group (**\_stepLocking.\_isEnabled: true**). When **\_autoHide** is set to `true`, the button will fade-out when the learner scrolls up, away from the button. The default is `true`.
Expand All @@ -70,10 +70,16 @@ The attributes listed below are properly formatted as JSON in [*example.json*](h
>>**text** (string): Defines the default button text. The default is `"Continue"`.
>>**ariaLabel** (string): Defines the default button aria label. The default is `""`.
>>**startText** (string): Defines the first item button text when set on the article with **\_onChildren** set to `true`. The default is `"Begin"`.
>>**startAriaLabel** (string): Defines the first item button aria label when set on the article with **\_onChildren** set to `true`. The default is `""`.
>>**finalText** (string): Defines the last item button text when set on the article with **\_onChildren** set to `true`. The default is `"Finish"`.
>>**finalAriaLabel** (string): Defines the last item button aria label when set on the article with **\_onChildren** set to `true`. The default is `""`.
>>**\_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"`.
>**\_stepLocking** (object): Step locking (section hiding) attributes group contains values for **\_isEnabled**, **\_isCompletionRequired**, and **\_isLockedOnRevisit**.
Expand All @@ -93,8 +99,8 @@ The following attribute can be added to *config.json* to overide which completio
No known limitations.

----------------------------
**Version number:** 4.0.6 <a href="https://community.adaptlearning.org/" target="_blank"><img src="https://github.com/adaptlearning/documentation/blob/master/04_wiki_assets/plug-ins/images/adapt-logo-mrgn-lft.jpg" alt="adapt learning logo" align="right"></a>
**Framework versions:** 5+
**Version number:** 5.0.0 <a href="https://community.adaptlearning.org/" target="_blank"><img src="https://github.com/adaptlearning/documentation/blob/master/04_wiki_assets/plug-ins/images/adapt-logo-mrgn-lft.jpg" alt="adapt learning logo" align="right"></a>
**Framework versions:** 5.8+
**Author / maintainer:** Adapt Core Team with [contributors](https://github.com/adaptlearning/adapt-contrib-trickle/graphs/contributors)
**Accessibility support:** WAI AA
**RTL support:** Yes
Expand Down
4 changes: 2 additions & 2 deletions bower.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "adapt-contrib-trickle",
"version": "4.0.6",
"framework": ">=5",
"version": "5.0.0",
"framework": ">=5.8",
"homepage": "https://github.com/adaptlearning/adapt-contrib-trickle",
"issues": "https://github.com/adaptlearning/adapt_framework/issues/new",
"extension" : "trickle",
Expand Down
8 changes: 6 additions & 2 deletions example.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,28 @@
// Manual Configuration Options
"_trickle": {
"_isEnabled": true,
"_isInherited": false,
"_autoScroll": true,
"_scrollDuration": 500,
"_onChildren": true,
"_scrollTo": "@block +1",
"_button": {
"_isEnabled": true,
"_comment": "_styleBeforeCompletion = hidden | visible",
"_comment": "_styleBeforeCompletion = hidden | disabled | visible",
"_styleBeforeCompletion": "hidden",
"_comment": "_styleAfterClick = hidden | disabled | scroll",
"_comment": "_styleAfterClick = hidden | disabled | visible",
"_styleAfterClick": "hidden",
"_isFullWidth": true,
"_comment": "_autoHide = it is recommended this be left set to false in courses that need to be screenreader-compatible.",
"_autoHide": false,
"_className": "",
"_hasIcon": false,
"text": "Continue",
"ariaLabel": "",
"startText": "Begin",
"startAriaLabel": "",
"finalText": "Finish",
"finalAriaLabel": "",
"_component": "trickle-button"
},
"_stepLocking": {
Expand Down
183 changes: 183 additions & 0 deletions js/TrickleButtonModel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import Adapt from 'core/js/adapt';
import controller from './controller';
import ComponentModel from 'core/js/models/componentModel';
import {
getModelConfig,
getCompletionAttribute
} from './models';

export default class TrickleButtonModel extends ComponentModel {

/**
* @returns {boolean} true if the button is enabled in its trickle configuration
*/
isEnabled() {
const trickleConfig = getModelConfig(this);
const isEnabled = trickleConfig._isEnabled && trickleConfig._button?._isEnabled;
return isEnabled;
}

/**
* @return {boolean} true if page truncation (step locking) is active
*/
isStepLocking() {
const config = getModelConfig(this);
const isStepLocking = config._stepLocking?._isEnabled;
return isStepLocking;
}

/**
* @return {boolean} true if completion is required to unlock this step
*/
isStepLockingCompletionRequired() {
const config = getModelConfig(this);
const isStepLockingCompletionRequired = config._stepLocking &&
config._stepLocking._isEnabled &&
config._stepLocking._isCompletionRequired;
return isStepLockingCompletionRequired;
}

/**
* @returns {boolean} true if all available siblings are complete, optional or not available
*/
isStepUnlocked() {
const completionAttribute = getCompletionAttribute();
return this.getSiblings().every(sibling => {
if (sibling === this) {
return true;
}
return sibling.get(completionAttribute) ||
sibling.get('_isOptional') ||
!sibling.get('_isAvailable');
});
}

/**
* @returns {boolean} true if this button should always be locked on revisit
*/
isStepLockedOnRevisit() {
const trickleConfig = getModelConfig(this);
return Boolean(trickleConfig._stepLocking._isLockedOnRevisit);
}

/**
* @return {boolean} true if completion is not required or if completion has been fulfilled
* and the button has been clicked
*/
isFinished() {
const isStepUnlocked = this.isStepUnlocked();
const isStepLockingCompletionRequired = this.isStepLockingCompletionRequired();
const isButtonComplete = this.get('_isComplete');
const isFinished = ((isStepUnlocked || !isStepLockingCompletionRequired) && isButtonComplete);
return isFinished;
}

/**
* Apply start and final text amongst the current siblings
* Siblings in assessments can be randomised so this must be derived at runtime
*/
calculateButtonText() {
const siteId = this.get('_trickleConfigId');
const trickleConfig = getModelConfig(Adapt.findById(siteId));
let isStart = false;
let isFinal = false;
if (trickleConfig._onChildren) {
const parentId = this.getParent().get('_id');
const trickleParent = Adapt.findById(siteId);
const trickleSiblings = trickleParent.getAllDescendantModels(true).filter(model => {
return model.get('_isAvailable') && model.get('_isTrickled') && model.get('_trickleConfigId') === siteId;
});
const index = trickleSiblings.findIndex(model => model.get('_id') === parentId);
isStart = (index === 0);
isFinal = (index === trickleSiblings.length - 1 && !trickleParent.get('_canRequestChild'));
}
const text = (isStart && trickleConfig._button.startText) ?
trickleConfig._button.startText :
(isFinal && trickleConfig._button.finalText) ?
trickleConfig._button.finalText :
trickleConfig._button.text;
const ariaLabel = (isStart && trickleConfig._button.startAriaLabel) ?
trickleConfig._button.startAriaLabel :
(isFinal && trickleConfig._button.finalAriaLabel) ?
trickleConfig._button.finalAriaLabel :
trickleConfig._button.ariaLabel;
this.set({
buttonText: text,
buttonAriaLabel: ariaLabel
});
}

/**
* Will reset this button if the step has been unlocked and should be relocked
* on revisit
*/
checkIfResetOnRevisit() {
if (this.isStepUnlocked() && !this.isStepLockedOnRevisit()) return;
this.set({
_isComplete: false,
_isInteractionComplete: false
});
}

/**
* Calculate the current button visible and enabled states
* @param {boolean} isButtonDisableForced Set to true to signify that the button must not be enabled
*/
calculateButtonState(isButtonDisableForced = false) {
if (!this.isEnabled()) {
this.set({
_isButtonVisible: false,
_isButtonDisabled: true
});
return;
};

const isTrickleKilled = controller.isKilled;

const isStepUnlocked = this.isStepUnlocked() || isTrickleKilled;
const isFinished = this.isFinished() || isTrickleKilled;
const trickleConfig = getModelConfig(this);

const isButtonVisibleBeforeCompletion = (trickleConfig._button._styleBeforeCompletion !== 'hidden');
// Force button to be hidden after completion if _isFullWidth, otherwise absolutely positioned buttons will stack
const isButtonVisibleAfterCompletion = (trickleConfig._button._styleAfterClick !== 'hidden') && !trickleConfig._button._isFullWidth;

const isStepLockingCompletionRequired = this.isStepLockingCompletionRequired();

const isNoCompletionRequiredAndLockedVisible = (!isStepLockingCompletionRequired && !isFinished && isButtonVisibleBeforeCompletion);
const isNoCompletionRequiredAndUnlockedVisible = (!isStepLockingCompletionRequired && isStepUnlocked && !isFinished);
const isNoCompletionRequiredAndFinishedVisible = (!isStepLockingCompletionRequired && isFinished && isButtonVisibleAfterCompletion);
const isStepLockedAndVisibleBeforeCompletion = (isStepLockingCompletionRequired && !isStepUnlocked && isButtonVisibleBeforeCompletion);
const isFinishedAndVisibleAfterCompletion = (isStepLockingCompletionRequired && isFinished && isButtonVisibleAfterCompletion);
const isStepUnlockedAndButtonIncomplete = (isStepLockingCompletionRequired && isStepUnlocked && !isFinished);

const isButtonVisible = isNoCompletionRequiredAndLockedVisible ||
isNoCompletionRequiredAndUnlockedVisible ||
isNoCompletionRequiredAndFinishedVisible ||
isStepLockedAndVisibleBeforeCompletion ||
isFinishedAndVisibleAfterCompletion ||
isStepUnlockedAndButtonIncomplete;

const isButtonEnabledBeforeCompletion = (trickleConfig._button._styleBeforeCompletion !== 'disabled');
const isButtonEnabledAfterCompletion = (trickleConfig._button._styleAfterClick !== 'disabled');

const isNoCompletionRequiredAndLockedEnabled = (!isStepLockingCompletionRequired && !isFinished && isButtonEnabledBeforeCompletion);
const isNoCompletionRequiredAndUnlockedEnabled = (!isStepLockingCompletionRequired && isStepUnlocked && !isFinished);
const isNoCompletionRequiredAndFinishedEnabled = (!isStepLockingCompletionRequired && isFinished && isButtonEnabledAfterCompletion);
const isStepUnlockedAndButtonIncompleteWithoutButtonDisabledForced = (isStepLockingCompletionRequired && isStepUnlockedAndButtonIncomplete && !isButtonDisableForced);
const isFinishedAndEnabledAfterCompletion = (isStepLockingCompletionRequired && isFinished && isButtonEnabledAfterCompletion);

const isButtonEnabled = isNoCompletionRequiredAndLockedEnabled ||
isNoCompletionRequiredAndUnlockedEnabled ||
isNoCompletionRequiredAndFinishedEnabled ||
isStepUnlockedAndButtonIncompleteWithoutButtonDisabledForced ||
isFinishedAndEnabledAfterCompletion ||
false;

this.set({
_isButtonVisible: isButtonVisible,
_isButtonDisabled: !isButtonEnabled
});
}

}
Loading

0 comments on commit a0f97e8

Please sign in to comment.