Skip to content

Commit

Permalink
New: Added longdescription and horizontal scrolling
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverfoster authored Aug 8, 2022
2 parents 052d1a8 + 7a9d0a0 commit 053a0e6
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 5 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ guide the learner’s interaction with the component.

>**alt** (string): The alternative text for this image or link. For image only, assign [alt text](https://github.com/adaptlearning/adapt_framework/wiki/Providing-good-alt-text) for images that convey course content only. For links, the alt text should convey the navigation or resource.
>**large** (string): File name (including path) of the image used with large device width. Path should be relative to the *src* folder (e.g., *course/en/images/origami-menu-two.jpg*).
>**longdescription** (string): A long description of the image. This text appear below the image.
>**large** (string): File name (including path) of the image used with large device width. Path should be relative to the *src* folder (e.g., *course/en/images/origami-menu-two.jpg*).
>**small** (string): File name (including path) of the image used with small device width. Path should be relative to the *src* folder (e.g., *course/en/images/origami-menu-two.jpg*).
Expand All @@ -55,6 +57,10 @@ guide the learner’s interaction with the component.
>**\_target** (string): The target attribute specifies where to open the link or linked document. Acceptable values are `_blank` opens the linked document in a new window or tab, `_parent` opens the linked document in the parent frame, `_top` opens the linked document in the full body of the window or `_self` opens the linked document in the same frame as it was selected. If no value is set, the default is `_blank`.
**_isScrollable** (boolean): Scrolls horizontally for extra wide images.

**_defaultScrollPercent** (number): Starting scroll position. `0` is to the left, `50` is in the center, `100` is to the right.

### Notes
If you don't need this component to display a different image for large/small screen sizes, you can use **src** (instead of **large** and **small**) to specify an image that will be displayed for all screen sizes.

Expand Down
3 changes: 3 additions & 0 deletions example.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@
"instruction": "",
"_graphic": {
"alt": "",
"longdescription": "",
"large": "https://github.com/adaptlearning/documentation/raw/master/04_wiki_assets/adapt_framework/adapt-logo_208x200.gif",
"small": "http://minionslovebananas.com/images/check-in-minion.jpg",
"attribution": "Copyright © 2019",
"_url": "",
"_target": ""
},
"_isScrollable": false,
"_defaultScrollPercent": 0,
"_pageLevelProgress": {
"_isEnabled": true
}
Expand Down
8 changes: 7 additions & 1 deletion js/GraphicModel.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import ComponentModel from 'core/js/models/componentModel';

export default class GraphicModel extends ComponentModel {}
export default class GraphicModel extends ComponentModel {

init() {
this.set('_scrollPercent', this.get('_defaultScrollPercent'));
}

}
64 changes: 63 additions & 1 deletion js/GraphicView.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,74 @@ import ComponentView from 'core/js/views/componentView';

class GraphicView extends ComponentView {

className() {
return [
super.className(),
this.model.get('_isScrollable') && 'is-scrollable'
].filter(Boolean).join(' ');
}

events() {
return {
'click .js-graphic-link': 'onClick'
'click .js-graphic-link': 'onClick',
'keydown .js-graphic-scrollbar': 'onKeyDown'
};
}

postRender() {
this.$('.graphic__widget').imageready(() => {
this.setReadyStatus();
this.setupInviewCompletion('.graphic__widget');
this.setupScrollable();
});
}

setupScrollable() {
if (!this.model.get('_isScrollable')) return;
this.onScroll = _.debounce(this.onScroll.bind(this), 17);
const $scrollbar = this.$('.js-graphic-scrollbar');
const $scrollContainer = this.$(`#${$scrollbar.attr('aria-controls')}`);
$scrollContainer.on('scroll', this.onScroll);
this.onKeyDown();
}

onScroll(event) {
if (!this.model.get('_isScrollable')) return;
const $scrollbar = this.$('.js-graphic-scrollbar');
const $scrollContainer = this.$(`#${$scrollbar.attr('aria-controls')}`);
const { clientWidth, scrollWidth } = $scrollContainer[0];
const scrollableWidth = (scrollWidth - clientWidth);
const left = $scrollContainer.scrollLeft();
const calculatedScrollPercent = parseInt(left / scrollableWidth * 100);
this.model.set('_scrollPercent', calculatedScrollPercent);
}

onKeyDown(event) {
if (!this.model.get('_isScrollable')) return;
const $scrollbar = this.$('.js-graphic-scrollbar');
const $scrollContainer = this.$(`#${$scrollbar.attr('aria-controls')}`);
const { clientWidth, scrollWidth } = $scrollContainer[0];
const scrollableWidth = (scrollWidth - clientWidth);
const step = (clientWidth * 0.1);
let left = $scrollContainer.scrollLeft();
const calculatedScrollPercent = parseInt(left / scrollableWidth * 100);
const definedScrollPercent = this.model.get('_scrollPercent') ?? 0;
if (definedScrollPercent !== calculatedScrollPercent) {
// set inital position
left = definedScrollPercent / 100 * scrollableWidth;
}
switch (event?.which) {
case 37: // left
left -= step;
break;
case 39: // right
left += step;
break;
}
left = _.max([0, _.min([scrollableWidth, left])]);
$scrollContainer.scrollLeft(left);
}

onClick(event) {
if (event) event.preventDefault();

Expand All @@ -28,6 +83,13 @@ class GraphicView extends ComponentView {
window.location.href = url;
}

preRemove() {
if (!this.model.get('_isScrollable')) return;
const $scrollbar = this.$('.js-graphic-scrollbar');
const $scrollContainer = this.$(`#${$scrollbar.attr('aria-controls')}`);
$scrollContainer.off('scroll', this.onScroll);
}

}

GraphicView.template = 'graphic.jsx';
Expand Down
14 changes: 14 additions & 0 deletions less/graphic.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.graphic {

&.is-scrollable .graphic__image-container {
display: block;
width: 100%;
overflow-x: scroll;
overflow-y: hidden;
}

&.is-scrollable .graphic__image-container img {
max-width: initial;
}

}
35 changes: 35 additions & 0 deletions properties.schema
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@
"inputType": "Text",
"validators": [],
"translatable": true
},
"scrollAriaLabel": {
"type": "string",
"required": true,
"default": "Use the scrollbar to pan the image left and right. {{#if _graphic.alt}}{{_graphic.alt}}{{/if}}",
"inputType": "Text",
"validators": [],
"translatable": true
}
},
"properties": {
Expand Down Expand Up @@ -45,6 +53,16 @@
"help": "A description of the image; required when it has meaning that must be conveyed to the learner. For 'decorative' images, leave this blank.",
"translatable": true
},
"longdescription": {
"type": "string",
"required": false,
"default": "",
"title": "Long image description",
"inputType": "Text",
"validators": [],
"help": "A long description of the image. This text appear below the image.",
"translatable": true
},
"large": {
"type": "string",
"required": true,
Expand Down Expand Up @@ -93,6 +111,23 @@
"help": "This targets where to open the link. Acceptable values are '_blank' (opens the linked document in a new window or tab), '_parent' (opens the linked document in the parent frame), '_top' (opens the linked document in the full body of the window) or '_self' (opens the linked document in the same frame as it was selected. If no value is set, the default is '_blank'."
}
}
},
"_isScrollable": {
"type": "boolean",
"required": true,
"default": false,
"title": "Enable horizontal scrolling",
"inputType": "Checkbox",
"validators": []
},
"_defaultScrollPercent": {
"type": "number",
"required": false,
"default": 0,
"title": "Scroll percent",
"inputType": "Number",
"validators": ["required", "number"],
"help": "0 is left most, 50 is in the middle, 100 is right most."
}
}
}
38 changes: 38 additions & 0 deletions schema/component.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@
"translatable": true
}
},
"longdescription": {
"type": "string",
"title": "Long image description",
"help": "A long description of the image. This text appear below the image.",
"default": "",
"_adapt": {
"translatable": true
}
},
"large": {
"type": "string",
"isObjectId": true,
Expand Down Expand Up @@ -75,8 +84,37 @@
"_adapt": {
"translatable": true
}
},
"_url": {
"type": "string",
"title": "URL",
"description": "When the graphic is selected this is the url it will follow.",
"default": ""
},
"_target": {
"type": "string",
"title": "Target attribute",
"description": "This targets where to open the link. Acceptable values are '_blank' (opens the linked document in a new window or tab), '_parent' (opens the linked document in the parent frame), '_top' (opens the linked document in the full body of the window) or '_self' (opens the linked document in the same frame as it was selected. If no value is set, the default is '_blank'.",
"enum": [
"_blank",
"_self",
"_parent",
"_top"],
"default": ""
}
}
},
"_isScrollable": {
"type": "boolean",
"title": "Enable horizontal scrolling",
"description": "Allow the user to view the 'model answer' if they answer the question incorrectly",
"default": false
},
"_defaultScrollPercent": {
"type": "number",
"title": "Scroll percent",
"description": "0 is left most, 50 is in the middle, 100 is right most.",
"default": 0
}
}
}
Expand Down
38 changes: 36 additions & 2 deletions templates/graphic.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,33 @@ const LinkWrapper = ({ href, children, target, className, role }) =>

