FEATURE: new date/time components (#7898)
This commit is contained in:
parent
194a2b612f
commit
95ad4f9077
|
@ -0,0 +1,101 @@
|
|||
/* global Pikaday:true */
|
||||
import loadScript from "discourse/lib/load-script";
|
||||
import {
|
||||
default as computed,
|
||||
on
|
||||
} from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ["d-date-input"],
|
||||
date: null,
|
||||
_picker: null,
|
||||
|
||||
@computed("site.mobileView")
|
||||
inputType(mobileView) {
|
||||
return mobileView ? "date" : "text";
|
||||
},
|
||||
|
||||
@on("didInsertElement")
|
||||
_loadDatePicker() {
|
||||
const container = this.element.querySelector(`#${this.containerId}`);
|
||||
|
||||
if (this.site.mobileView) {
|
||||
this._loadNativePicker(container);
|
||||
} else {
|
||||
this._loadPikadayPicker(container);
|
||||
}
|
||||
},
|
||||
|
||||
didUpdateAttrs() {
|
||||
this._super(...arguments);
|
||||
|
||||
if (this._picker) {
|
||||
this._picker.setDate(this.date, true);
|
||||
}
|
||||
},
|
||||
|
||||
_loadPikadayPicker(container) {
|
||||
loadScript("/javascripts/pikaday.js").then(() => {
|
||||
Ember.run.next(() => {
|
||||
const default_opts = {
|
||||
field: this.element.querySelector(".date-picker"),
|
||||
container: container || this.element,
|
||||
bound: container === null,
|
||||
format: "LL",
|
||||
firstDay: 1,
|
||||
i18n: {
|
||||
previousMonth: I18n.t("dates.previous_month"),
|
||||
nextMonth: I18n.t("dates.next_month"),
|
||||
months: moment.months(),
|
||||
weekdays: moment.weekdays(),
|
||||
weekdaysShort: moment.weekdaysShort()
|
||||
},
|
||||
onSelect: date => this._handleSelection(date)
|
||||
};
|
||||
|
||||
this._picker = new Pikaday(Object.assign(default_opts, this._opts()));
|
||||
this._picker.setDate(this.date, true);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_loadNativePicker(container) {
|
||||
const wrapper = container || this.element;
|
||||
const picker = wrapper.querySelector("input.date-picker");
|
||||
picker.onchange = () => this._handleSelection(picker.value);
|
||||
picker.hide = () => {
|
||||
/* do nothing for native */
|
||||
};
|
||||
picker.destroy = () => {
|
||||
/* do nothing for native */
|
||||
};
|
||||
this._picker = picker;
|
||||
},
|
||||
|
||||
_handleSelection(value) {
|
||||
if (!this.element || this.isDestroying || this.isDestroyed) return;
|
||||
|
||||
this._picker && this._picker.hide();
|
||||
|
||||
if (this.onChange) {
|
||||
this.onChange(moment(value).toDate());
|
||||
}
|
||||
},
|
||||
|
||||
@on("willDestroyElement")
|
||||
_destroy() {
|
||||
if (this._picker) {
|
||||
this._picker.destroy();
|
||||
}
|
||||
this._picker = null;
|
||||
},
|
||||
|
||||
@computed()
|
||||
placeholder() {
|
||||
return I18n.t("dates.placeholder");
|
||||
},
|
||||
|
||||
_opts() {
|
||||
return null;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,51 @@
|
|||
export default Ember.Component.extend({
|
||||
classNames: ["d-date-time-input-range"],
|
||||
|
||||
from: null,
|
||||
to: null,
|
||||
onChangeTo: null,
|
||||
onChangeFrom: null,
|
||||
currentPanel: "from",
|
||||
showFromTime: true,
|
||||
showToTime: true,
|
||||
error: null,
|
||||
|
||||
fromPanelActive: Ember.computed.equal("currentPanel", "from"),
|
||||
toPanelActive: Ember.computed.equal("currentPanel", "to"),
|
||||
|
||||
_valid(state) {
|
||||
if (state.to < state.from) {
|
||||
return I18n.t("date_time_picker.errors.to_before_from");
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
actions: {
|
||||
_onChange(options, value) {
|
||||
if (this.onChange) {
|
||||
const state = {
|
||||
from: this.from,
|
||||
to: this.to
|
||||
};
|
||||
|
||||
const diff = {};
|
||||
diff[options.prop] = value;
|
||||
|
||||
const newState = Object.assign(state, diff);
|
||||
|
||||
const validation = this._valid(newState);
|
||||
if (validation === true) {
|
||||
this.set("error", null);
|
||||
this.onChange(newState);
|
||||
} else {
|
||||
this.set("error", validation);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onChangePanel(panel) {
|
||||
this.set("currentPanel", panel);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
export default Ember.Component.extend({
|
||||
classNames: ["d-date-time-input"],
|
||||
date: null,
|
||||
showTime: true,
|
||||
|
||||
_hours: Ember.computed("date", function() {
|
||||
return this.date ? this.date.getHours() : null;
|
||||
}),
|
||||
|
||||
_minutes: Ember.computed("date", function() {
|
||||
return this.date ? this.date.getMinutes() : null;
|
||||
}),
|
||||
|
||||
actions: {
|
||||
onChangeTime(time) {
|
||||
if (this.onChange) {
|
||||
const year = this.date.getFullYear();
|
||||
const month = this.date.getMonth();
|
||||
const day = this.date.getDate();
|
||||
this.onChange(new Date(year, month, day, time.hours, time.minutes));
|
||||
}
|
||||
},
|
||||
|
||||
onChangeDate(date) {
|
||||
if (this.onChange) {
|
||||
const year = date.getFullYear();
|
||||
const month = date.getMonth();
|
||||
const day = date.getDate();
|
||||
this.onChange(
|
||||
new Date(year, month, day, this._hours || 0, this._minutes || 0)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,73 @@
|
|||
import { isNumeric } from "discourse/lib/utilities";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ["d-time-input"],
|
||||
hours: null,
|
||||
minutes: null,
|
||||
_hours: Ember.computed.oneWay("hours"),
|
||||
_minutes: Ember.computed.oneWay("minutes"),
|
||||
isSafari: Ember.computed.oneWay("capabilities.isSafari"),
|
||||
isMobile: Ember.computed.oneWay("site.mobileView"),
|
||||
nativePicker: Ember.computed.or("isSafari", "isMobile"),
|
||||
|
||||
actions: {
|
||||
onInput(options, event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (this.onChange) {
|
||||
let value = event.target.value;
|
||||
|
||||
if (!isNumeric(value)) {
|
||||
value = 0;
|
||||
} else {
|
||||
value = parseInt(value, 10);
|
||||
}
|
||||
|
||||
if (options.prop === "hours") {
|
||||
value = Math.max(0, Math.min(value, 23))
|
||||
.toString()
|
||||
.padStart(2, "0");
|
||||
this._processHoursChange(value);
|
||||
} else {
|
||||
value = Math.max(0, Math.min(value, 59))
|
||||
.toString()
|
||||
.padStart(2, "0");
|
||||
this._processMinutesChange(value);
|
||||
}
|
||||
|
||||
Ember.run.schedule("afterRender", () => (event.target.value = value));
|
||||
}
|
||||
},
|
||||
|
||||
onFocusIn(value, event) {
|
||||
if (value && event.target) {
|
||||
event.target.select();
|
||||
}
|
||||
},
|
||||
|
||||
onChangeTime(event) {
|
||||
const time = event.target.value;
|
||||
|
||||
if (time && this.onChange) {
|
||||
this.onChange({
|
||||
hours: time.split(":")[0],
|
||||
minutes: time.split(":")[1]
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_processHoursChange(hours) {
|
||||
this.onChange({
|
||||
hours,
|
||||
minutes: this._minutes || "00"
|
||||
});
|
||||
},
|
||||
|
||||
_processMinutesChange(minutes) {
|
||||
this.onChange({
|
||||
hours: this._hours || "00",
|
||||
minutes
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
{{input
|
||||
type=inputType
|
||||
class="date-picker"
|
||||
placeholder=placeholder
|
||||
value=value}}
|
|
@ -0,0 +1,34 @@
|
|||
<ul class="panels {{currentPanel}}">
|
||||
<li>
|
||||
{{d-button
|
||||
label="date_time_picker.from"
|
||||
class="from-panel"
|
||||
action=(action "onChangePanel" "from")}}
|
||||
</li>
|
||||
<li>
|
||||
{{d-button
|
||||
label="date_time_picker.to"
|
||||
class="to-panel"
|
||||
action=(action "onChangePanel" "to")}}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{{#if error}}
|
||||
<div class="alert error">{{error}}</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="panel from {{if fromPanelActive 'visible'}}">
|
||||
{{date-time-input
|
||||
date=from
|
||||
onChange=(action "_onChange" (hash prop="from"))
|
||||
showTime=showFromTime
|
||||
}}
|
||||
</div>
|
||||
|
||||
<div class="panel to {{if toPanelActive 'visible'}}">
|
||||
{{date-time-input
|
||||
date=to
|
||||
onChange=(action "_onChange" (hash prop="to"))
|
||||
showTime=showToTime
|
||||
}}
|
||||
</div>
|
|
@ -0,0 +1,9 @@
|
|||
{{date-input date=date onChange=(action "onChangeDate")}}
|
||||
|
||||
{{#if showTime}}
|
||||
{{time-input
|
||||
hours=_hours
|
||||
minutes=_minutes
|
||||
onChange=(action "onChangeTime")
|
||||
}}
|
||||
{{/if}}
|
|
@ -0,0 +1,40 @@
|
|||
<div class="fields">
|
||||
{{#if nativePicker}}
|
||||
{{input
|
||||
class="field time"
|
||||
type="time"
|
||||
value=(concat _hours ":" _minutes)
|
||||
change=(action "onChangeTime")
|
||||
}}
|
||||
{{else}}
|
||||
{{input
|
||||
class="field hours"
|
||||
type="number"
|
||||
title="Hours"
|
||||
minlength=2
|
||||
maxlength=2
|
||||
max="23"
|
||||
min="0"
|
||||
placeholder="00"
|
||||
value=_hours
|
||||
input=(action "onInput" (hash prop="hours"))
|
||||
focus-in=(action "onFocusIn")
|
||||
}}
|
||||
|
||||
<div class="separator">:</div>
|
||||
|
||||
{{input
|
||||
class="field minutes"
|
||||
title="Minutes"
|
||||
type="number"
|
||||
minlength=2
|
||||
maxlength=2
|
||||
max="59"
|
||||
min="0"
|
||||
placeholder="00"
|
||||
value=_minutes
|
||||
input=(action "onInput" (hash prop="minutes"))
|
||||
focus-in=(action "onFocusIn")
|
||||
}}
|
||||
{{/if}}
|
||||
</div>
|
|
@ -189,4 +189,40 @@ if (RegExp.prototype.flags === undefined) {
|
|||
});
|
||||
}
|
||||
|
||||
// https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
|
||||
if (!String.prototype.padStart) {
|
||||
String.prototype.padStart = function padStart(targetLength, padString) {
|
||||
targetLength = targetLength >> 0; //truncate if number, or convert non-number to 0;
|
||||
padString = String(typeof padString !== "undefined" ? padString : " ");
|
||||
if (this.length >= targetLength) {
|
||||
return String(this);
|
||||
} else {
|
||||
targetLength = targetLength - this.length;
|
||||
if (targetLength > padString.length) {
|
||||
padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed
|
||||
}
|
||||
return padString.slice(0, targetLength) + String(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd
|
||||
if (!String.prototype.padEnd) {
|
||||
String.prototype.padEnd = function padEnd(targetLength, padString) {
|
||||
targetLength = targetLength >> 0; //floor if number or convert non-number to 0;
|
||||
padString = String(typeof padString !== "undefined" ? padString : " ");
|
||||
if (this.length > targetLength) {
|
||||
return String(this);
|
||||
} else {
|
||||
targetLength = targetLength - this.length;
|
||||
if (targetLength > padString.length) {
|
||||
padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed
|
||||
}
|
||||
return String(this) + padString.slice(0, targetLength);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* eslint-enable */
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
.d-date-input {
|
||||
.date-picker {
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
outline: none;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.pika-single {
|
||||
margin-left: -1px;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
.d-date-time-input-range {
|
||||
padding: 0.5em;
|
||||
background: whitesmole;
|
||||
border: 1px solid $primary-low;
|
||||
width: 300px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.panels {
|
||||
display: inline-flex;
|
||||
list-style: none;
|
||||
margin: 0 0 0.5em 0;
|
||||
flex: 1;
|
||||
|
||||
&.from {
|
||||
.from-panel {
|
||||
background: $danger;
|
||||
color: $secondary;
|
||||
}
|
||||
}
|
||||
|
||||
&.to {
|
||||
.to-panel {
|
||||
background: $danger;
|
||||
color: $secondary;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.panel {
|
||||
display: none;
|
||||
flex: 1;
|
||||
|
||||
&.visible {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
.d-date-time-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid $primary-low;
|
||||
width: 258px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
flex: 1;
|
||||
justify-content: space-between;
|
||||
|
||||
.date-picker,
|
||||
.fields {
|
||||
border: 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
.d-time-input {
|
||||
box-sizing: border-box;
|
||||
|
||||
.fields {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid $primary-low;
|
||||
|
||||
.field {
|
||||
text-align: center;
|
||||
width: auto;
|
||||
margin: 0;
|
||||
border: none;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
width: 32px;
|
||||
|
||||
&.time {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&.hours,
|
||||
&.minutes {
|
||||
text-align: center;
|
||||
width: 45px;
|
||||
}
|
||||
|
||||
&.hours {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
&.minutes {
|
||||
padding-left: 10px;
|
||||
width: 55px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1518,6 +1518,12 @@ en:
|
|||
one: "Select at least {{count}} item."
|
||||
other: "Select at least {{count}} items."
|
||||
|
||||
date_time_picker:
|
||||
from: From
|
||||
to: To
|
||||
errors:
|
||||
to_before_from: "To date must be later than from date."
|
||||
|
||||
emoji_picker:
|
||||
filter_placeholder: Search for emoji
|
||||
smileys_&_emotion: Smileys and Emotion
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
import componentTest from "helpers/component-test";
|
||||
|
||||
moduleForComponent("date-input", { integration: true });
|
||||
|
||||
function dateInput() {
|
||||
return find(".date-picker");
|
||||
}
|
||||
|
||||
function setDate(date) {
|
||||
this.set("date", date);
|
||||
}
|
||||
|
||||
async function pika(year, month, day) {
|
||||
await click(
|
||||
`.pika-button.pika-day[data-pika-year="${year}"][data-pika-month="${month}"][data-pika-day="${day}"]`
|
||||
);
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
|
||||
const DEFAULT_DATE = new Date(2019, 0, 29);
|
||||
|
||||
componentTest("default", {
|
||||
template: `{{date-input date=date}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ date: DEFAULT_DATE });
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(dateInput().val(), "January 29, 2019");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("prevents mutations", {
|
||||
template: `{{date-input date=date onChange=onChange}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ date: DEFAULT_DATE });
|
||||
this.set("onChange", noop);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await click(dateInput());
|
||||
await pika(2019, 0, 2);
|
||||
|
||||
assert.ok(this.date.getTime() === DEFAULT_DATE.getTime());
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("allows mutations through actions", {
|
||||
template: `{{date-input date=date onChange=onChange}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ date: DEFAULT_DATE });
|
||||
this.set("onChange", setDate);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await click(dateInput());
|
||||
await pika(2019, 0, 2);
|
||||
|
||||
assert.ok(this.date.getTime() === new Date(2019, 0, 2).getTime());
|
||||
}
|
||||
});
|
|
@ -0,0 +1,102 @@
|
|||
import componentTest from "helpers/component-test";
|
||||
|
||||
moduleForComponent("date-time-input-range", { integration: true });
|
||||
|
||||
function fromDateInput() {
|
||||
return find(".from .date-picker");
|
||||
}
|
||||
|
||||
function fromHoursInput() {
|
||||
return find(".from .field.hours");
|
||||
}
|
||||
|
||||
function fromMinutesInput() {
|
||||
return find(".from .field.minutes");
|
||||
}
|
||||
|
||||
function toDateInput() {
|
||||
return find(".to .date-picker");
|
||||
}
|
||||
|
||||
function toHoursInput() {
|
||||
return find(".to .field.hours");
|
||||
}
|
||||
|
||||
function toMinutesInput() {
|
||||
return find(".to .field.minutes");
|
||||
}
|
||||
|
||||
function setDates(dates) {
|
||||
this.setProperties(dates);
|
||||
}
|
||||
|
||||
async function pika(year, month, day) {
|
||||
await click(
|
||||
`.pika-button.pika-day[data-pika-year="${year}"][data-pika-month="${month}"][data-pika-day="${day}"]`
|
||||
);
|
||||
}
|
||||
|
||||
const DEFAULT_DATE_TIME = new Date(2019, 0, 29, 14, 45);
|
||||
|
||||
componentTest("default", {
|
||||
template: `{{date-time-input-range from=date to=to}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ date: DEFAULT_DATE_TIME, to: null });
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(fromDateInput().val(), "January 29, 2019");
|
||||
assert.equal(fromHoursInput().val(), "14");
|
||||
assert.equal(fromMinutesInput().val(), "45");
|
||||
|
||||
assert.equal(toDateInput().val(), "");
|
||||
assert.equal(toHoursInput().val(), "");
|
||||
assert.equal(toMinutesInput().val(), "");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("can switch panels", {
|
||||
template: `{{date-time-input-range}}`,
|
||||
|
||||
async test(assert) {
|
||||
assert.ok(exists(".panel.from.visible"));
|
||||
assert.notOk(exists(".panel.to.visible"));
|
||||
|
||||
await click(".panels .to-panel");
|
||||
|
||||
assert.ok(exists(".panel.to.visible"));
|
||||
assert.notOk(exists(".panel.from.visible"));
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("prevents toDate to be before fromDate", {
|
||||
template: `{{date-time-input-range from=from to=to onChange=onChange}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({
|
||||
from: DEFAULT_DATE_TIME,
|
||||
to: DEFAULT_DATE_TIME,
|
||||
onChange: setDates
|
||||
});
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
assert.notOk(exists(".error"));
|
||||
|
||||
await click(toDateInput());
|
||||
await pika(2019, 0, 1);
|
||||
|
||||
assert.ok(exists(".error"));
|
||||
assert.ok(
|
||||
this.to.getTime() === DEFAULT_DATE_TIME.getTime(),
|
||||
"it didnt trigger a mutation"
|
||||
);
|
||||
|
||||
await click(toDateInput());
|
||||
await pika(2019, 0, 30);
|
||||
|
||||
assert.notOk(exists(".error"));
|
||||
assert.ok(this.to.getTime() === new Date(2019, 0, 30, 14, 45).getTime());
|
||||
}
|
||||
});
|
|
@ -0,0 +1,84 @@
|
|||
import componentTest from "helpers/component-test";
|
||||
|
||||
moduleForComponent("date-time-input", { integration: true });
|
||||
|
||||
function dateInput() {
|
||||
return find(".date-picker");
|
||||
}
|
||||
|
||||
function hoursInput() {
|
||||
return find(".field.hours");
|
||||
}
|
||||
|
||||
function minutesInput() {
|
||||
return find(".field.minutes");
|
||||
}
|
||||
|
||||
function setDate(date) {
|
||||
this.set("date", date);
|
||||
}
|
||||
|
||||
async function pika(year, month, day) {
|
||||
await click(
|
||||
`.pika-button.pika-day[data-pika-year="${year}"][data-pika-month="${month}"][data-pika-day="${day}"]`
|
||||
);
|
||||
}
|
||||
|
||||
const DEFAULT_DATE_TIME = new Date(2019, 0, 29, 14, 45);
|
||||
|
||||
componentTest("default", {
|
||||
template: `{{date-time-input date=date}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ date: DEFAULT_DATE_TIME });
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(dateInput().val(), "January 29, 2019");
|
||||
assert.equal(hoursInput().val(), "14");
|
||||
assert.equal(minutesInput().val(), "45");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("prevents mutations", {
|
||||
template: `{{date-time-input date=date}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ date: DEFAULT_DATE_TIME });
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await click(dateInput());
|
||||
await pika(2019, 0, 2);
|
||||
|
||||
assert.ok(this.date.getTime() === DEFAULT_DATE_TIME.getTime());
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("allows mutations through actions", {
|
||||
template: `{{date-time-input date=date onChange=onChange}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ date: DEFAULT_DATE_TIME });
|
||||
this.set("onChange", setDate);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await click(dateInput());
|
||||
await pika(2019, 0, 2);
|
||||
|
||||
assert.ok(this.date.getTime() === new Date(2019, 0, 2, 14, 45).getTime());
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("can hide time", {
|
||||
template: `{{date-time-input date=date showTime=false}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ date: DEFAULT_DATE_TIME });
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
assert.notOk(exists(hoursInput()));
|
||||
}
|
||||
});
|
|
@ -0,0 +1,97 @@
|
|||
import componentTest from "helpers/component-test";
|
||||
|
||||
moduleForComponent("time-input", { integration: true });
|
||||
|
||||
function hoursInput() {
|
||||
return find(".field.hours");
|
||||
}
|
||||
|
||||
function minutesInput() {
|
||||
return find(".field.minutes");
|
||||
}
|
||||
|
||||
function setTime(time) {
|
||||
this.setProperties(time);
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
|
||||
componentTest("default", {
|
||||
template: `{{time-input hours=hours minutes=minutes}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ hours: "14", minutes: "58" });
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(hoursInput().val(), "14");
|
||||
assert.equal(minutesInput().val(), "58");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("prevents mutations", {
|
||||
template: `{{time-input hours=hours minutes=minutes}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ hours: "14", minutes: "58" });
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await fillIn(hoursInput(), "12");
|
||||
assert.ok(this.hours === "14");
|
||||
|
||||
await fillIn(minutesInput(), "36");
|
||||
assert.ok(this.minutes === "58");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("allows mutations through actions", {
|
||||
template: `{{time-input hours=hours minutes=minutes onChange=onChange}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({ hours: "14", minutes: "58" });
|
||||
this.set("onChange", setTime);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await fillIn(hoursInput(), "12");
|
||||
assert.ok(this.hours === "12");
|
||||
|
||||
await fillIn(minutesInput(), "36");
|
||||
assert.ok(this.minutes === "36");
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("hours and minutes have boundaries", {
|
||||
template: `{{time-input hours=14 minutes=58 onChange=onChange}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.set("onChange", noop);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await fillIn(hoursInput(), "2");
|
||||
assert.equal(hoursInput().val(), "02");
|
||||
|
||||
await fillIn(hoursInput(), "@");
|
||||
assert.equal(hoursInput().val(), "00");
|
||||
|
||||
await fillIn(hoursInput(), "24");
|
||||
assert.equal(hoursInput().val(), "23");
|
||||
|
||||
await fillIn(hoursInput(), "-1");
|
||||
assert.equal(hoursInput().val(), "00");
|
||||
|
||||
await fillIn(minutesInput(), "@");
|
||||
assert.equal(minutesInput().val(), "00");
|
||||
|
||||
await fillIn(minutesInput(), "2");
|
||||
assert.equal(minutesInput().val(), "02");
|
||||
|
||||
await fillIn(minutesInput(), "60");
|
||||
assert.equal(minutesInput().val(), "59");
|
||||
|
||||
await fillIn(minutesInput(), "-1");
|
||||
assert.equal(minutesInput().val(), "00");
|
||||
}
|
||||
});
|
Loading…
Reference in New Issue