Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

replaces ember-render-modifiers with custom modifier in DatePicker component #8312

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions packages/ilios-common/.lint-todo
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
add|ember-template-lint|no-at-ember-render-modifiers|5|2|5|2|fb8a149d14413d4dfc84ffd31349ef3f2ac6d17b|1731542400000|1762646400000|1793750400000|addon/components/date-picker.hbs
add|ember-template-lint|no-at-ember-render-modifiers|6|2|6|2|993f1e23f796f19a221eae6e24872755e0436cb4|1731542400000|1762646400000|1793750400000|addon/components/date-picker.hbs
add|ember-template-lint|no-at-ember-render-modifiers|6|8|6|8|c628ee621a6e921e369bf6bcb158a5ef932e6741|1731542400000|1762646400000|1793750400000|addon/components/editable-field.hbs
add|ember-template-lint|no-at-ember-render-modifiers|2|2|2|2|ad17d66e0fe1720bc8ddedc12dff3a105709765c|1731542400000|1762646400000|1793750400000|addon/components/html-editor.hbs
add|ember-template-lint|no-at-ember-render-modifiers|3|2|3|2|d39abab22a3e75d93f69335da422e7ef73b36283|1731542400000|1762646400000|1793750400000|addon/components/html-editor.hbs
Expand Down
5 changes: 2 additions & 3 deletions packages/ilios-common/addon/components/date-picker.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
aria-label={{t "general.pickADate"}}
class="date-picker"
data-test-date-picker
{{did-insert (perform this.setupPicker)}}
{{did-update (perform this.updatePicker) @value @maxDate @minDate}}
{{date-picker @value minDate=@minDate maxDate=@maxDate locale=this.intl.primaryLocale onChangeHandler=@onChange}}
...attributes
/>
/>
66 changes: 0 additions & 66 deletions packages/ilios-common/addon/components/date-picker.js
Original file line number Diff line number Diff line change
@@ -1,72 +1,6 @@
import Component from '@glimmer/component';
import { service } from '@ember/service';
import { dropTask, restartableTask, waitForProperty } from 'ember-concurrency';
import { tracked } from '@glimmer/tracking';
import flatpickr from 'flatpickr';
import { later, next } from '@ember/runloop';
import { isTesting } from '@embroider/macros';

export default class DatePickerComponent extends Component {
@service intl;

@tracked _flatPickerInstance;
@tracked isOpen = false;

updatePicker = restartableTask(async (element, [value]) => {
await waitForProperty(this, '_flatPickerInstance');
if (this._flatPickerInstance.selectedDates[0] != value) {
this._flatPickerInstance.setDate(value);
}
});

setupPicker = dropTask(async (element) => {
const currentLocale = this.intl.primaryLocale;
let locale;
switch (currentLocale) {
case 'fr':
// eslint-disable-next-line no-case-declarations
const { French } = await import('flatpickr/dist/l10n/fr.js');
locale = French;
break;
case 'es':
// eslint-disable-next-line no-case-declarations
const { Spanish } = await import('flatpickr/dist/l10n/es.js');
locale = Spanish;
break;
default:
locale = 'en';
}
this._flatPickerInstance = flatpickr(element, {
locale,
defaultDate: this.args.value,
formatDate: (dateObj) =>
this.intl.formatDate(dateObj, { day: '2-digit', month: '2-digit', year: 'numeric' }),
onChange: (selectedDates) => this.onChange(selectedDates[0]),
onOpen: () => {
// eslint-disable-next-line ember/no-runloop
later(() => {
this.isOpen = true;
}, 250);
},
onClose: () => {
this.isOpen = false;
},
maxDate: this.args.maxDate ?? null,
minDate: this.args.minDate ?? null,
disableMobile: isTesting(),
});
});

willDestroy() {
super.willDestroy(...arguments);
if (this._flatPickerInstance) {
this._flatPickerInstance.destroy();
}
}

async onChange(date) {
await this.args.onChange(date);
// eslint-disable-next-line ember/no-runloop
await next(() => {});
}
}
83 changes: 83 additions & 0 deletions packages/ilios-common/addon/modifiers/date-picker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import Modifier from 'ember-modifier';
import { registerDestructor } from '@ember/destroyable';
import flatpickr from 'flatpickr';
import { French } from 'flatpickr/dist/l10n/fr.js';
import { Spanish } from 'flatpickr/dist/l10n/es.js';
import { isTesting } from '@embroider/macros';
import { service } from '@ember/service';

export default class DatePickerModifier extends Modifier {
@service intl;
flatpickr = null;
locale = null;
onChangeHandler = null;

constructor(owner, args) {
super(owner, args);
registerDestructor(this, () => {
this.locale = null;
this.onChangeHandler = null;
if (this.flatpickr) {
this.flatpickr.destroy();
this.flatpickr = null;
}
});
}

modify(element, [value], { minDate, maxDate, locale, onChangeHandler }) {
// We only need to set this once.
if (!this.onChangeHandler) {
this.onChangeHandler = onChangeHandler;
}
if (!this.flatpickr) {
this.locale = locale ?? this.intl.primaryLocale;
this.flatpickr = this.initPicker(element, value, minDate, maxDate, this.locale);
}

if (this.flatpickr.selectedDates[0] !== value) {
this.flatpickr.setDate(value);
}
if (this.flatpickr.minDate !== minDate) {
this.flatpickr.set('minDate', minDate);
}
if (this.flatpickr.maxDate !== maxDate) {
this.flatpickr.set('maxDate', maxDate);
}

if (locale && this.locale !== locale) {
this.locale = locale;
this.flatpickr.set('locale', this.getFlatpickrLocale(locale));
}
}

// @see https://flatpickr.js.org/localization/
getFlatpickrLocale(localeIdentifier) {
switch (localeIdentifier) {
case 'fr':
return French;
case 'es':
return Spanish;
default:
return 'en';
}
}

initPicker(element, value, minDate, maxDate, locale) {
return flatpickr(element, {
locale: this.getFlatpickrLocale(locale),
defaultDate: value,
formatDate: (dateObj) =>
this.intl.formatDate(dateObj, { day: '2-digit', month: '2-digit', year: 'numeric' }),
onChange: (selectedDates) => this.onChange(selectedDates[0]),
maxDate: maxDate ?? null,
minDate: minDate ?? null,
disableMobile: isTesting(),
});
}

async onChange(date) {
if (this.onChangeHandler) {
await this.onChangeHandler(date);
}
}
}
1 change: 1 addition & 0 deletions packages/ilios-common/app/modifiers/date-picker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'ilios-common/modifiers/date-picker';
111 changes: 111 additions & 0 deletions packages/test-app/tests/integration/modifiers/date-picker-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'test-app/tests/helpers';
import { render, find } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';

