Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
- [#3740](https://github.com/plotly/dash/pull/3740) Fix cannot tab into dropdowns in Safari
- [#2462](https://github.com/plotly/dash/issues/2462) Allow `MATCH` in `Input`/`State` when the callback's `Output` has no wildcards (fixed-id Output, no Output, or `ALL`-only wildcard Output). `ALLSMALLER` still requires a corresponding `MATCH` in an Output.
- [#3759](https://github.com/plotly/dash/pull/3759) Fix the issue where `Patch` objects cannot be updated via `set_props()` in `websocket` callback. Fix [#3742](https://github.com/plotly/dash/issues/3742)
- [#3789](https://github.com/plotly/dash/pull/3789) Fixed extra wrapper in `DatePickerRange` and `DatePickerSingle` causing styling and layout issues.

## [4.2.0rc3] - 2026-05-12

Expand Down
295 changes: 147 additions & 148 deletions components/dash-core-components/src/fragments/DatePickerRange.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
const minimumNightsDates: Date[] = [];
for (let i = 1; i < minimum_nights; i++) {
minimumNightsDates.push(addDays(internalStartDate, i));
minimumNightsDates.push(subDays(internalStartDate, i));

Check warning on line 89 in components/dash-core-components/src/fragments/DatePickerRange.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Do not call `Array#push()` multiple times.

See more on https://sonarcloud.io/project/issues?id=plotly_dash&issues=AZ5fn_aYmRGlxnBVzMwu&open=AZ5fn_aYmRGlxnBVzMwu&pullRequest=3789
}
return [...baseDates, ...minimumNightsDates];
}
Expand All @@ -105,8 +105,8 @@
const containerRef = useRef<HTMLDivElement>(null);
const startInputRef = useRef<HTMLInputElement | null>(null);
const endInputRef = useRef<HTMLInputElement | null>(null);
const startAutosizeRef = useRef<any>(null);

Check warning on line 108 in components/dash-core-components/src/fragments/DatePickerRange.tsx

View workflow job for this annotation

GitHub Actions / DCC Lint Tests (Python 3.8)

Unexpected any. Specify a different type

Check warning on line 108 in components/dash-core-components/src/fragments/DatePickerRange.tsx

View workflow job for this annotation

GitHub Actions / DCC Lint Tests (Python 3.12)

Unexpected any. Specify a different type
const endAutosizeRef = useRef<any>(null);

Check warning on line 109 in components/dash-core-components/src/fragments/DatePickerRange.tsx

View workflow job for this annotation

GitHub Actions / DCC Lint Tests (Python 3.8)

Unexpected any. Specify a different type

Check warning on line 109 in components/dash-core-components/src/fragments/DatePickerRange.tsx

View workflow job for this annotation

GitHub Actions / DCC Lint Tests (Python 3.12)

Unexpected any. Specify a different type
const calendarRef = useRef<CalendarHandle>(null);
const isNewRangeRef = useRef(false);
const hasPortal = with_portal || with_full_screen_portal;
Expand Down Expand Up @@ -161,13 +161,13 @@
start_date: dateAsStr(internalStartDate),
end_date: dateAsStr(internalEndDate),
});
} else if (!internalStartDate && !internalEndDate) {
// Both dates cleared - send both
setProps({
start_date: dateAsStr(internalStartDate),
end_date: dateAsStr(internalEndDate),
});
} else if (endChanged && !internalEndDate) {

Check warning on line 170 in components/dash-core-components/src/fragments/DatePickerRange.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This branch's code block is the same as the block for the branch on line 158.

See more on https://sonarcloud.io/project/issues?id=plotly_dash&issues=AZ5fn_aYmRGlxnBVzMwv&open=AZ5fn_aYmRGlxnBVzMwv&pullRequest=3789
// End date was cleared (user started a new range).
setProps({
start_date: dateAsStr(internalStartDate) ?? null,
Expand Down Expand Up @@ -308,7 +308,7 @@
: ArrowLeftIcon;

const handleSelectionChange = useCallback(
(start?: Date, end?: Date) => {

Check failure on line 311 in components/dash-core-components/src/fragments/DatePickerRange.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 21 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=plotly_dash&issues=AZ5fn_aYmRGlxnBVzMww&open=AZ5fn_aYmRGlxnBVzMww&pullRequest=3789
const isNewSelection =
isSameDay(start, end) &&
((!internalStartDate && !internalEndDate) ||
Expand Down Expand Up @@ -357,159 +357,158 @@
);

return (
<ResizeDetector onResize={handleResize} targets={[containerRef]}>
<div className="dash-datepicker" ref={containerRef}>
<Popover.Root
open={!disabled && isCalendarOpen}
onOpenChange={disabled ? undefined : setIsCalendarOpen}
>
<Popover.Trigger asChild disabled={disabled}>
<div
id={accessibleId + '-wrapper'}
className={classNames}
style={style}
aria-labelledby={`${accessibleId} ${accessibleId}-end-date ${start_date_id} ${end_date_id}`}
aria-haspopup="dialog"
aria-expanded={isCalendarOpen}
aria-disabled={disabled}
onClick={e => {
e.preventDefault();
if (!isCalendarOpen && !disabled) {
setIsCalendarOpen(true);
}
}}
>
<AutosizeInput
ref={startAutosizeRef}
inputRef={node => {
startInputRef.current = node;
}}
type="text"
id={start_date_id || accessibleId}
inputClassName="dash-datepicker-input dash-datepicker-start-date"
value={startInputValue}
onChange={e =>
setStartInputValue(e.target?.value)
}
onKeyDown={handleStartInputKeyDown}
onFocus={() => {
if (isCalendarOpen) {
sendStartInputAsDate();
}
}}
placeholder={start_date_placeholder_text}
disabled={disabled}
dir={direction}
aria-label={start_date_placeholder_text}
/>
<ArrowIcon className="dash-datepicker-range-arrow" />
<AutosizeInput
ref={endAutosizeRef}
inputRef={node => {
endInputRef.current = node;
}}
type="text"
id={end_date_id || accessibleId + '-end-date'}
inputClassName="dash-datepicker-input dash-datepicker-end-date"
value={endInputValue}
onChange={e =>
setEndInputValue(e.target?.value)
}
onKeyDown={handleEndInputKeyDown}
onFocus={() => {
if (isCalendarOpen) {
sendEndInputAsDate();
}
}}
placeholder={end_date_placeholder_text}
disabled={disabled}
dir={direction}
aria-label={end_date_placeholder_text}
/>
{clearable && !disabled && (
<a
className="dash-datepicker-clear"
onClick={clearSelection}
aria-label="Clear Dates"
>
<Cross1Icon />
</a>
)}
<CaretDownIcon className="dash-datepicker-caret-icon" />
</div>
</Popover.Trigger>

<Popover.Portal
container={hasPortal ? undefined : containerRef.current}
<div className="dash-datepicker" ref={containerRef}>
<ResizeDetector onResize={handleResize} targets={[containerRef]}/>
<Popover.Root
open={!disabled && isCalendarOpen}
onOpenChange={disabled ? undefined : setIsCalendarOpen}
>
<Popover.Trigger asChild disabled={disabled}>
<div
id={accessibleId + '-wrapper'}
className={classNames}
style={style}
aria-labelledby={`${accessibleId} ${accessibleId}-end-date ${start_date_id} ${end_date_id}`}
aria-haspopup="dialog"
aria-expanded={isCalendarOpen}
aria-disabled={disabled}
onClick={e => {
e.preventDefault();
if (!isCalendarOpen && !disabled) {
setIsCalendarOpen(true);
}
}}
>

Check warning on line 381 in components/dash-core-components/src/fragments/DatePickerRange.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Avoid non-native interactive elements. If using native HTML is not possible, add an appropriate role and support for tabbing, mouse, keyboard, and touch inputs to an interactive content element.

See more on https://sonarcloud.io/project/issues?id=plotly_dash&issues=AZ5fn_aYmRGlxnBVzMwx&open=AZ5fn_aYmRGlxnBVzMwx&pullRequest=3789

Check warning on line 381 in components/dash-core-components/src/fragments/DatePickerRange.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Visible, non-interactive elements with click handlers must have at least one keyboard listener.

See more on https://sonarcloud.io/project/issues?id=plotly_dash&issues=AZ5fn_aYmRGlxnBVzMwy&open=AZ5fn_aYmRGlxnBVzMwy&pullRequest=3789
<Popover.Content
className={`dash-datepicker-content${
hasPortal ? ' dash-datepicker-portal' : ''
}${
with_full_screen_portal
? ' dash-datepicker-fullscreen'
: ''
}`}
style={portalStyle}
align={hasPortal ? 'center' : 'start'}
sideOffset={hasPortal ? 0 : 5}
avoidCollisions={!hasPortal}
onInteractOutside={
with_full_screen_portal
? e => e.preventDefault()
: undefined
<AutosizeInput
ref={startAutosizeRef}
inputRef={node => {
startInputRef.current = node;
}}
type="text"
id={start_date_id || accessibleId}
inputClassName="dash-datepicker-input dash-datepicker-start-date"
value={startInputValue}
onChange={e =>
setStartInputValue(e.target?.value)
}
onOpenAutoFocus={e => e.preventDefault()}
onCloseAutoFocus={e => {
e.preventDefault();
// Only focus if focus is not already on one of the inputs
const inputs: (Element | null)[] = [
startInputRef.current,
endInputRef.current,
];
if (inputs.includes(document.activeElement)) {
return;
onKeyDown={handleStartInputKeyDown}
onFocus={() => {
if (isCalendarOpen) {
sendStartInputAsDate();
}

// Keeps focus on the component when the calendar closes
if (!startInputValue) {
startInputRef.current?.focus();
} else {
endInputRef.current?.focus();
}}
placeholder={start_date_placeholder_text}
disabled={disabled}
dir={direction}
aria-label={start_date_placeholder_text}
/>
<ArrowIcon className="dash-datepicker-range-arrow" />
<AutosizeInput
ref={endAutosizeRef}
inputRef={node => {
endInputRef.current = node;
}}
type="text"
id={end_date_id || accessibleId + '-end-date'}
inputClassName="dash-datepicker-input dash-datepicker-end-date"
value={endInputValue}
onChange={e =>
setEndInputValue(e.target?.value)
}
onKeyDown={handleEndInputKeyDown}
onFocus={() => {
if (isCalendarOpen) {
sendEndInputAsDate();
}
}}
>
{with_full_screen_portal && (
<button
className="dash-datepicker-close-button"
onClick={() => setIsCalendarOpen(false)}
aria-label="Close calendar"
>
<Cross1Icon />
</button>
)}
<Calendar
ref={calendarRef}
initialVisibleDate={initialCalendarDate}
selectionStart={internalStartDate}
selectionEnd={internalEndDate}
minDateAllowed={minDate}
maxDateAllowed={maxDate}
disabledDates={disabledDates}
firstDayOfWeek={first_day_of_week}
showOutsideDays={show_outside_days}
monthFormat={month_format}
numberOfMonthsShown={number_of_months_shown}
calendarOrientation={calendar_orientation}
daySize={day_size}
direction={direction}
onSelectionChange={handleSelectionChange}
/>
</Popover.Content>
</Popover.Portal>
</Popover.Root>
</div>
</ResizeDetector>
placeholder={end_date_placeholder_text}
disabled={disabled}
dir={direction}
aria-label={end_date_placeholder_text}
/>
{clearable && !disabled && (
<a
className="dash-datepicker-clear"
onClick={clearSelection}
aria-label="Clear Dates"
>

Check warning on line 434 in components/dash-core-components/src/fragments/DatePickerRange.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Anchor used as a button. Anchors are primarily expected to navigate. Use the button element instead.

See more on https://sonarcloud.io/project/issues?id=plotly_dash&issues=AZ5fn_aYmRGlxnBVzMw0&open=AZ5fn_aYmRGlxnBVzMw0&pullRequest=3789

Check warning on line 434 in components/dash-core-components/src/fragments/DatePickerRange.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Avoid non-native interactive elements. If using native HTML is not possible, add an appropriate role and support for tabbing, mouse, keyboard, and touch inputs to an interactive content element.

See more on https://sonarcloud.io/project/issues?id=plotly_dash&issues=AZ5fn_aYmRGlxnBVzMwz&open=AZ5fn_aYmRGlxnBVzMwz&pullRequest=3789

Check warning on line 434 in components/dash-core-components/src/fragments/DatePickerRange.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Visible, non-interactive elements with click handlers must have at least one keyboard listener.

See more on https://sonarcloud.io/project/issues?id=plotly_dash&issues=AZ5fn_aYmRGlxnBVzMw1&open=AZ5fn_aYmRGlxnBVzMw1&pullRequest=3789
<Cross1Icon />
</a>
)}
<CaretDownIcon className="dash-datepicker-caret-icon" />
</div>
</Popover.Trigger>

<Popover.Portal
container={hasPortal ? undefined : containerRef.current}
>
<Popover.Content
className={`dash-datepicker-content${
hasPortal ? ' dash-datepicker-portal' : ''
}${
with_full_screen_portal
? ' dash-datepicker-fullscreen'
: ''
}`}
style={portalStyle}
align={hasPortal ? 'center' : 'start'}
sideOffset={hasPortal ? 0 : 5}
avoidCollisions={!hasPortal}
onInteractOutside={
with_full_screen_portal
? e => e.preventDefault()
: undefined
}
onOpenAutoFocus={e => e.preventDefault()}
onCloseAutoFocus={e => {
e.preventDefault();
// Only focus if focus is not already on one of the inputs
const inputs: (Element | null)[] = [
startInputRef.current,
endInputRef.current,
];
if (inputs.includes(document.activeElement)) {
return;
}

// Keeps focus on the component when the calendar closes
if (!startInputValue) {

Check warning on line 475 in components/dash-core-components/src/fragments/DatePickerRange.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected negated condition.

See more on https://sonarcloud.io/project/issues?id=plotly_dash&issues=AZ5fn_aYmRGlxnBVzMw2&open=AZ5fn_aYmRGlxnBVzMw2&pullRequest=3789
startInputRef.current?.focus();
} else {
endInputRef.current?.focus();
}
}}
>
{with_full_screen_portal && (
<button
className="dash-datepicker-close-button"
onClick={() => setIsCalendarOpen(false)}
aria-label="Close calendar"
>
<Cross1Icon />
</button>
)}
<Calendar
ref={calendarRef}
initialVisibleDate={initialCalendarDate}
selectionStart={internalStartDate}
selectionEnd={internalEndDate}
minDateAllowed={minDate}
maxDateAllowed={maxDate}
disabledDates={disabledDates}
firstDayOfWeek={first_day_of_week}
showOutsideDays={show_outside_days}
monthFormat={month_format}
numberOfMonthsShown={number_of_months_shown}
calendarOrientation={calendar_orientation}
daySize={day_size}
direction={direction}
onSelectionChange={handleSelectionChange}
/>
</Popover.Content>
</Popover.Portal>
</Popover.Root>
</div>
);
};

Expand Down
Loading
Loading