@hazelcast/ui 1.3.1-next.0 → 1.3.1
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.
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React, { FC } from 'react'
|
|
2
|
+
import { ReactDatePickerProps } from 'react-datepicker'
|
|
3
|
+
import { ChevronLeft, ChevronRight } from 'react-feather'
|
|
4
|
+
import { format } from 'date-fns'
|
|
5
|
+
|
|
6
|
+
import { IconButton } from '../../IconButton'
|
|
7
|
+
|
|
8
|
+
import styles from '../Calendar.module.scss'
|
|
9
|
+
import styleConsts from '../../../styles/constants/export.module.scss'
|
|
10
|
+
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
|
+
type ExtractFnArgumentType<T> = T extends (arg: infer U) => any ? U : never
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* Note:
|
|
16
|
+
* react-datepicker does expose header props type as "renderCustomHeader", however the property is defined
|
|
17
|
+
* as a render-prop.
|
|
18
|
+
* This way we can pick the argument type from the render function and convert the property
|
|
19
|
+
* to use a regular component, rather than a render-prop.
|
|
20
|
+
*
|
|
21
|
+
* There is no significant benefit other than readability.
|
|
22
|
+
*/
|
|
23
|
+
export type CalendarHeaderProps = ExtractFnArgumentType<ReactDatePickerProps['renderCustomHeader']>
|
|
24
|
+
|
|
25
|
+
export const CalendarHeader: FC<CalendarHeaderProps> = ({ date, decreaseMonth, increaseMonth }) => {
|
|
26
|
+
return (
|
|
27
|
+
<div data-test="date-picker-header" className={styles.headerContainer}>
|
|
28
|
+
<IconButton
|
|
29
|
+
data-test="date-picker-header-icon-previous"
|
|
30
|
+
icon={ChevronLeft}
|
|
31
|
+
ariaLabel="Previous month"
|
|
32
|
+
iconColor={styleConsts.colorPrimary}
|
|
33
|
+
onClick={decreaseMonth}
|
|
34
|
+
/>
|
|
35
|
+
<div className={styles.headerMonthAndYear}>{format(date, 'MMMM y')}</div>
|
|
36
|
+
<IconButton
|
|
37
|
+
data-test="date-picker-header-icon-next"
|
|
38
|
+
icon={ChevronRight}
|
|
39
|
+
ariaLabel="Next month"
|
|
40
|
+
iconColor={styleConsts.colorPrimary}
|
|
41
|
+
onClick={increaseMonth}
|
|
42
|
+
/>
|
|
43
|
+
</div>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React, { FC, ChangeEvent, InputHTMLAttributes, forwardRef } from 'react'
|
|
2
|
+
import { Calendar } from 'react-feather'
|
|
3
|
+
|
|
4
|
+
import { TextField } from '../../TextField'
|
|
5
|
+
import { CalendarSize } from '../Calendar'
|
|
6
|
+
|
|
7
|
+
export type CalendarInputInternalProps = {
|
|
8
|
+
onChange: (e: ChangeEvent<HTMLInputElement>) => void
|
|
9
|
+
} & CalendarInputExtraProps &
|
|
10
|
+
Omit<InputHTMLAttributes<HTMLInputElement>, 'onChange'>
|
|
11
|
+
|
|
12
|
+
export const CalendarInputInternal: FC<CalendarInputInternalProps> = ({ className, value, onChange, label, textFieldSize, ...props }) => (
|
|
13
|
+
<TextField<'text'>
|
|
14
|
+
{...props}
|
|
15
|
+
size={textFieldSize}
|
|
16
|
+
data-test="calendar-input"
|
|
17
|
+
value={value?.toString()}
|
|
18
|
+
onChange={onChange}
|
|
19
|
+
inputContainerClassName={className}
|
|
20
|
+
type="text"
|
|
21
|
+
name="calendar-input"
|
|
22
|
+
label={label}
|
|
23
|
+
inputTrailingIcon={Calendar}
|
|
24
|
+
inputTrailingIconLabel="Calendar Icon"
|
|
25
|
+
/>
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
export type CalendarInputExtraProps = {
|
|
29
|
+
label: string
|
|
30
|
+
textFieldSize?: CalendarSize
|
|
31
|
+
className?: string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/*
|
|
35
|
+
* Note:
|
|
36
|
+
* The types are not available, the element is internally instantiated via React.cloneElement
|
|
37
|
+
* We need to accept the props as unknown and cast them in place
|
|
38
|
+
* Otherwise "Calendar.customInput" would require us to pass them explicitly
|
|
39
|
+
*/
|
|
40
|
+
export type CalendarInputProps = unknown & CalendarInputExtraProps
|
|
41
|
+
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
43
|
+
export const CalendarInput: FC<CalendarInputProps> = forwardRef((props, ref) => {
|
|
44
|
+
// Note: Cast the "unknown" props to the actual type
|
|
45
|
+
const propsTyped = props as CalendarInputInternalProps
|
|
46
|
+
|
|
47
|
+
return <CalendarInputInternal {...propsTyped} />
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
CalendarInput.displayName = 'CalendarInput'
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import React, { ChangeEvent, FC, useCallback, useEffect, useState } from 'react'
|
|
2
|
+
import { format, parse } from 'date-fns'
|
|
3
|
+
|
|
4
|
+
import { Button } from '../../Button'
|
|
5
|
+
import { timePoints } from '../helpers/consts'
|
|
6
|
+
import { TimeField } from '../../TimeField'
|
|
7
|
+
|
|
8
|
+
import styles from './CalendarTime.module.scss'
|
|
9
|
+
|
|
10
|
+
// Note: AM/PM 1-12 hours time
|
|
11
|
+
const DATE_FORMAT = 'hh:mm a'
|
|
12
|
+
// Note: 0-23 hours time
|
|
13
|
+
const DATE_FORMAT_NO_MERIDIEM = 'HH:mm'
|
|
14
|
+
|
|
15
|
+
export type CalendarTimeInternalProps = {
|
|
16
|
+
date: Date
|
|
17
|
+
onChange: (timeString: string) => void
|
|
18
|
+
value: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const CalendarTimeInternal: FC<CalendarTimeInternalProps> = ({ date, value, onChange }) => {
|
|
22
|
+
const [time, setTime] = useState(value)
|
|
23
|
+
const [timeInputError, setTimeInputError] = useState<string | undefined>()
|
|
24
|
+
|
|
25
|
+
/*
|
|
26
|
+
* Note:
|
|
27
|
+
* We cannot override how react-datepicker handles time values.
|
|
28
|
+
* It always expects format of "xx:xx" to parse the hours and minutes from. (See Source 2. link below)
|
|
29
|
+
*
|
|
30
|
+
* This is not an issue for majority of browsers, however as for IE11, where <input type="time" />
|
|
31
|
+
* fall back to <input type="text" />, this becomes an issue.
|
|
32
|
+
*
|
|
33
|
+
* Furthermore, the package even does not handle potential thrown error by date-fns' parse.
|
|
34
|
+
* Even the live demos do crash on an input like "10:10q" (See source 1. link below).
|
|
35
|
+
*
|
|
36
|
+
* For this reasons we need to provide our own wrapper around the functionality to achieve 2 things:
|
|
37
|
+
* - Provide IE11 with a proper fallback
|
|
38
|
+
* - Prevent the invalid input value crash
|
|
39
|
+
* - Provide the user with an input error in case the value is invalid
|
|
40
|
+
*
|
|
41
|
+
* Source(1): https://reactdatepicker.com/#example-custom-time-input
|
|
42
|
+
* Source(2): https://github.com/Hacker0x01/react-datepicker/blob/master/src/inputTime.jsx#L32
|
|
43
|
+
*/
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
setTime(value)
|
|
46
|
+
}, [value])
|
|
47
|
+
|
|
48
|
+
const handleTimeInputChange = useCallback(
|
|
49
|
+
({ target: { value } }: ChangeEvent<HTMLInputElement>) => {
|
|
50
|
+
setTime(value)
|
|
51
|
+
const parsedDate = parse(value, DATE_FORMAT_NO_MERIDIEM, date)
|
|
52
|
+
|
|
53
|
+
if (!isNaN(parsedDate.valueOf())) {
|
|
54
|
+
setTimeInputError(undefined)
|
|
55
|
+
onChange(value)
|
|
56
|
+
} else {
|
|
57
|
+
setTimeInputError('Invalid time')
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
[onChange, date],
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
const handleDateClick = useCallback(
|
|
64
|
+
(dp: string) => () => {
|
|
65
|
+
// Note: Input time (derived from timePoints) is in 'DATE_FORMAT'
|
|
66
|
+
const parsedDate = parse(dp, DATE_FORMAT, date)
|
|
67
|
+
// Note: Output time, that we feed back to 'react-datetime' is in 'DATE_FORMAT_NO_MERIDIEM' format
|
|
68
|
+
const timeStringWithoutAm = format(parsedDate, DATE_FORMAT_NO_MERIDIEM)
|
|
69
|
+
onChange(timeStringWithoutAm)
|
|
70
|
+
},
|
|
71
|
+
[onChange, date],
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<div data-test="calendar-time" className={styles.container}>
|
|
76
|
+
<div data-test="calendar-time-header" className={styles.header}>
|
|
77
|
+
<TimeField
|
|
78
|
+
showAriaLabel
|
|
79
|
+
label="Time Input"
|
|
80
|
+
error={timeInputError}
|
|
81
|
+
className={styles.input}
|
|
82
|
+
name="time"
|
|
83
|
+
value={time.toString()}
|
|
84
|
+
onChange={handleTimeInputChange}
|
|
85
|
+
/>
|
|
86
|
+
</div>
|
|
87
|
+
<div data-test="calendar-time-timePoints" className={styles.timePoints}>
|
|
88
|
+
{timePoints.map((tP) => (
|
|
89
|
+
<Button
|
|
90
|
+
className={styles.timePoint}
|
|
91
|
+
bodyClassName={styles.timePointBody}
|
|
92
|
+
key={tP}
|
|
93
|
+
kind="transparent"
|
|
94
|
+
onClick={handleDateClick(tP)}
|
|
95
|
+
>
|
|
96
|
+
{tP}
|
|
97
|
+
</Button>
|
|
98
|
+
))}
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/*
|
|
105
|
+
* Note:
|
|
106
|
+
* The types are not available, the element is internally instantiated via React.cloneElement
|
|
107
|
+
* We need to accept the props as unknown and cast them in place
|
|
108
|
+
* Otherwise "Calendar.customTimeInput" would require us to pass them explicitly
|
|
109
|
+
*/
|
|
110
|
+
export type CalendarTimeProps = unknown
|
|
111
|
+
|
|
112
|
+
export const CalendarTime: FC<CalendarTimeProps> = (props) => {
|
|
113
|
+
// Note: Cast the "unknown" props to the actual type
|
|
114
|
+
const propsTyped = props as CalendarTimeInternalProps
|
|
115
|
+
|
|
116
|
+
return <CalendarTimeInternal {...propsTyped} />
|
|
117
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
export const timePoints = [
|
|
2
|
+
// Source: https://simple.wikipedia.org/wiki/12-hour_clock
|
|
3
|
+
'12:00 AM',
|
|
4
|
+
'12:15 AM',
|
|
5
|
+
'12:30 AM',
|
|
6
|
+
'12:45 AM',
|
|
7
|
+
'01:00 AM',
|
|
8
|
+
'01:15 AM',
|
|
9
|
+
'01:30 AM',
|
|
10
|
+
'01:45 AM',
|
|
11
|
+
'02:00 AM',
|
|
12
|
+
'02:15 AM',
|
|
13
|
+
'02:30 AM',
|
|
14
|
+
'02:45 AM',
|
|
15
|
+
'03:00 AM',
|
|
16
|
+
'03:15 AM',
|
|
17
|
+
'03:30 AM',
|
|
18
|
+
'03:45 AM',
|
|
19
|
+
'04:00 AM',
|
|
20
|
+
'04:15 AM',
|
|
21
|
+
'04:30 AM',
|
|
22
|
+
'04:45 AM',
|
|
23
|
+
'05:00 AM',
|
|
24
|
+
'05:15 AM',
|
|
25
|
+
'05:30 AM',
|
|
26
|
+
'05:45 AM',
|
|
27
|
+
'06:00 AM',
|
|
28
|
+
'06:15 AM',
|
|
29
|
+
'06:30 AM',
|
|
30
|
+
'06:45 AM',
|
|
31
|
+
'07:00 AM',
|
|
32
|
+
'07:15 AM',
|
|
33
|
+
'07:30 AM',
|
|
34
|
+
'07:45 AM',
|
|
35
|
+
'08:00 AM',
|
|
36
|
+
'08:15 AM',
|
|
37
|
+
'08:30 AM',
|
|
38
|
+
'08:45 AM',
|
|
39
|
+
'09:00 AM',
|
|
40
|
+
'09:15 AM',
|
|
41
|
+
'09:30 AM',
|
|
42
|
+
'09:45 AM',
|
|
43
|
+
'10:00 AM',
|
|
44
|
+
'10:15 AM',
|
|
45
|
+
'10:30 AM',
|
|
46
|
+
'10:45 AM',
|
|
47
|
+
'11:00 AM',
|
|
48
|
+
'11:15 AM',
|
|
49
|
+
'11:30 AM',
|
|
50
|
+
'11:45 AM',
|
|
51
|
+
'12:00 PM',
|
|
52
|
+
'12:15 PM',
|
|
53
|
+
'12:30 PM',
|
|
54
|
+
'12:45 PM',
|
|
55
|
+
'01:00 PM',
|
|
56
|
+
'01:15 PM',
|
|
57
|
+
'01:30 PM',
|
|
58
|
+
'01:45 PM',
|
|
59
|
+
'02:00 PM',
|
|
60
|
+
'02:15 PM',
|
|
61
|
+
'02:30 PM',
|
|
62
|
+
'02:45 PM',
|
|
63
|
+
'03:00 PM',
|
|
64
|
+
'03:15 PM',
|
|
65
|
+
'03:30 PM',
|
|
66
|
+
'03:45 PM',
|
|
67
|
+
'04:00 PM',
|
|
68
|
+
'04:15 PM',
|
|
69
|
+
'04:30 PM',
|
|
70
|
+
'04:45 PM',
|
|
71
|
+
'05:00 PM',
|
|
72
|
+
'05:15 PM',
|
|
73
|
+
'05:30 PM',
|
|
74
|
+
'05:45 PM',
|
|
75
|
+
'06:00 PM',
|
|
76
|
+
'06:15 PM',
|
|
77
|
+
'06:30 PM',
|
|
78
|
+
'06:45 PM',
|
|
79
|
+
'07:00 PM',
|
|
80
|
+
'07:15 PM',
|
|
81
|
+
'07:30 PM',
|
|
82
|
+
'07:45 PM',
|
|
83
|
+
'08:00 PM',
|
|
84
|
+
'08:15 PM',
|
|
85
|
+
'08:30 PM',
|
|
86
|
+
'08:45 PM',
|
|
87
|
+
'09:00 PM',
|
|
88
|
+
'09:15 PM',
|
|
89
|
+
'09:30 PM',
|
|
90
|
+
'09:45 PM',
|
|
91
|
+
'10:00 PM',
|
|
92
|
+
'10:15 PM',
|
|
93
|
+
'10:30 PM',
|
|
94
|
+
'10:45 PM',
|
|
95
|
+
'11:00 PM',
|
|
96
|
+
'11:15 PM',
|
|
97
|
+
'11:30 PM',
|
|
98
|
+
'11:45 PM',
|
|
99
|
+
]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hazelcast/ui",
|
|
3
|
-
"version": "1.3.1
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Hazelcast design system components",
|
|
5
5
|
"author": "",
|
|
6
6
|
"homepage": "https://github.com/hazelcast/frontend-shared#readme",
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"@codemirror/basic-setup": "^0.19.1",
|
|
50
50
|
"@codemirror/stream-parser": "^0.19.3",
|
|
51
51
|
"@codemirror/view": "^0.19.35",
|
|
52
|
-
"@hazelcast/helpers": "^1.3.1
|
|
53
|
-
"@hazelcast/services": "^1.3.1
|
|
52
|
+
"@hazelcast/helpers": "^1.3.1",
|
|
53
|
+
"@hazelcast/services": "^1.3.1",
|
|
54
54
|
"@headlessui/react": "^1.4.2",
|
|
55
55
|
"@icons-pack/react-simple-icons": "^4.6.1",
|
|
56
56
|
"@popperjs/core": "^2.11.0",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
79
|
"@codemirror/legacy-modes": "0.19.0",
|
|
80
|
-
"@hazelcast/test-helpers": "^1.3.1
|
|
80
|
+
"@hazelcast/test-helpers": "^1.3.1",
|
|
81
81
|
"@storybook/addon-docs": "^6.4.9",
|
|
82
82
|
"@storybook/addons": "^6.4.9",
|
|
83
83
|
"@storybook/builder-webpack5": "^6.4.9",
|
|
@@ -113,5 +113,5 @@
|
|
|
113
113
|
"webpack": "^5.65.0",
|
|
114
114
|
"yup": "^0.32.11"
|
|
115
115
|
},
|
|
116
|
-
"gitHead": "
|
|
116
|
+
"gitHead": "d24ee0a98517f6e5e6ebd27e7c3b1be3ac6a768a"
|
|
117
117
|
}
|