Skip to content

Commit

Permalink
NavigationButtons added
Browse files Browse the repository at this point in the history
  • Loading branch information
Ivo Sonderegger committed Oct 21, 2019
1 parent d9776d1 commit 05583df
Show file tree
Hide file tree
Showing 2 changed files with 242 additions and 0 deletions.
146 changes: 146 additions & 0 deletions src/meteoJS/timeline/NavigationButtons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/**
* @module meteoJS/timeline/navigationButtons
*/
import addEventFunctions from '../Events.js';
import Timeline from '../Timeline.js';

/**
* Determines how the time is chosen, when a button for time navigation is
* clicked. On "exact" the time in the timeline is only changed if the time
* exists. In all other cases the time will be changed and a suitable timestamp
* is chosen.
*
* @typedef {string="exact","nearest","before","later"}
* module:meteoJS/timeline/navigationButtons~findTimeBy
*/

/**
* Options for NavigationButtons.
*
* @typedef {Object} module:meteoJS/timeline/navigationButtons~options
* @param {module:meteoJS/timeline~Timeline} timeline - Timeline object.
* @param {module:meteoJS/timeline/navigationButtons~findTimeBy} findTimeBy
* Determines how the time is chosen, when a button is clicked.
* @param {string|undefined} buttonClass - Default button class.
*/

/**
* @classdesc Class to create buttons and insert them into the DOM to navigate
* through the times of the passed timeline.
* Events, wenn gedrückt (und vorallem wenn Time nicht vorhanden)
* Wenn Zeit nicht vorhanden, wähle nächster Zeitpunkt/früherer/späterere/kein Zeitpunkt
* In Verbindung mit keyboardNavigation -> Tooltip dazu
*
* @fires module:meteoJS/timeline/navigationButtons#click:button
*/
export class NavigationButtons {

/**
* @param {module:meteoJS/timeline/navigationButtons~options} [options]
* Options.
*/
constructor({ timeline,
findTimeBy = "exact",
buttonClass,
} = {}) {
/**
* @type module:meteoJS/timeline~Timeline
*/
this.timeline = timeline;

/**
* @type module:meteoJS/timeline/navigationButtons~findTimeBy
*/
this.findTimeBy = findTimeBy;

/**
* @type string|undefined
*/
this.buttonClass = buttonClass;
}

/**
* @typedef {Object} module:meteoJS/timeline/navigationButtons~buttonDefinition
* @param {string|undefined} [buttonClass} - Class.
* @param {string="first","last","prev","next","nextAllEnabled","prevAllEnabled","add","sub"}
* methodName - Method to execute on timeline, when button is clicked.
* @param {integer} [timeAmount] - Required when methodName is "add" or "sub."
* @param {string} [timeKey] - Required when methodName is "add" or "sub."
* @param {string} [text] - Text for button.
* @param {string} [title] - Title for button.
*/

/**
* Creates button HTMLElements and append them to the passed node.
*
* @param {HTMLElement} node - Node to insert the buttons into it.
* @param {...module:meteoJS/timeline/navigationButtons~buttonDefinition}
* buttons - Button defintions to insert.
*/
insertButtonInto(node, ...buttons) {
buttons.forEach(({ buttonClass,
methodName,
timeAmount,
timeKey,
text,
title } = {}) => {
if (!/^(first|last|prev|next|nextAllEnabled|prevAllEnabled|add|sub)$/
.test(methodName))
return;
if (text === undefined)
switch (methodName) {
case 'first':
text = '|«';
break;
case 'last':
text = '»|';
break;
case 'prev':
text = '«';
break;
case 'next':
text = '»';
break;
case 'nextAllEnabled':
text = '»';
break;
case 'prevAllEnabled':
text = '«';
break;
case 'add':
text = `+${timeAmount}${timeKey}`;
break;
case 'sub':
text = `-${timeAmount}${timeKey}`;
break;
}
let button = document.createElement('button');
button.appendChild(document.createTextNode(text));
button.setAttribute('type', 'button');
if (typeof buttonClass == 'string')
button.classList.add(buttonClass.split(' '));
else if (typeof this.buttonClass == 'string')
button.classList.add(this.buttonClass.split(' '));
if (title !== undefined)
button.setAttribute('title', title);
button.addEventListener('onclick', () => {
switch (methodName) {
case 'add':
this.timeline.add(timeAmount, timeKey);
break;
case 'sub':
this.timeline.sub(timeAmount, timeKey);
break;
default:
this.timeline[methodName]();
}
this.trigger('click:button');
});
node.appendChild(button);
});
}

}
addEventFunctions(NavigationButtons.prototype);
export default NavigationButtons;
96 changes: 96 additions & 0 deletions test/meteoJS/timeline/navigationButtons.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import assert from 'assert';
import 'jsdom-global/register';
import Timeline from '../../../src/meteoJS/Timeline.js';
import NavigationButtons
from '../../../src/meteoJS/timeline/NavigationButtons.js';
import { NavigationButtons as NavigationButtonsClass }
from '../../../src/meteoJS/timeline/NavigationButtons.js';

