Skip to content

Commit 42c8436

Browse files
authored
fix: apply inline styles to component slots (#2995)
* fix: apply inline styles to component slots * chore: add changeset for inline styles fix
1 parent 839f270 commit 42c8436

6 files changed

Lines changed: 142 additions & 6 deletions

File tree

.changeset/clean-masks-drop.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"react-day-picker": patch
3+
---
4+
5+
fix: apply inline styles to all component slots.

packages/react-day-picker/src/DayPicker.test.tsx

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import type { MonthsProps } from "./components/Months";
1717
import { DayPicker } from "./DayPicker";
1818
import { labelMonthDropdown, labelYearDropdown } from "./labels/index.js";
1919
import { ja } from "./locale/ja.js";
20+
import { UI } from "./UI";
2021

2122
const testId = "test";
2223
const dayPicker = () => screen.getByTestId(testId);
@@ -49,6 +50,99 @@ test("apply classnames and style according to props", () => {
4950
expect(dayPicker()).toHaveStyle({ color: "rgb(255, 0, 0)" });
5051
});
5152

53+
describe("when rendering custom inline styles for component slots", () => {
54+
beforeEach(() => {
55+
render(
56+
<DayPicker
57+
data-testid={testId}
58+
styles={{
59+
[UI.CaptionLabel]: { color: "purple" },
60+
[UI.Chevron]: { fill: "red" },
61+
[UI.NextMonthButton]: { backgroundColor: "green" },
62+
[UI.PreviousMonthButton]: { backgroundColor: "blue" },
63+
}}
64+
/>,
65+
);
66+
});
67+
68+
test("applies the previous button style", () => {
69+
expect(previousButton()).toHaveAttribute(
70+
"style",
71+
"background-color: blue;",
72+
);
73+
});
74+
75+
test("applies the next button style", () => {
76+
expect(nextButton()).toHaveAttribute("style", "background-color: green;");
77+
});
78+
79+
test("applies the chevron style", () => {
80+
expect(document.querySelector(".rdp-chevron")).toHaveAttribute(
81+
"style",
82+
"fill: red;",
83+
);
84+
});
85+
86+
test("applies the caption label style", () => {
87+
expect(document.querySelector(".rdp-caption_label")).toHaveAttribute(
88+
"style",
89+
"color: purple;",
90+
);
91+
});
92+
});
93+
94+
describe("when rendering custom inline styles for dropdown slots", () => {
95+
beforeEach(() => {
96+
render(
97+
<DayPicker
98+
captionLayout="dropdown"
99+
endMonth={new Date(2025, 11)}
100+
month={new Date(2024, 0)}
101+
startMonth={new Date(2024, 0)}
102+
styles={{
103+
[UI.CaptionLabel]: { color: "purple" },
104+
[UI.Chevron]: { fill: "red" },
105+
[UI.Dropdown]: { backgroundColor: "yellow" },
106+
[UI.DropdownRoot]: { borderColor: "blue" },
107+
[UI.MonthsDropdown]: { color: "green" },
108+
[UI.YearsDropdown]: { fontWeight: 700 },
109+
}}
110+
/>,
111+
);
112+
});
113+
114+
test("applies the dropdown root style", () => {
115+
expect(
116+
screen.getByRole("combobox", { name: labelMonthDropdown() })
117+
.parentElement,
118+
).toHaveAttribute("style", "border-color: blue;");
119+
});
120+
121+
test("applies the month dropdown style with the base dropdown style", () => {
122+
expect(
123+
screen.getByRole("combobox", { name: labelMonthDropdown() }),
124+
).toHaveAttribute("style", "background-color: yellow; color: green;");
125+
});
126+
127+
test("applies the year dropdown style with the base dropdown style", () => {
128+
expect(
129+
screen.getByRole("combobox", { name: labelYearDropdown() }),
130+
).toHaveAttribute("style", "background-color: yellow; font-weight: 700;");
131+
});
132+
133+
test("applies the dropdown caption label style", () => {
134+
expect(
135+
document.querySelector(".rdp-dropdown_root .rdp-caption_label"),
136+
).toHaveAttribute("style", "color: purple;");
137+
});
138+
139+
test("applies the dropdown chevron style", () => {
140+
expect(
141+
document.querySelector(".rdp-dropdown_root .rdp-chevron"),
142+
).toHaveAttribute("style", "fill: red;");
143+
});
144+
});
145+
52146
test("forward aria attributes to the root element", () => {
53147
render(
54148
<DayPicker

packages/react-day-picker/src/DayPicker.tsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,20 @@ export function DayPicker(initialProps: DayPickerProps) {
360360

361361
const dataAttributes = getDataAttributes(props);
362362

363+
const getDropdownStyle = (dropdown: UI.MonthsDropdown | UI.YearsDropdown) => {
364+
const dropdownStyle = styles?.[UI.Dropdown];
365+
const specificDropdownStyle = styles?.[dropdown];
366+
367+
if (!dropdownStyle && !specificDropdownStyle) {
368+
return undefined;
369+
}
370+
371+
return {
372+
...dropdownStyle,
373+
...specificDropdownStyle,
374+
};
375+
};
376+
363377
const rootElRef = useRef<HTMLDivElement>(null);
364378
useAnimation(rootElRef, Boolean(props.animate), {
365379
classNames,
@@ -438,6 +452,7 @@ export function DayPicker(initialProps: DayPickerProps) {
438452
<components.PreviousMonthButton
439453
type="button"
440454
className={classNames[UI.PreviousMonthButton]}
455+
style={styles?.[UI.PreviousMonthButton]}
441456
tabIndex={previousMonth ? undefined : -1}
442457
aria-disabled={previousMonth ? undefined : true}
443458
aria-label={labelPrevious(previousMonth)}
@@ -447,6 +462,7 @@ export function DayPicker(initialProps: DayPickerProps) {
447462
<components.Chevron
448463
disabled={previousMonth ? undefined : true}
449464
className={classNames[UI.Chevron]}
465+
style={styles?.[UI.Chevron]}
450466
orientation={props.dir === "rtl" ? "right" : "left"}
451467
/>
452468
</components.PreviousMonthButton>
@@ -483,7 +499,7 @@ export function DayPicker(initialProps: DayPickerProps) {
483499
formatters,
484500
dateLib,
485501
)}
486-
style={styles?.[UI.Dropdown]}
502+
style={getDropdownStyle(UI.MonthsDropdown)}
487503
value={dateLib.getMonth(calendarMonth.date)}
488504
/>
489505
) : (
@@ -511,7 +527,7 @@ export function DayPicker(initialProps: DayPickerProps) {
511527
dateLib,
512528
Boolean(props.reverseYears),
513529
)}
514-
style={styles?.[UI.Dropdown]}
530+
style={getDropdownStyle(UI.YearsDropdown)}
515531
value={dateLib.getYear(calendarMonth.date)}
516532
/>
517533
) : (
@@ -553,6 +569,7 @@ export function DayPicker(initialProps: DayPickerProps) {
553569
) : (
554570
<components.CaptionLabel
555571
className={classNames[UI.CaptionLabel]}
572+
style={styles?.[UI.CaptionLabel]}
556573
role="status"
557574
aria-live="polite"
558575
>
@@ -570,6 +587,7 @@ export function DayPicker(initialProps: DayPickerProps) {
570587
<components.NextMonthButton
571588
type="button"
572589
className={classNames[UI.NextMonthButton]}
590+
style={styles?.[UI.NextMonthButton]}
573591
tabIndex={nextMonth ? undefined : -1}
574592
aria-disabled={nextMonth ? undefined : true}
575593
aria-label={labelNext(nextMonth)}
@@ -579,6 +597,7 @@ export function DayPicker(initialProps: DayPickerProps) {
579597
<components.Chevron
580598
disabled={nextMonth ? undefined : true}
581599
className={classNames[UI.Chevron]}
600+
style={styles?.[UI.Chevron]}
582601
orientation={props.dir === "rtl" ? "left" : "right"}
583602
/>
584603
</components.NextMonthButton>

packages/react-day-picker/src/components/Chevron.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import React from "react";
88
*/
99
export function Chevron(props: {
1010
className?: string;
11+
style?: React.CSSProperties;
1112
/**
1213
* The size of the chevron.
1314
*
@@ -19,11 +20,17 @@ export function Chevron(props: {
1920
/** The orientation of the chevron. */
2021
orientation?: "up" | "down" | "left" | "right";
2122
}) {
22-
const { size = 24, orientation = "left", className } = props;
23+
const { size = 24, orientation = "left", className, style } = props;
2324

2425
return (
2526
// biome-ignore lint/a11y/noSvgWithoutTitle: handled by the parent component
26-
<svg className={className} width={size} height={size} viewBox="0 0 24 24">
27+
<svg
28+
className={className}
29+
style={style}
30+
width={size}
31+
height={size}
32+
viewBox="0 0 24 24"
33+
>
2734
{orientation === "up" && (
2835
<polygon points="6.77 17 12.5 11.43 18.24 17 20 15.28 12.5 8 5 15.28" />
2936
)}

packages/react-day-picker/src/components/Dropdown.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export function Dropdown(
2525
} & Omit<SelectHTMLAttributes<HTMLSelectElement>, "children">,
2626
) {
2727
const { options, className, ...selectProps } = props;
28-
const { classNames, components } = useDayPicker();
28+
const { classNames, components, styles } = useDayPicker();
2929

3030
const cssClassSelect = [classNames[UI.Dropdown], className].join(" ");
3131

@@ -36,6 +36,7 @@ export function Dropdown(
3636
<span
3737
data-disabled={selectProps.disabled}
3838
className={classNames[UI.DropdownRoot]}
39+
style={styles?.[UI.DropdownRoot]}
3940
>
4041
<components.Select className={cssClassSelect} {...selectProps}>
4142
{options?.map(({ value, label, disabled }) => (
@@ -44,12 +45,17 @@ export function Dropdown(
4445
</components.Option>
4546
))}
4647
</components.Select>
47-
<span className={classNames[UI.CaptionLabel]} aria-hidden>
48+
<span
49+
className={classNames[UI.CaptionLabel]}
50+
style={styles?.[UI.CaptionLabel]}
51+
aria-hidden
52+
>
4853
{selectedOption?.label}
4954
<components.Chevron
5055
orientation="down"
5156
size={18}
5257
className={classNames[UI.Chevron]}
58+
style={styles?.[UI.Chevron]}
5359
/>
5460
</span>
5561
</span>

packages/react-day-picker/src/components/Nav.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export function Nav(
3636
const {
3737
components,
3838
classNames,
39+
styles,
3940
labels: { labelPrevious, labelNext },
4041
} = useDayPicker();
4142

@@ -62,6 +63,7 @@ export function Nav(
6263
<components.PreviousMonthButton
6364
type="button"
6465
className={classNames[UI.PreviousMonthButton]}
66+
style={styles?.[UI.PreviousMonthButton]}
6567
tabIndex={previousMonth ? undefined : -1}
6668
aria-disabled={previousMonth ? undefined : true}
6769
aria-label={labelPrevious(previousMonth)}
@@ -70,12 +72,14 @@ export function Nav(
7072
<components.Chevron
7173
disabled={previousMonth ? undefined : true}
7274
className={classNames[UI.Chevron]}
75+
style={styles?.[UI.Chevron]}
7376
orientation="left"
7477
/>
7578
</components.PreviousMonthButton>
7679
<components.NextMonthButton
7780
type="button"
7881
className={classNames[UI.NextMonthButton]}
82+
style={styles?.[UI.NextMonthButton]}
7983
tabIndex={nextMonth ? undefined : -1}
8084
aria-disabled={nextMonth ? undefined : true}
8185
aria-label={labelNext(nextMonth)}
@@ -85,6 +89,7 @@ export function Nav(
8589
disabled={nextMonth ? undefined : true}
8690
orientation="right"
8791
className={classNames[UI.Chevron]}
92+
style={styles?.[UI.Chevron]}
8893
/>
8994
</components.NextMonthButton>
9095
</nav>

0 commit comments

Comments
 (0)