@patternfly/react-core 4.222.12 → 4.222.13
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/CHANGELOG.md +8 -0
- package/dist/esm/components/CalendarMonth/CalendarMonth.d.ts +3 -1
- package/dist/esm/components/CalendarMonth/CalendarMonth.d.ts.map +1 -1
- package/dist/esm/components/CalendarMonth/CalendarMonth.js +21 -14
- package/dist/esm/components/CalendarMonth/CalendarMonth.js.map +1 -1
- package/dist/esm/components/DatePicker/DatePicker.d.ts +14 -4
- package/dist/esm/components/DatePicker/DatePicker.d.ts.map +1 -1
- package/dist/esm/components/DatePicker/DatePicker.js +20 -8
- package/dist/esm/components/DatePicker/DatePicker.js.map +1 -1
- package/dist/esm/components/Popover/Popover.js +2 -2
- package/dist/esm/components/Popover/Popover.js.map +1 -1
- package/dist/esm/components/TimePicker/TimePicker.d.ts +6 -1
- package/dist/esm/components/TimePicker/TimePicker.d.ts.map +1 -1
- package/dist/esm/components/TimePicker/TimePicker.js +32 -19
- package/dist/esm/components/TimePicker/TimePicker.js.map +1 -1
- package/dist/js/components/CalendarMonth/CalendarMonth.d.ts +3 -1
- package/dist/js/components/CalendarMonth/CalendarMonth.d.ts.map +1 -1
- package/dist/js/components/CalendarMonth/CalendarMonth.js +21 -14
- package/dist/js/components/CalendarMonth/CalendarMonth.js.map +1 -1
- package/dist/js/components/DatePicker/DatePicker.d.ts +14 -4
- package/dist/js/components/DatePicker/DatePicker.d.ts.map +1 -1
- package/dist/js/components/DatePicker/DatePicker.js +20 -8
- package/dist/js/components/DatePicker/DatePicker.js.map +1 -1
- package/dist/js/components/Popover/Popover.js +1 -1
- package/dist/js/components/Popover/Popover.js.map +1 -1
- package/dist/js/components/TimePicker/TimePicker.d.ts +6 -1
- package/dist/js/components/TimePicker/TimePicker.d.ts.map +1 -1
- package/dist/js/components/TimePicker/TimePicker.js +32 -19
- package/dist/js/components/TimePicker/TimePicker.js.map +1 -1
- package/dist/umd/react-core.css +20 -0
- package/dist/umd/react-core.js +76 -43
- package/dist/umd/react-core.min.css +1088 -1068
- package/dist/umd/react-core.min.js +2 -2
- package/package.json +6 -6
- package/src/components/CalendarMonth/CalendarMonth.tsx +21 -13
- package/src/components/DatePicker/DatePicker.tsx +36 -11
- package/src/components/Modal/examples/Modal.md +2 -0
- package/src/components/OverflowMenu/examples/OverflowMenu.md +8 -0
- package/src/components/Popover/Popover.tsx +2 -2
- package/src/components/TimePicker/TimePicker.tsx +36 -19
- package/src/components/Toolbar/examples/Toolbar.md +4 -0
- package/src/demos/Card/Card.md +2 -0
- package/src/demos/{DatePicker.md → DatePicker/DatePicker.md} +8 -0
- package/src/demos/DatePicker/examples/DateTimePickerInModal.tsx +51 -0
- package/src/demos/PrimaryDetail.md +10 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patternfly/react-core",
|
|
3
|
-
"version": "4.222.
|
|
3
|
+
"version": "4.222.13",
|
|
4
4
|
"description": "This library provides a set of common React components for use with the PatternFly reference implementation.",
|
|
5
5
|
"main": "dist/js/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -34,16 +34,16 @@
|
|
|
34
34
|
"generate": "node scripts/copyStyles.js"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@patternfly/react-icons": "^4.73.
|
|
38
|
-
"@patternfly/react-styles": "^4.72.
|
|
39
|
-
"@patternfly/react-tokens": "^4.74.
|
|
37
|
+
"@patternfly/react-icons": "^4.73.13",
|
|
38
|
+
"@patternfly/react-styles": "^4.72.13",
|
|
39
|
+
"@patternfly/react-tokens": "^4.74.13",
|
|
40
40
|
"focus-trap": "6.9.2",
|
|
41
41
|
"react-dropzone": "9.0.0",
|
|
42
42
|
"tippy.js": "5.1.2",
|
|
43
43
|
"tslib": "^2.0.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@patternfly/patternfly": "4.
|
|
46
|
+
"@patternfly/patternfly": "4.198.1",
|
|
47
47
|
"@rollup/plugin-commonjs": "^21.0.0",
|
|
48
48
|
"@rollup/plugin-node-resolve": "^13.0.0",
|
|
49
49
|
"@rollup/plugin-replace": "^3.0.0",
|
|
@@ -60,5 +60,5 @@
|
|
|
60
60
|
"react": "^16.8.0 || ^17.0.0",
|
|
61
61
|
"react-dom": "^16.8.0 || ^17.0.0"
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "12411175163290e8c6dd598cfb5ce53ba58688b3"
|
|
64
64
|
}
|
|
@@ -55,6 +55,8 @@ export interface CalendarProps extends CalendarFormat, Omit<React.HTMLProps<HTML
|
|
|
55
55
|
className?: string;
|
|
56
56
|
/** @hide Internal prop to allow pressing escape in select menu to not close popover */
|
|
57
57
|
onSelectToggle?: (open: boolean) => void;
|
|
58
|
+
/** Flag to set browser focus on the passed date **/
|
|
59
|
+
isDateFocused?: boolean;
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
// Must be numeric given current header design
|
|
@@ -110,19 +112,24 @@ export const CalendarMonth = ({
|
|
|
110
112
|
nextMonthAriaLabel = 'Next month',
|
|
111
113
|
yearInputAriaLabel = 'Select year',
|
|
112
114
|
cellAriaLabel,
|
|
115
|
+
isDateFocused = false,
|
|
113
116
|
...props
|
|
114
117
|
}: CalendarProps) => {
|
|
115
118
|
const longMonths = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].map(monthNum => new Date(1990, monthNum)).map(monthFormat);
|
|
116
119
|
const [isSelectOpen, setIsSelectOpen] = React.useState(false);
|
|
117
120
|
// eslint-disable-next-line prefer-const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (isValidDate(
|
|
121
|
-
|
|
121
|
+
const [focusedDate, setFocusedDate] = React.useState(() => {
|
|
122
|
+
const initDate = new Date(dateProp);
|
|
123
|
+
if (isValidDate(initDate)) {
|
|
124
|
+
return initDate;
|
|
122
125
|
} else {
|
|
123
|
-
|
|
126
|
+
if (isValidDate(rangeStart)) {
|
|
127
|
+
return rangeStart;
|
|
128
|
+
} else {
|
|
129
|
+
return today;
|
|
130
|
+
}
|
|
124
131
|
}
|
|
125
|
-
}
|
|
132
|
+
});
|
|
126
133
|
const [hoveredDate, setHoveredDate] = React.useState(new Date(focusedDate));
|
|
127
134
|
const focusRef = React.useRef<HTMLButtonElement>();
|
|
128
135
|
const [hiddenMonthId] = React.useState(getUniqueId('hidden-month-span'));
|
|
@@ -131,21 +138,22 @@ export const CalendarMonth = ({
|
|
|
131
138
|
const isValidated = (date: Date) => validators.every(validator => validator(date));
|
|
132
139
|
const focusedDateValidated = isValidated(focusedDate);
|
|
133
140
|
useEffect(() => {
|
|
134
|
-
if (
|
|
141
|
+
if (isValidDate(dateProp) && !isSameDate(focusedDate, dateProp)) {
|
|
135
142
|
setFocusedDate(dateProp);
|
|
143
|
+
} else if (!dateProp) {
|
|
144
|
+
setFocusedDate(today);
|
|
136
145
|
}
|
|
137
146
|
}, [dateProp]);
|
|
138
147
|
|
|
139
148
|
useEffect(() => {
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
149
|
+
// Calendar month should not be focused on page load
|
|
150
|
+
// Datepicker should place focus in calendar month when opened
|
|
151
|
+
if ((shouldFocus || isDateFocused) && focusedDateValidated && focusRef.current) {
|
|
152
|
+
focusRef.current.focus();
|
|
145
153
|
} else {
|
|
146
154
|
setShouldFocus(true);
|
|
147
155
|
}
|
|
148
|
-
}, [focusedDate]);
|
|
156
|
+
}, [focusedDate, isDateFocused, focusedDateValidated, focusRef]);
|
|
149
157
|
|
|
150
158
|
const onMonthClick = (newDate: Date) => {
|
|
151
159
|
setFocusedDate(newDate);
|
|
@@ -8,6 +8,7 @@ import { InputGroup } from '../InputGroup/InputGroup';
|
|
|
8
8
|
import OutlinedCalendarAltIcon from '@patternfly/react-icons/dist/esm/icons/outlined-calendar-alt-icon';
|
|
9
9
|
import { CalendarMonth, CalendarFormat, isValidDate } from '../CalendarMonth';
|
|
10
10
|
import { useImperativeHandle } from 'react';
|
|
11
|
+
import { KeyTypes } from '../../helpers';
|
|
11
12
|
|
|
12
13
|
export interface DatePickerProps
|
|
13
14
|
extends CalendarFormat,
|
|
@@ -36,8 +37,13 @@ export interface DatePickerProps
|
|
|
36
37
|
helperText?: React.ReactNode;
|
|
37
38
|
/** Aria label for the button to open the date picker */
|
|
38
39
|
buttonAriaLabel?: string;
|
|
39
|
-
/** The
|
|
40
|
-
|
|
40
|
+
/** The container to append the menu to. Defaults to 'parent'.
|
|
41
|
+
* If your menu is being cut off you can append it to an element higher up the DOM tree.
|
|
42
|
+
* Some examples:
|
|
43
|
+
* menuAppendTo={() => document.body}
|
|
44
|
+
* menuAppendTo={document.getElementById('target')}
|
|
45
|
+
*/
|
|
46
|
+
appendTo?: HTMLElement | ((ref?: HTMLElement) => HTMLElement) | 'parent';
|
|
41
47
|
/** Props to pass to the Popover */
|
|
42
48
|
popoverProps?: Omit<PopoverProps, 'appendTo'>;
|
|
43
49
|
/** Functions that returns an error message if a date is invalid */
|
|
@@ -49,8 +55,13 @@ export interface DatePickerProps
|
|
|
49
55
|
export interface DatePickerRef {
|
|
50
56
|
/** Sets the calendar open status */
|
|
51
57
|
setCalendarOpen: (isOpen: boolean) => void;
|
|
52
|
-
/** Toggles the calendar open status
|
|
53
|
-
|
|
58
|
+
/** Toggles the calendar open status. If no parameters are passed, the calendar will simply toggle its open status.
|
|
59
|
+
* If the isOpen parameter is passed, that will set the calendar open status to the value of the isOpen parameter.
|
|
60
|
+
* If the eventKey parameter is set to 'Escape', that will invoke the date pickers onEscapePress event to toggle the
|
|
61
|
+
* correct control appropriately. */
|
|
62
|
+
toggleCalendar: (isOpen?: boolean, eventKey?: string) => void;
|
|
63
|
+
/** Current calendar open status */
|
|
64
|
+
isCalendarOpen: boolean;
|
|
54
65
|
}
|
|
55
66
|
|
|
56
67
|
export const yyyyMMddFormat = (date: Date) =>
|
|
@@ -74,7 +85,7 @@ const DatePickerBase = (
|
|
|
74
85
|
onBlur = (): any => undefined,
|
|
75
86
|
invalidFormatText = 'Invalid date',
|
|
76
87
|
helperText,
|
|
77
|
-
appendTo,
|
|
88
|
+
appendTo = 'parent',
|
|
78
89
|
popoverProps,
|
|
79
90
|
monthFormat,
|
|
80
91
|
weekdayFormat,
|
|
@@ -98,6 +109,7 @@ const DatePickerBase = (
|
|
|
98
109
|
const widthChars = React.useMemo(() => Math.max(dateFormat(new Date()).length, placeholder.length), [dateFormat]);
|
|
99
110
|
const style = { '--pf-c-date-picker__input--c-form-control--width-chars': widthChars, ...styleProps };
|
|
100
111
|
const buttonRef = React.useRef<HTMLButtonElement>();
|
|
112
|
+
const datePickerWrapperRef = React.useRef<HTMLDivElement>();
|
|
101
113
|
|
|
102
114
|
React.useEffect(() => {
|
|
103
115
|
setValue(valueProp);
|
|
@@ -159,13 +171,21 @@ const DatePickerBase = (
|
|
|
159
171
|
ref,
|
|
160
172
|
() => ({
|
|
161
173
|
setCalendarOpen: (isOpen: boolean) => setPopoverOpen(isOpen),
|
|
162
|
-
toggleCalendar: (
|
|
174
|
+
toggleCalendar: (setOpen?: boolean, eventKey?: string) => {
|
|
175
|
+
if (eventKey === KeyTypes.Escape && popoverOpen && !selectOpen) {
|
|
176
|
+
setPopoverOpen(prev => (setOpen !== undefined ? setOpen : !prev));
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
isCalendarOpen: popoverOpen
|
|
163
180
|
}),
|
|
164
|
-
[setPopoverOpen]
|
|
181
|
+
[setPopoverOpen, popoverOpen, selectOpen]
|
|
165
182
|
);
|
|
166
183
|
|
|
184
|
+
const getParentElement = () =>
|
|
185
|
+
datePickerWrapperRef && datePickerWrapperRef.current ? datePickerWrapperRef.current : null;
|
|
186
|
+
|
|
167
187
|
return (
|
|
168
|
-
<div className={css(styles.datePicker, className)} style={style} {...props}>
|
|
188
|
+
<div className={css(styles.datePicker, className)} ref={datePickerWrapperRef} style={style} {...props}>
|
|
169
189
|
<Popover
|
|
170
190
|
position="bottom"
|
|
171
191
|
bodyContent={
|
|
@@ -182,14 +202,16 @@ const DatePickerBase = (
|
|
|
182
202
|
dayFormat={dayFormat}
|
|
183
203
|
weekStart={weekStart}
|
|
184
204
|
rangeStart={rangeStart}
|
|
205
|
+
isDateFocused
|
|
185
206
|
/>
|
|
186
207
|
}
|
|
187
208
|
showClose={false}
|
|
188
209
|
isVisible={popoverOpen}
|
|
189
210
|
shouldClose={(_1, _2, event) => {
|
|
190
211
|
event = event as KeyboardEvent;
|
|
191
|
-
|
|
192
|
-
|
|
212
|
+
if (event.key === KeyTypes.Escape && selectOpen) {
|
|
213
|
+
event.stopPropagation();
|
|
214
|
+
setSelectOpen(false);
|
|
193
215
|
return false;
|
|
194
216
|
}
|
|
195
217
|
// Let our button handle toggling
|
|
@@ -197,12 +219,15 @@ const DatePickerBase = (
|
|
|
197
219
|
return false;
|
|
198
220
|
}
|
|
199
221
|
setPopoverOpen(false);
|
|
222
|
+
if (event.key === KeyTypes.Escape && popoverOpen) {
|
|
223
|
+
event.stopPropagation();
|
|
224
|
+
}
|
|
200
225
|
return true;
|
|
201
226
|
}}
|
|
202
227
|
withFocusTrap
|
|
203
228
|
hasNoPadding
|
|
204
229
|
hasAutoWidth
|
|
205
|
-
appendTo={appendTo}
|
|
230
|
+
appendTo={appendTo === 'parent' ? getParentElement() : appendTo}
|
|
206
231
|
{...popoverProps}
|
|
207
232
|
>
|
|
208
233
|
<div className={styles.datePickerInput}>
|
|
@@ -61,6 +61,8 @@ class SimpleOverflowMenu extends React.Component {
|
|
|
61
61
|
isOpen={isOpen}
|
|
62
62
|
isPlain
|
|
63
63
|
dropdownItems={dropdownItems}
|
|
64
|
+
isFlipEnabled
|
|
65
|
+
menuAppendTo="parent"
|
|
64
66
|
/>
|
|
65
67
|
</OverflowMenuControl>
|
|
66
68
|
</OverflowMenu>
|
|
@@ -153,6 +155,8 @@ class OverflowMenuGroupTypes extends React.Component {
|
|
|
153
155
|
isOpen={isOpen}
|
|
154
156
|
isPlain
|
|
155
157
|
dropdownItems={dropdownItems}
|
|
158
|
+
isFlipEnabled
|
|
159
|
+
menuAppendTo="parent"
|
|
156
160
|
/>
|
|
157
161
|
</OverflowMenuControl>
|
|
158
162
|
</OverflowMenu>
|
|
@@ -239,6 +243,8 @@ class OverflowMenuAdditionalOptions extends React.Component {
|
|
|
239
243
|
isOpen={isOpen}
|
|
240
244
|
isPlain
|
|
241
245
|
dropdownItems={dropdownItems}
|
|
246
|
+
isFlipEnabled
|
|
247
|
+
menuAppendTo="parent"
|
|
242
248
|
/>
|
|
243
249
|
</OverflowMenuControl>
|
|
244
250
|
</OverflowMenu>
|
|
@@ -301,6 +307,8 @@ class OverflowMenuPersist extends React.Component {
|
|
|
301
307
|
isOpen={isOpen}
|
|
302
308
|
isPlain
|
|
303
309
|
dropdownItems={dropdownItems}
|
|
310
|
+
isFlipEnabled
|
|
311
|
+
menuAppendTo="parent"
|
|
304
312
|
/>
|
|
305
313
|
</OverflowMenuControl>
|
|
306
314
|
</OverflowMenu>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { KeyTypes } from '../../helpers/constants';
|
|
4
4
|
import styles from '@patternfly/react-styles/css/components/Popover/popover';
|
|
5
5
|
import { css } from '@patternfly/react-styles';
|
|
6
6
|
import { PopoverContext } from './PopoverContext';
|
|
@@ -330,7 +330,7 @@ export const Popover: React.FunctionComponent<PopoverProps> = ({
|
|
|
330
330
|
const hasCustomMinWidth = minWidth !== popoverMinWidth.value;
|
|
331
331
|
const hasCustomMaxWidth = maxWidth !== popoverMaxWidth.value;
|
|
332
332
|
const onDocumentKeyDown = (event: KeyboardEvent) => {
|
|
333
|
-
if (event.
|
|
333
|
+
if (event.key === KeyTypes.Escape && visible) {
|
|
334
334
|
if (triggerManually) {
|
|
335
335
|
shouldClose(null, hide, event);
|
|
336
336
|
} else {
|
|
@@ -67,11 +67,15 @@ export interface TimePickerProps
|
|
|
67
67
|
maxTime?: string | Date;
|
|
68
68
|
/** Includes number of seconds with the chosen time and allows users to manually edit the seconds value. */
|
|
69
69
|
includeSeconds?: boolean;
|
|
70
|
+
/** Flag to control the opened state of the time picker menu */
|
|
71
|
+
isOpen?: boolean;
|
|
72
|
+
/** Handler invoked each time the open state of time picker updates */
|
|
73
|
+
setIsOpen?: (isOpen?: boolean) => void;
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
interface TimePickerState {
|
|
73
77
|
isInvalid: boolean;
|
|
74
|
-
|
|
78
|
+
isTimeOptionsOpen: boolean;
|
|
75
79
|
timeState: string;
|
|
76
80
|
focusedIndex: number;
|
|
77
81
|
scrollIndex: number;
|
|
@@ -102,12 +106,13 @@ export class TimePicker extends React.Component<TimePickerProps, TimePickerState
|
|
|
102
106
|
stepMinutes: 30,
|
|
103
107
|
inputProps: {},
|
|
104
108
|
minTime: '',
|
|
105
|
-
maxTime: ''
|
|
109
|
+
maxTime: '',
|
|
110
|
+
setIsOpen: () => {}
|
|
106
111
|
};
|
|
107
112
|
|
|
108
113
|
constructor(props: TimePickerProps) {
|
|
109
114
|
super(props);
|
|
110
|
-
const { is24Hour, delimiter, time, includeSeconds } = this.props;
|
|
115
|
+
const { is24Hour, delimiter, time, includeSeconds, isOpen } = this.props;
|
|
111
116
|
let { minTime, maxTime } = this.props;
|
|
112
117
|
if (minTime === '') {
|
|
113
118
|
const minSeconds = includeSeconds ? `${delimiter}00` : '';
|
|
@@ -120,7 +125,7 @@ export class TimePicker extends React.Component<TimePickerProps, TimePickerState
|
|
|
120
125
|
const timeRegex = this.getRegExp();
|
|
121
126
|
this.state = {
|
|
122
127
|
isInvalid: false,
|
|
123
|
-
|
|
128
|
+
isTimeOptionsOpen: isOpen,
|
|
124
129
|
timeState: parseTime(time, timeRegex, delimiter, !is24Hour, includeSeconds),
|
|
125
130
|
focusedIndex: null,
|
|
126
131
|
scrollIndex: 0,
|
|
@@ -145,18 +150,18 @@ export class TimePicker extends React.Component<TimePickerProps, TimePickerState
|
|
|
145
150
|
onDocClick = (event: MouseEvent | TouchEvent) => {
|
|
146
151
|
const clickedOnToggle = this.toggleRef?.current?.contains(event.target as Node);
|
|
147
152
|
const clickedWithinMenu = this.menuRef?.current?.contains(event.target as Node);
|
|
148
|
-
if (this.state.
|
|
153
|
+
if (this.state.isTimeOptionsOpen && !(clickedOnToggle || clickedWithinMenu)) {
|
|
149
154
|
this.onToggle(false);
|
|
150
155
|
}
|
|
151
156
|
};
|
|
152
157
|
|
|
153
158
|
handleGlobalKeys = (event: KeyboardEvent) => {
|
|
154
|
-
const {
|
|
159
|
+
const { isTimeOptionsOpen, focusedIndex, scrollIndex } = this.state;
|
|
155
160
|
// keyboard pressed while focus on toggle
|
|
156
161
|
if (this.inputRef?.current?.contains(event.target as Node)) {
|
|
157
|
-
if (!
|
|
162
|
+
if (!isTimeOptionsOpen && event.key !== KeyTypes.Tab && event.key !== KeyTypes.Escape) {
|
|
158
163
|
this.onToggle(true);
|
|
159
|
-
} else if (
|
|
164
|
+
} else if (isTimeOptionsOpen) {
|
|
160
165
|
if (event.key === KeyTypes.Escape || event.key === KeyTypes.Tab) {
|
|
161
166
|
this.onToggle(false);
|
|
162
167
|
} else if (event.key === KeyTypes.Enter) {
|
|
@@ -188,9 +193,13 @@ export class TimePicker extends React.Component<TimePickerProps, TimePickerState
|
|
|
188
193
|
};
|
|
189
194
|
|
|
190
195
|
componentDidUpdate(prevProps: TimePickerProps, prevState: TimePickerState) {
|
|
191
|
-
const { timeState,
|
|
192
|
-
const { time, is24Hour, delimiter, includeSeconds } = this.props;
|
|
193
|
-
if (isOpen
|
|
196
|
+
const { timeState, isTimeOptionsOpen, isInvalid, timeRegex } = this.state;
|
|
197
|
+
const { time, is24Hour, delimiter, includeSeconds, isOpen } = this.props;
|
|
198
|
+
if (prevProps.isOpen !== isOpen) {
|
|
199
|
+
this.onToggle(isOpen);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (isTimeOptionsOpen && !prevState.isTimeOptionsOpen && timeState && !isInvalid) {
|
|
194
203
|
this.scrollToSelection(timeState);
|
|
195
204
|
}
|
|
196
205
|
if (delimiter !== prevProps.delimiter) {
|
|
@@ -340,18 +349,21 @@ export class TimePicker extends React.Component<TimePickerProps, TimePickerState
|
|
|
340
349
|
const { timeRegex, isInvalid } = prevState;
|
|
341
350
|
const { delimiter, is24Hour, includeSeconds } = this.props;
|
|
342
351
|
const time = parseTime(prevState.timeState, timeRegex, delimiter, !is24Hour, includeSeconds);
|
|
343
|
-
|
|
344
352
|
return {
|
|
345
|
-
isOpen,
|
|
353
|
+
isTimeOptionsOpen: isOpen,
|
|
346
354
|
timeState: time,
|
|
347
355
|
isInvalid: isOpen ? isInvalid : !this.isValid(time)
|
|
348
356
|
};
|
|
349
357
|
});
|
|
358
|
+
this.props.setIsOpen(isOpen);
|
|
359
|
+
if (!isOpen) {
|
|
360
|
+
this.inputRef.current.focus();
|
|
361
|
+
}
|
|
350
362
|
};
|
|
351
363
|
|
|
352
364
|
onSelect = (e: any) => {
|
|
353
365
|
const { timeRegex, timeState } = this.state;
|
|
354
|
-
const { delimiter, is24Hour, includeSeconds } = this.props;
|
|
366
|
+
const { delimiter, is24Hour, includeSeconds, setIsOpen } = this.props;
|
|
355
367
|
const time = parseTime(e.target.textContent, timeRegex, delimiter, !is24Hour, includeSeconds);
|
|
356
368
|
if (time !== timeState) {
|
|
357
369
|
this.onInputChange(time);
|
|
@@ -359,13 +371,14 @@ export class TimePicker extends React.Component<TimePickerProps, TimePickerState
|
|
|
359
371
|
|
|
360
372
|
this.inputRef.current.focus();
|
|
361
373
|
this.setState({
|
|
362
|
-
|
|
374
|
+
isTimeOptionsOpen: false,
|
|
363
375
|
isInvalid: false
|
|
364
376
|
});
|
|
377
|
+
setIsOpen(false);
|
|
365
378
|
};
|
|
366
379
|
|
|
367
380
|
onInputClick = (e: any) => {
|
|
368
|
-
if (!this.state.
|
|
381
|
+
if (!this.state.isTimeOptionsOpen) {
|
|
369
382
|
this.onToggle(true);
|
|
370
383
|
}
|
|
371
384
|
e.stopPropagation();
|
|
@@ -417,6 +430,10 @@ export class TimePicker extends React.Component<TimePickerProps, TimePickerState
|
|
|
417
430
|
inputProps,
|
|
418
431
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
419
432
|
onChange,
|
|
433
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
434
|
+
setIsOpen,
|
|
435
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
436
|
+
isOpen,
|
|
420
437
|
time,
|
|
421
438
|
validateTime,
|
|
422
439
|
minTime,
|
|
@@ -425,7 +442,7 @@ export class TimePicker extends React.Component<TimePickerProps, TimePickerState
|
|
|
425
442
|
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
426
443
|
...props
|
|
427
444
|
} = this.props;
|
|
428
|
-
const { timeState,
|
|
445
|
+
const { timeState, isTimeOptionsOpen, isInvalid, minTimeState, maxTimeState } = this.state;
|
|
429
446
|
const style = { '--pf-c-date-picker__input--c-form-control--Width': width } as React.CSSProperties;
|
|
430
447
|
const options = makeTimeOptions(stepMinutes, !is24Hour, delimiter, minTimeState, maxTimeState, includeSeconds);
|
|
431
448
|
const isValidFormat = this.isValidFormat(timeState);
|
|
@@ -484,13 +501,13 @@ export class TimePicker extends React.Component<TimePickerProps, TimePickerState
|
|
|
484
501
|
appendTo={menuAppendTo === 'parent' ? getParentElement() : menuAppendTo}
|
|
485
502
|
trigger={textInput}
|
|
486
503
|
popper={menuContainer}
|
|
487
|
-
isVisible={
|
|
504
|
+
isVisible={isTimeOptionsOpen}
|
|
488
505
|
/>
|
|
489
506
|
) : (
|
|
490
507
|
textInput
|
|
491
508
|
)}
|
|
492
509
|
</div>
|
|
493
|
-
{
|
|
510
|
+
{isTimeOptionsOpen && menuAppendTo === 'inline' && menuContainer}
|
|
494
511
|
</div>
|
|
495
512
|
</InputGroup>
|
|
496
513
|
{isInvalid && (
|
|
@@ -811,6 +811,7 @@ import {
|
|
|
811
811
|
Dropdown,
|
|
812
812
|
DropdownItem,
|
|
813
813
|
DropdownSeparator,
|
|
814
|
+
DropdownPosition,
|
|
814
815
|
KebabToggle,
|
|
815
816
|
SearchInput
|
|
816
817
|
} from '@patternfly/react-core';
|
|
@@ -1036,6 +1037,7 @@ class ToolbarWithFilterExample extends React.Component {
|
|
|
1036
1037
|
isOpen={kebabIsOpen}
|
|
1037
1038
|
isPlain
|
|
1038
1039
|
dropdownItems={dropdownItems}
|
|
1040
|
+
position={DropdownPosition.right}
|
|
1039
1041
|
/>
|
|
1040
1042
|
</ToolbarItem>
|
|
1041
1043
|
</React.Fragment>
|
|
@@ -1080,6 +1082,7 @@ import {
|
|
|
1080
1082
|
DropdownToggle,
|
|
1081
1083
|
DropdownToggleCheckbox,
|
|
1082
1084
|
DropdownItem,
|
|
1085
|
+
DropdownPosition,
|
|
1083
1086
|
Divider,
|
|
1084
1087
|
OverflowMenu,
|
|
1085
1088
|
OverflowMenuContent,
|
|
@@ -1293,6 +1296,7 @@ class ToolbarStacked extends React.Component {
|
|
|
1293
1296
|
isOpen={kebabIsOpen}
|
|
1294
1297
|
isPlain
|
|
1295
1298
|
dropdownItems={dropdownItems}
|
|
1299
|
+
position={DropdownPosition.right}
|
|
1296
1300
|
/>
|
|
1297
1301
|
</OverflowMenuControl>
|
|
1298
1302
|
</OverflowMenu>
|
package/src/demos/Card/Card.md
CHANGED
|
@@ -146,3 +146,11 @@ DateTimeRangePicker = () => {
|
|
|
146
146
|
}
|
|
147
147
|
```
|
|
148
148
|
|
|
149
|
+
|
|
150
|
+
### Date and time pickers in modal
|
|
151
|
+
Modals trap focus and watch a few document level events. In order to place a date picker in a modal:
|
|
152
|
+
- To avoid the modal's escape press event handler from overruling the date picker's escape press handlers, use the `DatePickerRef` to close the calendar when it is open and the escape key is pressed.
|
|
153
|
+
- Append the calendar to the modal to keep it as close to the date picker in the DOM while maintaining correct layouts visually
|
|
154
|
+
In order to place a time picker in the modal, its menu must be appended to the time picker's parent.
|
|
155
|
+
```ts file="./examples/DateTimePickerInModal.tsx"
|
|
156
|
+
```
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { DatePicker, Modal, ModalVariant, Button, TimePicker, InputGroup } from '@patternfly/react-core';
|
|
3
|
+
|
|
4
|
+
export const SimpleModal = () => {
|
|
5
|
+
const [isModalOpen, setIsModalOpen] = React.useState(false);
|
|
6
|
+
const [isTimePickerOpen, setIsTimePickerOpen] = React.useState(false);
|
|
7
|
+
const dateRef = React.useRef(null);
|
|
8
|
+
|
|
9
|
+
const handleModalToggle = () => {
|
|
10
|
+
setIsModalOpen(!isModalOpen);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const onEscapePress = (event: KeyboardEvent) => {
|
|
14
|
+
if (dateRef && dateRef.current && dateRef.current.isCalendarOpen) {
|
|
15
|
+
dateRef.current.toggleCalendar(false, event.key);
|
|
16
|
+
} else if (isTimePickerOpen) {
|
|
17
|
+
setIsTimePickerOpen(false);
|
|
18
|
+
} else {
|
|
19
|
+
handleModalToggle();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<React.Fragment>
|
|
25
|
+
<Button variant="primary" onClick={handleModalToggle}>
|
|
26
|
+
Launch modal
|
|
27
|
+
</Button>
|
|
28
|
+
<Modal
|
|
29
|
+
id="date-time-picker-modal"
|
|
30
|
+
variant={ModalVariant.small}
|
|
31
|
+
title="Generic modal header"
|
|
32
|
+
isOpen={isModalOpen}
|
|
33
|
+
onEscapePress={onEscapePress}
|
|
34
|
+
onClose={handleModalToggle}
|
|
35
|
+
actions={[
|
|
36
|
+
<Button key="confirm" variant="primary" onClick={handleModalToggle}>
|
|
37
|
+
Confirm
|
|
38
|
+
</Button>,
|
|
39
|
+
<Button key="cancel" variant="link" onClick={handleModalToggle}>
|
|
40
|
+
Cancel
|
|
41
|
+
</Button>
|
|
42
|
+
]}
|
|
43
|
+
>
|
|
44
|
+
<InputGroup>
|
|
45
|
+
<DatePicker ref={dateRef} appendTo={() => document.getElementById('date-time-picker-modal')} />
|
|
46
|
+
<TimePicker menuAppendTo="parent" isOpen={isTimePickerOpen} setIsOpen={setIsTimePickerOpen} />
|
|
47
|
+
</InputGroup>
|
|
48
|
+
</Modal>
|
|
49
|
+
</React.Fragment>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
@@ -1246,6 +1246,8 @@ class PrimaryDetailCardView extends React.Component {
|
|
|
1246
1246
|
isOpen={isLowerToolbarKebabDropdownOpen}
|
|
1247
1247
|
isPlain
|
|
1248
1248
|
dropdownItems={toolbarKebabDropdownItems}
|
|
1249
|
+
isFlipEnabled
|
|
1250
|
+
menuAppendTo="parent"
|
|
1249
1251
|
/>
|
|
1250
1252
|
</OverflowMenuControl>
|
|
1251
1253
|
</OverflowMenu>
|
|
@@ -1337,14 +1339,14 @@ class PrimaryDetailCardView extends React.Component {
|
|
|
1337
1339
|
<Text component="p">This is a demo that showcases Patternfly Cards.</Text>
|
|
1338
1340
|
</TextContent>
|
|
1339
1341
|
</PageSection>
|
|
1342
|
+
<PageSection isFilled padding={{ default: 'noPadding' }}>
|
|
1343
|
+
<Toolbar id="card-view-data-toolbar-group-types" usePageInsets clearAllFilters={this.onDelete}>
|
|
1344
|
+
<ToolbarContent>{toolbarItems}</ToolbarContent>
|
|
1345
|
+
</Toolbar>
|
|
1346
|
+
<Divider component="div" />
|
|
1347
|
+
</PageSection>
|
|
1340
1348
|
<PageSection isFilled padding={{ default: 'noPadding' }}>
|
|
1341
1349
|
<Drawer isExpanded={isDrawerExpanded} className={'pf-m-inline-on-2xl'}>
|
|
1342
|
-
<DrawerSection>
|
|
1343
|
-
<Toolbar id="card-view-data-toolbar-group-types" usePageInsets clearAllFilters={this.onDelete}>
|
|
1344
|
-
<ToolbarContent>{toolbarItems}</ToolbarContent>
|
|
1345
|
-
</Toolbar>
|
|
1346
|
-
<Divider component="div" />
|
|
1347
|
-
</DrawerSection>
|
|
1348
1350
|
<DrawerContent panelContent={panelContent} className={'pf-m-no-background'}>
|
|
1349
1351
|
<DrawerContentBody hasPadding>{drawerContent}</DrawerContentBody>
|
|
1350
1352
|
</DrawerContent>
|
|
@@ -1663,6 +1665,8 @@ class PrimaryDetailDataListInCard extends React.Component {
|
|
|
1663
1665
|
}
|
|
1664
1666
|
isOpen={isDropdownOpen}
|
|
1665
1667
|
dropdownItems={dropdownItems}
|
|
1668
|
+
isFlipEnabled
|
|
1669
|
+
menuAppendTo="parent"
|
|
1666
1670
|
/>
|
|
1667
1671
|
</ToolbarItem>
|
|
1668
1672
|
</ToolbarContent>
|