describe('NavigationButtons', () => {
let timeline = new Timeline();
timeline.setTimesBySetID('',
[...Array(17).keys()]
.map(i => i*3)
.map(i => new Date(Date.UTC(2019, 10, 21 + Math.trunc(i/24), i % 24))));
it('Defaults', () => {
let clickCounter = 0;
let nB = new NavigationButtons({ timeline });
nB.on('click:button', () => clickCounter++);
let node = document.createElement('div');
nB.insertButtonInto(node,
{ methodName: 'first' },
{ methodName: 'prevAllEnabled' },
{ methodName: 'prev',
title: 'Previous' },
{ methodName: 'sub',
timeAmount: 12,
timeKey: 'h' },
{ methodName: 'sub',
timeAmount: 6,
timeKey: 'h',
text: 'ABC' },
{ methodName: 'sub',
timeAmount: 3,
timeKey: 'h' },
{ methodName: 'add',
timeAmount: 3,
timeKey: 'h' },
{ methodName: 'add',
timeAmount: 12,
timeKey: 'h' },
{ methodName: 'next',
buttonClass: 'btn' },
{ methodName: 'nextAllEnabled' },
{ methodName: 'last' });
assert.equal(node.childElementCount, 11, 'children count');
[['', undefined, '|«'],
['', undefined, '«'],
['', 'Previous', '«'],
['', undefined, '-12h'],
['', undefined, 'ABC'],
['', undefined, '-3h'],
['', undefined, '+3h'],
['', undefined, '+12h'],
['btn', undefined, '»'],
['', undefined, '»'],
['', undefined, '»|']].forEach((result, i) => {
assert.equal(node.children[i].className, result[0], `${i}: class`);
assert.equal(node.children[i].getAttribute('title'), result[1], `${i}: title`);
assert.equal(node.children[i].textContent, result[2], `${i}: textContent`);
});
node.children[10].dispatchEvent(new CustomEvent('onclick')); // last
assert.equal(timeline.getSelectedTime(), timeline.getTimes()[16], 'selected time 16');
node.children[6].dispatchEvent(new CustomEvent('onclick')); // +3h
assert.equal(timeline.getSelectedTime(), timeline.getTimes()[16], 'selected time 16');
node.children[2].dispatchEvent(new CustomEvent('onclick')); // Prev
assert.equal(timeline.getSelectedTime(), timeline.getTimes()[15], 'selected time 15');
node.children[3].dispatchEvent(new CustomEvent('onclick')); // -12h
assert.equal(timeline.getSelectedTime().valueOf(), timeline.getTimes()[11].valueOf(), 'selected time 11');
assert.equal(clickCounter, 4, 'click counter');
});
it('Constructor options: buttonClass', () => {
let nB = new NavigationButtons({
timeline,
buttonClass: 'btn btn-secondary'
});
let node = document.createElement('div');
nB.insertButtonInto(node,
{ methodName: 'first' },
{ methodName: 'last' });
assert.equal(node.childElementCount, 2, 'children count');
assert.equal(node.children[0].className, 'btn,btn-secondary', '0: class');
assert.equal(node.children[0].getAttribute('title'), undefined, '0: title');
assert.equal(node.children[0].textContent, '|«', '0: textContent');
assert.equal(node.children[1].className, 'btn,btn-secondary', '0: class');
assert.equal(node.children[1].getAttribute('title'), undefined, '0: title');
assert.equal(node.children[1].textContent, '»|', '0: textContent');
});
it('named import', () => {
let nB = new NavigationButtons({
timeline
});
let node = document.createElement('div');
nB.insertButtonInto(node, { methodName: 'first' });
assert.equal(node.childElementCount, 1, 'children count');
});
});

0 comments on commit 05583df

Please sign in to comment.