module('Integration | Modifier | date-picker', function (hooks) {
setupRenderingTest(hooks);

test('it works with minimal input', async function (assert) {
const value = new Date('2014-03-02');
this.set('value', value);
await render(hbs`<div data-test-picker-element {{date-picker this.value}}></div>`);
const flatpickr = find('[data-test-picker-element]')._flatpickr;
assert.strictEqual(value.getTime(), flatpickr.selectedDates[0].getTime());
assert.notOk(flatpickr.config.minDate);
assert.notOk(flatpickr.config.maxDate);
assert.dom('option:nth-of-type(1)', flatpickr.monthElements[0]).hasText('January');
});

test('it works with minDate, maxDate, and locale as input', async function (assert) {
const value = new Date('2014-03-02');
const minDate = new Date('2014-01-12');
const maxDate = new Date('2014-05-11');
const locale = 'es';
this.set('value', value);
this.set('minDate', minDate);
this.set('maxDate', maxDate);
this.set('locale', locale);
await render(
hbs`<div
data-test-picker-element
{{date-picker this.value minDate=this.minDate maxDate=this.maxDate locale=this.locale}}
></div>`,
);
const flatpickr = find('[data-test-picker-element]')._flatpickr;
assert.strictEqual(value.getTime(), flatpickr.selectedDates[0].getTime());
assert.strictEqual(minDate.getTime(), flatpickr.config.minDate.getTime());
assert.strictEqual(maxDate.getTime(), flatpickr.config.maxDate.getTime());
assert.dom('option:nth-of-type(1)', flatpickr.monthElements[0]).hasText('Enero');
});

test('changing value is responsive', async function (assert) {
const value = new Date('2014-03-02');
this.set('value', value);
await render(hbs`<div data-test-picker-element {{date-picker this.value}}></div>`);
const flatpickr = find('[data-test-picker-element]')._flatpickr;
assert.strictEqual(value.getTime(), flatpickr.selectedDates[0].getTime());
const newValue = new Date('2024-03-03');
this.set('value', newValue);
assert.strictEqual(newValue.getTime(), flatpickr.selectedDates[0].getTime());
});

test('changing minDate is responsive', async function (assert) {
const value = new Date('2014-03-02');
const minDate = new Date('2014-01-12');
this.set('value', value);
this.set('minDate', minDate);
await render(
hbs`<div data-test-picker-element {{date-picker this.value minDate=this.minDate}}></div>`,
);
const flatpickr = find('[data-test-picker-element]')._flatpickr;
assert.strictEqual(minDate.getTime(), flatpickr.config.minDate.getTime());
const newMinDate = new Date('2024-03-03');
this.set('minDate', newMinDate);
assert.strictEqual(newMinDate.getTime(), flatpickr.config.minDate.getTime());
});

test('changing maxDate is responsive', async function (assert) {
const value = new Date('2014-03-02');
const maxDate = new Date('2014-01-12');
this.set('value', value);
this.set('maxDate', maxDate);
await render(
hbs`<div data-test-picker-element {{date-picker this.value maxDate=this.maxDate}}></div>`,
);
const flatpickr = find('[data-test-picker-element]')._flatpickr;
assert.strictEqual(maxDate.getTime(), flatpickr.config.maxDate.getTime());
const newMaxDate = new Date('2024-03-03');
this.set('maxDate', newMaxDate);
assert.strictEqual(newMaxDate.getTime(), flatpickr.config.maxDate.getTime());
});

test('changing locale is responsive', async function (assert) {
const value = new Date('2014-03-02');
const locale = 'es';
this.set('value', value);
this.set('locale', locale);
await render(
hbs`<div data-test-picker-element {{date-picker this.value locale=this.locale}}></div>`,
);
const flatpickr = find('[data-test-picker-element]')._flatpickr;
assert.dom('option:nth-of-type(1)', flatpickr.monthElements[0]).hasText('Enero');
this.set('locale', 'fr');
assert.dom('option:nth-of-type(1)', flatpickr.monthElements[0]).hasText('janvier');
});

test('onChange callback fires', async function (assert) {
assert.expect(1);
const value = new Date('2014-03-02');
const newValue = new Date('2022-06-23');
this.set('value', value);
this.set('onChange', (date) => {
assert.strictEqual(newValue.getTime(), date.getTime());
});
await render(
hbs`<div data-test-picker-element {{date-picker this.value onChangeHandler=this.onChange}}></div>`,
);
const flatpickr = find('[data-test-picker-element]')._flatpickr;
flatpickr.setDate(newValue, true);
});
});
Loading