refactor: replace bootstrap-datepicker by ember-power-calendar
Also renders two linked calendars if there is enough space. Closes #143
This commit is contained in:
parent
5b34199f41
commit
06a92b947f
19 changed files with 382 additions and 372 deletions
|
@ -1,95 +1,81 @@
|
|||
import Component from '@ember/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { computed } from '@ember/object';
|
||||
import Component from '@ember/component';
|
||||
import { isArray } from '@ember/array';
|
||||
import { isEmpty } from '@ember/utils';
|
||||
import { isPresent } from '@ember/utils';
|
||||
import moment from 'moment';
|
||||
|
||||
export default Component.extend({
|
||||
i18n: service(),
|
||||
store: service('store'),
|
||||
|
||||
/*
|
||||
* maps optionsDates for bootstrap datepicker as a simple array of date objects
|
||||
*/
|
||||
optionsBootstrapDatepicker: computed('options', {
|
||||
get() {
|
||||
const options = this.options;
|
||||
const validDates = options.filter(function(option) {
|
||||
return moment(option.get('title')).isValid();
|
||||
});
|
||||
const normalizedDates = validDates.map(function(option) {
|
||||
return moment(option.get('title'))
|
||||
.hour(0)
|
||||
.minute(0)
|
||||
.millisecond(0);
|
||||
});
|
||||
// convert to primitive to allow support Ember.Array.uniq()
|
||||
const uniqueDateStrings = normalizedDates
|
||||
.map((moment) => {
|
||||
return moment.toISOString();
|
||||
})
|
||||
.uniq();
|
||||
const dateObjects = uniqueDateStrings.map(function(dateString) {
|
||||
return moment(dateString).toDate();
|
||||
});
|
||||
return dateObjects;
|
||||
},
|
||||
/*
|
||||
* value is an of Date objects set by ember-cli-bootstrap-datepicker
|
||||
*/
|
||||
set(key, days) {
|
||||
const options = this.options;
|
||||
|
||||
// remove all days if value isn't an array of if it's empty
|
||||
if (!isArray(days) || isEmpty(days)) {
|
||||
options.clear();
|
||||
return [];
|
||||
}
|
||||
|
||||
// get days in correct order
|
||||
days.sort(function(a, b) {
|
||||
return a.getTime() - b.getTime();
|
||||
});
|
||||
|
||||
// array of date objects
|
||||
const newDays = days.filter((day) => {
|
||||
return options.every((option) => {
|
||||
return moment(day).format('YYYY-MM-DD') !== option.get('day');
|
||||
});
|
||||
});
|
||||
// array of options fragments
|
||||
const optionsForRemovedDays = options.filter((option) => {
|
||||
return days.every((day) => {
|
||||
return moment(day).format('YYYY-MM-DD') !== option.get('day');
|
||||
});
|
||||
});
|
||||
|
||||
options.removeObjects(optionsForRemovedDays);
|
||||
newDays.forEach((newDay) => {
|
||||
// new days must be entered at correct position
|
||||
const insertBefore = options.find((option) => {
|
||||
// options are sorted
|
||||
// so we search for first option which value is greater than newDay
|
||||
return option.get('date').valueOf() > newDay.valueOf();
|
||||
});
|
||||
let position;
|
||||
if (isEmpty(insertBefore)) {
|
||||
// newDay is after all existing days
|
||||
position = options.get('length');
|
||||
} else {
|
||||
position = options.indexOf(insertBefore);
|
||||
}
|
||||
options.insertAt(
|
||||
position,
|
||||
this.store.createFragment('option', {
|
||||
title: moment(newDay).format('YYYY-MM-DD')
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
return days;
|
||||
}
|
||||
selectedDays: computed('options.[]', function() {
|
||||
return this.options
|
||||
// should be unique
|
||||
.uniqBy('day')
|
||||
// raw dates
|
||||
.map(({ date }) => date)
|
||||
// filter out invalid
|
||||
.filter(moment.isMoment)
|
||||
.toArray();
|
||||
}),
|
||||
calendarCenterNext: computed('calendarCenter', function() {
|
||||
return moment(this.calendarCenter).add(1, 'months');
|
||||
}),
|
||||
|
||||
store: service('store')
|
||||
actions: {
|
||||
daysSelected({ moment: newMoments }) {
|
||||
let { options } = this;
|
||||
|
||||
if (!isArray(newMoments)) {
|
||||
// special case: all options are unselected
|
||||
options.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// array of options that represent days missing in updated selection
|
||||
let removedOptions = options.filter((option) => {
|
||||
return !newMoments.find((newMoment) => newMoment.format('YYYY-MM-DD') === option.day);
|
||||
});
|
||||
|
||||
// array of moments that aren't represented yet by an option
|
||||
let addedMoments = newMoments.filter((moment) => {
|
||||
return !options.find((option) => moment.format('YYYY-MM-DD') === option.day);
|
||||
});
|
||||
|
||||
// remove options that represent deselected days
|
||||
options.removeObjects(removedOptions);
|
||||
|
||||
// add options for newly selected days
|
||||
let newOptions = addedMoments.map((moment) => {
|
||||
return this.store.createFragment('option', {
|
||||
title: moment.format('YYYY-MM-DD'),
|
||||
})
|
||||
});
|
||||
newOptions.forEach((newOption) => {
|
||||
// options must be insert into options array at correct position
|
||||
let insertBefore = options.find(({ date }) => {
|
||||
if (!moment.isMoment(date)) {
|
||||
// ignore options that do not represent a valid date
|
||||
return false;
|
||||
}
|
||||
|
||||
return date.isAfter(newOption.date);
|
||||
});
|
||||
let position = isPresent(insertBefore) ? options.indexOf(insertBefore) : options.length;
|
||||
options.insertAt(position, newOption);
|
||||
});
|
||||
},
|
||||
updateCalenderCenter(diff) {
|
||||
this.calendarCenter.add(diff, 'months');
|
||||
this.notifyPropertyChange('calenderCenter');
|
||||
},
|
||||
},
|
||||
|
||||
init() {
|
||||
this._super(arguments);
|
||||
|
||||
let { selectedDays } = this;
|
||||
this.set('calendarCenter', selectedDays.length >= 1 ? selectedDays[0] : moment());
|
||||
},
|
||||
});
|
||||
|
|
5
app/components/inline-datepicker.js
Normal file
5
app/components/inline-datepicker.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
});
|
|
@ -7,8 +7,11 @@ import localesMeta from 'croodle/locales/meta';
|
|||
export default Component.extend({
|
||||
tagName: 'select',
|
||||
classNames: [ 'language-select' ],
|
||||
|
||||
i18n: service(),
|
||||
moment: service(),
|
||||
powerCalendar: service(),
|
||||
|
||||
current: readOnly('i18n.locale'),
|
||||
|
||||
locales: computed('i18n.locales', function() {
|
||||
|
@ -28,6 +31,7 @@ export default Component.extend({
|
|||
|
||||
this.i18n.set('locale', locale);
|
||||
this.moment.changeLocale(locale);
|
||||
this.powerCalendar.set('locale', locale);
|
||||
|
||||
if (window.localStorage) {
|
||||
window.localStorage.setItem('locale', locale);
|
||||
|
|
|
@ -5,12 +5,14 @@ export default {
|
|||
initialize(appInstance) {
|
||||
let i18n = appInstance.lookup('service:i18n');
|
||||
let moment = appInstance.lookup('service:moment');
|
||||
let powerCalendar = appInstance.lookup('service:power-calendar');
|
||||
|
||||
let availableLocales = i18n.get('locales');
|
||||
let locale = getLocale(availableLocales);
|
||||
|
||||
i18n.set('locale', locale);
|
||||
moment.changeLocale(locale);
|
||||
powerCalendar.set('local', locale);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ export default {
|
|||
'ca': 'catalan',
|
||||
'de': 'deutsch',
|
||||
'en': 'english',
|
||||
'en-GB': 'english (GB)',
|
||||
'en-gb': 'english (GB)',
|
||||
'es': 'español',
|
||||
'it': 'italiano'
|
||||
};
|
||||
|
|
35
app/styles/_calendar.scss
Normal file
35
app/styles/_calendar.scss
Normal file
|
@ -0,0 +1,35 @@
|
|||
@import "ember-power-calendar";
|
||||
|
||||
@media only screen and (max-width: $screen-sm-min) {
|
||||
.ember-power-calendar {
|
||||
@include ember-power-calendar($cell-size: 63px);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: $screen-sm-min) {
|
||||
.ember-power-calendar {
|
||||
@include ember-power-calendar($cell-size: 47px);
|
||||
|
||||
float:left;
|
||||
|
||||
&:first-child {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.ember-power-calendar ~ .help-block {
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: $screen-md-min) {
|
||||
.ember-power-calendar {
|
||||
@include ember-power-calendar($cell-size: 40px);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: $screen-lg-min) {
|
||||
.ember-power-calendar {
|
||||
@include ember-power-calendar($cell-size: 50px);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
@import "ember-bootstrap/bootstrap";
|
||||
@import "calendar";
|
||||
|
||||
table tr td .form-group {
|
||||
margin-bottom:0;
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
{{#form.element
|
||||
classNames="days"
|
||||
label=(t "create.options.dates.label")
|
||||
property="options"
|
||||
as |el|
|
||||
data-test-form-element-for="days"
|
||||
}}
|
||||
{{bootstrap-datepicker-inline
|
||||
id=el.id
|
||||
value=optionsBootstrapDatepicker
|
||||
multidate=true
|
||||
calendarWeeks=true
|
||||
todayHighlight=true
|
||||
language=i18n.locale
|
||||
}}
|
||||
<InlineDatepicker
|
||||
@center={{calendarCenter}}
|
||||
@selectedDays={{selectedDays}}
|
||||
@onCenterChange={{action (mut calendarCenter) value="moment"}}
|
||||
@onSelect={{action "daysSelected"}}
|
||||
/>
|
||||
<InlineDatepicker
|
||||
@center={{calendarCenterNext}}
|
||||
@selectedDays={{selectedDays}}
|
||||
@onCenterChange={{action (mut calendarCenter) value="moment"}}
|
||||
@onSelect={{action "daysSelected"}}
|
||||
/>
|
||||
{{/form.element}}
|
||||
|
|
28
app/templates/components/inline-datepicker.hbs
Normal file
28
app/templates/components/inline-datepicker.hbs
Normal file
|
@ -0,0 +1,28 @@
|
|||
<PowerCalendarMultiple
|
||||
@center={{@center}}
|
||||
@selected={{@selectedDays}}
|
||||
@onCenterChange={{@onCenterChange}}
|
||||
@onSelect={{@onSelect}} as |calendar|
|
||||
>
|
||||
<nav class="ember-power-calendar-nav">
|
||||
<button
|
||||
type="button"
|
||||
class="ember-power-calendar-nav-control"
|
||||
onclick={{action calendar.actions.moveCenter -1 "month"}}
|
||||
>
|
||||
«
|
||||
</button>
|
||||
<div class="ember-power-calendar-nav-title">
|
||||
{{moment-format calendar.center "MMMM YYYY"}}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="ember-power-calendar-nav-control"
|
||||
onclick={{action calendar.actions.moveCenter 1 "month"}}
|
||||
>
|
||||
»
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<calendar.days />
|
||||
</PowerCalendarMultiple>
|
|
@ -36,7 +36,7 @@ module.exports = function(environment) {
|
|||
},
|
||||
|
||||
moment: {
|
||||
includeLocales: ['ca', 'de', 'es', 'it'],
|
||||
includeLocales: ['ca', 'de', 'en-gb', 'es', 'it'],
|
||||
includeTimezone: 'subset'
|
||||
},
|
||||
|
||||
|
|
|
@ -37,12 +37,6 @@ module.exports = function(defaults) {
|
|||
// please specify an object with the list of modules as keys
|
||||
// along with the exports of each module as its value.
|
||||
|
||||
app.import('vendor/bootstrap-datepicker-locales/bootstrap-datepicker.ca.min.js');
|
||||
app.import('vendor/bootstrap-datepicker-locales/bootstrap-datepicker.de.min.js');
|
||||
app.import('vendor/bootstrap-datepicker-locales/bootstrap-datepicker.en-GB.min.js');
|
||||
app.import('vendor/bootstrap-datepicker-locales/bootstrap-datepicker.es.min.js');
|
||||
app.import('vendor/bootstrap-datepicker-locales/bootstrap-datepicker.it.min.js');
|
||||
|
||||
app.import({
|
||||
development: 'node_modules/floatthead/dist/jquery.floatThead.js',
|
||||
production: 'node_modules/floatthead/dist/jquery.floatThead.min.js'
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
"ember-cli-acceptance-test-helpers": "^1.0.0",
|
||||
"ember-cli-app-version": "^3.2.0",
|
||||
"ember-cli-babel": "^6.16.0",
|
||||
"ember-cli-bootstrap-datepicker": "^0.6.1",
|
||||
"ember-cli-browser-navigation-button-test-helper": "^0.1.1",
|
||||
"ember-cli-chart": "^3.3.1",
|
||||
"ember-cli-clipboard": "^0.8.0",
|
||||
|
@ -63,6 +62,8 @@
|
|||
"ember-maybe-import-regenerator": "^0.1.6",
|
||||
"ember-moment": "^7.8.0",
|
||||
"ember-page-title": "^5.0.0",
|
||||
"ember-power-calendar": "^0.10.0",
|
||||
"ember-power-calendar-moment": "^0.1.4",
|
||||
"ember-radio-buttons": "^4.0.1",
|
||||
"ember-resolver": "^5.0.1",
|
||||
"ember-route-action-helper": "^2.0.6",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { currentURL, currentRouteName } from '@ember/test-helpers';
|
||||
import { currentURL, currentRouteName, findAll } from '@ember/test-helpers';
|
||||
import { module, test } from 'qunit';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
|
@ -86,7 +86,7 @@ module('Acceptance | create a poll', function(hooks) {
|
|||
'status bar has correct items disabled (options)'
|
||||
);
|
||||
|
||||
await pageCreateOptions.dateOptions(dates);
|
||||
await pageCreateOptions.selectDates(dates);
|
||||
await pageCreateOptions.next();
|
||||
assert.equal(currentRouteName(), 'create.options-datetime');
|
||||
assert.equal(
|
||||
|
@ -329,12 +329,12 @@ module('Acceptance | create a poll', function(hooks) {
|
|||
.next();
|
||||
assert.equal(currentRouteName(), 'create.options');
|
||||
|
||||
await pageCreateOptions.dateOptions(days);
|
||||
await pageCreateOptions.selectDates(days);
|
||||
await pageCreateOptions.next();
|
||||
assert.equal(currentRouteName(), 'create.options-datetime');
|
||||
assert.deepEqual(
|
||||
pageCreateOptionsDatetime.days().labels,
|
||||
days.map((day) => day.format(dayFormat)),
|
||||
days.map((day) => moment(day).format(dayFormat)),
|
||||
'time inputs having days as label'
|
||||
);
|
||||
|
||||
|
@ -389,7 +389,7 @@ module('Acceptance | create a poll', function(hooks) {
|
|||
.next();
|
||||
assert.equal(currentRouteName(), 'create.options');
|
||||
|
||||
await pageCreateOptions.dateOptions([ day ]);
|
||||
await pageCreateOptions.selectDates([ day ]);
|
||||
await pageCreateOptions.next();
|
||||
assert.equal(currentRouteName(), 'create.options-datetime');
|
||||
assert.deepEqual(
|
||||
|
@ -447,7 +447,7 @@ module('Acceptance | create a poll', function(hooks) {
|
|||
.next();
|
||||
assert.equal(currentRouteName(), 'create.options');
|
||||
|
||||
await pageCreateOptions.dateOptions([ day ]);
|
||||
await pageCreateOptions.selectDates([ day ]);
|
||||
await pageCreateOptions.next();
|
||||
assert.equal(currentRouteName(), 'create.options-datetime');
|
||||
assert.deepEqual(
|
||||
|
@ -501,7 +501,7 @@ module('Acceptance | create a poll', function(hooks) {
|
|||
.next();
|
||||
assert.equal(currentRouteName(), 'create.options');
|
||||
|
||||
await pageCreateOptions.dateOptions([ day ]);
|
||||
await pageCreateOptions.selectDates([ day ]);
|
||||
await pageCreateOptions.next();
|
||||
assert.equal(currentRouteName(), 'create.options-datetime');
|
||||
assert.deepEqual(
|
||||
|
@ -593,8 +593,8 @@ module('Acceptance | create a poll', function(hooks) {
|
|||
|
||||
test('create a poll and using back button (find a date)', async function(assert) {
|
||||
let days = [
|
||||
moment().add(1, 'day').hours(0).minutes(0).seconds(0).milliseconds(0),
|
||||
moment().add(1, 'week').hours(0).minutes(0).seconds(0).milliseconds(0)
|
||||
'2016-01-02',
|
||||
'2016-01-13',
|
||||
];
|
||||
const dayFormat = moment.localeData().longDateFormat('LLLL')
|
||||
.replace(
|
||||
|
@ -613,12 +613,14 @@ module('Acceptance | create a poll', function(hooks) {
|
|||
.next();
|
||||
assert.equal(currentRouteName(), 'create.options');
|
||||
|
||||
await pageCreateOptions.dateOptions(days);
|
||||
await pageCreateOptions.selectDates(
|
||||
days.map((day) => new Date(day))
|
||||
);
|
||||
await pageCreateOptions.next();
|
||||
assert.equal(currentRouteName(), 'create.options-datetime');
|
||||
assert.deepEqual(
|
||||
pageCreateOptionsDatetime.days().labels,
|
||||
days.map((day) => day.format(dayFormat)),
|
||||
days.map((day) => moment(day).format(dayFormat)),
|
||||
'time inputs having days as label'
|
||||
);
|
||||
|
||||
|
@ -626,8 +628,8 @@ module('Acceptance | create a poll', function(hooks) {
|
|||
await backButton();
|
||||
assert.equal(currentRouteName(), 'create.options');
|
||||
assert.deepEqual(
|
||||
pageCreateOptions.dateOptions().map((date) => date.toISOString()),
|
||||
days.map((day) => day.toISOString()),
|
||||
findAll('.ember-power-calendar-day--selected').map((el) => el.dataset.date),
|
||||
days,
|
||||
'days are still present after back button is used'
|
||||
);
|
||||
|
||||
|
@ -656,8 +658,8 @@ module('Acceptance | create a poll', function(hooks) {
|
|||
assert.deepEqual(
|
||||
pagePollParticipation.options().labels,
|
||||
[
|
||||
days[0].format(dayFormat),
|
||||
days[1].hour(10).minute(0).format('LLLL')
|
||||
moment(days[0]).format(dayFormat),
|
||||
moment(days[1]).hour(10).minute(0).format('LLLL')
|
||||
],
|
||||
'options are correctly labeled'
|
||||
);
|
||||
|
@ -680,7 +682,7 @@ module('Acceptance | create a poll', function(hooks) {
|
|||
.next();
|
||||
assert.equal(currentRouteName(), 'create.options');
|
||||
|
||||
await pageCreateOptions.dateOptions([new Date()]);
|
||||
await pageCreateOptions.selectDates([new Date()]);
|
||||
await pageCreateOptions.next();
|
||||
assert.equal(currentRouteName(), 'create.options-datetime');
|
||||
|
||||
|
|
|
@ -1,66 +1,50 @@
|
|||
import EmberObject from '@ember/object';
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render, find } from '@ember/test-helpers';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import moment from 'moment';
|
||||
import jQuery from 'jquery';
|
||||
import { calendarSelect } from 'ember-power-calendar/test-support';
|
||||
|
||||
module('Integration | Component | create options dates', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders a ember-cli-bootstrap-datepicker component', async function(assert) {
|
||||
test('it renders', async function(assert) {
|
||||
this.set('options', []);
|
||||
await render(hbs`{{#bs-form as |form|}}{{create-options-dates options=options form=form}}{{/bs-form}}`);
|
||||
|
||||
assert.dom('.days .datepicker').exists();
|
||||
assert.dom('[data-test-form-element-for="days"]').exists();
|
||||
});
|
||||
|
||||
test('bootstrap-datepicker shows dates in options', async function(assert) {
|
||||
test('calendar shows existing options as selected days', async function(assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
this.set('options', [
|
||||
EmberObject.create({ title: '2015-01-01' }),
|
||||
EmberObject.create({ title: '2015-01-02' })
|
||||
store.createFragment('option', { title: '2015-01-01' }),
|
||||
store.createFragment('option', { title: '2015-01-02' }),
|
||||
]);
|
||||
await render(hbs`{{#bs-form as |form|}}{{create-options-dates options=options form=form}}{{/bs-form}}`);
|
||||
|
||||
assert.equal(
|
||||
jQuery(find('.days .datepicker').parentElement).datepicker('getDates')[0].toISOString(),
|
||||
moment('2015-01-01').toISOString(),
|
||||
'date is correct (a)'
|
||||
);
|
||||
assert.equal(
|
||||
jQuery(find('.days .datepicker').parentElement).datepicker('getDates')[1].toISOString(),
|
||||
moment('2015-01-02').toISOString(),
|
||||
'date is correct (b)'
|
||||
);
|
||||
assert.dom('[data-test-form-element-for="days"] [data-date="2015-01-01"]')
|
||||
.hasClass('ember-power-calendar-day--selected');
|
||||
assert.dom('[data-test-form-element-for="days"] [data-date="2015-01-02"]')
|
||||
.hasClass('ember-power-calendar-day--selected');
|
||||
});
|
||||
|
||||
test('dates set in bootstrap-datepicker are set to options', async function(assert) {
|
||||
test('options are updated with dates selected in calendar', async function(assert) {
|
||||
this.set('options', []);
|
||||
await render(hbs`{{#bs-form as |form|}}{{create-options-dates options=options form=form}}{{/bs-form}}`);
|
||||
|
||||
jQuery(find('.days .datepicker').parentElement).datepicker('setDates', [
|
||||
moment('2015-01-01').toDate(),
|
||||
moment('2015-01-02').toDate()
|
||||
]);
|
||||
assert.equal(
|
||||
this.get('options.0.title'),
|
||||
'2015-01-01',
|
||||
'dates are correct (a)'
|
||||
);
|
||||
assert.equal(
|
||||
this.get('options.1.title'),
|
||||
'2015-01-02',
|
||||
'dates are correct (b)'
|
||||
await calendarSelect('[data-test-form-element-for="days"]', new Date('2015-01-01'));
|
||||
await calendarSelect('[data-test-form-element-for="days"]', new Date('2015-01-02'));
|
||||
assert.deepEqual(
|
||||
this.get('options').map((option) => option.title),
|
||||
['2015-01-01', '2015-01-02'],
|
||||
'dates are correct'
|
||||
);
|
||||
|
||||
jQuery(find('.days .datepicker').parentElement).datepicker('setDates', [
|
||||
moment('2016-12-31').toDate(),
|
||||
moment('2016-01-01').toDate()
|
||||
]);
|
||||
assert.equal(
|
||||
this.get('options.firstObject.title'),
|
||||
'2016-01-01',
|
||||
await calendarSelect('[data-test-form-element-for="days"]', new Date('2016-12-31'));
|
||||
await calendarSelect('[data-test-form-element-for="days"]', new Date('2016-01-01'));
|
||||
assert.deepEqual(
|
||||
this.get('options').map((option) => option.title),
|
||||
['2015-01-01', '2015-01-02', '2016-01-01', '2016-12-31'],
|
||||
'dates are sorted'
|
||||
);
|
||||
});
|
||||
|
|
15
tests/integration/components/inline-datepicker-test.js
Normal file
15
tests/integration/components/inline-datepicker-test.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
module('Integration | Component | inline-datepicker', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders an ember-power-calendar', async function(assert) {
|
||||
this.set('noop', () => {});
|
||||
await render(hbs`{{inline-datepicker onCenterChange=noop onSelect=noop}}`);
|
||||
|
||||
assert.dom('.ember-power-calendar').exists();
|
||||
});
|
||||
});
|
|
@ -1,49 +1,43 @@
|
|||
import { isPresent } from '@ember/utils';
|
||||
import PageObject from 'ember-cli-page-object';
|
||||
import { findElementWithAssert } from 'ember-cli-page-object';
|
||||
import { defaultsForCreate } from 'croodle/tests/pages/defaults';
|
||||
import { hasFocus } from 'croodle/tests/pages/helpers';
|
||||
|
||||
const {
|
||||
assign
|
||||
} = Object;
|
||||
|
||||
let {
|
||||
import {
|
||||
clickable,
|
||||
collection,
|
||||
create,
|
||||
fillable,
|
||||
hasClass,
|
||||
isVisible,
|
||||
text
|
||||
} = PageObject;
|
||||
} from 'ember-cli-page-object';
|
||||
import { defaultsForCreate } from 'croodle/tests/pages/defaults';
|
||||
import { hasFocus } from 'croodle/tests/pages/helpers';
|
||||
import { calendarSelect } from 'ember-power-calendar/test-support';
|
||||
import { assign } from '@ember/polyfills';
|
||||
import { isArray } from '@ember/array';
|
||||
import { assert } from '@ember/debug';
|
||||
import moment from 'moment';
|
||||
|
||||
const setBootstrapDatepicker = function(selector, options = {}) {
|
||||
function selectDates(selector) {
|
||||
return {
|
||||
isDescriptor: true,
|
||||
value(dates) {
|
||||
const el = findElementWithAssert(this, selector, options).parent();
|
||||
if (isPresent(dates)) {
|
||||
const normalizedDates = dates.map((date) => {
|
||||
if (typeof date.toDate === 'function') {
|
||||
date = date.toDate();
|
||||
}
|
||||
date.setHours(0);
|
||||
date.setMinutes(0);
|
||||
date.setSeconds(0);
|
||||
date.setMilliseconds(0);
|
||||
return date;
|
||||
});
|
||||
el.datepicker('setDates', normalizedDates);
|
||||
async value(dateOrMoments) {
|
||||
assert(
|
||||
'selectDates expects an array of date or moment objects as frist argument',
|
||||
isArray(dateOrMoments) && dateOrMoments.every((dateOrMoment) => dateOrMoment instanceof Date || moment.isMoment(dateOrMoment))
|
||||
)
|
||||
|
||||
for (let i = 0; i < dateOrMoments.length; i++) {
|
||||
let dateOrMoment = dateOrMoments[i];
|
||||
let date = moment.isMoment(dateOrMoment) ? dateOrMoment.toDate() : dateOrMoment;
|
||||
await calendarSelect(selector, date);
|
||||
}
|
||||
return el.datepicker('getDates');
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export default PageObject.create(assign({}, defaultsForCreate, {
|
||||
dateOptions: setBootstrapDatepicker('.days .datepicker'),
|
||||
export default create(assign({}, defaultsForCreate, {
|
||||
selectDates: selectDates('[data-test-form-element-for="days"]'),
|
||||
dateHasError: isVisible('.days.has-error'),
|
||||
dateError: text('.days .help-block'),
|
||||
|
||||
textOptions: collection({
|
||||
itemScope: '.form-group.option',
|
||||
item: {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { isArray } from '@ember/array';
|
||||
import EmberObject from '@ember/object';
|
||||
import { run } from '@ember/runloop';
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import { isArray } from '@ember/array';
|
||||
import moment from 'moment';
|
||||
|
||||
module('Unit | Component | create options dates', function(hooks) {
|
||||
|
@ -12,198 +10,147 @@ module('Unit | Component | create options dates', function(hooks) {
|
|||
this.store = this.owner.lookup('service:store');
|
||||
});
|
||||
|
||||
test('options get mapped to dates as optionsBootstrapDatepicker (used by ember-cli-bootstrap-datepicker)', function(assert) {
|
||||
let controller = this.owner.factoryFor('component:create-options-dates').create();
|
||||
controller.set('options', [
|
||||
EmberObject.create({ title: '1945-05-09' }),
|
||||
EmberObject.create({ title: '1987-05-01' }),
|
||||
EmberObject.create({ title: 'non valid date string' })
|
||||
]);
|
||||
test('#selectedDays: options representing days are mapped correctly', function(assert) {
|
||||
let values = [
|
||||
'1945-05-09',
|
||||
'1987-05-01',
|
||||
'non valid date string',
|
||||
];
|
||||
|
||||
let store = this.owner.lookup('service:store');
|
||||
let component = this.owner.factoryFor('component:create-options-dates').create({
|
||||
options: values.map((value) => store.createFragment('option', { title: value })),
|
||||
});
|
||||
|
||||
assert.ok(
|
||||
isArray(
|
||||
controller.get('optionsBootstrapDatepicker')
|
||||
),
|
||||
"it's an array"
|
||||
isArray(component.selectedDays),
|
||||
'it\'s an array'
|
||||
);
|
||||
assert.equal(
|
||||
controller.get('optionsBootstrapDatepicker.length'),
|
||||
2,
|
||||
component.selectedDays.length, 2,
|
||||
'array length is correct'
|
||||
);
|
||||
assert.ok(
|
||||
controller.get('optionsBootstrapDatepicker').every((el) => {
|
||||
return moment.isDate(el);
|
||||
component.selectedDays.every((el) => {
|
||||
return moment.isMoment(el);
|
||||
}),
|
||||
'array elements are date objects'
|
||||
'array elements are moment objects'
|
||||
);
|
||||
assert.equal(
|
||||
controller.get('optionsBootstrapDatepicker.firstObject').toISOString(),
|
||||
moment('1945-05-09').toISOString(),
|
||||
'date is correct'
|
||||
assert.deepEqual(
|
||||
component.selectedDays.map((el) => el.format('YYYY-MM-DD')),
|
||||
values.slice(0, 2),
|
||||
'values are correct'
|
||||
);
|
||||
});
|
||||
|
||||
test('options having times get mapped to dates as optionsBootstrapDatepicker (used by ember-cli-bootstrap-datepicker)', function(assert) {
|
||||
let controller = this.owner.factoryFor('component:create-options-dates').create();
|
||||
controller.set('options', [
|
||||
EmberObject.create({ title: '2014-01-01T12:00:00.00Z' }),
|
||||
EmberObject.create({ title: '2015-02-02T15:00:00.00Z' }),
|
||||
EmberObject.create({ title: '2015-02-02T15:00:00.00Z' }),
|
||||
EmberObject.create({ title: '2016-03-03' })
|
||||
]);
|
||||
test('#selectedDays: options representing days with times are mapped correctly', function(assert) {
|
||||
let values = [
|
||||
moment('2014-01-01T12:00').toISOString(),
|
||||
moment('2015-02-02T15:00').toISOString(),
|
||||
moment('2015-02-02T15:00').toISOString(),
|
||||
'2016-03-03',
|
||||
];
|
||||
|
||||
let store = this.owner.lookup('service:store');
|
||||
let component = this.owner.factoryFor('component:create-options-dates').create({
|
||||
options: values.map((value) => store.createFragment('option', { title: value })),
|
||||
});
|
||||
|
||||
assert.ok(
|
||||
isArray(
|
||||
controller.get('optionsBootstrapDatepicker')
|
||||
),
|
||||
"it's an array"
|
||||
isArray(component.selectedDays),
|
||||
'it\'s an array'
|
||||
);
|
||||
assert.equal(
|
||||
controller.get('optionsBootstrapDatepicker.length'),
|
||||
3,
|
||||
component.selectedDays.length, 3,
|
||||
'array length is correct'
|
||||
);
|
||||
assert.ok(
|
||||
controller.get('optionsBootstrapDatepicker').every((el) => {
|
||||
return moment.isDate(el);
|
||||
}),
|
||||
'array elements are date objects'
|
||||
component.selectedDays.every(moment.isMoment),
|
||||
'array elements are moment objects'
|
||||
);
|
||||
assert.deepEqual(
|
||||
controller.get('optionsBootstrapDatepicker').map((option) => {
|
||||
return option.toISOString();
|
||||
}),
|
||||
[
|
||||
moment('2014-01-01').toISOString(),
|
||||
moment('2015-02-02').toISOString(),
|
||||
moment('2016-03-03').toISOString()
|
||||
],
|
||||
'date is correct'
|
||||
component.selectedDays.map((day) => day.format('YYYY-MM-DD')),
|
||||
['2014-01-01', '2015-02-02', '2016-03-03'],
|
||||
'dates are correct'
|
||||
);
|
||||
});
|
||||
|
||||
test('options get set correctly by optionsBootstrapDatepicker (used by ember-cli-bootstrap-datepicker)', function(assert) {
|
||||
let controller = this.owner.factoryFor('component:create-options-dates').create();
|
||||
run(() => {
|
||||
controller.set('options', []);
|
||||
// dates must be in wrong order to test sorting
|
||||
controller.set('optionsBootstrapDatepicker', [
|
||||
moment('1918-11-09').toDate(),
|
||||
moment('1917-10-25').toDate()
|
||||
]);
|
||||
test('action #daysSelected: new days are added in correct order', function(assert) {
|
||||
// dates must be in wrong order to test sorting
|
||||
let values = ['1918-11-09', '1917-10-25'];
|
||||
let options = [];
|
||||
|
||||
let component = this.owner.factoryFor('component:create-options-dates').create({ options });
|
||||
component.actions.daysSelected.bind(component)({
|
||||
moment: values.map((_) => moment(_)),
|
||||
});
|
||||
|
||||
assert.ok(isArray(options), 'options is still an array');
|
||||
assert.equal(options.length, 2, 'two entries have been added');
|
||||
assert.ok(
|
||||
isArray(
|
||||
controller.get('options')
|
||||
),
|
||||
'options is still an array'
|
||||
);
|
||||
assert.equal(
|
||||
controller.get('options.length'),
|
||||
2,
|
||||
'array has correct length'
|
||||
options.every(({ title }) => typeof title === 'string'),
|
||||
'title property of options are strings'
|
||||
);
|
||||
assert.ok(
|
||||
controller.get('options').every((option) => {
|
||||
return typeof option.get('title') === 'string';
|
||||
}),
|
||||
'option.title is a string'
|
||||
options.every(({ title }) => moment(title, 'YYYY-MM-DD', true).isValid()),
|
||||
'title property of options are ISO-8601 date string without time'
|
||||
);
|
||||
assert.ok(
|
||||
controller.get('options').every((option) => {
|
||||
return moment(option.get('title'), 'YYYY-MM-DD', true).isValid();
|
||||
}),
|
||||
'option.title is an ISO-8601 date string without time'
|
||||
);
|
||||
assert.ok(
|
||||
controller.get('options').findBy('title', '1918-11-09'),
|
||||
'date is correct'
|
||||
);
|
||||
assert.equal(
|
||||
controller.get('options.firstObject.title'),
|
||||
'1917-10-25',
|
||||
'dates are in correct order'
|
||||
assert.deepEqual(
|
||||
options.map(({ title }) => title), values.sort(),
|
||||
'options having correct value and are sorted'
|
||||
);
|
||||
});
|
||||
|
||||
test('existing times are preserved if new days get selected', function(assert) {
|
||||
let component;
|
||||
run(() => {
|
||||
component = this.owner.factoryFor('component:create-options-dates').create({
|
||||
options: [
|
||||
this.store.createFragment('option', {
|
||||
title: moment('2015-01-01T11:11').toISOString()
|
||||
}),
|
||||
this.store.createFragment('option', {
|
||||
title: moment('2015-01-01T22:22').toISOString()
|
||||
}),
|
||||
this.store.createFragment('option', {
|
||||
title: moment('2015-06-06T08:08').toISOString()
|
||||
}),
|
||||
this.store.createFragment('option', {
|
||||
title: '2016-01-01'
|
||||
})
|
||||
]
|
||||
});
|
||||
test('action #daysSelected: existing times are preserved if new day is selected', function(assert) {
|
||||
let existing = [
|
||||
moment('2015-01-01T11:11').toISOString(),
|
||||
moment('2015-01-01T22:22').toISOString(),
|
||||
moment('2015-06-06T08:08').toISOString(),
|
||||
'2016-01-01'
|
||||
];
|
||||
let additional = '2016-06-06';
|
||||
let merged = existing.slice();
|
||||
merged.push(additional);
|
||||
|
||||
let store = this.owner.lookup('service:store');
|
||||
let component = this.owner.factoryFor('component:create-options-dates').create({
|
||||
options: existing.map((value) => store.createFragment('option', { title: value })),
|
||||
});
|
||||
// add another day
|
||||
run(() => {
|
||||
component.set('optionsBootstrapDatepicker', [
|
||||
moment('2015-01-01').toDate(),
|
||||
moment('2015-06-06').toDate(),
|
||||
moment('2016-01-01').toDate(),
|
||||
moment('2016-06-06').toDate() // new day
|
||||
]);
|
||||
|
||||
component.actions.daysSelected.bind(component)({
|
||||
moment: merged.map((_) => moment(_)),
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
component.get('options').map((option) => option.get('title')),
|
||||
[
|
||||
moment('2015-01-01T11:11').toISOString(),
|
||||
moment('2015-01-01T22:22').toISOString(),
|
||||
moment('2015-06-06T08:08').toISOString(),
|
||||
'2016-01-01',
|
||||
'2016-06-06'
|
||||
],
|
||||
component.options.map(({ title }) => title),
|
||||
merged,
|
||||
'preseve existing times if another day is added'
|
||||
);
|
||||
// delete a day
|
||||
run(() => {
|
||||
component.set('optionsBootstrapDatepicker', [
|
||||
moment('2015-06-06').toDate(),
|
||||
moment('2016-01-01').toDate(),
|
||||
moment('2016-06-06').toDate()
|
||||
]);
|
||||
});
|
||||
|
||||
test('action #daysSelected: existing times are preserved if day gets unselected', function(assert) {
|
||||
let existing = [
|
||||
moment('2015-01-01T11:11').toISOString(),
|
||||
moment('2015-01-01T22:22').toISOString(),
|
||||
moment('2015-06-06T08:08').toISOString(),
|
||||
'2016-01-01'
|
||||
];
|
||||
let reduced = existing.slice();
|
||||
reduced.splice(2, 1);
|
||||
|
||||
let store = this.owner.lookup('service:store');
|
||||
let component = this.owner.factoryFor('component:create-options-dates').create({
|
||||
options: existing.map((value) => store.createFragment('option', { title: value })),
|
||||
});
|
||||
|
||||
component.actions.daysSelected.bind(component)({
|
||||
moment: reduced.map((_) => moment(_)),
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
component.get('options').map((option) => option.get('title')),
|
||||
[
|
||||
moment('2015-06-06T08:08').toISOString(),
|
||||
'2016-01-01',
|
||||
'2016-06-06'
|
||||
],
|
||||
component.options.map(({ title }) => title),
|
||||
reduced,
|
||||
'preseve existing times if a day is deleted'
|
||||
);
|
||||
// order if multiple days are added
|
||||
run(() => {
|
||||
component.set('optionsBootstrapDatepicker', [
|
||||
moment('2015-06-06').toDate(),
|
||||
moment('2016-01-01').toDate(),
|
||||
moment('2016-06-06').toDate(),
|
||||
moment('2016-12-12').toDate(),
|
||||
moment('2015-01-01').toDate(),
|
||||
moment('2016-03-03').toDate()
|
||||
]);
|
||||
});
|
||||
assert.deepEqual(
|
||||
component.get('options').map((option) => option.get('title')),
|
||||
[
|
||||
'2015-01-01',
|
||||
moment('2015-06-06T08:08').toISOString(),
|
||||
'2016-01-01',
|
||||
'2016-03-03',
|
||||
'2016-06-06',
|
||||
'2016-12-12'
|
||||
],
|
||||
'options are in correct order after multiple days are added'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
59
yarn.lock
59
yarn.lock
|
@ -1899,13 +1899,6 @@ boom@2.x.x:
|
|||
dependencies:
|
||||
hoek "2.x.x"
|
||||
|
||||
bootstrap-datepicker@^1.7.1:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/bootstrap-datepicker/-/bootstrap-datepicker-1.8.0.tgz#c63513931e6f09f16ae9f11b62f32d950df3958e"
|
||||
integrity sha512-213St/G8KT3mjs4qu4qwww74KWysMaIeqgq5OhrboZjIjemIpyuxlSo9FNNI5+KzpkkxkRRba+oewiRGV42B1A==
|
||||
dependencies:
|
||||
jquery ">=1.7.1 <4.0.0"
|
||||
|
||||
bootstrap-sass@^3.3.7:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/bootstrap-sass/-/bootstrap-sass-3.4.0.tgz#b1c330a56782347f626d31d497fa4aea16b3f99b"
|
||||
|
@ -3452,6 +3445,13 @@ ember-array-helper@^5.0.0:
|
|||
dependencies:
|
||||
ember-cli-babel "^7.1.2"
|
||||
|
||||
ember-assign-helper@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ember-assign-helper/-/ember-assign-helper-0.2.0.tgz#02d1b5ee6b4f9cb68036b7d19db47f2a9d74f148"
|
||||
integrity sha512-WO6K24m6Wk+G1PBOMaXUtOMkzCTtt9z67SPIcAFxOVqc95hUU62wDRUdDiPa/854T5rroq5CJ7Ey4LgxorCsgg==
|
||||
dependencies:
|
||||
ember-cli-babel "^6.6.0"
|
||||
|
||||
ember-assign-polyfill@^2.0.1:
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/ember-assign-polyfill/-/ember-assign-polyfill-2.6.0.tgz#07847e3357ee35b33f886a0b5fbec6873f6860eb"
|
||||
|
@ -3565,7 +3565,7 @@ ember-cli-babel@^6.8.1:
|
|||
ember-cli-version-checker "^2.1.2"
|
||||
semver "^5.5.0"
|
||||
|
||||
ember-cli-babel@^7.0.0, ember-cli-babel@^7.1.2:
|
||||
ember-cli-babel@^7.0.0, ember-cli-babel@^7.1.2, ember-cli-babel@^7.2.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ember-cli-babel/-/ember-cli-babel-7.2.0.tgz#5c5bd877fb73f6fb198c878d3127ba9e18e9b8a0"
|
||||
integrity sha512-vwx/AgPD7P4ebgTFJMqFovbrSNCA02UMuItlR/Il16njYjgN9ZzjUqgYxaylN7k8RF88wdJq3jrtqyMS/oOq8A==
|
||||
|
@ -3587,16 +3587,6 @@ ember-cli-babel@^7.0.0, ember-cli-babel@^7.1.2:
|
|||
ensure-posix-path "^1.0.2"
|
||||
semver "^5.5.0"
|
||||
|
||||
ember-cli-bootstrap-datepicker@^0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/ember-cli-bootstrap-datepicker/-/ember-cli-bootstrap-datepicker-0.6.1.tgz#73ee152e9b3e4e5ff872d8c8a2ca87c544c9e8b5"
|
||||
integrity sha512-imo+NTPhpDuO4WBr02iU6bAXniNqO6zilvXbp2/4lz6K3/+ilg2MRorOmEgfTIt5wLiQJk+Fvky0s2p1GmiRvA==
|
||||
dependencies:
|
||||
bootstrap-datepicker "^1.7.1"
|
||||
broccoli-funnel "^2.0.1"
|
||||
broccoli-merge-trees "^2.0.0"
|
||||
ember-cli-babel "^6.6.0"
|
||||
|
||||
ember-cli-broccoli-sane-watcher@^2.1.1:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/ember-cli-broccoli-sane-watcher/-/ember-cli-broccoli-sane-watcher-2.2.2.tgz#9bb1b04ddeb2c086aecd8693cbaeca1d88dc160c"
|
||||
|
@ -3728,7 +3718,7 @@ ember-cli-htmlbars@^2.0.1, ember-cli-htmlbars@^2.0.2, ember-cli-htmlbars@^2.0.3:
|
|||
json-stable-stringify "^1.0.0"
|
||||
strip-bom "^3.0.0"
|
||||
|
||||
ember-cli-htmlbars@^3.0.0:
|
||||
ember-cli-htmlbars@^3.0.0, ember-cli-htmlbars@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ember-cli-htmlbars/-/ember-cli-htmlbars-3.0.1.tgz#01e21f0fd05e0a6489154f26614b1041769e3e58"
|
||||
integrity sha512-pyyB2s52vKTXDC5svU3IjU7GRLg2+5O81o9Ui0ZSiBS14US/bZl46H2dwcdSJAK+T+Za36ZkQM9eh1rNwOxfoA==
|
||||
|
@ -4073,7 +4063,7 @@ ember-composable-helpers@^2.1.0:
|
|||
broccoli-funnel "^1.0.1"
|
||||
ember-cli-babel "^6.6.0"
|
||||
|
||||
ember-concurrency@^0.8.7:
|
||||
ember-concurrency@^0.8.26, ember-concurrency@^0.8.7:
|
||||
version "0.8.26"
|
||||
resolved "https://registry.yarnpkg.com/ember-concurrency/-/ember-concurrency-0.8.26.tgz#7aeaa5c00e87903a57726823efe68787a83154b0"
|
||||
integrity sha512-4GrtZdNKUMTsRbWzYwmUFXW+9pqab+78smw9Nn4ECUl1yuzIyHVcV7zKsgxgPISIGwrY6HwhgmFBCxipyyYP5w==
|
||||
|
@ -4324,6 +4314,25 @@ ember-popper@^0.9.0:
|
|||
fastboot-transform "^0.1.0"
|
||||
popper.js "^1.14.1"
|
||||
|
||||
ember-power-calendar-moment@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/ember-power-calendar-moment/-/ember-power-calendar-moment-0.1.4.tgz#9233c11266eceba160132eff4a92772c11e13abb"
|
||||
integrity sha512-TsFtxOMJi22hbIF24yemFZvdQ5PFi6TVY4mIAFrt/DROhJfn/On7+jvz9Q9gyjo1GWxOTbc722md7czstHTTuQ==
|
||||
dependencies:
|
||||
broccoli-funnel "^2.0.1"
|
||||
ember-cli-babel "^6.6.0"
|
||||
ember-cli-moment-shim "^3.7.1"
|
||||
|
||||
ember-power-calendar@^0.10.0:
|
||||
version "0.10.0"
|
||||
resolved "https://registry.yarnpkg.com/ember-power-calendar/-/ember-power-calendar-0.10.0.tgz#ac1418599f8cc11b289beacbd3a2bd986f656367"
|
||||
integrity sha512-WhmtFkhAwnowV12cIartPrcAQabNZqI3Tq3H5LM2Rq2y6QgGGkEsO+PRrRPxTY/DESSUEcrL12MtkqVajtBSCg==
|
||||
dependencies:
|
||||
ember-assign-helper "^0.2.0"
|
||||
ember-cli-babel "^7.2.0"
|
||||
ember-cli-htmlbars "^3.0.1"
|
||||
ember-concurrency "^0.8.26"
|
||||
|
||||
ember-qunit@^3.5.0:
|
||||
version "3.5.3"
|
||||
resolved "https://registry.yarnpkg.com/ember-qunit/-/ember-qunit-3.5.3.tgz#bfd0bff8298c78c77e870cca43fe0826e78a0d09"
|
||||
|
@ -6399,16 +6408,16 @@ jquery-deferred@^0.3.0:
|
|||
resolved "https://registry.yarnpkg.com/jquery-deferred/-/jquery-deferred-0.3.1.tgz#596eca1caaff54f61b110962b23cafea74c35355"
|
||||
integrity sha1-WW7KHKr/VPYbEQlisjyv6nTDU1U=
|
||||
|
||||
"jquery@>=1.7.1 <4.0.0", jquery@^3.3.1:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca"
|
||||
integrity sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==
|
||||
|
||||
jquery@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.2.1.tgz#5c4d9de652af6cd0a770154a631bba12b015c787"
|
||||
integrity sha1-XE2d5lKvbNCncBVKYxu6ErAVx4c=
|
||||
|
||||
jquery@^3.3.1:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca"
|
||||
integrity sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==
|
||||
|
||||
js-levenshtein@^1.1.3:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.4.tgz#3a56e3cbf589ca0081eb22cd9ba0b1290a16d26e"
|
||||
|
|
Loading…
Reference in a new issue