mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
DEV: Move calendar date + time picker from local dates into core component (#23023)
This commit moves the calendar date and time picker shown in the local dates modal into a core component that can be reused in other places. Also add system specs to make sure there isn't any breakages with this feature, and a section to the styleguide.
This commit is contained in:
parent
0187ad0d37
commit
fb36af7799
@ -0,0 +1,24 @@
|
|||||||
|
<div
|
||||||
|
class="calendar-date-time-input"
|
||||||
|
{{did-insert this.setupInternalDateTime}}
|
||||||
|
{{did-insert this.setupPikaday}}
|
||||||
|
{{did-update this.changeMinDate @minDate}}
|
||||||
|
{{did-update this.changeDate @date}}
|
||||||
|
{{did-update this.changeTime @time}}
|
||||||
|
>
|
||||||
|
<Input class="fake-input" />
|
||||||
|
|
||||||
|
<div class="date-picker" id="picker-container-{{@datePickerId}}"></div>
|
||||||
|
|
||||||
|
<div class="time-pickers">
|
||||||
|
{{d-icon "far-clock"}}
|
||||||
|
<Input
|
||||||
|
maxlength={{5}}
|
||||||
|
placeholder="hh:mm"
|
||||||
|
@type="time"
|
||||||
|
@value={{this._time}}
|
||||||
|
class="time-picker"
|
||||||
|
{{on "input" (action this.onChangeTime)}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,109 @@
|
|||||||
|
/* global Pikaday:true */
|
||||||
|
import { isEmpty } from "@ember/utils";
|
||||||
|
import Component from "@glimmer/component";
|
||||||
|
import I18n from "I18n";
|
||||||
|
import loadScript from "discourse/lib/load-script";
|
||||||
|
import { Promise } from "rsvp";
|
||||||
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
|
||||||
|
export default class CalendarDateTimeInput extends Component {
|
||||||
|
_timeFormat = this.args.timeFormat || "HH:mm:ss";
|
||||||
|
_dateFormat = this.args.dateFormat || "YYYY-MM-DD";
|
||||||
|
_dateTimeFormat = this.args.dateTimeFormat || "YYYY-MM-DD HH:mm:ss";
|
||||||
|
_picker = null;
|
||||||
|
|
||||||
|
@tracked _time;
|
||||||
|
@tracked _date;
|
||||||
|
|
||||||
|
@action
|
||||||
|
setupInternalDateTime() {
|
||||||
|
this._time = this.args.time;
|
||||||
|
this._date = this.args.date;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
setupPikaday(element) {
|
||||||
|
this.#setupPicker(element).then((picker) => {
|
||||||
|
this._picker = picker;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
onChangeTime(event) {
|
||||||
|
this._time = event.target.value;
|
||||||
|
this.args.onChangeTime(this._time);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
changeDate() {
|
||||||
|
if (moment(this.args.date, this._dateFormat).isValid()) {
|
||||||
|
this._date = this.args.date;
|
||||||
|
this._picker.setDate(
|
||||||
|
moment.utc(this._date).format(this._dateFormat),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this._date = null;
|
||||||
|
this._picker.setDate(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
changeTime() {
|
||||||
|
if (isEmpty(this.args.time)) {
|
||||||
|
this._time = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/.test(this.args.time)) {
|
||||||
|
this._time = this.args.time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
changeMinDate() {
|
||||||
|
if (
|
||||||
|
this.args.minDate &&
|
||||||
|
moment(this.args.minDate, this._dateFormat).isValid()
|
||||||
|
) {
|
||||||
|
this._picker.setMinDate(
|
||||||
|
moment(this.args.minDate, this._dateFormat).toDate()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this._picker.setMinDate(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#setupPicker(element) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
loadScript("/javascripts/pikaday.js").then(() => {
|
||||||
|
const options = {
|
||||||
|
field: element.querySelector(".fake-input"),
|
||||||
|
container: element.querySelector(
|
||||||
|
`#picker-container-${this.args.datePickerId}`
|
||||||
|
),
|
||||||
|
bound: false,
|
||||||
|
format: "YYYY-MM-DD",
|
||||||
|
reposition: false,
|
||||||
|
firstDay: 1,
|
||||||
|
setDefaultDate: true,
|
||||||
|
keyboardInput: false,
|
||||||
|
i18n: {
|
||||||
|
previousMonth: I18n.t("dates.previous_month"),
|
||||||
|
nextMonth: I18n.t("dates.next_month"),
|
||||||
|
months: moment.months(),
|
||||||
|
weekdays: moment.weekdays(),
|
||||||
|
weekdaysShort: moment.weekdaysMin(),
|
||||||
|
},
|
||||||
|
onSelect: (date) => {
|
||||||
|
const formattedDate = moment(date).format("YYYY-MM-DD");
|
||||||
|
this.args.onChangeDate(formattedDate);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
resolve(new Pikaday(options));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@
|
|||||||
@import "color-input";
|
@import "color-input";
|
||||||
@import "char-counter";
|
@import "char-counter";
|
||||||
@import "conditional-loading-section";
|
@import "conditional-loading-section";
|
||||||
|
@import "calendar-date-time-input";
|
||||||
@import "convert-to-public-topic-modal";
|
@import "convert-to-public-topic-modal";
|
||||||
@import "d-lightbox";
|
@import "d-lightbox";
|
||||||
@import "d-tooltip";
|
@import "d-tooltip";
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
.calendar-date-time-input {
|
||||||
|
.fake-input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
padding: 5px;
|
||||||
|
border: 1px solid var(--primary-low);
|
||||||
|
z-index: 1;
|
||||||
|
background: var(--secondary);
|
||||||
|
width: 200px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-left: 1em;
|
||||||
|
|
||||||
|
.date-picker {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.pika-single {
|
||||||
|
position: relative !important;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-pickers {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex: 1;
|
||||||
|
margin-top: 1em;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.25em;
|
||||||
|
border-top: 1px solid var(--primary-low-mid);
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.d-icon {
|
||||||
|
color: var(--primary-medium);
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-picker {
|
||||||
|
box-shadow: none;
|
||||||
|
margin: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,3 @@
|
|||||||
/* global Pikaday:true */
|
|
||||||
import computed, {
|
import computed, {
|
||||||
debounce,
|
debounce,
|
||||||
observes,
|
observes,
|
||||||
@ -7,10 +6,7 @@ import Component from "@ember/component";
|
|||||||
import EmberObject, { action } from "@ember/object";
|
import EmberObject, { action } from "@ember/object";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { INPUT_DELAY } from "discourse-common/config/environment";
|
import { INPUT_DELAY } from "discourse-common/config/environment";
|
||||||
import { Promise } from "rsvp";
|
|
||||||
import { cookAsync } from "discourse/lib/text";
|
import { cookAsync } from "discourse/lib/text";
|
||||||
import { isEmpty } from "@ember/utils";
|
|
||||||
import loadScript from "discourse/lib/load-script";
|
|
||||||
import { notEmpty } from "@ember/object/computed";
|
import { notEmpty } from "@ember/object/computed";
|
||||||
import { propertyNotEqual } from "discourse/lib/computed";
|
import { propertyNotEqual } from "discourse/lib/computed";
|
||||||
import { schedule } from "@ember/runloop";
|
import { schedule } from "@ember/runloop";
|
||||||
@ -46,18 +42,14 @@ export default Component.extend({
|
|||||||
formats: (this.siteSettings.discourse_local_dates_default_formats || "")
|
formats: (this.siteSettings.discourse_local_dates_default_formats || "")
|
||||||
.split("|")
|
.split("|")
|
||||||
.filter((f) => f),
|
.filter((f) => f),
|
||||||
timezone: moment.tz.guess(),
|
timezone: this.currentUserTimezone,
|
||||||
date: moment().format(this.dateFormat),
|
date: moment().format(this.dateFormat),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
this.send("focusFrom");
|
||||||
this._setupPicker().then((picker) => {
|
|
||||||
this._picker = picker;
|
|
||||||
this.send("focusFrom");
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
@observes("computedConfig.{from,to,options}", "options", "isValid", "isRange")
|
@observes("computedConfig.{from,to,options}", "options", "isValid", "isRange")
|
||||||
@ -194,7 +186,7 @@ export default Component.extend({
|
|||||||
|
|
||||||
@computed
|
@computed
|
||||||
currentUserTimezone() {
|
currentUserTimezone() {
|
||||||
return moment.tz.guess();
|
return this.currentUser.user_option.timezone || moment.tz.guess();
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
@ -312,118 +304,79 @@ export default Component.extend({
|
|||||||
this.set("format", format);
|
this.set("format", format);
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
@computed("fromSelected", "toSelected")
|
||||||
setTime(event) {
|
selectedDate(fromSelected) {
|
||||||
this._setTimeIfValid(event.target.value, "time");
|
return fromSelected ? this.date : this.toDate;
|
||||||
},
|
},
|
||||||
|
|
||||||
setToTime(event) {
|
@computed("fromSelected", "toSelected")
|
||||||
this._setTimeIfValid(event.target.value, "toTime");
|
selectedTime(fromSelected) {
|
||||||
},
|
return fromSelected ? this.time : this.toTime;
|
||||||
|
},
|
||||||
|
|
||||||
eraseToDateTime() {
|
@action
|
||||||
this.setProperties({ toDate: null, toTime: null });
|
changeSelectedDate(date) {
|
||||||
this._setPickerDate(null);
|
if (this.fromSelected) {
|
||||||
},
|
this.set("date", date);
|
||||||
|
} else {
|
||||||
|
this.set("toDate", date);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
focusFrom() {
|
@action
|
||||||
this.setProperties({ fromSelected: true, toSelected: false });
|
changeSelectedTime(time) {
|
||||||
this._setPickerDate(this.get("fromConfig.date"));
|
if (this.fromSelected) {
|
||||||
this._setPickerMinDate(null);
|
this.set("time", time);
|
||||||
},
|
} else {
|
||||||
|
this.set("toTime", time);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
focusTo() {
|
@action
|
||||||
this.setProperties({ toSelected: true, fromSelected: false });
|
eraseToDateTime() {
|
||||||
this._setPickerDate(this.get("toConfig.date"));
|
this.setProperties({
|
||||||
this._setPickerMinDate(this.get("fromConfig.date"));
|
toDate: null,
|
||||||
},
|
toTime: null,
|
||||||
|
});
|
||||||
|
this.focusFrom();
|
||||||
|
},
|
||||||
|
|
||||||
advancedMode() {
|
@action
|
||||||
this.toggleProperty("advancedMode");
|
focusFrom() {
|
||||||
},
|
this.setProperties({
|
||||||
|
fromSelected: true,
|
||||||
|
toSelected: false,
|
||||||
|
minDate: null,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
save() {
|
@action
|
||||||
const markup = this.markup;
|
focusTo() {
|
||||||
|
this.setProperties({
|
||||||
|
toSelected: true,
|
||||||
|
fromSelected: false,
|
||||||
|
minDate: this.get("fromConfig.date"),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
if (markup) {
|
@action
|
||||||
this._closeModal();
|
toggleAdvancedMode() {
|
||||||
this.insertDate(markup);
|
this.toggleProperty("advancedMode");
|
||||||
}
|
},
|
||||||
},
|
|
||||||
|
|
||||||
cancel() {
|
@action
|
||||||
|
save() {
|
||||||
|
const markup = this.markup;
|
||||||
|
|
||||||
|
if (markup) {
|
||||||
this._closeModal();
|
this._closeModal();
|
||||||
},
|
this.insertDate(markup);
|
||||||
},
|
|
||||||
|
|
||||||
_setTimeIfValid(time, key) {
|
|
||||||
if (isEmpty(time)) {
|
|
||||||
this.set(key, null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (/^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/.test(time)) {
|
|
||||||
this.set(key, time);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_setupPicker() {
|
@action
|
||||||
return new Promise((resolve) => {
|
cancel() {
|
||||||
loadScript("/javascripts/pikaday.js").then(() => {
|
this._closeModal();
|
||||||
const options = {
|
|
||||||
field: this.element.querySelector(".fake-input"),
|
|
||||||
container: this.element.querySelector(
|
|
||||||
`#picker-container-${this.elementId}`
|
|
||||||
),
|
|
||||||
bound: false,
|
|
||||||
format: "YYYY-MM-DD",
|
|
||||||
reposition: false,
|
|
||||||
firstDay: 1,
|
|
||||||
setDefaultDate: true,
|
|
||||||
keyboardInput: false,
|
|
||||||
i18n: {
|
|
||||||
previousMonth: I18n.t("dates.previous_month"),
|
|
||||||
nextMonth: I18n.t("dates.next_month"),
|
|
||||||
months: moment.months(),
|
|
||||||
weekdays: moment.weekdays(),
|
|
||||||
weekdaysShort: moment.weekdaysMin(),
|
|
||||||
},
|
|
||||||
onSelect: (date) => {
|
|
||||||
const formattedDate = moment(date).format("YYYY-MM-DD");
|
|
||||||
|
|
||||||
if (this.fromSelected) {
|
|
||||||
this.set("date", formattedDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.toSelected) {
|
|
||||||
this.set("toDate", formattedDate);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
resolve(new Pikaday(options));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_setPickerMinDate(date) {
|
|
||||||
schedule("afterRender", () => {
|
|
||||||
if (moment(date, this.dateFormat).isValid()) {
|
|
||||||
this._picker.setMinDate(moment(date, this.dateFormat).toDate());
|
|
||||||
} else {
|
|
||||||
this._picker.setMinDate(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_setPickerDate(date) {
|
|
||||||
schedule("afterRender", () => {
|
|
||||||
if (moment(date, this.dateFormat).isValid()) {
|
|
||||||
this._picker.setDate(moment.utc(date), true);
|
|
||||||
} else {
|
|
||||||
this._picker.setDate(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_closeModal() {
|
_closeModal() {
|
||||||
|
@ -66,38 +66,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="picker-panel">
|
<div class="picker-panel">
|
||||||
<Input class="fake-input" />
|
<CalendarDateTimeInput
|
||||||
<div class="date-picker" id="picker-container-{{this.elementId}}"></div>
|
@datePickerId="local-date-create-form"
|
||||||
|
@date={{this.selectedDate}}
|
||||||
{{#if this.fromSelected}}
|
@time={{this.selectedTime}}
|
||||||
<div class="time-pickers">
|
@minDate={{this.minDate}}
|
||||||
{{d-icon "far-clock"}}
|
@timeFormat={{this.timeFormat}}
|
||||||
<Input
|
@dateFormat={{this.dateFormat}}
|
||||||
maxlength={{5}}
|
@onChangeDate={{action this.changeSelectedDate}}
|
||||||
placeholder="hh:mm"
|
@onChangeTime={{action this.changeSelectedTime}}
|
||||||
@type="time"
|
/>
|
||||||
@value={{this.time}}
|
|
||||||
class="time-picker"
|
|
||||||
{{on "input" (action "setTime")}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.toSelected}}
|
|
||||||
{{#if this.toDate}}
|
|
||||||
<div class="time-pickers">
|
|
||||||
{{d-icon "far-clock"}}
|
|
||||||
<Input
|
|
||||||
maxlength={{5}}
|
|
||||||
placeholder="hh:mm"
|
|
||||||
@type="time"
|
|
||||||
@value={{this.toTime}}
|
|
||||||
class="time-picker"
|
|
||||||
{{on "input" (action "setToTime")}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if this.site.mobileView}}
|
{{#if this.site.mobileView}}
|
||||||
@ -210,7 +188,7 @@
|
|||||||
|
|
||||||
<DButton
|
<DButton
|
||||||
@class="btn-default advanced-mode-btn"
|
@class="btn-default advanced-mode-btn"
|
||||||
@action={{action "advancedMode"}}
|
@action={{action "toggleAdvancedMode"}}
|
||||||
@icon="cog"
|
@icon="cog"
|
||||||
@label={{this.toggleModeBtnLabel}}
|
@label={{this.toggleModeBtnLabel}}
|
||||||
/>
|
/>
|
||||||
|
@ -70,25 +70,6 @@ div[data-tippy-root] {
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
|
|
||||||
.picker-panel {
|
|
||||||
padding: 5px;
|
|
||||||
border: 1px solid var(--primary-low);
|
|
||||||
}
|
|
||||||
|
|
||||||
.date-picker {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: auto;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
.pika-single {
|
|
||||||
position: relative !important;
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form {
|
.form {
|
||||||
flex: 1 0 0px;
|
flex: 1 0 0px;
|
||||||
|
|
||||||
@ -210,37 +191,6 @@ div[data-tippy-root] {
|
|||||||
.inputs-panel {
|
.inputs-panel {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.picker-panel {
|
|
||||||
z-index: 1;
|
|
||||||
background: var(--secondary);
|
|
||||||
width: 200px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
margin-left: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time-pickers {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
flex: 1;
|
|
||||||
margin-top: 1em;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0.25em;
|
|
||||||
border-top: 1px solid var(--primary-low-mid);
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
.d-icon {
|
|
||||||
color: var(--primary-medium);
|
|
||||||
margin-right: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time-picker {
|
|
||||||
box-shadow: none;
|
|
||||||
margin: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview {
|
.preview {
|
||||||
@ -318,17 +268,17 @@ html.mobile-view {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.picker-panel {
|
.calendar-date-time-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0 0 1em 0;
|
margin: 0 0 1em 0;
|
||||||
|
|
||||||
.pika-single {
|
.pika-single {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.time-picker {
|
.time-picker {
|
||||||
padding-top: 6px;
|
padding-top: 6px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,10 @@ describe "Local dates", type: :system do
|
|||||||
fab!(:topic) { Fabricate(:topic) }
|
fab!(:topic) { Fabricate(:topic) }
|
||||||
fab!(:current_user) { Fabricate(:user) }
|
fab!(:current_user) { Fabricate(:user) }
|
||||||
let(:year) { Time.zone.now.year + 1 }
|
let(:year) { Time.zone.now.year + 1 }
|
||||||
|
let(:month) { Time.zone.now.month }
|
||||||
let(:bookmark_modal) { PageObjects::Modals::Bookmark.new }
|
let(:bookmark_modal) { PageObjects::Modals::Bookmark.new }
|
||||||
|
let(:composer) { PageObjects::Components::Composer.new }
|
||||||
|
let(:insert_datetime_modal) { PageObjects::Modals::InsertDateTime.new }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
create_post(user: current_user, topic: topic, title: "Date range test post", raw: <<~RAW)
|
create_post(user: current_user, topic: topic, title: "Date range test post", raw: <<~RAW)
|
||||||
@ -69,6 +72,85 @@ describe "Local dates", type: :system do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "insert modal" do
|
||||||
|
let(:timezone) { "Australia/Brisbane" }
|
||||||
|
|
||||||
|
before do
|
||||||
|
current_user.user_option.update!(timezone: timezone)
|
||||||
|
sign_in(current_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "allows selecting a date without a time and inserts into the post" do
|
||||||
|
topic_page.visit_topic_and_open_composer(topic)
|
||||||
|
expect(topic_page).to have_expanded_composer
|
||||||
|
composer.click_toolbar_button("local-dates")
|
||||||
|
expect(insert_datetime_modal).to be_open
|
||||||
|
insert_datetime_modal.calendar_date_time_picker.select_year(year)
|
||||||
|
insert_datetime_modal.calendar_date_time_picker.select_day(16)
|
||||||
|
insert_datetime_modal.click_primary_button
|
||||||
|
expect(composer.composer_input.value).to have_content(
|
||||||
|
"[date=#{Date.parse("#{year}-#{month}-16").strftime("%Y-%m-%d")} timezone=\"#{timezone}\"]",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "allows selecting a date with a time and inserts into the post" do
|
||||||
|
topic_page.visit_topic_and_open_composer(topic)
|
||||||
|
expect(topic_page).to have_expanded_composer
|
||||||
|
composer.click_toolbar_button("local-dates")
|
||||||
|
expect(insert_datetime_modal).to be_open
|
||||||
|
insert_datetime_modal.calendar_date_time_picker.select_year(year)
|
||||||
|
insert_datetime_modal.calendar_date_time_picker.select_day(16)
|
||||||
|
insert_datetime_modal.calendar_date_time_picker.fill_time("11:45am")
|
||||||
|
insert_datetime_modal.click_primary_button
|
||||||
|
|
||||||
|
expect(composer.composer_input.value).to have_content(
|
||||||
|
"[date=#{Date.parse("#{year}-#{month}-16").strftime("%Y-%m-%d")} time=11:45:00 timezone=\"#{timezone}\"]",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "allows selecting a start date and time and an end date and time" do
|
||||||
|
topic_page.visit_topic_and_open_composer(topic)
|
||||||
|
expect(topic_page).to have_expanded_composer
|
||||||
|
composer.click_toolbar_button("local-dates")
|
||||||
|
expect(insert_datetime_modal).to be_open
|
||||||
|
insert_datetime_modal.calendar_date_time_picker.select_year(year)
|
||||||
|
insert_datetime_modal.calendar_date_time_picker.select_day(16)
|
||||||
|
insert_datetime_modal.calendar_date_time_picker.fill_time("11:45am")
|
||||||
|
insert_datetime_modal.select_to
|
||||||
|
|
||||||
|
insert_datetime_modal.calendar_date_time_picker.select_year(year)
|
||||||
|
insert_datetime_modal.calendar_date_time_picker.select_day(23)
|
||||||
|
insert_datetime_modal.calendar_date_time_picker.fill_time("12:45pm")
|
||||||
|
|
||||||
|
insert_datetime_modal.click_primary_button
|
||||||
|
expect(composer.composer_input.value).to have_content(
|
||||||
|
"[date-range from=#{Date.parse("#{year}-#{month}-16").strftime("%Y-%m-%d")}T11:45:00 to=#{Date.parse("#{year}-#{month}-23").strftime("%Y-%m-%d")}T12:45:00 timezone=\"#{timezone}\"]",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "allows clearing the end date and time" do
|
||||||
|
topic_page.visit_topic_and_open_composer(topic)
|
||||||
|
expect(topic_page).to have_expanded_composer
|
||||||
|
composer.click_toolbar_button("local-dates")
|
||||||
|
expect(insert_datetime_modal).to be_open
|
||||||
|
|
||||||
|
insert_datetime_modal.calendar_date_time_picker.select_year(year)
|
||||||
|
insert_datetime_modal.calendar_date_time_picker.select_day(16)
|
||||||
|
insert_datetime_modal.calendar_date_time_picker.fill_time("11:45am")
|
||||||
|
insert_datetime_modal.select_to
|
||||||
|
|
||||||
|
insert_datetime_modal.calendar_date_time_picker.select_year(year)
|
||||||
|
insert_datetime_modal.calendar_date_time_picker.select_day(23)
|
||||||
|
insert_datetime_modal.calendar_date_time_picker.fill_time("12:45pm")
|
||||||
|
insert_datetime_modal.delete_to
|
||||||
|
|
||||||
|
insert_datetime_modal.click_primary_button
|
||||||
|
expect(composer.composer_input.value).to have_content(
|
||||||
|
"[date=#{Date.parse("#{year}-#{month}-16").strftime("%Y-%m-%d")} time=11:45:00 timezone=\"#{timezone}\"]",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "bookmarks" do
|
describe "bookmarks" do
|
||||||
before do
|
before do
|
||||||
current_user.user_option.update!(timezone: "Asia/Singapore")
|
current_user.user_option.update!(timezone: "Asia/Singapore")
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module PageObjects
|
||||||
|
module Modals
|
||||||
|
class InsertDateTime < PageObjects::Modals::Base
|
||||||
|
MODAL_CSS_CLASS = ".discourse-local-dates-create-modal"
|
||||||
|
|
||||||
|
def calendar_date_time_picker
|
||||||
|
@calendar_date_time_picker ||=
|
||||||
|
PageObjects::Components::CalendarDateTimePicker.new(MODAL_CSS_CLASS)
|
||||||
|
end
|
||||||
|
|
||||||
|
def select_to
|
||||||
|
find(".date-time-control.to").click
|
||||||
|
end
|
||||||
|
|
||||||
|
def select_from
|
||||||
|
find(".date-time-control.from").click
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_to
|
||||||
|
find(".delete-to-date").click
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -130,7 +130,7 @@ acceptance("Local Dates - composer", function (needs) {
|
|||||||
await click(".delete-to-date");
|
await click(".delete-to-date");
|
||||||
|
|
||||||
assert.notOk(
|
assert.notOk(
|
||||||
query(".pika-table .is-selected"),
|
query(".date-time-control.to.is-selected"),
|
||||||
"deleting selected TO date works"
|
"deleting selected TO date works"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -24,4 +24,6 @@
|
|||||||
|
|
||||||
<StyleguideExample @title="<DatePicker>">
|
<StyleguideExample @title="<DatePicker>">
|
||||||
<DatePicker @defaultDate="YYYY-MM-DD" />
|
<DatePicker @defaultDate="YYYY-MM-DD" />
|
||||||
</StyleguideExample>
|
</StyleguideExample>
|
||||||
|
|
||||||
|
<Styleguide::CalendarDateTimeInput />
|
@ -0,0 +1,34 @@
|
|||||||
|
<StyleguideExample @title="<CalendarDateTimeInput>">
|
||||||
|
<Styleguide::Component>
|
||||||
|
<CalendarDateTimeInput
|
||||||
|
@datePickerId="styleguide"
|
||||||
|
@date={{this.date}}
|
||||||
|
@time={{this.time}}
|
||||||
|
@minDate={{this.minDate}}
|
||||||
|
@timeFormat={{this.timeFormat}}
|
||||||
|
@dateFormat={{this.dateFormat}}
|
||||||
|
@onChangeDate={{action this.changeDate}}
|
||||||
|
@onChangeTime={{action this.changeTime}}
|
||||||
|
/>
|
||||||
|
</Styleguide::Component>
|
||||||
|
|
||||||
|
<Styleguide::Controls>
|
||||||
|
<Styleguide::Controls::Row @name="Min date">
|
||||||
|
<DatePicker @defaultDate="YYYY-MM-DD" @value={{this.minDate}} />
|
||||||
|
</Styleguide::Controls::Row>
|
||||||
|
|
||||||
|
<Styleguide::Controls::Row @name="Date">
|
||||||
|
<DatePicker @defaultDate="YYYY-MM-DD" @value={{this.date}} />
|
||||||
|
</Styleguide::Controls::Row>
|
||||||
|
|
||||||
|
<Styleguide::Controls::Row @name="Time">
|
||||||
|
<Input
|
||||||
|
maxlength={{5}}
|
||||||
|
placeholder="hh:mm"
|
||||||
|
@type="time"
|
||||||
|
@value={{this.time}}
|
||||||
|
class="time-picker"
|
||||||
|
/>
|
||||||
|
</Styleguide::Controls::Row>
|
||||||
|
</Styleguide::Controls>
|
||||||
|
</StyleguideExample>
|
@ -0,0 +1,24 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
|
||||||
|
export default class StyleguideCalendarDateTimeInput extends Component {
|
||||||
|
@service currentUser;
|
||||||
|
|
||||||
|
@tracked dateFormat = "YYYY-MM-DD";
|
||||||
|
@tracked timeFormat = "HH:mm:ss";
|
||||||
|
@tracked date = null;
|
||||||
|
@tracked time = null;
|
||||||
|
@tracked minDate = null;
|
||||||
|
|
||||||
|
@action
|
||||||
|
changeDate(date) {
|
||||||
|
this.date = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
changeTime(time) {
|
||||||
|
this.time = time;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module PageObjects
|
||||||
|
module Components
|
||||||
|
class CalendarDateTimePicker < PageObjects::Components::Base
|
||||||
|
def initialize(context)
|
||||||
|
@context = context
|
||||||
|
end
|
||||||
|
|
||||||
|
def component
|
||||||
|
find(@context)
|
||||||
|
end
|
||||||
|
|
||||||
|
def select_day(day_number)
|
||||||
|
component.find("button.pika-button.pika-day[data-pika-day='#{day_number}']").click
|
||||||
|
end
|
||||||
|
|
||||||
|
def select_year(year)
|
||||||
|
component
|
||||||
|
.find(".pika-select-year", visible: false)
|
||||||
|
.find("option[value='#{year}']")
|
||||||
|
.select_option
|
||||||
|
end
|
||||||
|
|
||||||
|
def fill_time(time)
|
||||||
|
component.find(".time-picker").fill_in(with: time)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user