@indico-data/design-system 1.0.53 → 1.0.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/Icon/Icon.d.ts +1 -0
- package/lib/components/Icon/Icon.stories.d.ts +2 -0
- package/lib/components/inputs/NoInputDatePicker/NoInputDatePicker.d.ts +2 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.esm.js +38 -10
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +38 -10
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Icon/Icon.tsx +2 -0
- package/src/components/inputs/NoInputDatePicker/NoInputDatePicker.stories.tsx +1 -0
- package/src/components/inputs/NoInputDatePicker/NoInputDatePicker.tsx +51 -9
package/package.json
CHANGED
|
@@ -20,6 +20,7 @@ type Props = PermafrostComponent & {
|
|
|
20
20
|
size?: [string | number] | [string | number, string | number];
|
|
21
21
|
style?: any;
|
|
22
22
|
onClick?(): void;
|
|
23
|
+
ref?: React.Ref<SVGSVGElement>;
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
type IconComponentProps = {
|
|
@@ -53,6 +54,7 @@ export function Icon({ style = {}, ...props }: Props): React.ReactElement | null
|
|
|
53
54
|
{...iconComponentProps}
|
|
54
55
|
{...ariaLabel}
|
|
55
56
|
{...dimensions}
|
|
57
|
+
ref={props.ref}
|
|
56
58
|
role="img"
|
|
57
59
|
className={props.className}
|
|
58
60
|
data-cy={props['data-cy']}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// TODO: This component's migration was fast-tracked for Insights. Assess for potential refactor and documentation.
|
|
2
|
-
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
3
3
|
import { format } from 'date-fns';
|
|
4
4
|
import FocusTrap from 'focus-trap-react';
|
|
5
5
|
import {
|
|
@@ -25,21 +25,29 @@ type Props = PermafrostComponent & {
|
|
|
25
25
|
label?: string;
|
|
26
26
|
onChange: (value: Date | DateRange | undefined) => void;
|
|
27
27
|
selected?: Date | undefined;
|
|
28
|
+
selectedRange?: DateRange | undefined;
|
|
28
29
|
value: Date | undefined;
|
|
29
30
|
triggerIcon: IconName;
|
|
30
31
|
triggerIconSize: [string | number];
|
|
31
32
|
isRangePicker?: boolean;
|
|
32
33
|
isOpen?: boolean;
|
|
34
|
+
clearOnClose?: boolean;
|
|
33
35
|
};
|
|
34
36
|
|
|
35
37
|
function CustomCaption(props: CaptionProps) {
|
|
36
38
|
const { goToMonth, nextMonth, previousMonth } = useNavigation();
|
|
39
|
+
|
|
37
40
|
return (
|
|
38
41
|
<div className="custom__caption">
|
|
39
42
|
<button
|
|
40
43
|
className="custom__caption__action__button"
|
|
41
44
|
disabled={!previousMonth}
|
|
42
|
-
onClick={() =>
|
|
45
|
+
onClick={(event) => {
|
|
46
|
+
event.preventDefault();
|
|
47
|
+
if (previousMonth) {
|
|
48
|
+
goToMonth(previousMonth);
|
|
49
|
+
}
|
|
50
|
+
}}
|
|
43
51
|
>
|
|
44
52
|
<Icon size={[10]} ariaLabel="Previous Month" name="chevron-left" />
|
|
45
53
|
</button>
|
|
@@ -47,7 +55,12 @@ function CustomCaption(props: CaptionProps) {
|
|
|
47
55
|
<button
|
|
48
56
|
className="custom__caption__action__button"
|
|
49
57
|
disabled={!nextMonth}
|
|
50
|
-
onClick={() =>
|
|
58
|
+
onClick={(event) => {
|
|
59
|
+
event.preventDefault();
|
|
60
|
+
if (nextMonth) {
|
|
61
|
+
goToMonth(nextMonth);
|
|
62
|
+
}
|
|
63
|
+
}}
|
|
51
64
|
>
|
|
52
65
|
<Icon size={[10]} ariaLabel="Next Month" name="chevron-right" />
|
|
53
66
|
</button>
|
|
@@ -68,11 +81,15 @@ export const NoInputDatePicker = (props: Props) => {
|
|
|
68
81
|
triggerIcon,
|
|
69
82
|
triggerIconSize,
|
|
70
83
|
isRangePicker,
|
|
84
|
+
selectedRange,
|
|
71
85
|
isOpen,
|
|
86
|
+
clearOnClose,
|
|
72
87
|
} = props;
|
|
73
88
|
|
|
74
89
|
const [localSelected, setLocalSelected] = useState<Date | undefined>(selected || undefined);
|
|
75
|
-
const [
|
|
90
|
+
const [localSelectedRange, setLocalSelectedRange] = useState<DateRange | undefined>(
|
|
91
|
+
selectedRange || undefined,
|
|
92
|
+
);
|
|
76
93
|
const [isPopperOpen, setIsPopperOpen] = useState(isOpen || false);
|
|
77
94
|
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
|
78
95
|
const popperRef = useRef<HTMLDivElement>(null);
|
|
@@ -90,17 +107,40 @@ export const NoInputDatePicker = (props: Props) => {
|
|
|
90
107
|
|
|
91
108
|
const [isTrapActive, setIsTrapActive] = useState(false);
|
|
92
109
|
|
|
110
|
+
const datePickerRef = useRef<HTMLDivElement | null>(null);
|
|
111
|
+
|
|
112
|
+
// Addresses clickout side bug when implementing in an application
|
|
93
113
|
useEffect(() => {
|
|
114
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
115
|
+
// Close the date picker only when clicked outside
|
|
116
|
+
if (
|
|
117
|
+
datePickerRef.current &&
|
|
118
|
+
event.target instanceof Node &&
|
|
119
|
+
!datePickerRef.current.contains(event.target)
|
|
120
|
+
) {
|
|
121
|
+
closePopper();
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
126
|
+
return () => {
|
|
127
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
128
|
+
};
|
|
129
|
+
}, []);
|
|
130
|
+
|
|
131
|
+
// Allow datepicker to load before setting active trap
|
|
132
|
+
useLayoutEffect(() => {
|
|
94
133
|
if (isOpen) {
|
|
95
|
-
|
|
96
|
-
const timeoutId = setTimeout(() => setIsTrapActive(true), 0);
|
|
97
|
-
return () => clearTimeout(timeoutId);
|
|
134
|
+
setIsTrapActive(true);
|
|
98
135
|
} else {
|
|
99
136
|
setIsTrapActive(false);
|
|
100
137
|
}
|
|
101
138
|
}, [isOpen]);
|
|
102
139
|
|
|
103
140
|
const closePopper = () => {
|
|
141
|
+
clearOnClose && setLocalSelected(undefined);
|
|
142
|
+
clearOnClose && setLocalSelectedRange(undefined);
|
|
143
|
+
|
|
104
144
|
setIsPopperOpen(false);
|
|
105
145
|
};
|
|
106
146
|
|
|
@@ -115,7 +155,7 @@ export const NoInputDatePicker = (props: Props) => {
|
|
|
115
155
|
};
|
|
116
156
|
|
|
117
157
|
const handleRangeSelect: SelectRangeEventHandler = (range: DateRange | undefined) => {
|
|
118
|
-
|
|
158
|
+
setLocalSelectedRange(range);
|
|
119
159
|
if (range?.from && range?.to) {
|
|
120
160
|
onChange(range);
|
|
121
161
|
closePopper();
|
|
@@ -131,6 +171,7 @@ export const NoInputDatePicker = (props: Props) => {
|
|
|
131
171
|
data-cy={props['data-cy']}
|
|
132
172
|
data-testid={props['data-testid']}
|
|
133
173
|
id={id}
|
|
174
|
+
ref={datePickerRef}
|
|
134
175
|
>
|
|
135
176
|
{label ? (
|
|
136
177
|
<div id={`picker-label--${id}`} className="visually-hidden">
|
|
@@ -155,6 +196,7 @@ export const NoInputDatePicker = (props: Props) => {
|
|
|
155
196
|
allowOutsideClick: true,
|
|
156
197
|
clickOutsideDeactivates: true,
|
|
157
198
|
onDeactivate: closePopper,
|
|
199
|
+
fallbackFocus: popperRef.current || undefined,
|
|
158
200
|
}}
|
|
159
201
|
>
|
|
160
202
|
<div
|
|
@@ -175,7 +217,7 @@ export const NoInputDatePicker = (props: Props) => {
|
|
|
175
217
|
disabled={disabled}
|
|
176
218
|
mode={'range'}
|
|
177
219
|
defaultMonth={localSelected}
|
|
178
|
-
selected={
|
|
220
|
+
selected={localSelectedRange}
|
|
179
221
|
onSelect={handleRangeSelect}
|
|
180
222
|
fromDate={disableBeforeDate}
|
|
181
223
|
toDate={disableAfterDate}
|