Skip to content

Commit d036054

Browse files
committed
feat: add special birthday message in popover
- Show "É hoje!" / "It's today!" title when it's the birthday (0 days left) - Display "🎈🎂 Parabéns para o Léo! 🎂🎈" message on birthday - Keep default message "Quanto falta?" / "How many days?" when days > 0 - Support URL parameters (d1=YYYY-MM-DD&d2=YYYY-MM-DD) to simulate dates - Fix daysUntilNextBirthdayGivenNow to return 0 on birthday instead of 365 - Add E2E tests to validate birthday popover behavior in both languages
1 parent d3cbdd6 commit d036054

File tree

3 files changed

+94
-10
lines changed

3 files changed

+94
-10
lines changed

src/js/main.js

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
11
import dayjs from 'dayjs';
22
import * as bootstrap from 'bootstrap';
3-
import { daysUntilNextBirthday } from './modules/days.js';
3+
import { daysUntilNextBirthdayGivenNow } from './modules/days.js';
44
import { ver } from './modules/version.js';
55

66
// Initialize when DOM is loaded
77
document.addEventListener('DOMContentLoaded', () => {
88
// Bootstrap modals are initialized automatically via data-attributes
99
// No manual initialization needed for aria-hidden compatibility
10-
const leoBirthday = new Date(document.getElementById('leo-birthday').dataset.leoBirthday);
11-
const leoDaysUntilNextBirthday = daysUntilNextBirthday(leoBirthday);
12-
const today = new Date();
10+
11+
// Check for URL parameters to simulate dates
12+
const params = new URLSearchParams(window.location.search);
13+
let leoBirthday = new Date(document.getElementById('leo-birthday').dataset.leoBirthday);
14+
let today = new Date();
15+
16+
// If d1 and d2 are provided in URL, use them for simulation
17+
if (params.has('d1') && params.has('d2')) {
18+
const d1 = params.get('d1');
19+
const d2 = params.get('d2');
20+
if (/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(d1) && /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(d2)) {
21+
leoBirthday = new Date(d1);
22+
today = new Date(d2);
23+
}
24+
}
25+
26+
const leoDaysUntilNextBirthday = daysUntilNextBirthdayGivenNow(leoBirthday, today);
1327

1428
// Update version display
1529
const v = ver(leoBirthday, today);
@@ -39,7 +53,8 @@ document.addEventListener('DOMContentLoaded', () => {
3953
};
4054

4155
// Create popover content
42-
const content = `
56+
let popoverTitle = messageDaysUntil[languageDatepicker].title;
57+
let popoverContent = `
4358
<div class="container">
4459
<div class="row text-center">
4560
<p class="fs-6">${messageDaysUntil[languageDatepicker].content[0]}</p>
@@ -50,13 +65,18 @@ document.addEventListener('DOMContentLoaded', () => {
5065
</div>
5166
`;
5267

68+
if (leoDaysUntilNextBirthday === 0) {
69+
popoverTitle = languageDatepicker === 'en' ? "It's today!" : "É hoje!";
70+
popoverContent = `<div class="container"><div class="row text-center"><p class="fs-4">🎈🎂 Parabéns para o Léo! 🎂🎈</p></div></div>`;
71+
}
72+
5373
// Initialize all popovers
5474
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]');
5575
[...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl, {
5676
trigger: 'focus',
5777
html: true,
58-
title: `<p class="text-center m-0">🎂🎈🎉${messageDaysUntil[languageDatepicker].title}🎂🎈🎉</p>`,
59-
content: content,
78+
title: `<p class="text-center m-0">🎂🎈🎉${popoverTitle}🎂🎈🎉</p>`,
79+
content: popoverContent,
6080
placement: 'bottom',
6181
delay: { show: 200, hide: 0 }
6282
}));
@@ -121,8 +141,7 @@ document.addEventListener('DOMContentLoaded', () => {
121141
dateEnd.value = dayjs().format('YYYY-MM-DD');
122142
}
123143

124-
// Handle URL parameters
125-
const params = new URLSearchParams(window.location.search);
144+
// Handle URL parameters (already parsed at the top)
126145
if (params.has('d1')) {
127146
const d1 = params.get('d1');
128147
if (/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(d1)) {

src/js/modules/days.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,13 @@ export function daysUntilNextBirthdayGivenNow(birthday, nowMoment) {
2323
const birthdayThisYear = dayjs(`${thisYear}-${b.format('MM')}-${b.format('DD')}`).startOf('day');
2424
const birthdayNextYear = birthdayThisYear.add(1, 'year').startOf('day');
2525

26-
const hadBirthdayThisYear = birthdayThisYear.isBefore(now) || birthdayThisYear.isSame(now);
26+
// If today is the birthday, return 0
27+
if (birthdayThisYear.isSame(now)) {
28+
return 0;
29+
}
30+
31+
// If birthday already happened this year, calculate days until next year
32+
const hadBirthdayThisYear = birthdayThisYear.isBefore(now);
2733
if (hadBirthdayThisYear) {
2834
return birthdayNextYear.diff(now, 'day');
2935
} else {

test/e2e/modal-birthday.spec.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
const leoBirthday = '2015-10-22';
4+
5+
test.describe('Modal de dias para o aniversário', () => {
6+
test('Exibe mensagem padrão quando faltam dias', async ({ page }) => {
7+
await page.goto(`/?d1=${leoBirthday}&d2=2025-10-21`);
8+
9+
// Abrir o modal primeiro
10+
await page.click('button[data-bs-toggle="modal"]');
11+
await page.waitForSelector('.modal.show', { state: 'visible' });
12+
13+
// Clicar no botão do popover dentro do modal
14+
await page.click('[data-bs-toggle="popover"]');
15+
16+
const popover = await page.locator('.popover');
17+
await expect(popover).toContainText('Quanto falta?');
18+
await expect(popover).toContainText('Faltam');
19+
await expect(popover).toContainText('dias');
20+
await expect(popover).toContainText('pro niver do Léo!');
21+
await expect(popover).not.toContainText('Parabéns para o Léo!');
22+
});
23+
24+
test('Exibe mensagem especial no dia do aniversário', async ({ page }) => {
25+
await page.goto(`/?d1=${leoBirthday}&d2=2025-10-22`);
26+
27+
// Abrir o modal primeiro
28+
await page.click('button[data-bs-toggle="modal"]');
29+
await page.waitForSelector('.modal.show', { state: 'visible' });
30+
31+
// Clicar no botão do popover dentro do modal
32+
await page.click('[data-bs-toggle="popover"]');
33+
34+
const popover = await page.locator('.popover');
35+
await expect(popover).toContainText('É hoje!');
36+
await expect(popover).toContainText('Parabéns para o Léo!');
37+
await expect(popover).not.toContainText('Faltam');
38+
await expect(popover).not.toContainText('dias');
39+
await expect(popover).not.toContainText('pro niver do Léo!');
40+
});
41+
42+
test('Exibe mensagem especial em inglês no dia do aniversário', async ({ page }) => {
43+
await page.goto(`/index_en.html?d1=${leoBirthday}&d2=2025-10-22`);
44+
45+
// Abrir o modal primeiro
46+
await page.click('button[data-bs-toggle="modal"]');
47+
await page.waitForSelector('.modal.show', { state: 'visible' });
48+
49+
// Clicar no botão do popover dentro do modal
50+
await page.click('[data-bs-toggle="popover"]');
51+
52+
const popover = await page.locator('.popover');
53+
await expect(popover).toContainText("It's today!");
54+
await expect(popover).toContainText("Parabéns para o Léo!");
55+
await expect(popover).not.toContainText('There are');
56+
await expect(popover).not.toContainText('days');
57+
await expect(popover).not.toContainText("until Léo's birthday!");
58+
});
59+
});

0 commit comments

Comments
 (0)