@planningcenter/tapestry-react 2.6.0-rc.9 → 2.6.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.
- package/dist/cjs/Button/Button.js +8 -1
- package/dist/cjs/Button/Button.test.js +51 -8
- package/dist/cjs/Collapse/Collapse.js +3 -1
- package/dist/cjs/DataTable/components/BodyRow.js +2 -2
- package/dist/cjs/DataTable/hooks/useCollapsibleRows.js +2 -2
- package/dist/cjs/Dropdown/Dropdown.js +23 -4
- package/dist/cjs/Dropdown/Dropdown.test.js +3 -3
- package/dist/cjs/Dropdown/Link.js +2 -4
- package/dist/cjs/Input/InputLabel.js +40 -63
- package/dist/cjs/Modal/Modal.js +18 -8
- package/dist/cjs/Modal/Modal.test.js +2 -2
- package/dist/cjs/Popover/Popover.js +10 -2
- package/dist/cjs/Scrim/Scrim.js +16 -4
- package/dist/cjs/Table/Table.js +5 -3
- package/dist/cjs/ThemeProvider/ThemeProvider.js +2 -2
- package/dist/cjs/TimeField/TimeField.js +38 -3
- package/dist/cjs/TimeField/TimeField.test.js +179 -10
- package/dist/cjs/Tooltip/Tooltip.js +27 -23
- package/dist/cjs/Tooltip/Tooltip.test.js +178 -0
- package/dist/cjs/system/utils.js +2 -2
- package/dist/cjs/utils.js +3 -3
- package/dist/esm/Button/Button.js +8 -1
- package/dist/esm/Button/Button.test.js +67 -9
- package/dist/esm/Collapse/Collapse.js +3 -1
- package/dist/esm/DataTable/components/BodyRow.js +2 -2
- package/dist/esm/DataTable/hooks/useCollapsibleRows.js +1 -1
- package/dist/esm/Dropdown/Dropdown.js +24 -5
- package/dist/esm/Dropdown/Dropdown.test.js +1 -1
- package/dist/esm/Dropdown/Link.js +1 -2
- package/dist/esm/Input/InputLabel.js +40 -63
- package/dist/esm/Modal/Modal.js +16 -8
- package/dist/esm/Modal/Modal.test.js +1 -1
- package/dist/esm/Popover/Popover.js +8 -2
- package/dist/esm/Scrim/Scrim.js +15 -4
- package/dist/esm/Table/Table.js +2 -1
- package/dist/esm/ThemeProvider/ThemeProvider.js +1 -1
- package/dist/esm/TimeField/TimeField.js +37 -3
- package/dist/esm/TimeField/TimeField.test.js +153 -10
- package/dist/esm/Tooltip/Tooltip.js +29 -24
- package/dist/esm/Tooltip/Tooltip.test.js +160 -0
- package/dist/esm/system/utils.js +1 -1
- package/dist/esm/utils.js +1 -1
- package/dist/types/Button/Button.d.ts +4 -0
- package/dist/types/ThemeProvider/ThemeProvider.d.ts +3 -3
- package/dist/types/Tooltip/Tooltip.test.d.ts +1 -0
- package/dist/types/index.d.ts +1 -1
- package/package.json +4 -4
- package/src/Button/Button.test.tsx +30 -0
- package/src/Button/Button.tsx +14 -1
- package/src/Collapse/Collapse.js +1 -1
- package/src/DataTable/DataTable.js +1 -1
- package/src/DataTable/components/BodyRow.js +1 -1
- package/src/DataTable/hooks/useCollapsibleRows.js +1 -1
- package/src/Dropdown/Dropdown.js +19 -5
- package/src/Dropdown/Dropdown.mdx +3 -3
- package/src/Dropdown/Dropdown.test.tsx +1 -1
- package/src/Dropdown/Link.js +1 -7
- package/src/Input/InputLabel.js +39 -36
- package/src/Input/InputLabel.mdx +1 -0
- package/src/Modal/Modal.js +16 -6
- package/src/Modal/Modal.mdx +2 -1
- package/src/Modal/Modal.test.tsx +1 -1
- package/src/Popover/Popover.mdx +1 -0
- package/src/Popover/Popover.tsx +8 -2
- package/src/Scrim/Scrim.mdx +1 -0
- package/src/Scrim/Scrim.tsx +11 -6
- package/src/Sidebar/Sidebar.mdx +0 -1
- package/src/Table/Table.js +2 -1
- package/src/ThemeProvider/ThemeProvider.tsx +7 -6
- package/src/TimeField/TimeField.js +36 -3
- package/src/TimeField/TimeField.test.tsx +105 -1
- package/src/Tooltip/Tooltip.js +19 -21
- package/src/Tooltip/Tooltip.test.tsx +136 -0
- package/src/index.d.ts +1 -1
- package/src/system/utils.js +1 -1
- package/src/utils.js +1 -1
package/src/Dropdown/Dropdown.js
CHANGED
|
@@ -9,7 +9,7 @@ import Popover from '../Popover'
|
|
|
9
9
|
import { cloneChildren, generateId } from '../utils'
|
|
10
10
|
|
|
11
11
|
import Item, { ITEM_DISPLAY_NAME } from './Item'
|
|
12
|
-
import Link, { LINK_DISPLAY_NAME
|
|
12
|
+
import Link, { LINK_DISPLAY_NAME } from './Link'
|
|
13
13
|
|
|
14
14
|
type Props = {
|
|
15
15
|
children?: React.ReactNode,
|
|
@@ -91,6 +91,7 @@ class Dropdown extends Component<Props> {
|
|
|
91
91
|
super(props)
|
|
92
92
|
this.state = {
|
|
93
93
|
isPopoverOpen: props.defaultOpen || props.forceOpen,
|
|
94
|
+
preventBlur: false,
|
|
94
95
|
}
|
|
95
96
|
this.id = generateId('dropdown')
|
|
96
97
|
}
|
|
@@ -112,6 +113,7 @@ class Dropdown extends Component<Props> {
|
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
togglePopover = (event) => {
|
|
116
|
+
this.setState({ preventBlur: false })
|
|
115
117
|
if (this.state.isPopoverOpen) {
|
|
116
118
|
this.closePopover(event)
|
|
117
119
|
} else {
|
|
@@ -125,7 +127,7 @@ class Dropdown extends Component<Props> {
|
|
|
125
127
|
this.closePopover()
|
|
126
128
|
this.popover.focusAnchor()
|
|
127
129
|
}
|
|
128
|
-
if (
|
|
130
|
+
if (node.tagName === 'A' && event.type !== 'click') {
|
|
129
131
|
node.click()
|
|
130
132
|
} else if (this.props.onSelect) {
|
|
131
133
|
this.props.onSelect(data)
|
|
@@ -215,18 +217,27 @@ class Dropdown extends Component<Props> {
|
|
|
215
217
|
'aria-haspopup': true,
|
|
216
218
|
'aria-expanded': isPopoverOpen,
|
|
217
219
|
[arrowIconOnly ? 'icon' : 'iconRight']: {
|
|
218
|
-
name: isPopoverOpen
|
|
220
|
+
name: isPopoverOpen
|
|
221
|
+
? 'general.upCaret'
|
|
222
|
+
: 'general.downCaret',
|
|
219
223
|
size: 'sm',
|
|
220
224
|
},
|
|
221
225
|
title: arrowIconOnly ? 'arrow down' : restProps.title,
|
|
222
226
|
tabIndex: 0,
|
|
223
227
|
cursor: 'pointer',
|
|
224
|
-
onBlur:
|
|
225
|
-
|
|
228
|
+
onBlur: (event) => {
|
|
229
|
+
if (this.state.preventBlur) {
|
|
230
|
+
this.setState({ preventBlur: false })
|
|
231
|
+
} else {
|
|
232
|
+
requestBlur(event)
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
onClick: (event) => {
|
|
226
236
|
this.togglePopover()
|
|
227
237
|
if (!isPopoverOpen) {
|
|
228
238
|
this.popover.focusAnchor()
|
|
229
239
|
}
|
|
240
|
+
onClick && onClick(event)
|
|
230
241
|
},
|
|
231
242
|
onKeyDown: (event) => {
|
|
232
243
|
anchorProps.onKeyDown(event)
|
|
@@ -274,6 +285,9 @@ class Dropdown extends Component<Props> {
|
|
|
274
285
|
})
|
|
275
286
|
}
|
|
276
287
|
},
|
|
288
|
+
onMouseDown: () => {
|
|
289
|
+
this.setState({ preventBlur: true })
|
|
290
|
+
},
|
|
277
291
|
...restProps,
|
|
278
292
|
}
|
|
279
293
|
if (React.isValidElement(triggerElement)) {
|
|
@@ -26,10 +26,10 @@ render(
|
|
|
26
26
|
render(
|
|
27
27
|
<Dropdown title="Links" size="sm" variant="outline">
|
|
28
28
|
<Dropdown.Link to="http://planning.center" external>
|
|
29
|
-
Planning Center
|
|
29
|
+
Planning Center (external)
|
|
30
30
|
</Dropdown.Link>
|
|
31
|
-
<Dropdown.Link to="
|
|
32
|
-
React
|
|
31
|
+
<Dropdown.Link to="/button">
|
|
32
|
+
Tapestry React Button
|
|
33
33
|
</Dropdown.Link>
|
|
34
34
|
</Dropdown>
|
|
35
35
|
)
|
|
@@ -2,7 +2,7 @@ import React, { useState } from 'react'
|
|
|
2
2
|
import { fireEvent, render, screen } from '@testing-library/react'
|
|
3
3
|
import userEvent from '@testing-library/user-event'
|
|
4
4
|
import '@testing-library/jest-dom/extend-expect'
|
|
5
|
-
import
|
|
5
|
+
import noop from 'lodash/noop'
|
|
6
6
|
import { Box, Button, Text, ThemeProvider } from '..'
|
|
7
7
|
import Dropdown from './Dropdown'
|
|
8
8
|
|
package/src/Dropdown/Link.js
CHANGED
|
@@ -4,7 +4,6 @@ import { ItemListItem } from '../ItemList'
|
|
|
4
4
|
import Menu from '../Menu'
|
|
5
5
|
|
|
6
6
|
export const LINK_DISPLAY_NAME = 'Dropdown.Link'
|
|
7
|
-
export const LINK_DATA = 'link'
|
|
8
7
|
|
|
9
8
|
class Link extends Component {
|
|
10
9
|
// Graphql wasn't picking up the correct displayName when this was
|
|
@@ -17,12 +16,7 @@ class Link extends Component {
|
|
|
17
16
|
restProps.target = '_blank'
|
|
18
17
|
}
|
|
19
18
|
return (
|
|
20
|
-
<ItemListItem
|
|
21
|
-
data={LINK_DATA}
|
|
22
|
-
text={text}
|
|
23
|
-
disabled={disabled}
|
|
24
|
-
index={index}
|
|
25
|
-
>
|
|
19
|
+
<ItemListItem data="link" text={text} disabled={disabled} index={index}>
|
|
26
20
|
{({ id, highlight, highlighted, clearHighlight, select }) => (
|
|
27
21
|
<Menu.Item
|
|
28
22
|
as="a"
|
package/src/Input/InputLabel.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// @flow
|
|
2
|
-
import React, {
|
|
2
|
+
import React, { useCallback, useRef, useEffect } from 'react'
|
|
3
3
|
|
|
4
4
|
import { getColor } from '../system'
|
|
5
5
|
import Text from '../Text'
|
|
6
|
+
import { useThemeProps } from '../system'
|
|
6
7
|
|
|
7
8
|
import { inputs, inputLabels } from './utils'
|
|
8
9
|
|
|
@@ -10,54 +11,56 @@ export type InputLabelProps = {
|
|
|
10
11
|
/**
|
|
11
12
|
* The `id` of the input to control. Compatible with all Tapestry-React form components.
|
|
12
13
|
*/
|
|
13
|
-
controls
|
|
14
|
+
controls?: string,
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* The current state of the label. Should match corresponding `Input`'s state prop.
|
|
17
18
|
*/
|
|
18
|
-
state
|
|
19
|
+
state?: 'warning' | 'error' | 'success',
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
this.input = inputs[this.props.controls]
|
|
24
|
-
inputLabels[this.props.controls] = true
|
|
25
|
-
}
|
|
22
|
+
function InputLabel({ controls, state, ...restProps }: InputLabelProps) {
|
|
23
|
+
const { ...themeProps } = useThemeProps('inputLabel', restProps)
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
delete inputLabels[this.props.controls]
|
|
29
|
-
}
|
|
25
|
+
const input = useRef(null)
|
|
30
26
|
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
if (controls) {
|
|
28
|
+
themeProps.id = `${controls}-label`
|
|
33
29
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
this.input && this.input.setState({ isHovered: true })
|
|
30
|
+
if (state) {
|
|
31
|
+
themeProps.color = getColor(state)
|
|
37
32
|
}
|
|
38
33
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
input.current = inputs[controls]
|
|
36
|
+
inputLabels[controls] = true
|
|
42
37
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (controls) {
|
|
46
|
-
restProps.id = `${controls}-label`
|
|
47
|
-
}
|
|
48
|
-
if (state) {
|
|
49
|
-
restProps.color = getColor(state)
|
|
38
|
+
return () => {
|
|
39
|
+
delete inputLabels[controls]
|
|
50
40
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
41
|
+
}, [])
|
|
42
|
+
|
|
43
|
+
const focusInput = useCallback(() => {
|
|
44
|
+
input.current && input.current.focus()
|
|
45
|
+
}, [input])
|
|
46
|
+
|
|
47
|
+
const handleMouseOver = useCallback(() => {
|
|
48
|
+
input.current && input.current.setState({ isHovered: true })
|
|
49
|
+
}, [input])
|
|
50
|
+
|
|
51
|
+
const handleMouseOut = useCallback(() => {
|
|
52
|
+
input.current && input.current.setState({ isHovered: false })
|
|
53
|
+
}, [input])
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<Text
|
|
57
|
+
as="label"
|
|
58
|
+
onMouseOver={handleMouseOver}
|
|
59
|
+
onMouseOut={handleMouseOut}
|
|
60
|
+
onClick={focusInput}
|
|
61
|
+
{...themeProps}
|
|
62
|
+
/>
|
|
63
|
+
)
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
InputLabel.displayName = 'Input.InputLabel'
|
package/src/Input/InputLabel.mdx
CHANGED
|
@@ -4,6 +4,7 @@ category: Forms
|
|
|
4
4
|
summary: Provides accessibility as well as usability improvements for mouse users by allowing the user to click the <InputLabel/> component to focus the respective control. This mimics the browsers native label tag, but allows use with custom components like <Select/>.
|
|
5
5
|
propsSummary: Accepts [Text](/text) props.
|
|
6
6
|
parent: Input
|
|
7
|
+
themeKey: inputLabel
|
|
7
8
|
---
|
|
8
9
|
|
|
9
10
|
```jsx live
|
package/src/Modal/Modal.js
CHANGED
|
@@ -5,6 +5,7 @@ import Box from '../Box'
|
|
|
5
5
|
import Scrim from '../Scrim'
|
|
6
6
|
import { useDocumentEvent } from '../hooks'
|
|
7
7
|
import { trapFocus } from '../utils'
|
|
8
|
+
import { useThemeProps } from '../system'
|
|
8
9
|
|
|
9
10
|
export type ModalProps = {
|
|
10
11
|
children?: any,
|
|
@@ -23,6 +24,11 @@ export type ModalProps = {
|
|
|
23
24
|
* Determines whether the modal is open or not.
|
|
24
25
|
*/
|
|
25
26
|
open: boolean,
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Props passed to the internal [`<Scrim/>`](/scrim) component.
|
|
30
|
+
*/
|
|
31
|
+
scrimProps?: object,
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
function Modal({
|
|
@@ -32,6 +38,7 @@ function Modal({
|
|
|
32
38
|
open,
|
|
33
39
|
...restProps
|
|
34
40
|
}: ModalProps) {
|
|
41
|
+
const { scrimProps = {}, ...themeProps } = useThemeProps('modal', restProps)
|
|
35
42
|
const modalRef = useRef(null)
|
|
36
43
|
|
|
37
44
|
useLayoutEffect(() => {
|
|
@@ -58,16 +65,19 @@ function Modal({
|
|
|
58
65
|
onRequestClose()
|
|
59
66
|
}
|
|
60
67
|
}}
|
|
68
|
+
{...scrimProps}
|
|
61
69
|
>
|
|
62
70
|
<Box
|
|
71
|
+
backgroundColor="surface"
|
|
72
|
+
boxShadow="0 0 20px rgba(0, 0, 0, 0.15)"
|
|
63
73
|
innerRef={modalRef}
|
|
64
|
-
width="100%"
|
|
65
|
-
maxWidth={60}
|
|
66
|
-
padding={2}
|
|
67
74
|
margin={4}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
{
|
|
75
|
+
maxWidth={60}
|
|
76
|
+
padding={4}
|
|
77
|
+
paddingBottom={3}
|
|
78
|
+
radius={8}
|
|
79
|
+
width="100%"
|
|
80
|
+
{...themeProps}
|
|
71
81
|
>
|
|
72
82
|
{children}
|
|
73
83
|
</Box>
|
package/src/Modal/Modal.mdx
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
title: Modal
|
|
3
3
|
category: Overlays
|
|
4
4
|
propsSummary: Accepts [Box](/box) props.
|
|
5
|
+
themeKey: modal
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
```jsx live
|
|
@@ -16,7 +17,7 @@ function MyModal(props) {
|
|
|
16
17
|
return () => setLazyComponent(null)
|
|
17
18
|
}, [props.open])
|
|
18
19
|
return (
|
|
19
|
-
<Modal id="modal" closeOnOutsideClick height={
|
|
20
|
+
<Modal id="modal" closeOnOutsideClick height={25} {...props}>
|
|
20
21
|
<StackView spacing={2}>
|
|
21
22
|
{lazyComponent || <Text>Loading lazy component...</Text>}
|
|
22
23
|
<Select
|
package/src/Modal/Modal.test.tsx
CHANGED
|
@@ -5,7 +5,7 @@ import '@testing-library/jest-dom/extend-expect'
|
|
|
5
5
|
import Dropdown from '../Dropdown'
|
|
6
6
|
import Select from '../Select'
|
|
7
7
|
import { Button, Heading, Modal, ThemeProvider } from '../'
|
|
8
|
-
import
|
|
8
|
+
import noop from 'lodash/noop'
|
|
9
9
|
|
|
10
10
|
describe('Modal', () => {
|
|
11
11
|
const TestModal = () => {
|
package/src/Popover/Popover.mdx
CHANGED
package/src/Popover/Popover.tsx
CHANGED
|
@@ -7,6 +7,8 @@ import { lockScroll } from '../utils'
|
|
|
7
7
|
|
|
8
8
|
import rewireTabOrder from './rewireTabOrder'
|
|
9
9
|
import { getFixedParent, getModifiers } from './utils'
|
|
10
|
+
import { useThemeProps } from '../system'
|
|
11
|
+
import { useTheme } from '@emotion/react'
|
|
10
12
|
|
|
11
13
|
type anchorCallbackProps = {
|
|
12
14
|
innerRef(node: HTMLElement): void
|
|
@@ -116,6 +118,10 @@ export const Popover = React.forwardRef(
|
|
|
116
118
|
}: PopoverProps,
|
|
117
119
|
ref
|
|
118
120
|
) => {
|
|
121
|
+
const { zIndex = 10000, ...themeProps } = useThemeProps(
|
|
122
|
+
'popover',
|
|
123
|
+
restProps
|
|
124
|
+
)
|
|
119
125
|
const anchorRef = React.useRef(null)
|
|
120
126
|
const popperRef = React.useRef(null)
|
|
121
127
|
const unlockScroll = React.useRef(null)
|
|
@@ -253,8 +259,8 @@ export const Popover = React.forwardRef(
|
|
|
253
259
|
}
|
|
254
260
|
},
|
|
255
261
|
// ideally this should be pulled out to something like ThemeProvider for predictable z indices
|
|
256
|
-
style: { zIndex
|
|
257
|
-
...
|
|
262
|
+
style: { zIndex },
|
|
263
|
+
...themeProps,
|
|
258
264
|
})
|
|
259
265
|
: null}
|
|
260
266
|
</Portal>
|
package/src/Scrim/Scrim.mdx
CHANGED
package/src/Scrim/Scrim.tsx
CHANGED
|
@@ -1,33 +1,38 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
|
|
3
3
|
import StackView from '../StackView'
|
|
4
|
+
import { useThemeProps } from '../system'
|
|
4
5
|
|
|
5
6
|
type ScrimProps = {
|
|
6
7
|
/** Gain access to the internal ref. */
|
|
7
|
-
ref?: any
|
|
8
|
-
children?: any
|
|
8
|
+
ref?: any
|
|
9
|
+
children?: any
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
const Scrim = React.forwardRef(function (
|
|
12
|
+
const Scrim = React.forwardRef(function (
|
|
13
|
+
{ ref, ...restProps }: ScrimProps,
|
|
14
|
+
_ref: any
|
|
15
|
+
) {
|
|
12
16
|
React.useLayoutEffect(() => {
|
|
13
17
|
document.body.style.overflow = 'hidden'
|
|
14
18
|
return () => {
|
|
15
19
|
document.body.style.overflow = ''
|
|
16
20
|
}
|
|
17
21
|
}, [])
|
|
22
|
+
const { zIndex = 10000, ...themeProps } = useThemeProps('scrim', restProps)
|
|
18
23
|
return (
|
|
19
24
|
<StackView
|
|
20
|
-
innerRef={
|
|
25
|
+
innerRef={ref}
|
|
21
26
|
position="fixed"
|
|
22
27
|
top={0}
|
|
23
28
|
right={0}
|
|
24
29
|
bottom={0}
|
|
25
30
|
left={0}
|
|
26
|
-
zIndex={
|
|
31
|
+
zIndex={zIndex}
|
|
27
32
|
overflow="auto"
|
|
28
33
|
// @ts-ignore
|
|
29
34
|
backgroundColor="hsla(0,0%,0%,0.4)"
|
|
30
|
-
{...
|
|
35
|
+
{...themeProps}
|
|
31
36
|
/>
|
|
32
37
|
)
|
|
33
38
|
})
|
package/src/Sidebar/Sidebar.mdx
CHANGED
package/src/Table/Table.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
import React, { PureComponent, Children, Fragment } from 'react'
|
|
3
3
|
import { Global } from '@emotion/react'
|
|
4
|
-
import
|
|
4
|
+
import camelCase from 'lodash/camelCase'
|
|
5
|
+
import snakeCase from 'lodash/snakeCase'
|
|
5
6
|
|
|
6
7
|
import type { PaginationProps } from '../Pagination/Pagination'
|
|
7
8
|
|
|
@@ -2,7 +2,7 @@ import React, { useLayoutEffect, useState } from 'react'
|
|
|
2
2
|
import { ThemeProvider as EmotionThemeProvider } from '@emotion/react'
|
|
3
3
|
import { CacheProvider } from '@emotion/react'
|
|
4
4
|
import createCache from '@emotion/cache'
|
|
5
|
-
import
|
|
5
|
+
import merge from 'lodash/merge'
|
|
6
6
|
import { Box, BoxProps } from '../Box'
|
|
7
7
|
|
|
8
8
|
import defaultTheme from '../system/default-theme'
|
|
@@ -58,15 +58,16 @@ function mergeThemes(a: Theme = {}, b: Theme = {}) {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
type Props = {
|
|
62
|
+
theme?: Theme
|
|
63
|
+
children: React.ReactNode
|
|
64
|
+
} & BoxProps
|
|
65
|
+
|
|
61
66
|
export function ThemeProvider({
|
|
62
67
|
theme = emptyTheme,
|
|
63
68
|
children,
|
|
64
69
|
...boxProps
|
|
65
|
-
}: {
|
|
66
|
-
theme?: Theme
|
|
67
|
-
children: React.ReactNode
|
|
68
|
-
boxProps?: BoxProps
|
|
69
|
-
}) {
|
|
70
|
+
}: Props) {
|
|
70
71
|
const [mergedTheme, setMergedTheme] = useState(() =>
|
|
71
72
|
mergeThemes(defaultTheme, theme)
|
|
72
73
|
)
|
|
@@ -18,6 +18,9 @@ import {
|
|
|
18
18
|
getTimeFromDate,
|
|
19
19
|
} from './utils'
|
|
20
20
|
|
|
21
|
+
const MIN_MINUTE = 0
|
|
22
|
+
const MAX_MINUTE = 59
|
|
23
|
+
|
|
21
24
|
type Props = {
|
|
22
25
|
/**
|
|
23
26
|
* An array of keys to ignore when pushed.
|
|
@@ -119,9 +122,31 @@ class TimeField extends Component<Props> {
|
|
|
119
122
|
}
|
|
120
123
|
|
|
121
124
|
handleHoursKeyDown = (event) => {
|
|
125
|
+
const hour = Number(event.target.value)
|
|
126
|
+
const isTwelveHourClock = this.props.twelveHourClock
|
|
127
|
+
const isHourEleven = HOURS_TO_NOON - 1
|
|
128
|
+
const maxHour = isTwelveHourClock ? HOURS_TO_NOON : HOURS_IN_DAY - 1
|
|
129
|
+
const minHour = isTwelveHourClock ? 1 : 0
|
|
130
|
+
|
|
122
131
|
if (event.key === 'ArrowRight') {
|
|
123
132
|
this.inputBox.focus(1)
|
|
124
133
|
}
|
|
134
|
+
|
|
135
|
+
if (event.key === 'ArrowUp' && isTwelveHourClock && hour === isHourEleven) {
|
|
136
|
+
this.toggleMeridiem()
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (event.key === 'ArrowUp' && hour === maxHour) {
|
|
140
|
+
this.updateHours(1)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (event.key === 'ArrowDown' && isTwelveHourClock && hour === minHour) {
|
|
144
|
+
this.toggleMeridiem()
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (event.key === 'ArrowDown' && hour === minHour) {
|
|
148
|
+
this.updateHours(-1)
|
|
149
|
+
}
|
|
125
150
|
}
|
|
126
151
|
|
|
127
152
|
handleMinutesChange = (minutes) => {
|
|
@@ -129,6 +154,8 @@ class TimeField extends Component<Props> {
|
|
|
129
154
|
}
|
|
130
155
|
|
|
131
156
|
handleMinutesKeyDown = (event) => {
|
|
157
|
+
const minute = Number(event.target.value)
|
|
158
|
+
|
|
132
159
|
if (this.props.ignoredKeys.includes(event.key)) {
|
|
133
160
|
return
|
|
134
161
|
}
|
|
@@ -138,6 +165,12 @@ class TimeField extends Component<Props> {
|
|
|
138
165
|
if (event.key === 'ArrowRight') {
|
|
139
166
|
this.inputBox.focus(2)
|
|
140
167
|
}
|
|
168
|
+
if (event.key === 'ArrowUp' && minute >= MAX_MINUTE) {
|
|
169
|
+
this.updateMinutes(1)
|
|
170
|
+
}
|
|
171
|
+
if (event.key === 'ArrowDown' && minute <= MIN_MINUTE) {
|
|
172
|
+
this.updateMinutes(-1)
|
|
173
|
+
}
|
|
141
174
|
}
|
|
142
175
|
|
|
143
176
|
handleAMPMKeyDown = (event) => {
|
|
@@ -214,8 +247,8 @@ class TimeField extends Component<Props> {
|
|
|
214
247
|
fontVariantNumeric="tabular-nums"
|
|
215
248
|
moveFocusOnMax
|
|
216
249
|
value={minutes}
|
|
217
|
-
min={
|
|
218
|
-
max={
|
|
250
|
+
min={MIN_MINUTE}
|
|
251
|
+
max={MAX_MINUTE}
|
|
219
252
|
pad={2}
|
|
220
253
|
grow={0}
|
|
221
254
|
width="2ch"
|
|
@@ -230,7 +263,7 @@ class TimeField extends Component<Props> {
|
|
|
230
263
|
highlightOnInteraction
|
|
231
264
|
value={this.state.meridiem}
|
|
232
265
|
grow={0}
|
|
233
|
-
width=
|
|
266
|
+
width="2em"
|
|
234
267
|
textAlign="center"
|
|
235
268
|
aria-label="AM/PM"
|
|
236
269
|
onChange={noop} // prevent React warnings
|
|
@@ -121,7 +121,7 @@ it('does not change the meridiem on arrow key if arrow keys are ignored', () =>
|
|
|
121
121
|
expect(meridiemInput).toHaveValue('PM')
|
|
122
122
|
})
|
|
123
123
|
|
|
124
|
-
it('
|
|
124
|
+
it('increment hour on arrow up', () => {
|
|
125
125
|
const { hourInput } = setup( { hours: 1, minutes: 42 })
|
|
126
126
|
|
|
127
127
|
expect(hourInput).toHaveValue("01")
|
|
@@ -129,6 +129,110 @@ it('changes the value of hours on arrow up or arrow down', () => {
|
|
|
129
129
|
expect(hourInput).toHaveValue("02")
|
|
130
130
|
})
|
|
131
131
|
|
|
132
|
+
it('increment hour if arrow up on minutes exceeds max value', () => {
|
|
133
|
+
const { hourInput, minuteInput } = setup( { hours: 1, minutes: 59 })
|
|
134
|
+
|
|
135
|
+
expect(hourInput).toHaveValue("01")
|
|
136
|
+
userEvent.type(minuteInput, '{arrowup}')
|
|
137
|
+
expect(hourInput).toHaveValue("02")
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('decrement hour on arrow down', () => {
|
|
141
|
+
const { hourInput } = setup( { hours: 2, minutes: 42 })
|
|
142
|
+
|
|
143
|
+
expect(hourInput).toHaveValue("02")
|
|
144
|
+
userEvent.type(hourInput, '{arrowdown}')
|
|
145
|
+
expect(hourInput).toHaveValue("01")
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('decrement hour if arrow down on minutes exceeds min value', () => {
|
|
149
|
+
const { hourInput, minuteInput } = setup( { hours: 2, minutes: 0 })
|
|
150
|
+
|
|
151
|
+
expect(hourInput).toHaveValue("02")
|
|
152
|
+
userEvent.type(minuteInput, '{arrowdown}')
|
|
153
|
+
expect(hourInput).toHaveValue("01")
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('increment minute on arrow up', () => {
|
|
157
|
+
const { minuteInput } = setup( { hours: 1, minutes: 42 })
|
|
158
|
+
|
|
159
|
+
expect(minuteInput).toHaveValue("42")
|
|
160
|
+
userEvent.type(minuteInput, '{arrowup}')
|
|
161
|
+
expect(minuteInput).toHaveValue("43")
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('decrement minute on arrow down', () => {
|
|
165
|
+
const { minuteInput } = setup( { hours: 2, minutes: 43 })
|
|
166
|
+
|
|
167
|
+
expect(minuteInput).toHaveValue("43")
|
|
168
|
+
userEvent.type(minuteInput, '{arrowdown}')
|
|
169
|
+
expect(minuteInput).toHaveValue("42")
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('set minute to min value if arrow up on minutes exceeds max value', () => {
|
|
173
|
+
const { minuteInput } = setup( { hours: 1, minutes: 59 })
|
|
174
|
+
|
|
175
|
+
expect(minuteInput).toHaveValue("59")
|
|
176
|
+
userEvent.type(minuteInput, '{arrowup}')
|
|
177
|
+
expect(minuteInput).toHaveValue("00")
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('set minute to max value if arrow down on minutes exceeds min value', () => {
|
|
181
|
+
const { minuteInput } = setup( { hours: 1, minutes: 0 })
|
|
182
|
+
|
|
183
|
+
expect(minuteInput).toHaveValue("00")
|
|
184
|
+
userEvent.type(minuteInput, '{arrowdown}')
|
|
185
|
+
expect(minuteInput).toHaveValue("59")
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('toggle meridiem if arrow up on hour exceeds eleven', () => {
|
|
189
|
+
const { hourInput, meridiemInput } = setup( { hours: 11 })
|
|
190
|
+
|
|
191
|
+
expect(meridiemInput).toHaveValue('AM')
|
|
192
|
+
userEvent.type(hourInput, '{arrowup}')
|
|
193
|
+
expect(meridiemInput).toHaveValue('PM')
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it('toggle meridiem if arrow down on hour exceeds min value', () => {
|
|
197
|
+
const { hourInput, meridiemInput } = setup( { hours: 1 })
|
|
198
|
+
|
|
199
|
+
expect(meridiemInput).toHaveValue('AM')
|
|
200
|
+
userEvent.type(hourInput, '{arrowdown}')
|
|
201
|
+
expect(meridiemInput).toHaveValue('PM')
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it('set 12 hour clock to min value if arrow up on hour exceeds max value', () => {
|
|
205
|
+
const { hourInput } = setup( { hours: 12 })
|
|
206
|
+
|
|
207
|
+
expect(hourInput).toHaveValue("12")
|
|
208
|
+
userEvent.type(hourInput, '{arrowup}')
|
|
209
|
+
expect(hourInput).toHaveValue("01")
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it('set 24 hour clock to min value if arrow up on hour exceeds max value', () => {
|
|
213
|
+
const { hourInput } = setup( { hours: 23, twelveHourClock: false })
|
|
214
|
+
|
|
215
|
+
expect(hourInput).toHaveValue("23")
|
|
216
|
+
userEvent.type(hourInput, '{arrowup}')
|
|
217
|
+
expect(hourInput).toHaveValue("00")
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
it('set 12 hour clock to max value if arrow down on hour exceeds min value', () => {
|
|
221
|
+
const { hourInput } = setup( { hours: 1 })
|
|
222
|
+
|
|
223
|
+
expect(hourInput).toHaveValue("01")
|
|
224
|
+
userEvent.type(hourInput, '{arrowdown}')
|
|
225
|
+
expect(hourInput).toHaveValue("12")
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
it('set 24 hour clock to max value if arrow down on hour exceeds min value', () => {
|
|
229
|
+
const { hourInput } = setup( { hours: 0, twelveHourClock: false })
|
|
230
|
+
|
|
231
|
+
expect(hourInput).toHaveValue("00")
|
|
232
|
+
userEvent.type(hourInput, '{arrowdown}')
|
|
233
|
+
expect(hourInput).toHaveValue("23")
|
|
234
|
+
})
|
|
235
|
+
|
|
132
236
|
it('does not change the value of hours on arrow up or arrow down if ignored', () => {
|
|
133
237
|
const { hourInput } = setup( { hours: 1, minutes: 42, ignoredKeys: ['ArrowUp', 'ArrowDown'] })
|
|
134
238
|
|