Skip to content

Commit

Permalink
Items component model/accessibility update (#42)
Browse files Browse the repository at this point in the history
* split code into model and view, as per hotgraphic

* set correct framework dependency; update version number & cross platform coverage list in README

* updates for compatibility with Adapt v4 style accessibility

* schema, README & example.json improvements
  • Loading branch information
Matt Leathes authored May 17, 2019
1 parent 11853b9 commit 407d08b
Show file tree
Hide file tree
Showing 10 changed files with 623 additions and 299 deletions.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ The attributes listed below are used in *components.json* to configure **Hotgrid

**instruction** (string): This optional text appears above the component. It is frequently used to guide the learner’s interaction with the component.

**_canCycleThroughPagination** (boolean): Enables the pop-ups to be cycled through endlessly using either the previous or next icon. When set to `true`, clicking "next" on the final stage will display the very first stage. When set to `false`, the final stage will display only a "previous" icon. The default is `false`.

**_hidePagination** (boolean): When set to `true`, hides the "previous" and "next" icons and progress indicator (e.g., "1/5") on the pop-up's toolbar. The default is `false`.

**\_columns** (number): This value determines the number of columns within the grid. Any number of columns can be set however keep in mind the more columns there are the smaller the items will be.

Hotgrid has a dynamic layout system. If you have 5 items but set the columns to 3, hotgrid will put 3 items in the first row and 2 on the second. The second row then will be automatically centred. This works with any amount of items and columns - ie that last row will always be centred for you.
Expand All @@ -46,12 +50,11 @@ Hotgrid has a dynamic layout system. If you have 5 items but set the columns to
## Limitations

Hotgrid automatically switches to 2 columns in mobile mode for the best user experience however this can be overridden in the css.
Version 2.1.0 contains two changes that could potentially break if users update from previous versions. The first, the introduction of `font-size: 0;` on the item container div, may cause item titles not to appear. Applying a font size to the titles will resolve this issue. The second, changing clickable elements from `<a>` to `<button>`, may cause display errors if the `<a>` tag has been directly targeted in the JS or LESS files. To resolve, target the `<button>` tag instead.

----------------------------
**Version number:** 2.1.3
**Framework versions:** 2.0
**Version number:** 3.0.0
**Framework versions:** 3.2+
**Author / maintainer:** Kineo
**Accessibility support:** WAI AA
**RTL support:** yes
**Cross-platform coverage:** Chrome, Chrome for Android, Firefox (ESR + latest version), Edge, IE11, IE Mobile 11, Safari 10+11 for macOS+iOS, Opera
**Cross-platform coverage:** Chrome, Chrome for Android, Firefox (ESR + latest version), Edge, IE11, Safari 11+12 for macOS+iOS, Opera
4 changes: 2 additions & 2 deletions bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"type": "git",
"url": "git://github.com/cgkineo/adapt-hotgrid"
},
"framework": ">=2",
"version": "2.1.3",
"framework": ">=3.2",
"version": "3.0.0",
"homepage": "https://github.com/cgkineo/adapt-hotgrid",
"issues": "https://github.com/cgkineo/adapt-hotgrid/issues/",
"displayName": "Hotgrid",
Expand Down
5 changes: 4 additions & 1 deletion example.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"title": "Adapt Hotgrid",
"displayTitle": "Adapt Hotgrid",
"body": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis.",
"_canCycleThroughPagination": false,
"_hidePagination": false,
"instruction": "",
"_columns": 3,
"_items": [
Expand Down Expand Up @@ -95,5 +97,6 @@
// Accessibility supported courses require ARIA instruction - apply to _globals in course.json

"_hotgrid": {
"ariaRegion": "This component contains selectable grid items. Select an item to trigger a popup that includes an image with display text. Select the close button to close the popup."
"ariaRegion": "Selectable image component. Select each item to show more information.",
"popupPagination": "{{itemNumber}} / {{totalItems}}"
}
127 changes: 6 additions & 121 deletions js/adapt-hotgrid.js
Original file line number Diff line number Diff line change
@@ -1,126 +1,11 @@

define([
'core/js/adapt',
'core/js/views/componentView'
], function(Adapt, ComponentView) {

var Hotgrid = ComponentView.extend({

events: {
'click .hotgrid-item-image': 'onItemClicked'
},

isPopupOpen: false,

preRender: function () {
_.each(this.model.get('_items'), function(item) {
if (item._graphic.srcHover && item._graphic.srcVisited) {
item._graphic.hasImageStates = true;
}
});

this.listenTo(Adapt, 'device:changed', this.resizeControl);

this.setDeviceSize();

this.checkIfResetOnRevisit();
},

setDeviceSize: function() {
if (Adapt.device.screenSize === 'large') {
this.$el.addClass('desktop').removeClass('mobile');
this.model.set('_isDesktop', true);
} else {
this.$el.addClass('mobile').removeClass('desktop');
this.model.set('_isDesktop', false);
}
},

checkIfResetOnRevisit: function() {
var isResetOnRevisit = this.model.get('_isResetOnRevisit');

// If reset is enabled set defaults
if (isResetOnRevisit) {
this.model.reset(isResetOnRevisit);

_.each(this.model.get('_items'), function(item) {
item._isVisited = false;
});
}
},

postRender: function() {
this.setUpColumns();
this.$('.hotgrid-widget').imageready(this.setReadyStatus.bind(this));
},

resizeControl: function() {
this.setDeviceSize();
this.render();
},
'core/js/models/itemsComponentModel',
'./hotgridView'
], function(Adapt, ItemsComponentModel, HotgridView) {

setUpColumns: function() {
var columns = this.model.get('_columns');

if (columns && Adapt.device.screenSize === 'large') {
this.$('.hotgrid-grid-item').css('width', (100 / columns) + '%');
}
},

onItemClicked: function(event) {
if (event) event.preventDefault();

var $link = $(event.currentTarget);
var $item = $link.parent();
var itemModel = this.model.get('_items')[$item.index()];

if(!itemModel._isVisited) {
$link.addClass('visited');
itemModel._isVisited = true;
// append the word 'visited.' to the link's aria-label
var visitedLabel = this.model.get('_globals')._accessibility._ariaLabels.visited + ".";
$link.attr('aria-label', function(index, val) {
return val + " " + visitedLabel;
});
}

this.showItemContent(itemModel);
},

showItemContent: function(itemModel) {
if(this.isPopupOpen) return;// ensure multiple clicks don't open multiple notify popups

Adapt.trigger('notify:popup', {
title: itemModel.title,
body: "<div class='hotgrid-notify-container'><div class='hotgrid-notify-body'>" + itemModel.body + "</div>" +
"<img class='hotgrid-notify-graphic' src='" +
itemModel._itemGraphic.src + "' alt='" +
itemModel._itemGraphic.alt + "'/></div>"
});

this.isPopupOpen = true;

Adapt.once('notify:closed', function() {
this.isPopupOpen = false;
this.evaluateCompletion();
}.bind(this));
},

getVisitedItems: function() {
return _.filter(this.model.get('_items'), function(item) {
return item._isVisited;
});
},

evaluateCompletion: function() {
if (this.getVisitedItems().length === this.model.get('_items').length) {
this.setCompletionStatus();
}
}

},{
template: "hotgrid"
return Adapt.register('hotgrid', {
model: ItemsComponentModel,
view: HotgridView
});

return Adapt.register("hotgrid", Hotgrid);
});
138 changes: 138 additions & 0 deletions js/hotgridPopupView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
define([
'core/js/adapt'
], function(Adapt) {
'use strict';

var HotgridPopupView = Backbone.View.extend({

className: 'hotgrid-popup',

events: {
'click .hotgrid-popup-done': 'closePopup',
'click .hotgrid-popup-controls': 'onControlClick'
},

initialize: function() {
this.listenToOnce(Adapt, 'notify:opened', this.onOpened);
this.listenTo(this.model.get('_children'), {
'change:_isActive': this.onItemsActiveChange,
'change:_isVisited': this.onItemsVisitedChange
});
this.render();
},

onOpened: function() {
this.applyNavigationClasses(this.model.getActiveItem().get('_index'));
this.updatePageCount();
this.handleTabs();
},

applyNavigationClasses: function (index) {
var itemCount = this.model.get('_items').length;
var canCycleThroughPagination = this.model.get('_canCycleThroughPagination');

var shouldEnableBack = index > 0 || canCycleThroughPagination;
var shouldEnableNext = index < itemCount - 1 || canCycleThroughPagination;
var $controls = this.$('.hotgrid-popup-controls');

this.$('hotgrid-popup-nav')
.toggleClass('first', !shouldEnableBack)
.toggleClass('last', !shouldEnableNext);

$controls.filter('.back').a11y_cntrl_enabled(shouldEnableBack);
$controls.filter('.next').a11y_cntrl_enabled(shouldEnableNext);
},

updatePageCount: function() {
var template = Adapt.course.get('_globals')._components._hotgrid.popupPagination;
var labelText = Handlebars.compile(template || '')({
itemNumber: this.model.getActiveItem().get('_index') + 1,
totalItems: this.model.get('_items').length
});
this.$('.hotgrid-popup-count').html(labelText);
},

handleTabs: function() {
this.$('.hotgrid-popup-inner').a11y_on(false);
this.$('.hotgrid-popup-inner .active').a11y_on(true);
},

onItemsActiveChange: function(item, _isActive) {
if (!_isActive) return;

var index = item.get('_index');
this.applyNavigationClasses(index);
this.updatePageCount();
this.handleTabs();
this.applyItemClasses(index);
this.handleFocus();
},

applyItemClasses: function(index) {
this.$('.hotgrid-item.active').removeClass('active');
this.$('.hotgrid-item').filter('[data-index="' + index + '"]').addClass('active');
},

handleFocus: function() {
this.$('.hotgrid-popup-inner .active').a11y_focus();
},

onItemsVisitedChange: function(item, _isVisited) {
if (!_isVisited) return;

this.$('.hotgrid-item')
.filter('[data-index="' + item.get('_index') + '"]')
.addClass('visited');
},

render: function() {
var data = this.model.toJSON();
data.view = this;
var template = Handlebars.templates['hotgridPopup'];
this.$el.html(template(data));
},

closePopup: function(event) {
Adapt.trigger('notify:close');
},

onControlClick: function(event) {
event.preventDefault();

var direction = $(event.currentTarget).hasClass('back') ? 'back' : 'next';
var index = this.getNextIndex(direction);

if (index === -1) return;

this.setItemState(index);
},

getNextIndex: function(direction) {
var index = this.model.getActiveItem().get('_index');
var lastIndex = this.model.get('_items').length - 1;

switch (direction) {
case 'back':
if (index > 0) return --index;
if (this.model.get('_canCycleThroughPagination')) return lastIndex;
break;
case 'next':
if (index < lastIndex) return ++index;
if (this.model.get('_canCycleThroughPagination')) return 0;
}
return -1;
},

setItemState: function(index) {
this.model.getActiveItem().toggleActive();

var nextItem = this.model.getItem(index);
nextItem.toggleActive();
nextItem.toggleVisited(true);
}

});

return HotgridPopupView;

});
Loading

0 comments on commit 407d08b

Please sign in to comment.