export default function Graphic(props) {
const {
_graphic
_id,
_isScrollable,
_scrollPercent,
_graphic,
_globals
} = props;

const scrollableProperties = _isScrollable
? {
role: 'slider',
className: 'component__widget graphic__widget js-graphic-scrollbar',
'aria-controls': `graphic__scroll__container__${_id}`,
'aria-orientation': 'horizontal',
'aria-valuemax': '100',
'aria-valuemin': '0',
'aria-valuenow': _scrollPercent,
'aria-label': Handlebars.compile(_globals._components._graphic.scrollAriaLabel)(props),
'aria-describedby': _graphic.longdescription ? `graphic__longdescription__${_id}` : undefined,
tabIndex: '0'
}
: {};
return (
<div className='component__inner graphic__inner'>

<templates.header {...props} />

<div className='component__widget graphic__widget'>
<div className='component__widget graphic__widget' {...scrollableProperties}>

<LinkWrapper
href = {_graphic._url}
Expand All @@ -25,12 +44,27 @@ export default function Graphic(props) {
>

<templates.image {..._graphic}
aria-hidden={_isScrollable}
id={`graphic__scroll__container__${_id}`}
longDescriptionId={`graphic__longdescription__${_id}`}
classes="js-graphic-scroll-container"
classNamePrefixes={[
'component',
'graphic'
]}
/>

{_graphic.longdescription &&
<div
id={`graphic__longdescription__${_id}`}
className="graphic__longdescription"
>
<div className="graphic__longdescription-inner">
{_graphic.longdescription}
</div>
</div>
}

</LinkWrapper>

</div>
Expand Down

0 comments on commit 053a0e6

Please sign in to comment.