Skip to content

Add Event feature to BitCalendar (#12261)#12262

Open
msynk wants to merge 1 commit intobitfoundation:developfrom
msynk:12261-blazorui-calendar-events
Open

Add Event feature to BitCalendar (#12261)#12262
msynk wants to merge 1 commit intobitfoundation:developfrom
msynk:12261-blazorui-calendar-events

Conversation

@msynk
Copy link
Copy Markdown
Member

@msynk msynk commented Apr 15, 2026

closes #12261

Summary by CodeRabbit

  • New Features
    • Calendar now supports displaying events on specific days with visual indicators
    • Hovering over events displays a tooltip with event details
    • Clicking on a day with events opens a modal showing full event information including title, time (if applicable), and description
    • Events can be optionally timed or marked as all-day

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 15, 2026

Walkthrough

The pull request introduces an event feature to the BitCalendar component, allowing users to display custom events for each calendar day via a new Events parameter. Days with events show a visual indicator dot, display a tooltip on hover, and open a modal dialog when clicked to view full event details including title, time range, and body text.

Changes

Cohort / File(s) Summary
Core Component Logic
BitCalendar.razor.cs, BitCalendar.razor
Added new Events parameter accepting IEnumerable<BitCalendarEvent>. Introduced event modal state management (_showEventModal, _eventModalDate, _eventModalEvents) and helper methods for filtering day events and formatting tooltips. Modified day click handler to HandleDayClick to conditionally open modal when events exist. Updated Razor markup with event indicator visual, conditional tooltips, and event details modal overlay with dismissal and event list rendering.
Styling & Configuration
BitCalendar.scss, BitCalendarClassStyles.cs
Added 9 new SCSS classes for event UI: indicator dot, overlay backdrop, modal container, header, close button, event items, and typography. Extended BitCalendarClassStyles with 9 nullable string properties mapping to these new event-related class names.
Event Data Model
BitCalendarEvent.cs
Created new public BitCalendarEvent class with required Title and Body string properties, DateOnly Date, and optional TimeOnly? StartTime/EndTime for time range details.
Demo Updates
BitCalendarDemo.razor, BitCalendarDemo.razor.cs, BitCalendarDemo.razor.samples.cs
Added "Events" demo showcasing the new parameter with sample event data. Introduced Events parameter documentation entry and BitCalendarEvent subclass metadata. Renumbered subsequent demo examples (Validation, Templates, etc.) to accommodate the new example. Added demo code snippets demonstrating event usage.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A calendar blessed with events to display,
Dots dancing on dates, tooltips in the fray,
Click to reveal all the details within,
Modal magic—let the event show begin!
Each day now tells stories, from start until end. ✨📅

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly and concisely describes the main change: adding an Event feature to the BitCalendar component, which matches the primary objective.
Linked Issues check ✅ Passed The changes fully implement the Event feature for BitCalendar [#12261], including event display per day, custom event support with title/body/date/time information, visual indicators, tooltips, and modal details.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the Event feature for BitCalendar; no unrelated modifications or scope creep detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.razor.cs (1)

1006-1011: Index events once instead of filtering them per cell render.

GetDayEvents walks the full Events sequence for every visible day button, so each rerender becomes ~42 full enumerations plus list allocations. Precomputing a Dictionary<DateOnly, List<BitCalendarEvent>> when Events changes will keep the render path flat and avoid scaling cost with larger event sets.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.razor.cs`
around lines 1006 - 1011, GetDayEvents currently scans the entire Events
sequence for each day button causing O(n*m) cost; instead, build a
Dictionary<DateOnly, List<BitCalendarEvent>> when Events changes and have
GetDayEvents simply look up the date key. Add a private field (e.g.
_eventsByDate) and populate it in the component's parameter-set/update path
(e.g. in the Events setter or OnParametersSet/OnParametersSetAsync) by grouping
Events by DateOnly.FromDateTime(e.Date) or e.Date if already DateOnly; ensure
null/empty Events clears the dictionary; then change GetDayEvents to return the
list from _eventsByDate.TryGetValue(dateOnly, out var list) ? list : an empty
list to avoid per-cell enumeration and allocations.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.razor`:
- Around line 589-616: Replace hard-coded date/time formats and English labels
in the modal and the tooltip formatter with a single culture-aware
formatter/localizer: change the _eventModalDate.ToString("MMMM d, yyyy") and the
time renderings for evt.StartTime/evt.EndTime to call a shared method (e.g.,
Create a FormatEventDate(DateTime) and FormatEventTime(TimeSpan?/DateTime?) or a
single FormatEventRange(start,end)) that uses CultureInfo.CurrentCulture (and
the BitTimeFormat setting to choose 12/24-hour patterns) and returns localized
"From"/"Until" text via resources; also update the tooltip formatter in the
BitCalendar class to call the same formatter so all date/time displays use
consistent, culture-aware formatting.
- Around line 579-625: The event popup markup controlled by _showEventModal
should be turned into an accessible dialog: add role="dialog" and
aria-modal="true" on the modal container and set aria-labelledby to the header
span (give the header span a unique id, e.g., eventModalTitle) which should
reference `@_eventModalDate` display; make the container focusable (tabindex="0")
and capture an ElementReference (e.g., _eventModalRef) so when _showEventModal
becomes true you call FocusAsync on that ref and save/restore the previously
focused element; wire an `@onkeydown` handler on the dialog container to close via
Escape (call CloseEventModal) and to trap Tab/Shift+Tab (or call a small JS
focus-trap helper via IJSRuntime) so keyboard users cannot tab out; ensure
CloseEventModal returns focus to the saved element when closing and keep
references to _eventModalEvents and CloseEventModal in these changes to locate
the code.

---

Nitpick comments:
In `@src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.razor.cs`:
- Around line 1006-1011: GetDayEvents currently scans the entire Events sequence
for each day button causing O(n*m) cost; instead, build a Dictionary<DateOnly,
List<BitCalendarEvent>> when Events changes and have GetDayEvents simply look up
the date key. Add a private field (e.g. _eventsByDate) and populate it in the
component's parameter-set/update path (e.g. in the Events setter or
OnParametersSet/OnParametersSetAsync) by grouping Events by
DateOnly.FromDateTime(e.Date) or e.Date if already DateOnly; ensure null/empty
Events clears the dictionary; then change GetDayEvents to return the list from
_eventsByDate.TryGetValue(dateOnly, out var list) ? list : an empty list to
avoid per-cell enumeration and allocations.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 646fab1c-3a88-44be-8c98-a33934be6383

📥 Commits

Reviewing files that changed from the base of the PR and between 92115fb and 400b139.

📒 Files selected for processing (8)
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.scss
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendarClassStyles.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendarEvent.cs
  • src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Calendar/BitCalendarDemo.razor
  • src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Calendar/BitCalendarDemo.razor.cs
  • src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Calendar/BitCalendarDemo.razor.samples.cs

Comment on lines +579 to +625
@if (_showEventModal)
{
<div class="bit-cal-eov @Classes?.EventModalOverlay"
style="@Styles?.EventModalOverlay"
@onclick="CloseEventModal">
<div class="bit-cal-emc @Classes?.EventModalContainer"
style="@Styles?.EventModalContainer"
@onclick:stopPropagation="true">
<div class="bit-cal-emh @Classes?.EventModalHeader"
style="@Styles?.EventModalHeader">
<span>@_eventModalDate.ToString("MMMM d, yyyy")</span>
<button type="button"
@onclick="CloseEventModal"
style="@Styles?.EventModalCloseButton"
class="bit-cal-emx @Classes?.EventModalCloseButton">&times;</button>
</div>
@foreach (var evt in _eventModalEvents)
{
<div class="bit-cal-emi @Classes?.EventItem"
style="@Styles?.EventItem">
<div class="bit-cal-eit @Classes?.EventItemTitle"
style="@Styles?.EventItemTitle">@evt.Title</div>
@if (evt.StartTime.HasValue || evt.EndTime.HasValue)
{
<div class="bit-cal-eis @Classes?.EventItemTime"
style="@Styles?.EventItemTime">
@if (evt.StartTime.HasValue && evt.EndTime.HasValue)
{
@($"{evt.StartTime:HH:mm} \u2013 {evt.EndTime:HH:mm}")
}
else if (evt.StartTime.HasValue)
{
@($"From {evt.StartTime:HH:mm}")
}
else
{
@($"Until {evt.EndTime:HH:mm}")
}
</div>
}
<div class="bit-cal-eib @Classes?.EventItemBody"
style="@Styles?.EventItemBody">@evt.Body</div>
</div>
}
</div>
</div>
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make the event popup an actual dialog.

This is visually a modal, but it has no role="dialog", aria-modal, labelled title, Escape handling, or focus handoff when it opens. Keyboard and screen-reader users can remain on the underlying calendar and miss the popup entirely.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.razor`
around lines 579 - 625, The event popup markup controlled by _showEventModal
should be turned into an accessible dialog: add role="dialog" and
aria-modal="true" on the modal container and set aria-labelledby to the header
span (give the header span a unique id, e.g., eventModalTitle) which should
reference `@_eventModalDate` display; make the container focusable (tabindex="0")
and capture an ElementReference (e.g., _eventModalRef) so when _showEventModal
becomes true you call FocusAsync on that ref and save/restore the previously
focused element; wire an `@onkeydown` handler on the dialog container to close via
Escape (call CloseEventModal) and to trap Tab/Shift+Tab (or call a small JS
focus-trap helper via IJSRuntime) so keyboard users cannot tab out; ensure
CloseEventModal returns focus to the saved element when closing and keep
references to _eventModalEvents and CloseEventModal in these changes to locate
the code.

Comment on lines +589 to +616
<span>@_eventModalDate.ToString("MMMM d, yyyy")</span>
<button type="button"
@onclick="CloseEventModal"
style="@Styles?.EventModalCloseButton"
class="bit-cal-emx @Classes?.EventModalCloseButton">&times;</button>
</div>
@foreach (var evt in _eventModalEvents)
{
<div class="bit-cal-emi @Classes?.EventItem"
style="@Styles?.EventItem">
<div class="bit-cal-eit @Classes?.EventItemTitle"
style="@Styles?.EventItemTitle">@evt.Title</div>
@if (evt.StartTime.HasValue || evt.EndTime.HasValue)
{
<div class="bit-cal-eis @Classes?.EventItemTime"
style="@Styles?.EventItemTime">
@if (evt.StartTime.HasValue && evt.EndTime.HasValue)
{
@($"{evt.StartTime:HH:mm} \u2013 {evt.EndTime:HH:mm}")
}
else if (evt.StartTime.HasValue)
{
@($"From {evt.StartTime:HH:mm}")
}
else
{
@($"Until {evt.EndTime:HH:mm}")
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Render event date/time with the calendar’s culture and time format.

The modal hard-codes "MMMM d, yyyy", "HH:mm", and English "From"/"Until" text, so localized calendars and BitTimeFormat.TwelveHours show event details in the wrong format. The tooltip formatter in src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.razor.cs:1013-1017 has the same problem, so this should come from one culture-aware formatter/localizer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.razor`
around lines 589 - 616, Replace hard-coded date/time formats and English labels
in the modal and the tooltip formatter with a single culture-aware
formatter/localizer: change the _eventModalDate.ToString("MMMM d, yyyy") and the
time renderings for evt.StartTime/evt.EndTime to call a shared method (e.g.,
Create a FormatEventDate(DateTime) and FormatEventTime(TimeSpan?/DateTime?) or a
single FormatEventRange(start,end)) that uses CultureInfo.CurrentCulture (and
the BitTimeFormat setting to choose 12/24-hour patterns) and returns localized
"From"/"Until" text via resources; also update the tooltip formatter in the
BitCalendar class to call the same formatter so all date/time displays use
consistent, culture-aware formatting.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an “Events” feature to BitCalendar, enabling days to display an event indicator and show event details via tooltip + modal, and updates the demo/docs accordingly.

Changes:

  • Introduces BitCalendarEvent model and a new Events parameter on BitCalendar.
  • Renders an event indicator dot on days with events and shows an overlay modal with event details on click.
  • Updates the Calendar demo page, sample snippets, and parameter/class-style documentation to include the new feature.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Calendar/BitCalendarDemo.razor.samples.cs Adds sample snippet/code for Events and shifts example numbering.
src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Calendar/BitCalendarDemo.razor.cs Documents new Events parameter, event-related class/style hooks, and BitCalendarEvent metadata.
src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Calendar/BitCalendarDemo.razor Adds an “Events” demo section and updates subsequent example references/IDs.
src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendarEvent.cs Introduces the new event DTO used by the calendar.
src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendarClassStyles.cs Adds class/style extension points for the new indicator and modal UI.
src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.scss Adds styles for the event indicator and modal overlay/container/items.
src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.razor.cs Adds Events parameter and supporting logic to retrieve events, build tooltips, and manage modal state.
src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.razor Renders event indicator dot per day, sets tooltip, and adds the event details modal markup.

/// <summary>
/// The full body/description of the event.
/// </summary>
public required string Body { get; set; }
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BitCalendarEvent.Body is marked as required, which forces consumers to always provide a body even when they only want a title/time (and the UI could reasonably omit an empty description). Consider making Body optional (e.g., string?) or default it to string.Empty and render it conditionally, to keep the API lightweight and avoid unnecessary boilerplate for callers.

Suggested change
public required string Body { get; set; }
public string Body { get; set; } = string.Empty;

Copilot uses AI. Check for mistakes.
Comment on lines +991 to +998
DefaultValue = "",
Description = "The title of the event."
},
new()
{
Name = "Body",
Type = "string",
DefaultValue = "",
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the demo metadata for BitCalendarEvent, Title and Body have DefaultValue = "". These properties are modeled as required in BitCalendarEvent, so showing an empty-string default is misleading in the generated docs. Consider marking them as “required” (or string.Empty if you change the model), so the docs accurately reflect how consumers should construct events.

Suggested change
DefaultValue = "",
Description = "The title of the event."
},
new()
{
Name = "Body",
Type = "string",
DefaultValue = "",
DefaultValue = "required",
Description = "The title of the event."
},
new()
{
Name = "Body",
Type = "string",
DefaultValue = "required",

Copilot uses AI. Check for mistakes.
Comment on lines +1006 to +1011
private List<BitCalendarEvent> GetDayEvents(DateTime date)
{
if (Events is null) return [];
var dateOnly = DateOnly.FromDateTime(date);
return Events.Where(e => e.Date == dateOnly).ToList();
}
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetDayEvents filters Events via Where(...).ToList() for every rendered day cell. This repeatedly enumerates the IEnumerable and results in O(days × events) work per render (and can be problematic if Events is a non-materialized enumerable). Consider materializing/grouping once (e.g., build a Dictionary<DateOnly, List<BitCalendarEvent>> in OnParametersSet) and then do O(1) lookups per day.

Copilot uses AI. Check for mistakes.
Comment on lines +127 to +131
/// <summary>
/// The list of events to display on calendar days.
/// </summary>
[Parameter] public IEnumerable<BitCalendarEvent>? Events { get; set; }

Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new events feature is not covered by the existing bUnit test suite for BitCalendar. Please add tests that verify (1) days with events render an indicator, (2) the title tooltip text is set, and (3) clicking a day with events opens/closes the modal (including overlay click/close button).

Copilot uses AI. Check for mistakes.
@onclick:stopPropagation="true">
<div class="bit-cal-emh @Classes?.EventModalHeader"
style="@Styles?.EventModalHeader">
<span>@_eventModalDate.ToString("MMMM d, yyyy")</span>
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The event modal date header uses _eventModalDate.ToString("MMMM d, yyyy"), which formats using the current thread culture rather than the calendar’s configured culture (_culture). When consumers pass a custom Culture to BitCalendar, the modal header can display in a different language/format than the calendar itself. Use an overload that specifies _culture (or reuse the same formatting logic used elsewhere in the component).

Suggested change
<span>@_eventModalDate.ToString("MMMM d, yyyy")</span>
<span>@_eventModalDate.ToString("MMMM d, yyyy", _culture)</span>

Copilot uses AI. Check for mistakes.
Comment on lines +583 to +622
@onclick="CloseEventModal">
<div class="bit-cal-emc @Classes?.EventModalContainer"
style="@Styles?.EventModalContainer"
@onclick:stopPropagation="true">
<div class="bit-cal-emh @Classes?.EventModalHeader"
style="@Styles?.EventModalHeader">
<span>@_eventModalDate.ToString("MMMM d, yyyy")</span>
<button type="button"
@onclick="CloseEventModal"
style="@Styles?.EventModalCloseButton"
class="bit-cal-emx @Classes?.EventModalCloseButton">&times;</button>
</div>
@foreach (var evt in _eventModalEvents)
{
<div class="bit-cal-emi @Classes?.EventItem"
style="@Styles?.EventItem">
<div class="bit-cal-eit @Classes?.EventItemTitle"
style="@Styles?.EventItemTitle">@evt.Title</div>
@if (evt.StartTime.HasValue || evt.EndTime.HasValue)
{
<div class="bit-cal-eis @Classes?.EventItemTime"
style="@Styles?.EventItemTime">
@if (evt.StartTime.HasValue && evt.EndTime.HasValue)
{
@($"{evt.StartTime:HH:mm} \u2013 {evt.EndTime:HH:mm}")
}
else if (evt.StartTime.HasValue)
{
@($"From {evt.StartTime:HH:mm}")
}
else
{
@($"Until {evt.EndTime:HH:mm}")
}
</div>
}
<div class="bit-cal-eib @Classes?.EventItemBody"
style="@Styles?.EventItemBody">@evt.Body</div>
</div>
}
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The event modal markup is missing key accessibility semantics (e.g., role="dialog", aria-modal="true", and labeling via aria-labelledby/aria-label). It also doesn’t handle keyboard dismissal (Escape) or focus management when the modal opens/closes. Please update the modal container to follow accessible dialog patterns so keyboard and screen reader users can use the feature.

Suggested change
@onclick="CloseEventModal">
<div class="bit-cal-emc @Classes?.EventModalContainer"
style="@Styles?.EventModalContainer"
@onclick:stopPropagation="true">
<div class="bit-cal-emh @Classes?.EventModalHeader"
style="@Styles?.EventModalHeader">
<span>@_eventModalDate.ToString("MMMM d, yyyy")</span>
<button type="button"
@onclick="CloseEventModal"
style="@Styles?.EventModalCloseButton"
class="bit-cal-emx @Classes?.EventModalCloseButton">&times;</button>
</div>
@foreach (var evt in _eventModalEvents)
{
<div class="bit-cal-emi @Classes?.EventItem"
style="@Styles?.EventItem">
<div class="bit-cal-eit @Classes?.EventItemTitle"
style="@Styles?.EventItemTitle">@evt.Title</div>
@if (evt.StartTime.HasValue || evt.EndTime.HasValue)
{
<div class="bit-cal-eis @Classes?.EventItemTime"
style="@Styles?.EventItemTime">
@if (evt.StartTime.HasValue && evt.EndTime.HasValue)
{
@($"{evt.StartTime:HH:mm} \u2013 {evt.EndTime:HH:mm}")
}
else if (evt.StartTime.HasValue)
{
@($"From {evt.StartTime:HH:mm}")
}
else
{
@($"Until {evt.EndTime:HH:mm}")
}
</div>
}
<div class="bit-cal-eib @Classes?.EventItemBody"
style="@Styles?.EventItemBody">@evt.Body</div>
</div>
}
@onclick="async () => { CloseEventModal(); await RootElement.FocusAsync(); }"
@onkeydown="async args => { if (args.Key is \"Escape\") { CloseEventModal(); await RootElement.FocusAsync(); } }"
tabindex="-1">
<div class="bit-cal-emc @Classes?.EventModalContainer"
style="@Styles?.EventModalContainer"
role="dialog"
aria-modal="true"
aria-labelledby="@($"{_Id}-event-modal-title")"
aria-describedby="@($"{_Id}-event-modal-content")"
tabindex="-1"
@onclick:stopPropagation="true"
@onkeydown="async args => { if (args.Key is \"Escape\") { CloseEventModal(); await RootElement.FocusAsync(); } }">
<div class="bit-cal-emh @Classes?.EventModalHeader"
style="@Styles?.EventModalHeader">
<span id="@($"{_Id}-event-modal-title")">@_eventModalDate.ToString("MMMM d, yyyy")</span>
<button type="button"
aria-label="Close event details"
autofocus
@onclick="async () => { CloseEventModal(); await RootElement.FocusAsync(); }"
style="@Styles?.EventModalCloseButton"
class="bit-cal-emx @Classes?.EventModalCloseButton">&times;</button>
</div>
<div id="@($"{_Id}-event-modal-content")">
@foreach (var evt in _eventModalEvents)
{
<div class="bit-cal-emi @Classes?.EventItem"
style="@Styles?.EventItem">
<div class="bit-cal-eit @Classes?.EventItemTitle"
style="@Styles?.EventItemTitle">@evt.Title</div>
@if (evt.StartTime.HasValue || evt.EndTime.HasValue)
{
<div class="bit-cal-eis @Classes?.EventItemTime"
style="@Styles?.EventItemTime">
@if (evt.StartTime.HasValue && evt.EndTime.HasValue)
{
@($"{evt.StartTime:HH:mm} \u2013 {evt.EndTime:HH:mm}")
}
else if (evt.StartTime.HasValue)
{
@($"From {evt.StartTime:HH:mm}")
}
else
{
@($"Until {evt.EndTime:HH:mm}")
}
</div>
}
<div class="bit-cal-eib @Classes?.EventItemBody"
style="@Styles?.EventItemBody">@evt.Body</div>
</div>
}
</div>

Copilot uses AI. Check for mistakes.
style="@Styles?.EventModalHeader">
<span>@_eventModalDate.ToString("MMMM d, yyyy")</span>
<button type="button"
@onclick="CloseEventModal"
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The close button only renders "×" and has no accessible name. Add an aria-label (and/or visually hidden text) so screen readers announce it as “Close” (or equivalent), and ensure it’s reachable/obvious for keyboard users.

Suggested change
@onclick="CloseEventModal"
@onclick="CloseEventModal"
aria-label="Close"

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The Event feature for the BitCalendar component

2 participants