feat!: v2 — compound components, RTL, dark mode, a11y, responsive, i18n, loading states, slot creation, and breaking refactors#70
Merged
Merged
Conversation
- Add ESLint with TypeScript and React plugins for code quality - Include linting npm scripts for manual and auto-fix runs - Configure ESLint to ignore build and storybook directories
This resolves potential dependency conflicts when installing packages with mismatched peer dependencies, ensuring a smoother installation process.
- Add .prettierrc with formatting rules and .prettierignore file - Integrate eslint-config-prettier to avoid conflicts between ESLint and Prettier - Update package.json with format scripts and add eslint-config-prettier dependency - Apply Prettier formatting to existing code (quotes, semicolons, trailing commas) - Fix missing newline at end of file in index.css and preview.ts
- Add weekStartsOn and weekEndsOn props to various views - Update WeekView to calculate days based on config - Modify MonthView to generate custom week range grids - Extend useMonthGrid hook to accept week boundaries - Set default values (0 for Sun, 6 for Sat) in constants
… range - Add minHour and maxHour props to Calendar component and default constants - Update TimeColumn, DayColumn, DayView, WeekView and CurrentTimeLine to respect the time boundaries - Filter events in useDayEventLayout hook to only include those within the specified hour range - Adjust event positioning and current time line calculation based on minHour offset - Hide current time line when current time is outside the displayed range
Add storybook examples for the newly introduced props: - WithTimeLimits story in DayView and WeekView to demonstrate minHour/maxHour - CustomWeekStartEnd story in MonthView and WeekView to show weekStartsOn/weekEndsOn Update README and FEATURES documentation to include these new props
… display Add a new boolean prop showAdjacentMonths to CalendarProps that controls whether dates from adjacent months should be displayed in the month view. This provides users with the option to hide empty cells from previous/next months for a cleaner interface when desired. The prop defaults to true to maintain backward compatibility with existing behavior.
…display - Introduce new `customDays` view type for displaying 1-10 consecutive days - Add `CustomDaysView` with time grid layout and configurable day count - Update CalendarContext to handle navigation for custom day ranges - Extend Header component for date range and navigation in custom views - Add Storybook stories for custom day configurations (1, 3, 5, 10 days) - Include validation to ensure customDays is between 1 and 10 inclusive
…ures - Add storybook examples demonstrating showAdjacentMonths prop behavior - Update FEATURES.md to document adjacent months functionality and customDays view - Update README.md with customDays view example and prop documentation - Extend ECalendarViewType type definition to include "customDays"
- Rename CALENDAR_CONSTANTS to LAYOUT_CONSTANTS to better reflect its purpose - Move calendar-related constants from time.ts to new calendar.ts file - Fix typo in CALENDER_STRINGS to CALENDAR_STRINGS - Update all imports to reference new constant names and locations - Remove BOM character from theme.ts file
Move CalendarClassNames, ThemeStyle, and CalendarTheme interfaces from calendar.ts to a new theme.ts file for better organization. Update calendar.ts to import these types from the new module and re-export them via the main index.ts barrel file.
- Change CalendarEvent interface from color?: string to style?: CSSProperties - Update all event rendering components to use style prop with fallback to default color - Modify storybook examples and playground to use style.backgroundColor instead of color - This allows custom styling beyond just background color (e.g., borders, gradients)
Update all documentation files to reflect the change from `color` to `style` property in the CalendarEvent interface. Add migration guide section for v1.2.0 breaking change and include new Storybook stories demonstrating event styling customization.
Remove the 72-character line length restriction for commit message bodies to allow more flexible formatting.
- Add renderEvent prop to allow custom rendering of event items across all views - Add renderHeader prop to enable custom header component with navigation controls - Add renderHourCell prop for custom hour slot rendering in time grid views - Add renderDateCell prop for custom date cell rendering in month and week views - Update all view components (DayView, WeekView, MonthView, CustomDaysView, ScheduleView) to pass through render props - Update core components (Popover, AllDayBanner, DayColumn, DayWeekEventItem, MonthEventItem) to support custom rendering - Extend CalendarProps interface with new renderer type definitions
- Add comprehensive documentation for custom renderer props (renderEvent, renderHeader, renderHourCell, renderDateCell) in README.md and FEATURES.md - Update CustomView story title for better clarity in Storybook navigation - Add example story demonstrating all custom renderers in action
…change When enabled, changing the calendar view (e.g., from Month to Week) will automatically reset the selected date to today. This provides better UX when users switch views and want to quickly see the current period.
Add a new `showAllDayRow` prop to the Calendar component, allowing users to hide the all-day event banner at the top of the Day and Week views. When set to false, all-day and multi-day events are rendered within the main time grid, spanning from the configured minHour to maxHour. This provides more layout flexibility for applications that do not require a separate all-day section. - Extend dayjs with isBetween plugin to support date overlap checks. - Update layout hook logic to conditionally include all-day/multi-day events in the grid. - Add storybook example demonstrating the feature.
Introduce `renderScheduleSeparator` prop to Schedule view, allowing custom UI components to be rendered between date groups. This provides visual separation and customization options for multi-day schedules. The default border is conditionally hidden when a custom separator is provided.
Introduce a new prop to control overlapping event layout in day/week/custom views. When set to 0 (default), events use tiled layout with width expansion. When set to a positive percentage (e.g., 15), events stack with offset creating a layered appearance. This provides visual distinction for concurrent events while maintaining clear time boundaries.
- Replace CustomView.stories.tsx with enhanced CustomDayView.stories.tsx featuring mock events and improved layout - Update FEATURES.md with detailed descriptions of new calendar capabilities including all-day row, event overlap styling, and custom separators - Extend TEST_CASES.md with test scenarios for new props and view behaviors - Update README.md props table with showAllDayRow, eventOverlapOffset, resetDateOnViewChange, and renderScheduleSeparator
Add comprehensive test stories covering different view types, time formatting, user interactions, layout constraints, and edge cases. These stories help verify the calendar component's behavior across various scenarios including overlapping events, controlled state, time range limits, and data validation.
…events - Add `enableEnrichedEvents`, `eventsAreSorted`, `isEventOrderingEnabled`, and `sortedMonthView` props to skip expensive computations - Support pre-sorted and pre-filtered events via `enrichedEventsByDate` to reduce runtime processing - Bypass layout algorithms when ordering is disabled for faster rendering - Update all view components and hooks to accept new performance options
Ensure event z-index stays below timeHeaderSpacer (z-index: 15) and stickyTopContainer (z-index: 20) to prevent visual stacking issues. Also sets columnIndex to 0 when ordering is disabled for consistent DOM stacking.
- Add documentation for `enableEnrichedEvents`, `enrichedEventsByDate`, `eventsAreSorted`, `isEventOrderingEnabled`, and `sortedMonthView` props in README.md - Document performance optimization strategies in FEATURES.md - Add test cases for performance options in TEST_CASES.md - Create Performance.stories.tsx with stories demonstrating optimization techniques for handling large event datasets
- Configure vitest with jsdom environment and setup file - Add test script commands to package.json - Create initial unit tests for date utilities and Calendar component - Install required testing dependencies (@testing-library, jsdom, vitest)
…ontext - Add formatting.test.ts for generateTooltipText and getGmtOffset functions - Add common.test.ts for calculateMaxEvents, isAllDayEvent, and isMultiDay functions - Add CalendarContext.test.tsx for CalendarProvider and useCalendar hook - Tests cover various view types, date manipulations, and edge cases
- Test useEvents hook filtering and enriched events behavior - Test useAllDayBanner event stacking and visibility logic - Test useResizeObserver observation and size updates - Test useScheduleView event grouping and time/title rendering - Test useMonthGrid Tetris event placement and ordering options - Test useDayEventLayout overlapping event handling and layout modes
…th views - Header: replace 640px query with 768px; compact controls stack into two rows - MonthView: shorter header row (30px) and tighter cell padding at 768px - MonthEventItem: smaller chips at 768px; dot-only 6px bars at 480px
Replace scrollIntoView() with direct scrollTop manipulation using getBoundingClientRect() so autoScrollToCurrentTime only scrolls the ScheduleView container, not outer overflow:hidden ancestors that would push the calendar header out of view.
…iews - WeekView: fixed 100px columns at 768px; single-column fill at 480px - CustomDaysView: add missing overflow-x: auto; same column breakpoints as WeekView - DayView: reduce day number font to 16px at 768px
- ScheduleView: reduce horizontal padding at 480px; narrow time column from 140px to 100px; shrink date number, event time, and title fonts - Also add missing bottom padding (16px) to base .scheduleView rule
…ping The .views container had height: 100%, which in the Calendar's flex-column layout caused it to request the full container height (e.g., 800px) while the Header also consumed space (~68px). This created overflow that was clipped by overflow: hidden on the Calendar root, hiding the last date groups in ScheduleView (e.g., May 5 not visible, May 4 half-clipped). Change .views to use flex: 1 and min-height: 0 instead, which correctly distributes remaining space after the Header. This also allows DayView and WeekView to drop their calc(100% - 68px) workarounds and simply use height: 100% of the bounded parent. All tests pass. Verified fix resolves clipping in ScheduleView while maintaining proper behavior in other views.
- README: detail 768px/480px breakpoints in responsive feature bullet - README: add new "Responsive & Mobile" section with breakpoint table - FEATURES.md: rewrite "Responsive Layout" section with per-view breakpoint changes at 768px and 480px - Include fluid container usage tips for both files
Merge 3 single-export constant files (datetime.ts, formats.ts, ui.ts) into existing files by domain: - TIME_CONSTANTS, DATE_FORMATS → calendar.ts (calendar domain) - KEYBOARD_SHORTCUTS → theme.ts (UI interaction constants) Move locale utility functions to dedicated module: - getDayListNames(), getMonthList() → src/utils/locale.ts - Update 15 files to import from utils instead of constants Replace remaining magic numbers with named constants across components and hooks (60 min/hr, 24 hr/day, 7 days/week, millisecond intervals). Fix TypeScript issues: - Explicit string type for formatStr in formatting.ts - Remove invalid ignoreDeprecations from tsconfig.json Verify: All tests pass, lint clean, build succeeds. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…atting - Move handleKeyboardActivation from keyboard.ts into common.ts - Move getDayListNames and getMonthList from locale.ts into formatting.ts - Remove locale barrel export; keyboard was never in the barrel - Update 6 component imports from utils/keyboard to utils barrel - Delete keyboard.ts and locale.ts - Add tests for handleKeyboardActivation, getDayListNames, getMonthList
Document every interface, enum, and prop across calendar.ts, events.ts, and theme.ts. - File-level @file docblocks on all three modules - Inline comments on every prop in CalendarProps, CalendarClassNames, CalendarEvent, CalendarTheme, and supporting types - Clarify cross-prop relationships (minHour/maxHour, weekStartsOn/weekEndsOn) - Describe internal types: RequiredSome, CalendarContentProps, EventListType
Add file-level @file docblocks and inline JSDoc on all exported constants in calendar.ts and theme.ts, and a full function-level JSDoc on handleKeyboardActivation in utils/common.ts. - Document CALENDAR_STRINGS, VIEW_OPTIONS, defaultCalendarProps, CALENDAR_ACTIONS, TIME_CONSTANTS, DATE_FORMATS - Document defaultTheme, LAYOUT_CONSTANTS, KEYBOARD_SHORTCUTS - Describe handleKeyboardActivation params, return type, and why it prevents default and stops propagation
Add file-level @file docblocks and inline JSDoc to CalendarContext.tsx, useCalendarProps.ts, and useEvents.ts. - Document CalendarState, CalendarAction, CalendarContextValue, and CalendarProviderProps interfaces - Describe calendarReducer navigation logic and Luxon duration mapping - Document CalendarProvider and useCalendar function signatures - Document useCalendarProps merge precedence rules - Expand useEvents @param entries for eventsAreSorted and enableEnrichedEvents
feat!: v2 — compound components, responsive design, accessibility, and new calendar features
- Add `direction` prop to CalendarProps; auto-infers rtl from locale when omitted - Apply dir attribute to calendar root and container elements - Migrate all CSS physical properties to logical equivalents across all stylesheets (border-inline-end, inset-inline-start, padding-inline, etc.) - Add RTL clip-path overrides for bannerChip and popover arrow shapes - Mirror navigation arrow icons in RTL via CSS scaleX(-1) - Move resolveDirection from constants to utils/common - Add ArabicLTR and HebrewRTL Storybook stories; extend Localization controls - Fix formatting.test.ts: EDayType.short → EDayType.half (key did not exist) - Add tsconfig ignoreDeprecations: "6.0" to silence node10 moduleResolution warning Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implement dark mode via a new useColorScheme hook that detects the OS prefers-color-scheme preference and applies live updates. Add colorScheme prop to force light/dark mode. Apply resolved scheme as data-color-scheme attribute on calendar roots, switching CSS variable palettes. - New useColorScheme hook: reads OS preference, respects explicit prop - New ColorScheme type: "light" | "dark" | "auto" - Extend CalendarTheme with dark/light sub-objects for per-scheme overrides - New resolveTheme() utility to merge flat theme with scheme-specific values - Replace all hardcoded colors with CSS variables for unified theming - Add matchMedia mock to setupTests.ts for dark mode testing - Apply data-color-scheme to Calendar, CalendarContent, and Popover roots
…dark mode Implements color scheme prop with three modes: 'light', 'dark', and 'auto' (default). Auto mode detects OS preference via prefers-color-scheme media query and updates live when the user toggles their system theme. Adds useColorScheme hook, comprehensive tests, and Storybook stories demonstrating light, dark, and auto modes across all view types.
Overhaul both documentation files with a tighter, more navigable structure. Adds a Table of Contents, removes decorative emoji from headings, condenses verbose prose into scannable bullet lists, and reorganizes sections to group related features (RTL, color schemes, accessibility) together. - Add Table of Contents to FEATURES.md - Replace prose paragraphs with concise bullet descriptions - Standardize punctuation (em-dashes, separators) - Reorganize feature groupings for easier discovery
The public API declares onMoreClick(date, hiddenEvents?) but the second argument was never populated — the internal prop type lacked the parameter, both call sites omitted it, and the MonthView boundary wrapper dropped it. - Extended MonthEventItem.onMoreClick prop type to accept hiddenEvents - Computed hiddenEventsList (non-spacer overflow events) inside the visibility-capping block and passed it at both call sites - Updated MonthView wrapper to forward hiddenEvs alongside the date - Updated MonthView tests to assert the correct events array is received
In compound-component mode (Calendar with children), CalendarContent
was never rendered so its <section data-testid="…-container"> was
never mounted. The outer <div> also lacked the --calendar-width /
--calendar-height CSS variable overrides, causing MonthView cells
to fall back to the 0px defaults from Calendar.module.css.
- Add data-testid="${testId}-container" to outer <div> when children
are present
- Spread --calendar-width / --calendar-height into the inline style
only in the children path (Path A is unchanged)
- Add regression test asserting the testId is accessible in compound
mode
customDays was initialised from CalendarProvider's initialCustomDays but never updated when the prop changed after mount, causing the CustomDaysView to silently render with a stale day count. - Add SET_CUSTOM_DAYS to CALENDAR_ACTIONS, CalendarAction union, and calendarReducer (mirrors SET_VIEW / SET_DATE pattern) - Add useEffect in CalendarContent to dispatch SET_CUSTOM_DAYS whenever props.customDays changes - Test SET_CUSTOM_DAYS reducer updates state.customDays (3 → 5) - Test Calendar rerender with new customDays keeps view mounted
- Expand eventsAreSorted JSDoc in CalendarProps and useEvents to explicitly state that unsorted input renders in the provided order (the library never re-sorts the array) - Add regression test labelled K-03 that asserts input order is preserved when eventsAreSorted=true even when events are not chronologically sorted
- Expand CalendarEvent.endDate JSDoc to note that endDate < startDate causes silent filtering (negative-duration guard, C-TC3) - Expand useEvents hook description to state the filter is intentional and events have no renderable duration - Add regression test labelled C-TC3 asserting that filtered events are dropped silently with no console.warn emitted
- Correct isLoading and renderLoading JSDoc in CalendarProps: describe both paths (skeleton for empty events, overlay for non-empty events) - Add data-testid="calendar-loading-overlay" to the overlay div so tests can assert its presence without coupling to CSS class names - Add DI-3 regression test verifying overlay renders (not skeleton) when isLoading=true and events are present
Created CalendarErrorBoundary component to prevent the entire calendar from crashing when custom renderers (renderHeader, etc.) throw. The boundary catches exceptions and renders a fallback div instead of propagating the error up. Added integration test verifying the error boundary catches renderer exceptions.
Added rollup-plugin-visualizer to analyze bundle size and composition. Generates stats.html on every build showing gzip and brotli-compressed sizes for each module.
feat: RTL support, color scheme/dark mode, error boundary, and bug fixes
|
🎉 This PR is included in version 2.0.0-beta.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
|
🎉 This PR is included in version 2.0.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request Checklist
Description
This is the v2 major release of
calendar-simple. It introduces a large set of new features, a full test suite, comprehensive documentation, and several breaking changes. The calendar has been significantly restructured to support compound component composition, a migrated date library (dayjs → Luxon), and a much richer public API.refactor!):dayjsreplaced withLuxonthroughout. All internal date handling now usesDateTime. Consumers passing rawDatevalues are unaffected (boundaries still accept JSDate), but custom hooks or utilities relying ondayjsobjects will need updating.fix!): Plural unit names corrected and ISO date format standardised — any code relying on the previously incorrect output will need to be updated.feat(calendar)!): Locale support is now driven entirely by Luxon. Thelocaleprop replaces any prior ad-hoc formatting configuration.feat!):CalendarProvider,useCalendar,CALENDAR_ACTIONS, and per-view props types (HeaderProps,ViewProps,MonthViewProps,WeekViewProps,DayViewProps,ScheduleViewProps,CustomViewProps) are now exported fromsrc/index.ts. This enables building fully custom calendar layouts using composition.color→style: Thecolorproperty onCalendarEventhas been replaced by astyle: CSSPropertiesprop for flexible event styling.New Features
Compound Component Pattern
<Calendar>now accepts children — compose<Header>,<View>, and other pieces as needed.CalendarProvider,useCalendar,CALENDAR_ACTIONS, and all view prop types.useCalendarProps<T>()hook merges context config with local overrides for prop distribution.Internationalisation (i18n / Localization)
localeprop accepts any BCP 47 language tag (e.g."fr","ar","ja").RTL (Right-to-Left) Layout
directionprop:"ltr"|"rtl".inline-start/end,block-start/end) so layout mirrors correctly in RTL locales.Dark Mode / Color Scheme
colorSchemeprop:"light"|"dark"|"auto"."auto"detects system preference viaprefers-color-scheme.useColorSchemehook exported for external use.Loading State & Skeleton UIs
isLoadingprop:MonthSkeleton,TimeGridSkeleton,ScheduleSkeleton).renderLoading?: () => ReactNodeprop for custom loading UI.Accessibility (a11y)
src/utils/contrast.ts.Responsive Design
Slot Creation
creatableprop enables clicking empty time-grid slots to trigger event creation.onSlotClick?: (startDate: Date, endDate: Date) => voidcallback receives the slot's time bounds.Custom Renderers
renderEvent?: (event: CalendarEvent) => ReactNode— replace event chips.renderHeader?: (props: RenderHeaderProps) => ReactNode— replace entire header.renderHourCell?: (date: Date) => ReactNode— replace hour labels in time grid.renderDateCell?: (props: RenderDateCellProps) => ReactNode— replace date numbers in Month view.renderScheduleSeparator?: (date: Date) => ReactNode— replace date group separators in Schedule view.Error Boundary
CalendarErrorBoundarycomponent wraps all custom renderers so an exception in user-supplied render props shows a graceful fallback instead of crashing the calendar (DI-4).Week Numbers
showWeekNumbersprop displays ISO week numbers in the leftmost column of the Month grid.CustomDays View
customDaysprop andcustomDaysview type (ECalendarViewType.customDays) for flexible multi-day time-grid layouts (e.g. 3-day view).Additional New Props
testIddata-testidon root element for testingminHour/maxHourweekStartsOn/weekEndsOnshowAdjacentMonthsresetDateOnViewChangeshowAllDayRoweventOverlapOffseteventsAreSortedenrichedEventsByDateBug Fixes
onMoreClicknow correctly passes the hidden events array as its second argument.testId-containerand root CSS variables are now properly applied in compound component mode.scrollIntoView(current-time auto-scroll) no longer scrolls ancestor containers unexpectedly.selectedDateprop now stays in sync on external updates; year picker list calculation corrected.z-indexcapped to prevent overlapping with the header.Refactoring
dayjsfully removed; all date logic uses LuxonDateTime.common.tsandformatting.ts.src/types/theme.ts;CalendarClassNamesmoved there too.useCalendarProps).eslint.config.js).Testing
useColorScheme,useAllDayBanner,useDayEventLayout,useMonthGrid,useScheduleView,useResizeObserver,useEvents,contrast.ts,common.ts,date.ts,formatting.ts,CalendarContext, all view components, all core components,Popover,Header, skeletons.QA/EdgeCases,QA/Interactions,QA/LayoutLimits,QA/Performance,QA/TimeFormatting,QA/Views.Accessibility,CustomDayView,Customization,Localization.setupTests.tsfor consistent test sizes.Documentation
CLAUDE.mdadded with full architecture guide and development patterns.README.mdandFEATURES.mdrestructured and expanded with all new features.CalendarContext, constants, and core hooks.useEventsedge-case caveats documented (negative-duration filter C-TC3;eventsAreSortedbypass K-03).docs/directory removed (content merged into README/FEATURES).Tooling & CI
.prettierrc,.prettierignore).npm testandnpm run buildon PRs.calendar-simpledependency.rollup-plugin-visualizeradded for bundle analysis.Fixes #DI-3, #DI-4
Type of change
How Has This Been Tested?
QA/EdgeCases,QA/Interactions,QA/LayoutLimits,QA/Performance,QA/TimeFormatting,QA/Views)npm run build)Unit tests added or updated for all hooks, utilities, components, and the context layer. Run with:
npm testTo run a specific area:
123 files changed — 19,082 insertions, 5,526 deletions across 80 commits. The headline items are the Luxon migration (breaking), compound component pattern (breaking), RTL, dark mode, full a11y, responsive breakpoints, loading skeletons, slot creation, and a complete unit test suite.