Skip to content

Commit 6cb27f4

Browse files
authored
Merge pull request #3554 from plotly/bugfix/rc5-feedback
Bugfix: dcc redesign rc5 feedback
2 parents b2594be + d51ac09 commit 6cb27f4

File tree

9 files changed

+850
-32
lines changed

9 files changed

+850
-32
lines changed

components/dash-core-components/src/components/Input.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,11 @@ function Input({
176176
(direction: 'increment' | 'decrement') => {
177177
const currentValue = parseFloat(input.current.value) || 0;
178178
const stepAsNum = parseFloat(step as string) || 1;
179+
180+
// Count decimal places to avoid floating point precision issues
181+
const decimalPlaces = (stepAsNum.toString().split('.')[1] || '')
182+
.length;
183+
179184
const newValue =
180185
direction === 'increment'
181186
? currentValue + stepAsNum
@@ -196,8 +201,13 @@ function Input({
196201
);
197202
}
198203

199-
input.current.value = constrainedValue.toString();
200-
setValue(constrainedValue.toString());
204+
// Round to the step's decimal precision
205+
const roundedValue = parseFloat(
206+
constrainedValue.toFixed(decimalPlaces)
207+
);
208+
209+
input.current.value = roundedValue.toString();
210+
setValue(roundedValue.toString());
201211
onEvent();
202212
},
203213
[step, props.min, props.max, onEvent]

components/dash-core-components/src/components/css/datepickers.css

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,75 @@
143143
z-index: 500;
144144
box-shadow: 0px 10px 38px -10px var(--Dash-Shading-Strong),
145145
0px 10px 20px -15px var(--Dash-Shading-Weak);
146+
overscroll-behavior: contain;
147+
}
148+
149+
.dash-datepicker
150+
[data-radix-popper-content-wrapper]:has(.dash-datepicker-portal) {
151+
transform: none !important;
152+
}
153+
154+
.dash-datepicker-portal {
155+
position: fixed;
156+
inset: 0;
157+
width: 100vw;
158+
height: 100vh;
159+
max-width: 100vw;
160+
display: flex;
161+
align-items: center;
162+
justify-content: center;
163+
background: var(--Dash-Shading-Strong);
164+
border: none;
165+
box-shadow: none;
166+
overflow: visible;
167+
padding: 0;
168+
pointer-events: none;
169+
}
170+
171+
.dash-datepicker-portal .dash-datepicker-calendar-wrapper {
172+
background: var(--Dash-Fill-Inverse-Strong);
173+
border-radius: var(--Dash-Spacing);
174+
border: 1px solid var(--Dash-Stroke-Strong);
175+
padding: 16px;
176+
box-shadow: 0px 10px 38px -10px var(--Dash-Shading-Strong),
177+
0px 10px 20px -15px var(--Dash-Shading-Weak);
178+
z-index: 1;
179+
pointer-events: auto;
180+
width: fit-content;
181+
max-width: 95vw;
182+
}
183+
184+
.dash-datepicker-fullscreen {
185+
pointer-events: auto;
186+
background: var(--Dash-Fill-Inverse-Strong);
187+
}
188+
189+
.dash-datepicker-close-button {
190+
position: fixed;
191+
top: calc(var(--Dash-Spacing) * 2);
192+
right: calc(var(--Dash-Spacing) * 2);
193+
width: 24px;
194+
height: 24px;
195+
display: flex;
196+
align-items: center;
197+
justify-content: center;
198+
background: var(--Dash-Fill-Inverse-Strong);
199+
border: none;
200+
border-radius: var(--Dash-Spacing);
201+
color: var(--Dash-Text-Strong);
202+
cursor: pointer;
203+
z-index: 501;
204+
pointer-events: auto;
205+
}
206+
207+
.dash-datepicker-close-button:hover {
208+
background: var(--Dash-Fill-Weak);
209+
color: var(--Dash-Fill-Interactive-Strong);
210+
}
211+
212+
.dash-datepicker-close-button:focus {
213+
outline: 2px solid var(--Dash-Fill-Interactive-Strong);
214+
outline-offset: 2px;
146215
}
147216

148217
.dash-datepicker-calendar-wrapper {
@@ -155,6 +224,7 @@
155224
display: flex;
156225
align-items: flex-start;
157226
gap: calc(var(--Dash-Spacing) * 4);
227+
flex-wrap: wrap;
158228
}
159229

160230
.dash-datepicker-controls {
@@ -226,8 +296,3 @@
226296
width: 20px;
227297
height: 20px;
228298
}
229-
230-
/* Override Radix's position: fixed to use position: absolute when using custom container */
231-
div[data-radix-popper-content-wrapper]:has(.dash-datepicker-content) {
232-
position: absolute !important;
233-
}

components/dash-core-components/src/components/css/dropdown.css

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
z-index: 500;
8585
box-shadow: 0px 10px 38px -10px var(--Dash-Shading-Strong),
8686
0px 10px 20px -15px var(--Dash-Shading-Weak);
87+
overscroll-behavior: contain;
8788
}
8889

