Skip to content

Commit

Permalink
Extract monthNavigator custom element
Browse files Browse the repository at this point in the history
  • Loading branch information
florimondmanca committed Dec 19, 2023
1 parent ef5a35a commit 605413d
Show file tree
Hide file tree
Showing 11 changed files with 221 additions and 153 deletions.
6 changes: 3 additions & 3 deletions e2e/faircalendar.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ test.describe('authenticated', () => {
test.use({ storageState: 'playwright/.auth/johnDoe.json' });

test('basic content', async ({ page }) => {
await page.goto('/app/faircalendar?year=2023&month=10'); // month=0..11
await page.goto('/app/faircalendar?year=2023&month=11'); // month=1..12
await expect(page).toHaveTitle('FairCalendar novembre 2023 - Permacoop');

const calendar = page.getByTestId('pc-event-calendar');
Expand All @@ -21,7 +21,7 @@ test.describe('authenticated', () => {
});

test('go to add event', async ({ page }) => {
await page.goto('/app/faircalendar?year=2023&month=10');
await page.goto('/app/faircalendar?year=2023&month=11');

const calendar = page.getByTestId('pc-event-calendar');

Expand All @@ -36,7 +36,7 @@ test.describe('authenticated', () => {
});

test('back button behavior', async ({ page }) => {
await page.goto('/app/faircalendar?year=2023&month=10');
await page.goto('/app/faircalendar?year=2023&month=11');

const calendar = page.getByTestId('pc-event-calendar');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,11 @@ export class FairCalendarController {
@Query() dto: FairCalendarControllerDTO,
@LoggedUser() user: User
) {
const date =
dto.month !== undefined && dto.year !== undefined
? new Date(dto.year, dto.month, 15)
: new Date();
let date = new Date();

if (dto.year !== undefined && dto.month !== undefined) {
date = new Date(dto.year, dto.month - 1, 15);
}

const userId = dto.userId ? dto.userId : user['id'];

Expand Down Expand Up @@ -101,7 +102,7 @@ export class FairCalendarController {
overviewTable,
fullCalendarEvents,
date,
currentMonth: date.getMonth(),
currentMonth: date.getMonth() + 1,
currentYear: date.getFullYear(),
userId
};
Expand Down
77 changes: 0 additions & 77 deletions src/assets/customElements/fairCalendarFiltersForm.js

This file was deleted.

4 changes: 2 additions & 2 deletions src/assets/customElements/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
import autoForm from './autoForm';
import clipboardButton from './clipboardButton';
import eventForm from './eventForm';
import fairCalendarFiltersForm from './fairCalendarFiltersForm';
import monthNavigator from './monthNavigator';
import navMenuButton from './navMenuButton';
import themeToggler from './themeToggler';

customElements.define('pc-auto-form', autoForm);
customElements.define('pc-clipboard-button', clipboardButton);
customElements.define('pc-event-form', eventForm);
customElements.define('pc-faircalendar-filters-form', fairCalendarFiltersForm);
customElements.define('pc-month-navigator', monthNavigator);
customElements.define('pc-nav-menu-button', navMenuButton);
customElements.define('pc-theme-toggler', themeToggler);
85 changes: 85 additions & 0 deletions src/assets/customElements/monthNavigator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// @ts-check
import { onParsed, waitForElement } from '../lib/customElements';

export default class extends HTMLElement {
/** @type {HTMLInputElement} */
#month;

/** @type {HTMLInputElement} */
#year;

connectedCallback() {
onParsed(() => {
const parent = /** @type {HTMLElement} */ (this.parentElement);

const findFields = /** @type {Promise<[HTMLInputElement, HTMLInputElement]>} */ (Promise.all(
[
waitForElement(this.dataset.monthTarget || '', parent, {
timeout: 3000
}),
waitForElement(this.dataset.yearTarget || '', parent, {
timeout: 3000
})
]
));

findFields.then(([month, year]) => {
// Assume month is 1 (January) to 12 (December)
this.#month = month;
this.#year = year;

const template = /** @type {HTMLTemplateElement} */ (this.querySelector(
'template'
));

this.appendChild(document.importNode(template.content, true));

const previousBtn = /** @type {HTMLButtonElement} */ (this.querySelector(
'button[data-previous]'
));

const todayBtn = /** @type {HTMLButtonElement} */ (this.querySelector(
'button[data-today]'
));

const nextBtn = /** @type {HTMLButtonElement} */ (this.querySelector(
'button[data-next]'
));

previousBtn.addEventListener('click', () => {
this._updateMonth(+month.value - 1);
});

todayBtn.addEventListener('click', () => {
const today = new Date();
month.value = (today.getMonth() + 1).toString();
year.value = today.getFullYear().toString();
// Trigger change on either field, but only once
year.dispatchEvent(new Event('change'));
});

nextBtn.addEventListener('click', () => {
this._updateMonth(+month.value + 1);
});
});
});
}

/**
* @param {number} month
*/
_updateMonth(month) {
if (month < 1) {
month += 12;
this.#year.value = (+this.#year.value - 1).toString();
}

if (month > 12) {
month -= 12;
this.#year.value = (+this.#year.value + 1).toString();
}

this.#month.value = month.toString();
this.#month.dispatchEvent(new Event('change'));
}
}
36 changes: 36 additions & 0 deletions src/assets/lib/customElements.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,39 @@
export function onParsed(cb) {
requestAnimationFrame(cb);
}

/**
* Wait until an element appears in the DOM.
* Credit: https://stackoverflow.com/a/61511955
* @param {string} selector
* @param {Element} root
* @param {{timeout?: number}} options
* @returns {Promise<Element>}
*/
export function waitForElement(selector, root, { timeout } = {}) {
return new Promise(resolve => {
let timeoutHandle;

if (timeout) {
timeoutHandle = setTimeout(
() => reject(`failed to find ${selector} after ${timeout} ms`),
timeout
);
}

if (document.querySelector(selector)) {
clearTimeout(timeoutHandle);
return resolve(document.querySelector(selector));
}

const observer = new MutationObserver(_mutations => {
if (document.querySelector(selector)) {
observer.disconnect();
clearTimeout(timeoutHandle);
resolve(document.querySelector(selector));
}
});

observer.observe(root, { childList: true, subtree: true });
});
}
20 changes: 20 additions & 0 deletions src/templates/macros/month_navigator.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{% import 'macros/icons.njk' as icons %}

{% macro month_navigator(monthTarget, yearTarget) %}
<pc-month-navigator data-month-target="{{ monthTarget }}" data-year-target="{{ yearTarget }}">
<template>
<div class="pc-cluster" style="--cluster-align: stretch">
{# TODO: translations #}
<button type="button" data-previous class="pc-btn pc-btn--muted" title="Mois précédent">
{{ '<' }}
</button>
<button type="button" data-today class="pc-btn pc-btn--muted" title="Aujourd'hui">
{{ icons.calendar() }}
</button>
<button type="button" data-next class="pc-btn pc-btn--muted" title="Mois suivant">
{{ '>' }}
</button>
</div>
</template>
</pc-month-navigator>
{% endmacro %}
78 changes: 37 additions & 41 deletions src/templates/pages/faircalendar/_filters_form.njk
Original file line number Diff line number Diff line change
@@ -1,44 +1,40 @@
{% from 'macros/month_navigator.njk' import month_navigator %}

{% macro filters_form() %}
<pc-faircalendar-filters-form>
<form
method="GET"
action="{{ path('faircalendar_index') }}"
class="pc-cluster pc-gap"
style="--cluster-align: flex-end"
>
<template id="faircalendar-filters-form-navigation">
<div class="pc-cluster" style="--cluster-align: stretch">
<button id="previousBtn" type="button" class="pc-btn pc-btn--muted" title="Mois précédent">{{ '<' }}</button>
<button id="todayBtn" type="button" class="pc-btn pc-btn--muted" title="Aujourd'hui">
{{ icons.calendar() }}
</button>
<button id="nextBtn" type="button" class="pc-btn pc-btn--muted" title="Mois suivant">{{ '>' }}</button>
</div>
</template>
<div class="pc-cluster pc-gap" style="--cluster-align: flex-end">
{{ month_navigator(monthTarget='#month', yearTarget='#year') }}

<div class="pc-select-group pc-m" style="--m: 0">
<label class="pc-label" for="month">{{ 'faircalendar-filters-month-title'|trans }}</label>
<select name="month" id="month">
{% for month in range(12) %}
<option value="{{ month }}" {% if month == currentMonth %}selected{% endif %}>{{ month|longMonth|capitalize }}</option>
{% endfor %}
</select>
</div>
<div class="pc-input-group pc-m" style="--m: 0">
<label class="pc-label" for="year">{{ 'faircalendar-filters-year-title'|trans }}</label>
<input name="year" id="year" type="number" value="{{ currentYear }}">
</div>
<div class="pc-select-group pc-m" style="--m: 0">
<label class="pc-label" for="userId">{{ 'faircalendar-filters-userId-title'|trans }}</label>
<select name="userId" id="userId">
{% for user in users %}
<option value="{{ user.id }}" {% if user.id == userId %}selected{% endif %}>{{ user|fullName }}</option>
{% endfor %}
</select>
</div>
<button type="submit" class="pc-btn pc-btn--secondary">
{{ 'common-form-update'|trans }}
</button>
</form>
</pc-faircalendar-filters-form>
<pc-auto-form>
<form
method="GET"
action="{{ path('faircalendar_index') }}"
class="pc-cluster pc-gap"
style="--cluster-align: flex-end"
>
<div class="pc-select-group pc-m" style="--m: 0">
<label class="pc-label" for="month">{{ 'faircalendar-filters-month-title'|trans }}</label>
<select name="month" id="month">
{% for month in range(1, 13) %}
<option value="{{ month }}" {% if month == currentMonth %}selected{% endif %}>{{ (month - 1)|longMonth|capitalize }}</option>
{% endfor %}
</select>
</div>
<div class="pc-input-group pc-m" style="--m: 0">
<label class="pc-label" for="year">{{ 'faircalendar-filters-year-title'|trans }}</label>
<input name="year" id="year" type="number" value="{{ currentYear }}">
</div>
<div class="pc-select-group pc-m" style="--m: 0">
<label class="pc-label" for="userId">{{ 'faircalendar-filters-userId-title'|trans }}</label>
<select name="userId" id="userId">
{% for user in users %}
<option value="{{ user.id }}" {% if user.id == userId %}selected{% endif %}>{{ user|fullName }}</option>
{% endfor %}
</select>
</div>
<button type="submit" class="pc-btn pc-btn--secondary">
{{ 'common-form-update'|trans }}
</button>
</form>
</pc-auto-form>
</div>
{% endmacro %}
Loading

0 comments on commit 605413d

Please sign in to comment.