@kaizen/components 1.80.2 → 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.
Files changed (68) hide show
  1. package/codemods/README.md +12 -0
  2. package/codemods/renameV2ComponentImportsAndUsages/index.ts +19 -0
  3. package/codemods/renameV2ComponentImportsAndUsages/renameV2ComponentImportsAndUsages.spec.ts +390 -0
  4. package/codemods/renameV2ComponentImportsAndUsages/renameV2ComponentImportsAndUsages.ts +230 -0
  5. package/codemods/utils/index.ts +1 -0
  6. package/codemods/utils/updateJsxElementTagName.spec.ts +129 -0
  7. package/codemods/utils/updateJsxElementTagName.ts +56 -0
  8. package/codemods/utils/updateKaioImports.spec.ts +82 -0
  9. package/codemods/utils/updateKaioImports.ts +16 -7
  10. package/dist/cjs/src/__alpha__/SingleSelect/SingleSelect.cjs +69 -16
  11. package/dist/cjs/src/__alpha__/SingleSelect/context/SingleSelectContext.cjs +13 -0
  12. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.cjs +54 -0
  13. package/dist/cjs/src/__alpha__/SingleSelect/{SingleSelect.module.css.cjs → subcomponents/Popover/Popover.module.css.cjs} +1 -1
  14. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.cjs +94 -0
  15. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.cjs +69 -0
  16. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.cjs +12 -0
  17. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.cjs +41 -5
  18. package/dist/esm/src/__alpha__/SingleSelect/SingleSelect.mjs +60 -10
  19. package/dist/esm/src/__alpha__/SingleSelect/context/SingleSelectContext.mjs +10 -0
  20. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.mjs +49 -0
  21. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css.mjs +4 -0
  22. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.mjs +92 -0
  23. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.mjs +67 -0
  24. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.mjs +10 -0
  25. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.mjs +43 -7
  26. package/dist/styles.css +43 -21
  27. package/dist/types/__alpha__/SingleSelect/SingleSelect.d.ts +7 -9
  28. package/dist/types/__alpha__/SingleSelect/context/SingleSelectContext.d.ts +12 -0
  29. package/dist/types/__alpha__/SingleSelect/context/index.d.ts +1 -0
  30. package/dist/types/__alpha__/SingleSelect/subcomponents/List/List.d.ts +2 -1
  31. package/dist/types/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.d.ts +2 -1
  32. package/dist/types/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.d.ts +2 -1
  33. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/Popover.d.ts +6 -0
  34. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/index.d.ts +1 -0
  35. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/index.d.ts +2 -0
  36. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.d.ts +4 -0
  37. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.d.ts +4 -0
  38. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.d.ts +1 -0
  39. package/dist/types/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.d.ts +2 -1
  40. package/dist/types/__alpha__/SingleSelect/subcomponents/index.d.ts +1 -0
  41. package/dist/types/__alpha__/SingleSelect/types.d.ts +45 -0
  42. package/package.json +4 -4
  43. package/src/__alpha__/SingleSelect/SingleSelect.tsx +79 -14
  44. package/src/__alpha__/SingleSelect/_docs/SingleSelect.mdx +5 -2
  45. package/src/__alpha__/SingleSelect/_docs/SingleSelect.spec.stories.tsx +100 -0
  46. package/src/__alpha__/SingleSelect/_docs/SingleSelect.stickersheet.stories.tsx +4 -4
  47. package/src/__alpha__/SingleSelect/_docs/SingleSelect.stories.tsx +21 -2
  48. package/src/__alpha__/SingleSelect/context/SingleSelectContext.tsx +21 -0
  49. package/src/__alpha__/SingleSelect/context/index.ts +1 -0
  50. package/src/__alpha__/SingleSelect/subcomponents/List/List.module.css +0 -1
  51. package/src/__alpha__/SingleSelect/subcomponents/List/List.tsx +2 -1
  52. package/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.module.css +7 -0
  53. package/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.tsx +2 -1
  54. package/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.tsx +3 -1
  55. package/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css +24 -0
  56. package/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.tsx +54 -0
  57. package/src/__alpha__/SingleSelect/subcomponents/Popover/index.ts +1 -0
  58. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/index.ts +2 -0
  59. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.ts +108 -0
  60. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.ts +75 -0
  61. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.ts +13 -0
  62. package/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.module.css +1 -0
  63. package/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.tsx +29 -7
  64. package/src/__alpha__/SingleSelect/subcomponents/index.ts +1 -0
  65. package/src/__alpha__/SingleSelect/types.ts +58 -0
  66. package/dist/esm/src/__alpha__/SingleSelect/SingleSelect.module.css.mjs +0 -4
  67. package/src/__alpha__/SingleSelect/SingleSelect.module.css +0 -9
  68. package/src/__alpha__/SingleSelect/SingleSelect.spec.tsx +0 -26
@@ -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
+ }
@@ -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,5 +1,6 @@
1
1
  @layer kz-components {
2
2
  .button {
3
+ anchor-name: var(--anchor-name);
3
4
  display: flex;
4
5
  align-items: center;
5
6
  justify-content: space-between;
@@ -1,13 +1,35 @@
1
- import React from 'react'
2
- import { Button as RACButton, SelectValue } from 'react-aria-components'
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
- export const Trigger = (): JSX.Element => {
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
- <RACButton className={styles.button}>
9
- <SelectValue />
10
- <Icon name="keyboard_arrow_down" isPresentational />
11
- </RACButton>
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
  }
@@ -2,3 +2,4 @@ export * from './List'
2
2
  export * from './ListSection'
3
3
  export * from './ListItem'
4
4
  export * from './Trigger'
5
+ export * from './Popover'
@@ -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,4 +0,0 @@
1
- var styles = {
2
- "popover": "SingleSelect-module_popover__ZjL9n"
3
- };
4
- export { styles as default };
@@ -1,9 +0,0 @@
1
- @layer kz-components {
2
- .popover {
3
- background-color: var(--color-white);
4
- border-radius: var(--spacing-8);
5
- padding: var(--spacing-8);
6
- width: 200px;
7
- box-shadow: var(--shadow-small-box-shadow);
8
- }
9
- }
@@ -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
- })