8990
.dash-dropdown-value-count,
@@ -235,8 +236,3 @@
235236
.dash-dropdown-wrapper {
236237
position: relative;
237238
}
238-
239-
/* Override Radix's position: fixed to use position: absolute when using custom container */
240-
div[data-radix-popper-content-wrapper]:has(.dash-dropdown-content) {
241-
position: absolute !important;
242-
}

components/dash-core-components/src/fragments/DatePickerRange.tsx

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ const DatePickerRange = ({
5151
end_date_id,
5252
start_date_placeholder_text = 'Start Date',
5353
end_date_placeholder_text = 'End Date',
54+
with_portal = false,
55+
with_full_screen_portal = false,
5456
}: DatePickerRangeProps) => {
5557
const [internalStartDate, setInternalStartDate] = useState(
5658
strAsDate(start_date)
@@ -102,6 +104,7 @@ const DatePickerRange = ({
102104
const startInputRef = useRef<HTMLInputElement | null>(null);
103105
const endInputRef = useRef<HTMLInputElement | null>(null);
104106
const calendarRef = useRef<CalendarHandle>(null);
107+
const hasPortal = with_portal || with_full_screen_portal;
105108

106109
useEffect(() => {
107110
setInternalStartDate(strAsDate(start_date));
@@ -333,7 +336,7 @@ const DatePickerRange = ({
333336
id={start_date_id || accessibleId}
334337
inputClassName="dash-datepicker-input dash-datepicker-start-date"
335338
value={startInputValue}
336-
onChange={e => setStartInputValue(e.target.value)}
339+
onChange={e => setStartInputValue(e.target?.value)}
337340
onKeyDown={handleStartInputKeyDown}
338341
onFocus={() => {
339342
if (isCalendarOpen) {
@@ -354,7 +357,7 @@ const DatePickerRange = ({
354357
id={end_date_id || accessibleId + '-end-date'}
355358
inputClassName="dash-datepicker-input dash-datepicker-end-date"
356359
value={endInputValue}
357-
onChange={e => setEndInputValue(e.target.value)}
360+
onChange={e => setEndInputValue(e.target?.value)}
358361
onKeyDown={handleEndInputKeyDown}
359362
onFocus={() => {
360363
if (isCalendarOpen) {
@@ -381,12 +384,21 @@ const DatePickerRange = ({
381384

382385
<Popover.Portal container={containerRef.current}>
383386
<Popover.Content
384-
className="dash-datepicker-content"
385-
align="start"
386-
sideOffset={5}
387-
collisionBoundary={containerRef.current?.closest(
388-
'#_dash-app-content'
389-
)}
387+
className={`dash-datepicker-content${
388+
hasPortal ? ' dash-datepicker-portal' : ''
389+
}${
390+
with_full_screen_portal
391+
? ' dash-datepicker-fullscreen'
392+
: ''
393+
}`}
394+
align={hasPortal ? 'center' : 'start'}
395+
sideOffset={hasPortal ? 0 : 5}
396+
avoidCollisions={!hasPortal}
397+
onInteractOutside={
398+
with_full_screen_portal
399+
? e => e.preventDefault()
400+
: undefined
401+
}
390402
onOpenAutoFocus={e => e.preventDefault()}
391403
onCloseAutoFocus={e => {
392404
e.preventDefault();
@@ -407,6 +419,15 @@ const DatePickerRange = ({
407419
}
408420
}}
409421
>
422+
{with_full_screen_portal && (
423+
<button
424+
className="dash-datepicker-close-button"
425+
onClick={() => setIsCalendarOpen(false)}
426+
aria-label="Close calendar"
427+
>
428+
<Cross1Icon />
429+
</button>
430+
)}
410431
<Calendar
411432
ref={calendarRef}
412433
initialVisibleDate={initialCalendarDate}

components/dash-core-components/src/fragments/DatePickerSingle.tsx

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ const DatePickerSingle = ({
3939
day_size = 34,
4040
number_of_months_shown = 1,
4141
calendar_orientation,
42+
with_portal = false,
43+
with_full_screen_portal = false,
4244
}: DatePickerSingleProps) => {
4345
const [internalDate, setInternalDate] = useState(strAsDate(date));
4446
const direction = is_RTL
@@ -61,6 +63,7 @@ const DatePickerSingle = ({
6163
const containerRef = useRef<HTMLDivElement>(null);
6264
const inputRef = useRef<HTMLInputElement | null>(null);
6365
const calendarRef = useRef<CalendarHandle>(null);
66+
const hasPortal = with_portal || with_full_screen_portal;
6467

6568
useEffect(() => {
6669
setInternalDate(strAsDate(date));
@@ -177,7 +180,7 @@ const DatePickerSingle = ({
177180
id={accessibleId}
178181
inputClassName="dash-datepicker-input dash-datepicker-end-date"
179182
value={inputValue}
180-
onChange={e => setInputValue(e.target.value)}
183+
onChange={e => setInputValue(e.target?.value)}
181184
onKeyDown={handleInputKeyDown}
182185
placeholder={placeholder}
183186
disabled={disabled}
@@ -200,12 +203,21 @@ const DatePickerSingle = ({
200203

201204
<Popover.Portal container={containerRef.current}>
202205
<Popover.Content
203-
className="dash-datepicker-content"
204-
align="start"
205-
sideOffset={5}
206-
collisionBoundary={containerRef.current?.closest(
207-
'#_dash-app-content'
208-
)}
206+
className={`dash-datepicker-content${
207+
hasPortal ? ' dash-datepicker-portal' : ''
208+
}${
209+
with_full_screen_portal
210+
? ' dash-datepicker-fullscreen'
211+
: ''
212+
}`}
213+
align={hasPortal ? 'center' : 'start'}
214+
sideOffset={hasPortal ? 0 : 5}
215+
avoidCollisions={!hasPortal}
216+
onInteractOutside={
217+
with_full_screen_portal
218+
? e => e.preventDefault()
219+
: undefined
220+
}
209221
onOpenAutoFocus={e => e.preventDefault()}
210222
onCloseAutoFocus={e => {
211223
e.preventDefault();
@@ -215,6 +227,15 @@ const DatePickerSingle = ({
215227
}
216228
}}
217229
>
230+
{with_full_screen_portal && (
231+
<button
232+
className="dash-datepicker-close-button"
233+
onClick={() => setIsCalendarOpen(false)}
234+
aria-label="Close calendar"
235+
>
236+
<Cross1Icon />
237+
</button>
238+
)}
218239
<Calendar
219240
ref={calendarRef}
220241
initialVisibleDate={initialMonth}

components/dash-core-components/src/fragments/Dropdown.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,6 @@ const Dropdown = (props: DropdownProps) => {
394394
}
395395
}}
396396
className={`dash-dropdown ${className ?? ''}`}
397-
style={style}
398397
aria-labelledby={`${accessibleId}-value-count ${accessibleId}-value`}
399398
aria-haspopup="listbox"
400399
aria-expanded={isOpen}
@@ -458,9 +457,6 @@ const Dropdown = (props: DropdownProps) => {
458457
className="dash-dropdown-content"
459458
align="start"
460459
sideOffset={5}
461-
collisionBoundary={positioningContainerRef.current?.closest(
462-
'#_dash-app-content'
463-
)}
464460
onOpenAutoFocus={e => e.preventDefault()}
465461
onKeyDown={handleKeyDown}
466462
style={{
@@ -543,7 +539,11 @@ const Dropdown = (props: DropdownProps) => {
543539
);
544540

545541
return (
546-
<div ref={positioningContainerRef} className="dash-dropdown-wrapper">
542+
<div
543+
ref={positioningContainerRef}
544+
className="dash-dropdown-wrapper"
545+
style={style}
546+
>
547547
{popover}
548548
</div>
549549
);

0 commit comments

Comments
 (0)