Improve calendar navigation UX with loading skeleton and optional sequential months#75
Conversation
…d example in README
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
WalkthroughThis patch release (v2.1.2) adds sequential month navigation for calendars with sparse events, refactors mobile detection to use viewport width instead of DOM inspection, introduces a skeleton loading UI for better perceived performance, and exposes a filter hook for day-events deduplication. All manifest files are updated, a new block attribute controls sequential month behavior with backend support, new skeleton CSS and template handle loading states, and JavaScript event handling is refined for API-driven calendar updates. Suggested reviewers
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/blocks/calendar/calendar.js (1)
204-231:⚠️ Potential issue | 🟠 Major | ⚡ Quick winGuard against out-of-order async responses during rapid navigation.
If users trigger multiple requests quickly, an older response can arrive last and overwrite newer calendar content. Add request sequencing (or abort previous requests) before calling
updateContent.💡 Suggested fix
constructor() { + this.lastRequestId = 0; this.DOM = { ... }; } sendCalendarRequest( date, calendarItem ) { + const requestId = ++this.lastRequestId; this.showLoading( calendarItem ); apiFetch( { path: '/simple-events/calendar', method: 'POST', data: { date, attributes, }, } ).then( ( result ) => { + if ( requestId !== this.lastRequestId ) { + return; + } if ( result.html ) { this.updateContent( calendarItem, result.html ); } else { console.log( result ); } } )🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/blocks/calendar/calendar.js` around lines 204 - 231, The sendCalendarRequest method can be overwritten by out-of-order async responses; add request sequencing by introducing a per-instance request counter (e.g., this._calendarRequestId) that you increment at the start of sendCalendarRequest, capture the incremented id in the promise chain, and only call updateContent, hideLoading, and initListeners if the captured id matches this._calendarRequestId; alternatively store an AbortController (e.g., this._calendarAbortController) and abort any prior request before calling apiFetch, then use the new controller.signal in apiFetch so only the latest response is applied — implement the chosen approach inside sendCalendarRequest around apiFetch and the promise callbacks to guard updateContent from stale responses.
🧹 Nitpick comments (1)
src/blocks/calendar/style.scss (1)
81-88: ⚡ Quick winRespect reduced-motion preference for shimmer animation.
Please disable the shimmer for users with
prefers-reduced-motion: reduceto avoid motion-triggered discomfort.♿ Suggested patch
`@keyframes` simple-events-skeleton-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } + +@media (prefers-reduced-motion: reduce) { + .simple-events-calendar-skeleton__top-bar > span, + .simple-events-calendar-skeleton__header > span, + .simple-events-calendar-skeleton__day { + animation: none; + } +}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/blocks/calendar/style.scss` around lines 81 - 88, The shimmer keyframes (`@keyframes` simple-events-skeleton-shimmer) must respect the user's reduced-motion preference: add a prefers-reduced-motion: reduce media query alongside the keyframes (or immediately after) and disable the shimmer animation there by setting the related skeleton element(s) to animation: none (or override animation-name/animation-duration) so the simple-events-skeleton no longer animates for users who opt into reduced motion. Ensure you target the same selector(s) that use simple-events-skeleton-shimmer so the override takes effect.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/blocks/calendar/calendar.js`:
- Around line 228-230: The current finally block calls
initListeners(calendarItem) after every request which rebinds global modal
handlers (handleModalFunctionality) and causes duplicate hover/toggle behavior;
update the code so global modal listeners are not re-attached on each
request—either call initListeners only once during component initialization (not
in the per-request finally), or modify initListeners/handleModalFunctionality to
check and early-return if listeners are already bound (e.g., a mounted flag)
before adding handlers; ensure hideLoading(calendarItem) still runs in finally
but move or guard the initListeners(calendarItem) invocation to prevent
duplicate binding.
---
Outside diff comments:
In `@src/blocks/calendar/calendar.js`:
- Around line 204-231: The sendCalendarRequest method can be overwritten by
out-of-order async responses; add request sequencing by introducing a
per-instance request counter (e.g., this._calendarRequestId) that you increment
at the start of sendCalendarRequest, capture the incremented id in the promise
chain, and only call updateContent, hideLoading, and initListeners if the
captured id matches this._calendarRequestId; alternatively store an
AbortController (e.g., this._calendarAbortController) and abort any prior
request before calling apiFetch, then use the new controller.signal in apiFetch
so only the latest response is applied — implement the chosen approach inside
sendCalendarRequest around apiFetch and the promise callbacks to guard
updateContent from stale responses.
---
Nitpick comments:
In `@src/blocks/calendar/style.scss`:
- Around line 81-88: The shimmer keyframes (`@keyframes`
simple-events-skeleton-shimmer) must respect the user's reduced-motion
preference: add a prefers-reduced-motion: reduce media query alongside the
keyframes (or immediately after) and disable the shimmer animation there by
setting the related skeleton element(s) to animation: none (or override
animation-name/animation-duration) so the simple-events-skeleton no longer
animates for users who opt into reduced motion. Ensure you target the same
selector(s) that use simple-events-skeleton-shimmer so the override takes
effect.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2d7b1d8c-e20d-495f-8525-8bd5ea2e6906
📒 Files selected for processing (11)
README.mdpackage.jsonplugin.phpsrc/blocks/calendar/block.jsonsrc/blocks/calendar/calendar.jssrc/blocks/calendar/index.jssrc/blocks/calendar/style.scsssrc/classes/class-se-blocks.phpsrc/classes/class-se-calendar.phpsrc/templates/calendar/calendar-container.phpsrc/templates/calendar/calendar-skeleton.php
| .finally( () => { | ||
| this.hideLoading( calendarItem ); | ||
| this.initListeners( calendarItem ); |
There was a problem hiding this comment.
Avoid rebinding global modal listeners on every request completion.
Line 230 calls initListeners() in finally, which also runs handleModalFunctionality() globally. This can stack duplicate hover handlers after repeated navigation and cause multiple modal toggles per interaction.
💡 Suggested fix
- .finally( () => {
- this.hideLoading( calendarItem );
- this.initListeners( calendarItem );
- } );
+ .finally( () => {
+ this.hideLoading( calendarItem );
+ // Rebind only listeners tied to replaced calendar markup.
+ this.addNavigationItemsListeners( calendarItem );
+ this.addCalendarDayListeners( calendarItem );
+ // Keep modal binding scoped/idempotent (adjust handleModalFunctionality separately).
+ } );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/blocks/calendar/calendar.js` around lines 228 - 230, The current finally
block calls initListeners(calendarItem) after every request which rebinds global
modal handlers (handleModalFunctionality) and causes duplicate hover/toggle
behavior; update the code so global modal listeners are not re-attached on each
request—either call initListeners only once during component initialization (not
in the per-request finally), or modify initListeners/handleModalFunctionality to
check and early-return if listeners are already bound (e.g., a mounted flag)
before adding handlers; ensure hideLoading(calendarItem) still runs in finally
but move or guard the initListeners(calendarItem) invocation to prevent
duplicate binding.
Changes proposed in this Pull Request
sequentialMonths), to support sequential month-by-month navigation.sequentialMonthsto off for existing blocks.simple_events_calendar_day_eventsfilter to allow pre-render calendar day event customization (e.g. deduping).2.1.2and add changelog notes inREADME.md.Testing instructions
sequentialMonths) default to OFF behavior.Mentions #
@gin0115 @tommusrhodus @ecairol - if one of you could review when you get a chance, we have a launch for ThePocketNYC on May 25 that would need these changes, ty!
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Chores