@kaizen/components 1.80.1 → 1.80.3
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/codemods/README.md +12 -0
- package/codemods/renameV2ComponentImportsAndUsages/index.ts +19 -0
- package/codemods/renameV2ComponentImportsAndUsages/renameV2ComponentImportsAndUsages.spec.ts +390 -0
- package/codemods/renameV2ComponentImportsAndUsages/renameV2ComponentImportsAndUsages.ts +230 -0
- package/codemods/utils/index.ts +1 -0
- package/codemods/utils/updateJsxElementTagName.spec.ts +129 -0
- package/codemods/utils/updateJsxElementTagName.ts +56 -0
- package/codemods/utils/updateKaioImports.spec.ts +82 -0
- package/codemods/utils/updateKaioImports.ts +16 -7
- package/dist/cjs/src/__alpha__/SingleSelect/SingleSelect.cjs +69 -16
- package/dist/cjs/src/__alpha__/SingleSelect/context/SingleSelectContext.cjs +13 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.cjs +54 -0
- package/dist/cjs/src/__alpha__/SingleSelect/{SingleSelect.module.css.cjs → subcomponents/Popover/Popover.module.css.cjs} +1 -1
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.cjs +94 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.cjs +69 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.cjs +12 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.cjs +41 -5
- package/dist/esm/src/__alpha__/SingleSelect/SingleSelect.mjs +60 -10
- package/dist/esm/src/__alpha__/SingleSelect/context/SingleSelectContext.mjs +10 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.mjs +49 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css.mjs +4 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.mjs +92 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.mjs +67 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.mjs +10 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.mjs +43 -7
- package/dist/styles.css +8958 -8934
- package/dist/types/__alpha__/SingleSelect/SingleSelect.d.ts +7 -9
- package/dist/types/__alpha__/SingleSelect/context/SingleSelectContext.d.ts +12 -0
- package/dist/types/__alpha__/SingleSelect/context/index.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/List/List.d.ts +2 -1
- package/dist/types/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.d.ts +2 -1
- package/dist/types/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.d.ts +2 -1
- package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/Popover.d.ts +6 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/index.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/index.d.ts +2 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.d.ts +4 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.d.ts +4 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.d.ts +2 -1
- package/dist/types/__alpha__/SingleSelect/subcomponents/index.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/types.d.ts +45 -0
- package/package.json +4 -4
- package/src/Notification/GlobalNotification/_docs/GlobalNotification.stickersheet.stories.tsx +2 -1
- package/src/Notification/InlineNotification/_docs/InlineNotification.stickersheet.stories.tsx +4 -1
- package/src/Notification/ToastNotification/_docs/ToastNotification.stickersheet.stories.tsx +20 -6
- package/src/Notification/subcomponents/GenericNotification/_mixins.scss +5 -3
- package/src/__alpha__/SingleSelect/SingleSelect.tsx +79 -14
- package/src/__alpha__/SingleSelect/_docs/SingleSelect.mdx +5 -2
- package/src/__alpha__/SingleSelect/_docs/SingleSelect.spec.stories.tsx +100 -0
- package/src/__alpha__/SingleSelect/_docs/SingleSelect.stickersheet.stories.tsx +4 -4
- package/src/__alpha__/SingleSelect/_docs/SingleSelect.stories.tsx +21 -2
- package/src/__alpha__/SingleSelect/context/SingleSelectContext.tsx +21 -0
- package/src/__alpha__/SingleSelect/context/index.ts +1 -0
- package/src/__alpha__/SingleSelect/subcomponents/List/List.module.css +0 -1
- package/src/__alpha__/SingleSelect/subcomponents/List/List.tsx +2 -1
- package/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.module.css +7 -0
- package/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.tsx +2 -1
- package/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.tsx +3 -1
- package/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css +24 -0
- package/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.tsx +54 -0
- package/src/__alpha__/SingleSelect/subcomponents/Popover/index.ts +1 -0
- package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/index.ts +2 -0
- package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.ts +108 -0
- package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.ts +75 -0
- package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.ts +13 -0
- package/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.module.css +1 -0
- package/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.tsx +29 -7
- package/src/__alpha__/SingleSelect/subcomponents/index.ts +1 -0
- package/src/__alpha__/SingleSelect/types.ts +58 -0
- package/dist/esm/src/__alpha__/SingleSelect/SingleSelect.module.css.mjs +0 -4
- package/src/__alpha__/SingleSelect/SingleSelect.module.css +0 -9
- package/src/__alpha__/SingleSelect/SingleSelect.spec.tsx +0 -26
|
@@ -5,5 +5,12 @@
|
|
|
5
5
|
font-size: var(--typography-paragraph-body-font-size);
|
|
6
6
|
line-height: var(--typography-paragraph-body-line-height);
|
|
7
7
|
letter-spacing: var(--typography-paragraph-body-letter-spacing);
|
|
8
|
+
padding: var(--spacing-8) var(--spacing-16);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.listItem:focus-visible {
|
|
12
|
+
background-color: var(--color-blue-200);
|
|
13
|
+
outline: none;
|
|
14
|
+
border-color: white;
|
|
8
15
|
}
|
|
9
16
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import React, { type PropsWithChildren } from 'react'
|
|
2
2
|
import classNames from 'classnames'
|
|
3
3
|
import { ListBoxItem as RACListBoxItem, type ListBoxItemProps } from 'react-aria-components'
|
|
4
|
+
import { type SelectItem } from '../../types'
|
|
4
5
|
import styles from './ListItem.module.css'
|
|
5
6
|
|
|
6
7
|
export const ListItem = ({
|
|
7
8
|
children,
|
|
8
9
|
className,
|
|
9
10
|
...props
|
|
10
|
-
}: ListBoxItemProps<
|
|
11
|
+
}: ListBoxItemProps<SelectItem> & PropsWithChildren): React.ReactElement => {
|
|
11
12
|
return (
|
|
12
13
|
<RACListBoxItem className={classNames(styles.listItem, className)} {...props}>
|
|
13
14
|
{children}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
ListBoxSection as RACListBoxSection,
|
|
6
6
|
type ListBoxSectionProps,
|
|
7
7
|
} from 'react-aria-components'
|
|
8
|
+
import { type SelectSection } from '../../types'
|
|
8
9
|
import styles from './ListSection.module.css'
|
|
9
10
|
|
|
10
11
|
export const ListSection = ({
|
|
@@ -12,7 +13,8 @@ export const ListSection = ({
|
|
|
12
13
|
className,
|
|
13
14
|
children,
|
|
14
15
|
...props
|
|
15
|
-
}: ListBoxSectionProps<
|
|
16
|
+
}: ListBoxSectionProps<SelectSection> &
|
|
17
|
+
PropsWithChildren & { name: string }): React.ReactElement => {
|
|
16
18
|
return (
|
|
17
19
|
<RACListBoxSection {...props}>
|
|
18
20
|
<RACHeader className={classNames(styles.listSectionHeader, className)}>{name}</RACHeader>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
@layer kz-components {
|
|
2
|
+
.popover {
|
|
3
|
+
position: absolute;
|
|
4
|
+
height: auto;
|
|
5
|
+
background-color: var(--color-white);
|
|
6
|
+
border-radius: var(--spacing-8);
|
|
7
|
+
padding: 0;
|
|
8
|
+
box-shadow: var(--shadow-small-box-shadow);
|
|
9
|
+
overflow: hidden auto;
|
|
10
|
+
margin: 0;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
|
|
13
|
+
/* TODO: update width based on design */
|
|
14
|
+
width: 200px;
|
|
15
|
+
|
|
16
|
+
@supports (anchor-name: --anchor) {
|
|
17
|
+
position-anchor: var(--position-anchor);
|
|
18
|
+
margin-block: var(--spacing-4);
|
|
19
|
+
position-area: var(--position-area) center;
|
|
20
|
+
/* stylelint-disable-next-line declaration-property-value-no-unknown */
|
|
21
|
+
width: anchor-size(width);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import React, { useLayoutEffect, useMemo, type PropsWithChildren } from 'react'
|
|
2
|
+
|
|
3
|
+
import { Popover as RACPopover } from 'react-aria-components'
|
|
4
|
+
import { useSingleSelectContext } from '../../context'
|
|
5
|
+
import { type PopoverProps } from '../../types'
|
|
6
|
+
import { usePositioningStyles } from './utils/usePositioningStyles'
|
|
7
|
+
import styles from './Popover.module.css'
|
|
8
|
+
|
|
9
|
+
export const Popover = ({
|
|
10
|
+
buttonRef,
|
|
11
|
+
popoverRef,
|
|
12
|
+
racPopoverRef,
|
|
13
|
+
children,
|
|
14
|
+
}: PopoverProps & PropsWithChildren): React.ReactElement => {
|
|
15
|
+
const { isOpen, setOpen, anchorName } = useSingleSelectContext()
|
|
16
|
+
|
|
17
|
+
const { popoverStyle, isPositioned } = usePositioningStyles(buttonRef, popoverRef, anchorName)
|
|
18
|
+
|
|
19
|
+
const shouldShowPopover = useMemo(() => isOpen && isPositioned, [isOpen, isPositioned])
|
|
20
|
+
|
|
21
|
+
useLayoutEffect(() => {
|
|
22
|
+
const popover = popoverRef.current
|
|
23
|
+
if (!popover?.showPopover || !popover?.hidePopover) return
|
|
24
|
+
|
|
25
|
+
if (shouldShowPopover) {
|
|
26
|
+
popover.showPopover()
|
|
27
|
+
} else {
|
|
28
|
+
popover.hidePopover()
|
|
29
|
+
}
|
|
30
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
31
|
+
}, [shouldShowPopover])
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<RACPopover
|
|
35
|
+
shouldUpdatePosition={false}
|
|
36
|
+
trigger="manual"
|
|
37
|
+
isOpen={isOpen}
|
|
38
|
+
onOpenChange={setOpen}
|
|
39
|
+
ref={racPopoverRef}
|
|
40
|
+
>
|
|
41
|
+
<div
|
|
42
|
+
// @ts-expect-error - popover attribute is not included in current ts version, ignore type error
|
|
43
|
+
popover="manual"
|
|
44
|
+
ref={popoverRef}
|
|
45
|
+
className={styles.popover}
|
|
46
|
+
style={popoverStyle}
|
|
47
|
+
>
|
|
48
|
+
{children}
|
|
49
|
+
</div>
|
|
50
|
+
</RACPopover>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
Popover.displayName = 'SingleSelect.Popover'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Popover'
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
type LogicalPosition,
|
|
4
|
+
type Position,
|
|
5
|
+
type UsePopoverPositioningProps,
|
|
6
|
+
} from '../../../types'
|
|
7
|
+
|
|
8
|
+
export function usePopoverPositioning({
|
|
9
|
+
triggerRef,
|
|
10
|
+
popoverRef,
|
|
11
|
+
direction = 'ltr',
|
|
12
|
+
offset = 4,
|
|
13
|
+
preferredPlacement = 'bottom',
|
|
14
|
+
}: UsePopoverPositioningProps): Position & { isPositioned: boolean } {
|
|
15
|
+
const [position, setPosition] = useState<Position>({
|
|
16
|
+
top: preferredPlacement === 'bottom' ? offset : 'auto',
|
|
17
|
+
bottom: preferredPlacement === 'top' ? offset : 'auto',
|
|
18
|
+
insetInlineStart: 0,
|
|
19
|
+
maxHeight: 300, // TODO: update this based on designs
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const [isPositioned, setIsPositioned] = useState(true)
|
|
23
|
+
|
|
24
|
+
const mountedRef = useRef<boolean>(false)
|
|
25
|
+
const isSSR = typeof window === 'undefined'
|
|
26
|
+
|
|
27
|
+
const updatePosition = useCallback(() => {
|
|
28
|
+
if (isSSR) return
|
|
29
|
+
|
|
30
|
+
const trigger = triggerRef.current
|
|
31
|
+
const popover = popoverRef.current
|
|
32
|
+
|
|
33
|
+
if (!mountedRef.current || !trigger || !popover?.isConnected) {
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const triggerRect = trigger.getBoundingClientRect()
|
|
38
|
+
if (!triggerRect) return
|
|
39
|
+
|
|
40
|
+
const doc = trigger.ownerDocument
|
|
41
|
+
const win = doc?.defaultView ?? window
|
|
42
|
+
const isRTL = direction === 'rtl'
|
|
43
|
+
|
|
44
|
+
const inlineStart = isRTL ? win.innerWidth - triggerRect.right : triggerRect.left
|
|
45
|
+
|
|
46
|
+
const triggerTop = triggerRect.top
|
|
47
|
+
const triggerBottom = triggerRect.bottom
|
|
48
|
+
const viewportHeight = win.innerHeight
|
|
49
|
+
|
|
50
|
+
const spaceAbove = triggerTop
|
|
51
|
+
const spaceBelow = viewportHeight - triggerBottom
|
|
52
|
+
|
|
53
|
+
const shouldFlip =
|
|
54
|
+
preferredPlacement === 'bottom' && spaceBelow < 200 && spaceAbove > spaceBelow
|
|
55
|
+
|
|
56
|
+
let top: LogicalPosition
|
|
57
|
+
let bottom: LogicalPosition
|
|
58
|
+
let maxHeight: number | undefined
|
|
59
|
+
|
|
60
|
+
if (shouldFlip) {
|
|
61
|
+
top = 'auto'
|
|
62
|
+
bottom = viewportHeight - triggerTop + offset
|
|
63
|
+
maxHeight = Math.max(0, spaceAbove - offset)
|
|
64
|
+
} else {
|
|
65
|
+
top = triggerBottom + offset
|
|
66
|
+
bottom = 'auto'
|
|
67
|
+
maxHeight = Math.max(0, spaceBelow - offset)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const newPosition = {
|
|
71
|
+
top,
|
|
72
|
+
bottom,
|
|
73
|
+
insetInlineStart: inlineStart,
|
|
74
|
+
maxHeight,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
setPosition(newPosition)
|
|
78
|
+
setIsPositioned(true)
|
|
79
|
+
}, [triggerRef, popoverRef, direction, offset, preferredPlacement, isSSR])
|
|
80
|
+
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (typeof window === 'undefined') return
|
|
83
|
+
|
|
84
|
+
mountedRef.current = true
|
|
85
|
+
|
|
86
|
+
const triggerEl = triggerRef.current
|
|
87
|
+
|
|
88
|
+
updatePosition()
|
|
89
|
+
|
|
90
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
91
|
+
updatePosition()
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
if (triggerEl) resizeObserver.observe(triggerEl)
|
|
95
|
+
|
|
96
|
+
const onWindowResize = (): void => updatePosition()
|
|
97
|
+
window.addEventListener('resize', onWindowResize, { passive: true })
|
|
98
|
+
|
|
99
|
+
return () => {
|
|
100
|
+
mountedRef.current = false
|
|
101
|
+
resizeObserver.disconnect()
|
|
102
|
+
window.removeEventListener('resize', onWindowResize)
|
|
103
|
+
setIsPositioned(false)
|
|
104
|
+
}
|
|
105
|
+
}, [updatePosition, triggerRef])
|
|
106
|
+
|
|
107
|
+
return { ...position, isPositioned }
|
|
108
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
import { useLocale } from '@react-aria/i18n'
|
|
3
|
+
import { type PositionData } from '../../../types'
|
|
4
|
+
import { usePopoverPositioning } from './usePopoverPositioning'
|
|
5
|
+
import { useSupportsAnchorPositioning } from './useSupportsAnchorPositioning'
|
|
6
|
+
|
|
7
|
+
const CSS_PROPS = {
|
|
8
|
+
POSITION_ANCHOR: '--position-anchor',
|
|
9
|
+
POSITION_AREA: '--position-area',
|
|
10
|
+
} as const
|
|
11
|
+
|
|
12
|
+
const DEFAULTS = {
|
|
13
|
+
MAX_HEIGHT: '300px',
|
|
14
|
+
} as const
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generates manual positioning styles for browsers without anchor positioning support or SSR
|
|
18
|
+
*/
|
|
19
|
+
const getManualPositioningStyles = (positionData: PositionData): React.CSSProperties => ({
|
|
20
|
+
top: positionData.top,
|
|
21
|
+
bottom: positionData.bottom,
|
|
22
|
+
insetInlineStart: positionData.insetInlineStart,
|
|
23
|
+
maxHeight: positionData.maxHeight,
|
|
24
|
+
left: 'auto',
|
|
25
|
+
right: 'auto',
|
|
26
|
+
position: 'fixed',
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const getAnchorPositioningStyles = (
|
|
30
|
+
anchorName: string,
|
|
31
|
+
positionData: PositionData,
|
|
32
|
+
): React.CSSProperties => {
|
|
33
|
+
const styles: React.CSSProperties = {
|
|
34
|
+
maxHeight: positionData.maxHeight ?? DEFAULTS.MAX_HEIGHT,
|
|
35
|
+
[CSS_PROPS.POSITION_ANCHOR]: anchorName,
|
|
36
|
+
[CSS_PROPS.POSITION_AREA]: positionData.top === 'auto' ? 'top' : 'bottom',
|
|
37
|
+
}
|
|
38
|
+
return styles
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const usePositioningStyles = (
|
|
42
|
+
buttonRef: React.RefObject<HTMLElement>,
|
|
43
|
+
popoverRef: React.RefObject<HTMLDivElement>,
|
|
44
|
+
anchorName: string,
|
|
45
|
+
): { popoverStyle: React.CSSProperties; isPositioned: boolean } => {
|
|
46
|
+
const { direction } = useLocale()
|
|
47
|
+
const hasAnchorSupport = useSupportsAnchorPositioning()
|
|
48
|
+
|
|
49
|
+
const { top, bottom, insetInlineStart, maxHeight, isPositioned } = usePopoverPositioning({
|
|
50
|
+
triggerRef: buttonRef,
|
|
51
|
+
popoverRef,
|
|
52
|
+
direction,
|
|
53
|
+
preferredPlacement: 'bottom',
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const positionData = useMemo(
|
|
57
|
+
() => ({
|
|
58
|
+
top,
|
|
59
|
+
bottom,
|
|
60
|
+
insetInlineStart,
|
|
61
|
+
maxHeight,
|
|
62
|
+
}),
|
|
63
|
+
[top, bottom, insetInlineStart, maxHeight],
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
const popoverStyle = useMemo(() => {
|
|
67
|
+
if (hasAnchorSupport === null || !hasAnchorSupport) {
|
|
68
|
+
return getManualPositioningStyles(positionData)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return getAnchorPositioningStyles(anchorName, positionData)
|
|
72
|
+
}, [hasAnchorSupport, anchorName, positionData])
|
|
73
|
+
|
|
74
|
+
return { popoverStyle, isPositioned }
|
|
75
|
+
}
|
package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
|
|
3
|
+
export const useSupportsAnchorPositioning = (): boolean => {
|
|
4
|
+
return useMemo(() => {
|
|
5
|
+
if (typeof window === 'undefined' || typeof CSS === 'undefined') {
|
|
6
|
+
return false
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
CSS.supports('position-anchor', 'auto') || CSS.supports('position-try-fallbacks: flip-block')
|
|
11
|
+
)
|
|
12
|
+
}, [])
|
|
13
|
+
}
|
|
@@ -1,13 +1,35 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { Button as RACButton
|
|
1
|
+
import React, { useMemo } from 'react'
|
|
2
|
+
import { Button as RACButton } from 'react-aria-components'
|
|
3
3
|
import { Icon } from '~components/__next__/Icon'
|
|
4
|
+
import { useSingleSelectContext } from '../../context'
|
|
5
|
+
import { type SelectItem, type SelectSection, type TriggerProps } from '../../types'
|
|
4
6
|
import styles from './Trigger.module.css'
|
|
5
7
|
|
|
6
|
-
|
|
8
|
+
function flattenItems(items: (SelectItem | SelectSection)[]): SelectItem[] {
|
|
9
|
+
return items.flatMap((item) => ('options' in item ? item.options : item))
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const Trigger = ({ buttonRef }: TriggerProps): JSX.Element => {
|
|
13
|
+
const { isOpen, setOpen, selectedKey, items, anchorName } = useSingleSelectContext()
|
|
14
|
+
const flattenedItems = useMemo(() => flattenItems(items), [items])
|
|
15
|
+
const selectedLabel = useMemo(() => {
|
|
16
|
+
const key = selectedKey
|
|
17
|
+
const item = flattenedItems.find((i) => i.value === key)
|
|
18
|
+
return item?.label ?? <div></div>
|
|
19
|
+
}, [flattenedItems, selectedKey])
|
|
20
|
+
|
|
7
21
|
return (
|
|
8
|
-
<
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
22
|
+
<div style={{ position: 'relative' }}>
|
|
23
|
+
<RACButton
|
|
24
|
+
className={styles.button}
|
|
25
|
+
ref={buttonRef}
|
|
26
|
+
onPress={() => setOpen(!isOpen)}
|
|
27
|
+
aria-expanded={isOpen}
|
|
28
|
+
style={{ '--anchor-name': anchorName } as React.CSSProperties}
|
|
29
|
+
>
|
|
30
|
+
{selectedLabel}
|
|
31
|
+
<Icon name="keyboard_arrow_down" isPresentational />
|
|
32
|
+
</RACButton>
|
|
33
|
+
</div>
|
|
12
34
|
)
|
|
13
35
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type RefObject } from 'react'
|
|
2
|
+
import { type Key } from '@react-types/shared'
|
|
3
|
+
|
|
4
|
+
// Shared types
|
|
5
|
+
export type SelectItem = {
|
|
6
|
+
label: string
|
|
7
|
+
value: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type SelectSection = {
|
|
11
|
+
label: string
|
|
12
|
+
options: SelectItem[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// SingleSelect related types
|
|
16
|
+
export type SingleSelectProps = {
|
|
17
|
+
children?: React.ReactNode
|
|
18
|
+
items: (SelectItem | SelectSection)[]
|
|
19
|
+
onSelectionChange?: (key: Key | null) => void
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Trigger related types
|
|
23
|
+
export type TriggerProps = {
|
|
24
|
+
buttonRef: React.RefObject<HTMLButtonElement>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Popover related types
|
|
28
|
+
export type PopoverProps = {
|
|
29
|
+
buttonRef: React.RefObject<HTMLElement>
|
|
30
|
+
popoverRef: React.RefObject<HTMLDivElement>
|
|
31
|
+
racPopoverRef: React.Ref<any>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type PositionDataProp = number | string | undefined
|
|
35
|
+
|
|
36
|
+
export type PositionData = {
|
|
37
|
+
top: PositionDataProp
|
|
38
|
+
bottom: PositionDataProp
|
|
39
|
+
insetInlineStart: PositionDataProp
|
|
40
|
+
maxHeight: PositionDataProp
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type LogicalPosition = number | 'auto' | undefined
|
|
44
|
+
|
|
45
|
+
export type Position = {
|
|
46
|
+
top: LogicalPosition
|
|
47
|
+
bottom: LogicalPosition
|
|
48
|
+
insetInlineStart: number
|
|
49
|
+
maxHeight?: number
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type UsePopoverPositioningProps = {
|
|
53
|
+
triggerRef: RefObject<HTMLElement>
|
|
54
|
+
popoverRef: RefObject<HTMLElement>
|
|
55
|
+
direction?: 'ltr' | 'rtl'
|
|
56
|
+
offset?: number
|
|
57
|
+
preferredPlacement?: 'top' | 'bottom'
|
|
58
|
+
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { render } from '@testing-library/react'
|
|
3
|
-
import { SingleSelect } from './SingleSelect'
|
|
4
|
-
import { singleMockItems } from './_docs/mockData'
|
|
5
|
-
|
|
6
|
-
const SingleSelectWrapper = (): JSX.Element => (
|
|
7
|
-
<SingleSelect>
|
|
8
|
-
<SingleSelect.List>
|
|
9
|
-
{singleMockItems.map((item) => (
|
|
10
|
-
<SingleSelect.ListItem key={item.value} value={{ value: item.value }}>
|
|
11
|
-
{item.label}
|
|
12
|
-
</SingleSelect.ListItem>
|
|
13
|
-
))}
|
|
14
|
-
</SingleSelect.List>
|
|
15
|
-
</SingleSelect>
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
describe('<SingleSelect />', () => {
|
|
19
|
-
describe('renders', () => {
|
|
20
|
-
it('a basic select component', () => {
|
|
21
|
-
const { getByRole } = render(<SingleSelectWrapper />)
|
|
22
|
-
const select = getByRole('button')
|
|
23
|
-
expect(select).toBeInTheDocument()
|
|
24
|
-
})
|
|
25
|
-
})
|
|
26
|
-
})
|