From 3e4885e846372fac15961ffd37041f324621770d Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Thu, 20 Nov 2025 16:20:12 +0530 Subject: [PATCH 1/8] feat: add dateInfo section on top of date in calendar --- apps/www/src/app/examples/page.tsx | 71 +++++++++++++- .../content/docs/components/calendar/demo.ts | 95 ++++++++++++------- .../docs/components/calendar/index.mdx | 7 ++ .../content/docs/components/calendar/props.ts | 9 +- .../calendar/__tests__/calendar.test.tsx | 79 +++++++++++++++ .../components/calendar/calendar.module.css | 34 +++++++ .../raystack/components/calendar/calendar.tsx | 28 +++++- .../raystack/components/calendar/index.tsx | 8 +- 8 files changed, 280 insertions(+), 51 deletions(-) diff --git a/apps/www/src/app/examples/page.tsx b/apps/www/src/app/examples/page.tsx index c5d95758b..5b8c1526a 100644 --- a/apps/www/src/app/examples/page.tsx +++ b/apps/www/src/app/examples/page.tsx @@ -4,6 +4,7 @@ import { Avatar, AvatarGroup, Button, + Calendar, Callout, DatePicker, Dialog, @@ -45,7 +46,7 @@ const Page = () => { const [selectValue1, setSelectValue1] = useState(''); const [selectValue2, setSelectValue2] = useState(''); const [inputValue, setInputValue] = useState(''); - const [rangeValue, setRangeValue] = useState({ + const [rangeValue, setRangeValue] = useState({ from: dayjs('2027-11-15').toDate(), to: dayjs('2027-12-10').toDate() }); @@ -165,7 +166,6 @@ const Page = () => { onClear={() => setSearch1('')} /> console.log(value)} @@ -187,10 +187,14 @@ const Page = () => { /> setRangeValue(range)} + onSelect={range => + setRangeValue({ + from: range.from ?? new Date(), + to: range.to ?? new Date() + }) + } calendarProps={{ captionLayout: 'dropdown', mode: 'range', @@ -233,6 +237,65 @@ const Page = () => { }} /> + + Calendar with Date Info + + + + + + 25% + + + ), + [dayjs().add(5, 'day').format('DD-MM-YYYY')]: ( + + + + 25% + + + ), + [dayjs().add(10, 'day').format('DD-MM-YYYY')]: ( + + + + 25% + + + ) + }} + /> + `, + name: 'Calendar', + code: `` }, { - name: "Range Picker", + name: 'Range Picker', code: ` - `, + ` }, { - name: "Date Picker", + name: 'Date Picker', code: ` - `, - }, - ], + ` + } + ] }; export const calendarDemo = { - type: "code", + type: 'code', tabs: [ { - name: "Basic", - code: ``, + name: 'Basic', + code: `` }, { - name: "With Loading", - code: ``, - }, - ], + name: 'With Loading', + code: `` + } + ] }; export const rangePickerDemo = { - type: "code", + type: 'code', tabs: [ { - name: "Basic", - code: ``, + name: 'Basic', + code: `` }, { - name: "Without Calendar Icon", - code: ``, + name: 'Without Calendar Icon', + code: `` }, { - name: "Custom Trigger", + name: 'Custom Trigger', code: ` )} - `, - }, - ], + ` + } + ] }; export const datePickerDemo = { - type: "code", + type: 'code', tabs: [ { - name: "Basic", - code: ``, + name: 'Basic', + code: `` }, { - name: "Without Calendar Icon", - code: ``, + name: 'Without Calendar Icon', + code: `` }, { - name: "Custom Trigger", + name: 'Custom Trigger', code: ` {({ selectedDate }) => ( @@ -96,7 +96,32 @@ export const datePickerDemo = { Selected: {selectedDate} )} - `, - }, - ], + ` + } + ] +}; + +export const dateInfoDemo = { + type: 'code', + tabs: [ + { + name: 'With Date Info', + code: ` + + + 25% + + ) + }} +/>` + } + ] }; diff --git a/apps/www/src/content/docs/components/calendar/index.mdx b/apps/www/src/content/docs/components/calendar/index.mdx index b47d7a9cb..694ac208b 100644 --- a/apps/www/src/content/docs/components/calendar/index.mdx +++ b/apps/www/src/content/docs/components/calendar/index.mdx @@ -8,6 +8,7 @@ import { datePickerDemo, rangePickerDemo, calendarDemo, + dateInfoDemo, } from "./demo.ts"; @@ -42,6 +43,12 @@ Choose between different variants to convey different meanings or importance lev +#### Custom Date Information + +You can display custom components above each date using the `dateInfo` prop. The keys should be date strings in `"dd-MM-yyyy"` format, and the values are React components that will be rendered above the date number. + + + ### Range Picker The Range Picker component allows selecting a date range with the following behaviors: diff --git a/apps/www/src/content/docs/components/calendar/props.ts b/apps/www/src/content/docs/components/calendar/props.ts index 342b8dbab..194c7222c 100644 --- a/apps/www/src/content/docs/components/calendar/props.ts +++ b/apps/www/src/content/docs/components/calendar/props.ts @@ -11,8 +11,13 @@ export interface CalendarProps { /** Boolean to show loading state */ loadingData?: boolean; - /** Object containing date-specific information like icons and text */ - dateInfo?: Record; + /** + * Object containing custom React components to render above each date. + * Keys should be date strings in "dd-MM-yyyy" format. + * The component will be rendered above the date number. + * Example: { "15-01-2024":
17%
} + */ + dateInfo?: Record; /** Boolean to show days from previous/next months */ showOutsideDays?: boolean; diff --git a/packages/raystack/components/calendar/__tests__/calendar.test.tsx b/packages/raystack/components/calendar/__tests__/calendar.test.tsx index 34dfdb1c9..ae76288e2 100644 --- a/packages/raystack/components/calendar/__tests__/calendar.test.tsx +++ b/packages/raystack/components/calendar/__tests__/calendar.test.tsx @@ -110,6 +110,85 @@ describe('Calendar', () => { }); }); + describe('DateInfo Support', () => { + it('renders custom component for date with dateInfo', () => { + const TestComponent = () => ( +
Custom Info
+ ); + const { container } = render( + + }} + /> + ); + + // The component should be rendered in the calendar + expect( + container.querySelector('[data-testid="custom-date-info"]') + ).toBeInTheDocument(); + }); + + it('applies day_button_with_info class when dateInfo is present', () => { + const { container } = render( + Info + }} + /> + ); + + const dayWithInfo = container.querySelector( + `.${styles.day_button_with_info}` + ); + expect(dayWithInfo).toBeInTheDocument(); + }); + + it('does not render dateInfo for dates not in the dateInfo object', () => { + const { container } = render( + Info + }} + /> + ); + + // Should only have one date with info + const dateInfos = container.querySelectorAll('[data-testid="date-info"]'); + expect(dateInfos.length).toBeGreaterThanOrEqual(0); + }); + + it('handles multiple dates with dateInfo', () => { + const { container } = render( + Info 1, + '20-01-2024':
Info 2
+ }} + /> + ); + + const info1 = container.querySelector('[data-testid="info-1"]'); + const info2 = container.querySelector('[data-testid="info-2"]'); + + // At least one should be present (depending on which month is visible) + expect(info1 || info2).toBeTruthy(); + }); + + it('renders date number even when dateInfo is present', () => { + const { container } = render( + Info + }} + /> + ); + + const dayNumber = container.querySelector(`.${styles.day_number}`); + expect(dayNumber).toBeInTheDocument(); + }); + }); + describe('Accessibility', () => { it('has correct ARIA roles', () => { render(); diff --git a/packages/raystack/components/calendar/calendar.module.css b/packages/raystack/components/calendar/calendar.module.css index be6414102..7f8e30f68 100644 --- a/packages/raystack/components/calendar/calendar.module.css +++ b/packages/raystack/components/calendar/calendar.module.css @@ -216,6 +216,10 @@ background-color: var(--rs-color-background-accent-emphasis); } +.day.today .day_button_with_info::after { + bottom: var(--rs-space-1); +} + .selected.day.today:not(.range_middle) button::after { background-color: var(--rs-color-foreground-base-emphasis); } @@ -243,6 +247,36 @@ position: relative; } +.day_button_with_info { + display: grid; + place-content: center; + position: relative; +} + +.day_info { + position: absolute; + top: -2px; + left: 50%; + transform: translateX(-50%); + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + gap: 0px; + width: 100%; + pointer-events: none; + z-index: 1; +} + +.day_number { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + position: relative; + z-index: 0; +} + .calendarFooter { padding: var(--rs-space-3); margin-top: var(--rs-space-2); diff --git a/packages/raystack/components/calendar/calendar.tsx b/packages/raystack/components/calendar/calendar.tsx index fcba549ab..ea75deaba 100644 --- a/packages/raystack/components/calendar/calendar.tsx +++ b/packages/raystack/components/calendar/calendar.tsx @@ -7,7 +7,7 @@ import { ChevronUpIcon } from '@radix-ui/react-icons'; import { cva, cx } from 'class-variance-authority'; -import { ChangeEvent, useEffect, useState } from 'react'; +import { ChangeEvent, ReactNode, useEffect, useState } from 'react'; import { DayPicker, DayPickerProps, @@ -29,6 +29,7 @@ interface OnDropdownOpen { interface CalendarPropsExtended { showTooltip?: boolean; tooltipMessages?: { [key: string]: any }; + dateInfo?: Record; loadingData?: boolean; timeZone?: string; } @@ -114,6 +115,7 @@ export const Calendar = function ({ onDropdownOpen, showTooltip = false, tooltipMessages = {}, + dateInfo = {}, loadingData = false, timeZone, ...props @@ -154,15 +156,31 @@ export const Calendar = function ({ ), DayButton: props => { const { day, ...buttonProps } = props; - const message = - tooltipMessages[dateLib.format(day.date, 'dd-MM-yyyy')]; + const dateKey = dateLib.format(day.date, 'dd-MM-yyyy'); + const message = tooltipMessages[dateKey]; + const dateComponent = dateInfo[dateKey]; + const hasDateInfo = Boolean(dateComponent); + return ( - ); }, diff --git a/packages/raystack/components/calendar/index.tsx b/packages/raystack/components/calendar/index.tsx index f237a5e11..697f683ca 100644 --- a/packages/raystack/components/calendar/index.tsx +++ b/packages/raystack/components/calendar/index.tsx @@ -1,5 +1,3 @@ -export { Calendar } from "./calendar"; -export { DatePicker } from "./date-picker"; -export { RangePicker } from "./range-picker"; - -// Todo: Add weather forecast icon with text. \ No newline at end of file +export { Calendar } from './calendar'; +export { DatePicker } from './date-picker'; +export { RangePicker } from './range-picker'; From 817e7d34e470527d9f683fc225d15c300f2061db Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Thu, 20 Nov 2025 16:22:17 +0530 Subject: [PATCH 2/8] fix: add apt type --- packages/raystack/components/calendar/calendar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/raystack/components/calendar/calendar.tsx b/packages/raystack/components/calendar/calendar.tsx index ea75deaba..eb62ae48c 100644 --- a/packages/raystack/components/calendar/calendar.tsx +++ b/packages/raystack/components/calendar/calendar.tsx @@ -28,7 +28,7 @@ interface OnDropdownOpen { interface CalendarPropsExtended { showTooltip?: boolean; - tooltipMessages?: { [key: string]: any }; + tooltipMessages?: Record; dateInfo?: Record; loadingData?: boolean; timeZone?: string; @@ -163,7 +163,7 @@ export const Calendar = function ({ return ( From aba922836f73604604acfc30b96ec378b93c931b Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Thu, 20 Nov 2025 16:26:52 +0530 Subject: [PATCH 3/8] style: change color on selection --- .../components/calendar/calendar.module.css | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/raystack/components/calendar/calendar.module.css b/packages/raystack/components/calendar/calendar.module.css index 7f8e30f68..5cffb485a 100644 --- a/packages/raystack/components/calendar/calendar.module.css +++ b/packages/raystack/components/calendar/calendar.module.css @@ -268,6 +268,19 @@ z-index: 1; } +.selected:not(.range_middle) .day_info { + color: var(--rs-color-foreground-base-emphasis); +} + +.selected:not(.range_middle) .day_info * { + color: var(--rs-color-foreground-base-emphasis); +} + +.selected:not(.range_middle) .day_info svg { + color: var(--rs-color-foreground-base-emphasis); + fill: var(--rs-color-foreground-base-emphasis); +} + .day_number { display: flex; align-items: center; From eb7c576c8131f791184480bf21defb55070744e6 Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Fri, 21 Nov 2025 08:27:39 +0530 Subject: [PATCH 4/8] fix: tests --- .../calendar/__tests__/calendar.test.tsx | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/raystack/components/calendar/__tests__/calendar.test.tsx b/packages/raystack/components/calendar/__tests__/calendar.test.tsx index ae76288e2..c4c6d8805 100644 --- a/packages/raystack/components/calendar/__tests__/calendar.test.tsx +++ b/packages/raystack/components/calendar/__tests__/calendar.test.tsx @@ -1,4 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react'; +import dayjs from 'dayjs'; import { describe, expect, it, vi } from 'vitest'; import { Calendar } from '../calendar'; import styles from '../calendar.module.css'; @@ -115,10 +116,11 @@ describe('Calendar', () => { const TestComponent = () => (
Custom Info
); + const today = dayjs().format('DD-MM-YYYY'); const { container } = render( + [today]: }} /> ); @@ -130,10 +132,11 @@ describe('Calendar', () => { }); it('applies day_button_with_info class when dateInfo is present', () => { + const today = dayjs().format('DD-MM-YYYY'); const { container } = render( Info + [today]:
Info
}} /> ); @@ -145,10 +148,11 @@ describe('Calendar', () => { }); it('does not render dateInfo for dates not in the dateInfo object', () => { + const today = dayjs().format('DD-MM-YYYY'); const { container } = render( Info + [today]:
Info
}} /> ); @@ -159,11 +163,13 @@ describe('Calendar', () => { }); it('handles multiple dates with dateInfo', () => { + const today = dayjs().format('DD-MM-YYYY'); + const tomorrow = dayjs().add(1, 'day').format('DD-MM-YYYY'); const { container } = render( Info 1, - '20-01-2024':
Info 2
+ [today]:
Info 1
, + [tomorrow]:
Info 2
}} /> ); @@ -176,10 +182,11 @@ describe('Calendar', () => { }); it('renders date number even when dateInfo is present', () => { + const today = dayjs().format('DD-MM-YYYY'); const { container } = render( Info + [today]:
Info
}} /> ); From 3f8d73a5cd342878a183969c07c951d9e80b3a82 Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Fri, 21 Nov 2025 08:45:09 +0530 Subject: [PATCH 5/8] refactor: change snake case to camelcase --- .../calendar/__tests__/calendar.test.tsx | 4 +- .../components/calendar/calendar.module.css | 58 +++++++++---------- .../raystack/components/calendar/calendar.tsx | 30 +++++----- 3 files changed, 45 insertions(+), 47 deletions(-) diff --git a/packages/raystack/components/calendar/__tests__/calendar.test.tsx b/packages/raystack/components/calendar/__tests__/calendar.test.tsx index c4c6d8805..6ded90187 100644 --- a/packages/raystack/components/calendar/__tests__/calendar.test.tsx +++ b/packages/raystack/components/calendar/__tests__/calendar.test.tsx @@ -142,7 +142,7 @@ describe('Calendar', () => { ); const dayWithInfo = container.querySelector( - `.${styles.day_button_with_info}` + `.${styles.dayButtonWithInfo}` ); expect(dayWithInfo).toBeInTheDocument(); }); @@ -191,7 +191,7 @@ describe('Calendar', () => { /> ); - const dayNumber = container.querySelector(`.${styles.day_number}`); + const dayNumber = container.querySelector(`.${styles.dayNumber}`); expect(dayNumber).toBeInTheDocument(); }); }); diff --git a/packages/raystack/components/calendar/calendar.module.css b/packages/raystack/components/calendar/calendar.module.css index 5cffb485a..191c2570a 100644 --- a/packages/raystack/components/calendar/calendar.module.css +++ b/packages/raystack/components/calendar/calendar.module.css @@ -7,9 +7,9 @@ min-height: 346px; } -.caption_label, +.captionLabel, .dropdowns > span, -.dropdown_trigger { +.dropdownTrigger { font-family: var(--rs-font-body); font-weight: var(--rs-font-weight-medium); font-size: var(--rs-font-size-mini); @@ -21,11 +21,11 @@ font-style: normal; } -.caption_label[aria-hidden="true"] { +.captionLabel[aria-hidden="true"] { display: none; } -.nav_button { +.navButton { position: absolute; top: 0; border: 0px; @@ -35,16 +35,16 @@ height: var(--rs-space-7); } -.nav_button:disabled { +.navButton:disabled { opacity: 0.5; color: var(--rs-color-foreground-base-tertiary); } -.nav_button_previous { +.navButtonPrevious { left: 0; } -.nav_button_next { +.navButtonNext { right: 0; } @@ -59,7 +59,7 @@ width: 100%; } -.month_caption { +.monthCaption { display: flex; justify-content: center; align-items: center; @@ -98,7 +98,7 @@ background-color: transparent; } -.selected:not(.range_middle) { +.selected:not(.rangeMiddle) { width: var(--rs-space-10, 40px); height: var(--rs-space-10, 40px); border-radius: var(--rs-radius-5); @@ -109,7 +109,7 @@ } } -.range_middle { +.rangeMiddle { border-radius: 0; background-color: var(--rs-color-background-base-primary-hover); margin-bottom: var(--rs-space-1); @@ -119,22 +119,22 @@ } } -.range_middle:first-of-type { +.rangeMiddle:first-of-type { border-top-left-radius: var(--rs-radius-5); border-bottom-left-radius: var(--rs-radius-5); } -.range_middle:last-of-type { +.rangeMiddle:last-of-type { border-top-right-radius: var(--rs-radius-5); border-bottom-right-radius: var(--rs-radius-5); } -.range_start:not(.range_end) { +.rangeStart:not(.rangeEnd) { border-top-right-radius: 0; border-bottom-right-radius: 0; } -.range_end:not(.range_start) { +.rangeEnd:not(.rangeStart) { border-top-left-radius: 0; border-bottom-left-radius: 0; } @@ -147,8 +147,8 @@ color: var(--rs-color-foreground-base-tertiary); } -.range_start, -.range_end { +.rangeStart, +.rangeEnd { color: var(--rs-color-foreground-base-emphasis); } @@ -187,15 +187,15 @@ gap: var(--rs-space-3); } -.dropdown_trigger { +.dropdownTrigger { padding: var(--rs-space-1) var(--rs-space-3); } -.dropdown_icon { +.dropdownIcon { margin-left: var(--rs-space-1); } -.dropdown_content { +.dropdownContent { max-height: 400px; } @@ -216,19 +216,19 @@ background-color: var(--rs-color-background-accent-emphasis); } -.day.today .day_button_with_info::after { +.day.today .dayButtonWithInfo::after { bottom: var(--rs-space-1); } -.selected.day.today:not(.range_middle) button::after { +.selected.day.today:not(.rangeMiddle) button::after { background-color: var(--rs-color-foreground-base-emphasis); } -.range_middle.day.today button::after { +.rangeMiddle.day.today button::after { background-color: var(--rs-color-background-accent-emphasis); } -.day_button { +.dayButton { cursor: pointer; border: none; width: 100%; @@ -247,13 +247,13 @@ position: relative; } -.day_button_with_info { +.dayButtonWithInfo { display: grid; place-content: center; position: relative; } -.day_info { +.dayInfo { position: absolute; top: -2px; left: 50%; @@ -268,20 +268,20 @@ z-index: 1; } -.selected:not(.range_middle) .day_info { +.selected:not(.range-middle) .dayInfo { color: var(--rs-color-foreground-base-emphasis); } -.selected:not(.range_middle) .day_info * { +.selected:not(.rangeMiddle) .dayInfo * { color: var(--rs-color-foreground-base-emphasis); } -.selected:not(.range_middle) .day_info svg { +.selected:not(.rangeMiddle) .dayInfo svg { color: var(--rs-color-foreground-base-emphasis); fill: var(--rs-color-foreground-base-emphasis); } -.day_number { +.dayNumber { display: flex; align-items: center; justify-content: center; diff --git a/packages/raystack/components/calendar/calendar.tsx b/packages/raystack/components/calendar/calendar.tsx index eb62ae48c..3d0a8bf69 100644 --- a/packages/raystack/components/calendar/calendar.tsx +++ b/packages/raystack/components/calendar/calendar.tsx @@ -71,9 +71,9 @@ function DropDown({ onOpenChange={setOpen} > - + @@ -171,15 +171,13 @@ export const Calendar = function ({ {...buttonProps} className={cx( buttonProps.className, - hasDateInfo && styles.day_button_with_info + hasDateInfo && styles.dayButtonWithInfo )} > {hasDateInfo && ( -
{dateComponent}
+
{dateComponent}
)} - - {buttonProps.children} - + {buttonProps.children}
); @@ -197,10 +195,10 @@ export const Calendar = function ({ ) }} classNames={{ - caption_label: styles.caption_label, - button_previous: `${styles.nav_button} ${styles.nav_button_previous}`, - button_next: `${styles.nav_button} ${styles.nav_button_next}`, - month_caption: styles.month_caption, + caption_label: styles.captionLabel, + button_previous: `${styles.navButton} ${styles.navButtonPrevious}`, + button_next: `${styles.navButton} ${styles.navButtonNext}`, + month_caption: styles.monthCaption, months: styles.months, nav: styles.nav, day: styles.day, @@ -211,10 +209,10 @@ export const Calendar = function ({ weekday: styles.weekday, disabled: styles.disabled, selected: styles.selected, - day_button: styles.day_button, - range_middle: styles.range_middle, - range_end: styles.range_end, - range_start: styles.range_start, + day_button: styles.dayButton, + range_middle: styles.rangeMiddle, + range_end: styles.rangeEnd, + range_start: styles.rangeStart, hidden: styles.hidden, dropdowns: styles.dropdowns, ...classNames From b00ef709d8c7fe3652d528c0c69b2010c95ca9fb Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Fri, 21 Nov 2025 08:51:10 +0530 Subject: [PATCH 6/8] fix: css --- apps/www/src/app/examples/page.tsx | 6 +++--- packages/raystack/components/calendar/calendar.module.css | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/apps/www/src/app/examples/page.tsx b/apps/www/src/app/examples/page.tsx index 5b8c1526a..1daaded5f 100644 --- a/apps/www/src/app/examples/page.tsx +++ b/apps/www/src/app/examples/page.tsx @@ -251,7 +251,7 @@ const Page = () => { [dayjs().format('DD-MM-YYYY')]: ( { [dayjs().add(5, 'day').format('DD-MM-YYYY')]: ( { [dayjs().add(10, 'day').format('DD-MM-YYYY')]: ( Date: Wed, 26 Nov 2025 13:37:45 +0530 Subject: [PATCH 7/8] merge: merge main --- .../components/badge/badge.module.css | 6 +---- .../components/callout/callout.module.css | 22 +++++-------------- .../empty-state/empty-state.module.css | 3 +-- .../filter-chip/filter-chip.module.css | 4 ++-- .../components/sheet/sheet.module.css | 2 +- .../raystack/components/tabs/tabs.module.css | 6 ++--- .../components/text-area/text-area.module.css | 2 +- 7 files changed, 14 insertions(+), 31 deletions(-) diff --git a/packages/raystack/components/badge/badge.module.css b/packages/raystack/components/badge/badge.module.css index ae0ecbd05..63c46ee4f 100644 --- a/packages/raystack/components/badge/badge.module.css +++ b/packages/raystack/components/badge/badge.module.css @@ -59,11 +59,7 @@ } .badge-gradient { - background: linear-gradient( - to right, - #AD00E933 0%, - #EF040433 100% - ); + background: linear-gradient(to right, #ad00e933 0%, #ef040433 100%); color: var(--rs-color-foreground-base-primary); } diff --git a/packages/raystack/components/callout/callout.module.css b/packages/raystack/components/callout/callout.module.css index db206bd6d..c447bef7e 100644 --- a/packages/raystack/components/callout/callout.module.css +++ b/packages/raystack/components/callout/callout.module.css @@ -101,11 +101,7 @@ } .callout-gradient { - background: radial-gradient( - circle, - #AD00E933 0%, - #EF040433 100% - ); + background: radial-gradient(circle, #ad00e933 0%, #ef040433 100%); color: var(--rs-color-foreground-base-primary); } @@ -199,21 +195,13 @@ /* Gradient variants */ .callout-gradient { - background: radial-gradient( - circle, - #AD00E933 0%, - #EF040433 100% - ); + background: radial-gradient(circle, #ad00e933 0%, #ef040433 100%); color: var(--rs-color-foreground-base-primary); } .callout-outline.callout-gradient { - background: radial-gradient( - circle, - #AD00E933 0%, - #EF040433 100% - ); - border: 1px solid #EF040444; + background: radial-gradient(circle, #ad00e933 0%, #ef040433 100%); + border: 1px solid #ef040444; color: var(--rs-color-foreground-base-primary); } @@ -250,4 +238,4 @@ background: var(--rs-color-background-base-primary); border: 1px solid var(--rs-color-border-base-tertiary); color: var(--rs-color-foreground-base-primary); -} \ No newline at end of file +} diff --git a/packages/raystack/components/empty-state/empty-state.module.css b/packages/raystack/components/empty-state/empty-state.module.css index e554cd210..078e62ff1 100644 --- a/packages/raystack/components/empty-state/empty-state.module.css +++ b/packages/raystack/components/empty-state/empty-state.module.css @@ -29,7 +29,6 @@ width: 100%; } - .icon::before { content: ""; position: absolute; @@ -110,4 +109,4 @@ width: 100%; height: 100%; padding: var(--rs-space-9) var(--rs-space-5); -} \ No newline at end of file +} diff --git a/packages/raystack/components/filter-chip/filter-chip.module.css b/packages/raystack/components/filter-chip/filter-chip.module.css index 488155e69..404fffe97 100644 --- a/packages/raystack/components/filter-chip/filter-chip.module.css +++ b/packages/raystack/components/filter-chip/filter-chip.module.css @@ -153,7 +153,7 @@ button.selectValue:hover { } /* Match height of InputField when FilterChip variant is text */ -.chip[data-variant='text'] .inputField { +.chip[data-variant="text"] .inputField { border: none; box-shadow: none; height: 24px; @@ -181,7 +181,7 @@ button.selectValue:hover { } /* Remove border and match height of the DatePicker when FilterChip variant is text */ -.chip[data-variant='text'] .dateField { +.chip[data-variant="text"] .dateField { border: none; box-shadow: none; height: 24px; diff --git a/packages/raystack/components/sheet/sheet.module.css b/packages/raystack/components/sheet/sheet.module.css index 2a7ba3952..01d826814 100644 --- a/packages/raystack/components/sheet/sheet.module.css +++ b/packages/raystack/components/sheet/sheet.module.css @@ -185,4 +185,4 @@ .overlay { animation: none; } -} \ No newline at end of file +} diff --git a/packages/raystack/components/tabs/tabs.module.css b/packages/raystack/components/tabs/tabs.module.css index aee3411a4..da0fc850e 100644 --- a/packages/raystack/components/tabs/tabs.module.css +++ b/packages/raystack/components/tabs/tabs.module.css @@ -43,7 +43,7 @@ color: var(--rs-color-foreground-base-primary); } -.trigger[data-state='active'] { +.trigger[data-state="active"] { background-color: var(--rs-color-background-base-primary); color: var(--rs-color-foreground-base-primary); box-shadow: var(--rs-shadow-feather); @@ -72,6 +72,6 @@ outline: none; } -.content[data-state='inactive'] { +.content[data-state="inactive"] { display: none; -} \ No newline at end of file +} diff --git a/packages/raystack/components/text-area/text-area.module.css b/packages/raystack/components/text-area/text-area.module.css index 69e0af183..3dd4781f3 100644 --- a/packages/raystack/components/text-area/text-area.module.css +++ b/packages/raystack/components/text-area/text-area.module.css @@ -117,4 +117,4 @@ .helperTextDisabled { color: var(--rs-color-foreground-base-tertiary); -} \ No newline at end of file +} From 132cbb2128ce6d78f526953e25a31a5b7ea69114 Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Wed, 26 Nov 2025 13:58:39 +0530 Subject: [PATCH 8/8] feat: add function | object support --- apps/www/src/app/examples/page.tsx | 80 ++++++++++++++++++- .../content/docs/components/calendar/props.ts | 19 ++++- .../calendar/__tests__/calendar.test.tsx | 43 ++++++++++ .../raystack/components/calendar/calendar.tsx | 9 ++- 4 files changed, 144 insertions(+), 7 deletions(-) diff --git a/apps/www/src/app/examples/page.tsx b/apps/www/src/app/examples/page.tsx index 1daaded5f..5eb06a1d6 100644 --- a/apps/www/src/app/examples/page.tsx +++ b/apps/www/src/app/examples/page.tsx @@ -242,7 +242,7 @@ const Page = () => { weight='medium' style={{ marginTop: '32px', marginBottom: '16px' }} > - Calendar with Date Info + Calendar with Date Info (Object)
{ }} /> + + Calendar with Date Info (Function) + + + { + const today = new Date(); + const isToday = + date.getDate() === today.getDate() && + date.getMonth() === today.getMonth() && + date.getFullYear() === today.getFullYear(); + + // Show info on Sundays + if (date.getDay() === 0) { + return ( + + + + Sun + + + ); + } + + // Show info on 15th of any month + if (date.getDate() === 15) { + return ( + + + + 15th + + + ); + } + + // Show info for today + if (isToday) { + return ( + + + + Today + + + ); + } + + return null; + }} + /> + 17% } + * + * @example + * // Object approach (static data) + * dateInfo={{ "15-01-2024":
17%
}} + * + * @example + * // Function approach (dynamic logic) + * dateInfo={(date) => date.getDay() === 0 ?
Sunday
: null} */ - dateInfo?: Record; + dateInfo?: + | Record + | ((date: Date) => React.ReactNode | null); /** Boolean to show days from previous/next months */ showOutsideDays?: boolean; diff --git a/packages/raystack/components/calendar/__tests__/calendar.test.tsx b/packages/raystack/components/calendar/__tests__/calendar.test.tsx index 6ded90187..7cf7b1422 100644 --- a/packages/raystack/components/calendar/__tests__/calendar.test.tsx +++ b/packages/raystack/components/calendar/__tests__/calendar.test.tsx @@ -181,6 +181,49 @@ describe('Calendar', () => { expect(info1 || info2).toBeTruthy(); }); + it('supports function-based dateInfo', () => { + const { container } = render( + { + // Show info only on Sundays + if (date.getDay() === 0) { + return
Sunday
; + } + return null; + }} + /> + ); + + // Should render for Sundays if any are visible in current month + // The querySelector will return null if not found, which is fine + const sundayInfo = container.querySelector('[data-testid="sunday-info"]'); + // Test passes if function approach works (may or may not find Sunday depending on month) + expect(container).toBeInTheDocument(); + }); + + it('supports function-based dateInfo with date logic', () => { + const today = new Date(); + const { container } = render( + { + // Show info only for today + if ( + date.getDate() === today.getDate() && + date.getMonth() === today.getMonth() && + date.getFullYear() === today.getFullYear() + ) { + return
Today
; + } + return null; + }} + /> + ); + + expect( + container.querySelector('[data-testid="today-info"]') + ).toBeInTheDocument(); + }); + it('renders date number even when dateInfo is present', () => { const today = dayjs().format('DD-MM-YYYY'); const { container } = render( diff --git a/packages/raystack/components/calendar/calendar.tsx b/packages/raystack/components/calendar/calendar.tsx index 3d0a8bf69..3fb94e441 100644 --- a/packages/raystack/components/calendar/calendar.tsx +++ b/packages/raystack/components/calendar/calendar.tsx @@ -29,7 +29,7 @@ interface OnDropdownOpen { interface CalendarPropsExtended { showTooltip?: boolean; tooltipMessages?: Record; - dateInfo?: Record; + dateInfo?: Record | ((date: Date) => ReactNode | null); loadingData?: boolean; timeZone?: string; } @@ -158,7 +158,12 @@ export const Calendar = function ({ const { day, ...buttonProps } = props; const dateKey = dateLib.format(day.date, 'dd-MM-yyyy'); const message = tooltipMessages[dateKey]; - const dateComponent = dateInfo[dateKey]; + + // Support both object and function for dateInfo + const dateComponent = + typeof dateInfo === 'function' + ? dateInfo(day.date) + : dateInfo[dateKey]; const hasDateInfo = Boolean(dateComponent); return (