Skip to content

Commit 2b682c1

Browse files
authored
Place calendar dialog relative to button in DateInput (#2781)
1 parent 0a6ec4e commit 2b682c1

File tree

2 files changed

+28
-7
lines changed

2 files changed

+28
-7
lines changed

.changeset/hungry-sloths-prove.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sumup-oss/circuit-ui': patch
3+
---
4+
5+
Improved the DateInput component's calendar dialog to be placed relative to the calendar button instead of the whole field. This improves the placement when the DateInput is rendered near the center of the viewport. The default `bottom-end` placement can be customized using the new `placement` prop.

packages/circuit-ui/components/DateInput/DateInput.tsx

+23-7
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,14 @@ import {
2424
type InputHTMLAttributes,
2525
} from 'react';
2626
import type { Temporal } from 'temporal-polyfill';
27-
import { flip, offset, shift, size, useFloating } from '@floating-ui/react-dom';
27+
import {
28+
flip,
29+
offset,
30+
shift,
31+
size,
32+
useFloating,
33+
type Placement,
34+
} from '@floating-ui/react-dom';
2835
import { Calendar as CalendarIcon } from '@sumup-oss/icons';
2936

3037
import type { ClickEvent } from '../../types/events.js';
@@ -134,6 +141,10 @@ export interface DateInputProps
134141
* A hint to the user agent specifying how to prefill the input.
135142
*/
136143
autoComplete?: 'bday';
144+
/**
145+
* One of the accepted placement values. Defaults to `bottom-end`.
146+
*/
147+
placement?: Placement;
137148
}
138149

139150
/**
@@ -171,6 +182,7 @@ export const DateInput = forwardRef<HTMLInputElement, DateInputProps>(
171182
monthInputLabel,
172183
dayInputLabel,
173184
autoComplete,
185+
placement = 'bottom-end',
174186
className,
175187
style,
176188
...props
@@ -180,7 +192,7 @@ export const DateInput = forwardRef<HTMLInputElement, DateInputProps>(
180192
const isMobile = useMedia('(max-width: 479px)');
181193

182194
const inputRef = useRef<HTMLInputElement>(null);
183-
const fieldRef = useRef<HTMLDivElement>(null);
195+
const calendarButtonRef = useRef<HTMLDivElement>(null);
184196
const dialogRef = useRef<HTMLDialogElement>(null);
185197

186198
const dialogId = useId();
@@ -208,21 +220,24 @@ export const DateInput = forwardRef<HTMLInputElement, DateInputProps>(
208220
const [open, setOpen] = useState(false);
209221
const [selection, setSelection] = useState<Temporal.PlainDate>();
210222

223+
const padding = 16; // px
224+
211225
const { floatingStyles, update } = useFloating({
212226
open,
213-
placement: 'bottom-start',
227+
placement,
214228
middleware: [
215229
offset(4),
216-
flip({ fallbackAxisSideDirection: 'end', crossAxis: false }),
217-
shift(),
230+
flip({ padding, fallbackAxisSideDirection: 'start' }),
231+
shift({ padding }),
218232
size({
233+
padding,
219234
apply({ availableHeight, elements }) {
220235
elements.floating.style.maxHeight = `${availableHeight}px`;
221236
},
222237
}),
223238
],
224239
elements: {
225-
reference: fieldRef.current,
240+
reference: calendarButtonRef.current,
226241
floating: dialogRef.current,
227242
},
228243
});
@@ -370,7 +385,7 @@ export const DateInput = forwardRef<HTMLInputElement, DateInputProps>(
370385
optionalLabel={optionalLabel}
371386
/>
372387
</FieldLegend>
373-
<div ref={fieldRef} className={classes.wrapper}>
388+
<div className={classes.wrapper}>
374389
<input
375390
type="date"
376391
ref={applyMultipleRefs(ref, inputRef)}
@@ -462,6 +477,7 @@ export const DateInput = forwardRef<HTMLInputElement, DateInputProps>(
462477
})}
463478
</div>
464479
<IconButton
480+
ref={calendarButtonRef}
465481
type="button"
466482
icon={CalendarIcon}
467483
variant="secondary"

0 commit comments

Comments
 (0)