@heliosgraphics/ui 2.0.0-alpha.94 → 2.0.0-alpha.96

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 (113) hide show
  1. package/components/Alert/Alert.tsx +2 -0
  2. package/components/Breadcrumb/Breadcrumb.tsx +17 -1
  3. package/components/Browser/Browser.tsx +2 -0
  4. package/components/Button/Button.tsx +11 -7
  5. package/components/ButtonGroup/ButtonGroup.tsx +2 -0
  6. package/components/Checkbox/Checkbox.tsx +66 -55
  7. package/components/Clock/Clock.tsx +23 -19
  8. package/components/Column/Column.tsx +2 -0
  9. package/components/Confirm/Confirm.tsx +2 -0
  10. package/components/DatePicker/DatePicker.meta.ts +12 -5
  11. package/components/DatePicker/DatePicker.module.css +70 -1
  12. package/components/DatePicker/DatePicker.tsx +116 -4
  13. package/components/DatePicker/DatePicker.types.ts +6 -1
  14. package/components/DatePicker/DatePicker.utils.ts +53 -0
  15. package/components/Debug/Debug.tsx +2 -0
  16. package/components/Details/Details.tsx +2 -0
  17. package/components/Dialog/Dialog.module.css +48 -40
  18. package/components/Dialog/Dialog.tsx +23 -58
  19. package/components/Donut/Donut.tsx +5 -6
  20. package/components/Dot/Dot.tsx +2 -0
  21. package/components/Dropdown/Dropdown.module.css +25 -10
  22. package/components/Dropdown/Dropdown.tsx +22 -29
  23. package/components/Fieldset/Fieldset.tsx +2 -0
  24. package/components/Flex/Flex.meta.ts +1 -0
  25. package/components/Flex/Flex.tsx +23 -2
  26. package/components/Flex/Flex.types.ts +1 -0
  27. package/components/Flex/Flex.utils.spec.ts +4 -1
  28. package/components/Flex/Flex.utils.ts +4 -1
  29. package/components/Grid/Grid.tsx +2 -0
  30. package/components/Heading/Heading.meta.ts +5 -0
  31. package/components/Heading/Heading.tsx +15 -9
  32. package/components/Heading/Heading.types.ts +1 -0
  33. package/components/Heading/components/H0/H0.tsx +2 -0
  34. package/components/Heading/components/H1/H1.tsx +4 -1
  35. package/components/Heading/components/H2/H2.tsx +4 -1
  36. package/components/Heading/components/H3/H3.tsx +4 -1
  37. package/components/Heading/components/H4/H4.tsx +4 -1
  38. package/components/Heading/components/H5/H5.tsx +4 -1
  39. package/components/Heading/components/H6/H6.tsx +4 -1
  40. package/components/Icon/Icon.tsx +2 -0
  41. package/components/Input/Input.tsx +103 -95
  42. package/components/Layout/Layout.tsx +2 -0
  43. package/components/Layout/components/LayoutAside/LayoutAside.tsx +2 -0
  44. package/components/Layout/components/LayoutAside/components/LayoutAsideContent/LayoutAsideContent.tsx +2 -0
  45. package/components/Layout/components/LayoutAside/components/LayoutAsideFooter/LayoutAsideFooter.tsx +2 -0
  46. package/components/Layout/components/LayoutAside/components/LayoutAsideToggle/LayoutAsideToggle.tsx +2 -0
  47. package/components/Layout/components/LayoutMain/LayoutMain.tsx +2 -0
  48. package/components/Layout/components/LayoutMain/components/LayoutMainContent/LayoutMainContent.tsx +2 -0
  49. package/components/Layout/components/LayoutNavigation/LayoutNavigation.tsx +2 -0
  50. package/components/Layout/components/LayoutSubNavigation/LayoutSubNavigation.tsx +2 -0
  51. package/components/Loading/Loading.tsx +2 -0
  52. package/components/Markdown/Markdown.tsx +2 -0
  53. package/components/Masonry/Masonry.tsx +5 -1
  54. package/components/Menu/Menu.tsx +2 -0
  55. package/components/Menu/components/MenuCategory/MenuCategory.tsx +2 -0
  56. package/components/Menu/components/MenuFilter/MenuFilter.tsx +2 -2
  57. package/components/Menu/components/MenuItem/MenuItem.tsx +2 -0
  58. package/components/Overlay/Overlay.module.css +42 -18
  59. package/components/Overlay/Overlay.tsx +26 -5
  60. package/components/Pie/Pie.tsx +2 -0
  61. package/components/Pill/Pill.meta.ts +9 -1
  62. package/components/Pill/Pill.module.css +11 -0
  63. package/components/Pill/Pill.tsx +30 -5
  64. package/components/Pill/Pill.types.ts +2 -0
  65. package/components/Placeholder/Placeholder.tsx +2 -0
  66. package/components/Progress/Progress.tsx +2 -0
  67. package/components/Radio/Radio.tsx +2 -0
  68. package/components/Range/Range.tsx +2 -0
  69. package/components/Segments/Segments.context.ts +19 -0
  70. package/components/Segments/Segments.meta.ts +4 -0
  71. package/components/Segments/Segments.tsx +34 -42
  72. package/components/Segments/Segments.types.ts +1 -0
  73. package/components/Segments/components/SegmentButton/SegmentButton.meta.ts +0 -4
  74. package/components/Segments/components/SegmentButton/SegmentButton.tsx +28 -2
  75. package/components/Segments/components/SegmentButton/SegmentButton.types.ts +0 -2
  76. package/components/Select/Select.tsx +40 -43
  77. package/components/Separator/Separator.tsx +2 -0
  78. package/components/Separator/components/HRMarkup/HRMarkup.tsx +2 -0
  79. package/components/Separator/components/HorizontalSeparator/HorizontalSeparator.tsx +2 -0
  80. package/components/Separator/components/VerticalSeparator/VerticalSeparator.tsx +2 -0
  81. package/components/Setup/Setup.tsx +3 -0
  82. package/components/Shimmer/Shimmer.tsx +2 -0
  83. package/components/Slider/Slider.tsx +2 -0
  84. package/components/Spacer/Spacer.tsx +2 -0
  85. package/components/Table/Table.tsx +2 -0
  86. package/components/Tabs/Tabs.meta.ts +12 -12
  87. package/components/Tabs/Tabs.module.css +25 -9
  88. package/components/Tabs/Tabs.tsx +50 -51
  89. package/components/Tabs/Tabs.types.ts +10 -3
  90. package/components/Text/Text.tsx +2 -0
  91. package/components/Text/components/Div/Div.tsx +4 -1
  92. package/components/Text/components/Micro/Micro.tsx +4 -1
  93. package/components/Text/components/P/P.tsx +4 -1
  94. package/components/Text/components/Small/Small.tsx +4 -1
  95. package/components/Text/components/Tiny/Tiny.tsx +4 -1
  96. package/components/Textarea/Textarea.tsx +14 -13
  97. package/components/Tile/Tile.tsx +2 -0
  98. package/components/Timestamp/Timestamp.tsx +2 -0
  99. package/components/Toggle/Toggle.tsx +2 -0
  100. package/components/Tooltip/Tooltip.module.css +13 -0
  101. package/components/Tooltip/Tooltip.tsx +19 -11
  102. package/components/Tooltip/Tooltip.types.ts +0 -1
  103. package/components/Tooltip/components/TooltipContent/TooltipContent.tsx +4 -0
  104. package/components/Tooltip/components/TooltipTrigger/TooltipTrigger.tsx +6 -2
  105. package/components/shared/InputLabel/InputLabel.tsx +2 -0
  106. package/components/shared/ResultList/ResultList.tsx +7 -5
  107. package/constants/components.ts +2 -2
  108. package/constants/meta.ts +9 -9
  109. package/contexts/LayoutContext/LayoutContext.tsx +15 -34
  110. package/contexts/LayoutContext/LayoutContext.types.ts +0 -1
  111. package/hooks/useLayoutContext.tsx +0 -1
  112. package/index.ts +5 -0
  113. package/package.json +4 -4
@@ -12,23 +12,49 @@
12
12
  border-radius: var(--radius-lg) 0 0 var(--radius-lg);
13
13
  touch-action: pan-y;
14
14
  transform: translateX(-50%);
15
+
16
+ opacity: 0;
17
+ transition:
18
+ opacity var(--speed-lg) ease-in-out,
19
+ transform var(--speed-lg) ease-in-out,
20
+ overlay var(--speed-lg) ease-in-out allow-discrete,
21
+ display var(--speed-lg) ease-in-out allow-discrete;
15
22
  }
16
23
 
17
- .dialogNarrow {
18
- width: 420px;
24
+ .dialog[open] {
25
+ opacity: 1;
26
+ transform: translateX(-50%);
27
+ }
28
+
29
+ @starting-style {
30
+ .dialog[open] {
31
+ opacity: 0;
32
+ transform: translateX(-50%) translateY(6px);
33
+ }
19
34
  }
20
35
 
21
- .dialog:not(.dialogCentered) {
22
- animation: dialogFadeIn var(--speed-lg) ease-in-out;
36
+ .dialogNarrow {
37
+ width: 420px;
23
38
  }
24
39
 
25
40
  .dialogCentered {
26
41
  top: 50%;
27
42
 
28
- animation: dialogCenterFadeIn var(--speed-lg) ease-in-out;
29
43
  transform: translateX(-50%) translateY(-50%);
30
44
  }
31
45
 
46
+ .dialogCentered[open] {
47
+ opacity: 1;
48
+ transform: translateX(-50%) translateY(-50%);
49
+ }
50
+
51
+ @starting-style {
52
+ .dialogCentered[open] {
53
+ opacity: 0;
54
+ transform: translateX(-50%) translateY(calc(-50% + 6px));
55
+ }
56
+ }
57
+
32
58
  .dialog__content {
33
59
  position: relative;
34
60
  z-index: var(--z-index-1);
@@ -48,54 +74,36 @@
48
74
  .dialog::backdrop {
49
75
  --ui-bg-backdrop: rgba(80, 80, 80, 0.5);
50
76
 
51
- animation: backdropFadeIn var(--speed-sm) ease-in-out;
52
77
  background-color: var(--ui-bg-backdrop);
53
78
  backdrop-filter: blur(2px);
54
- }
55
79
 
56
- @media (max-width: 576px) {
57
- .dialog:not(.dialogCentered) {
58
- width: calc(100% - 16px);
59
- }
60
-
61
- .dialog__content {
62
- padding: 12px;
63
- }
80
+ opacity: 0;
81
+ transition:
82
+ opacity var(--speed-sm) ease-in-out,
83
+ overlay var(--speed-sm) ease-in-out allow-discrete,
84
+ display var(--speed-sm) ease-in-out allow-discrete;
64
85
  }
65
86
 
66
- @media (max-width: 576px) {
67
- .dialogCentered {
68
- width: calc(100% - 16px);
69
- }
87
+ .dialog[open]::backdrop {
88
+ opacity: 1;
70
89
  }
71
90
 
72
- @keyframes dialogFadeIn {
73
- 0% {
91
+ @starting-style {
92
+ .dialog[open]::backdrop {
74
93
  opacity: 0;
75
- transform: translateX(-50%) translateY(6px);
76
- }
77
- 100% {
78
- opacity: 1;
79
- transform: translateX(-50%) translateY(0);
80
94
  }
81
95
  }
82
96
 
83
- @keyframes dialogCenterFadeIn {
84
- 0% {
85
- opacity: 0;
86
- transform: translateX(-50%) translateY(calc(-50% + 6px));
87
- }
88
- 100% {
89
- opacity: 1;
90
- transform: translateX(-50%) translateY(-50%);
97
+ @media (max-width: 576px) {
98
+ .dialog:not(.dialogCentered) {
99
+ width: calc(100% - 16px);
91
100
  }
92
- }
93
101
 
94
- @keyframes backdropFadeIn {
95
- 0% {
96
- opacity: 0;
102
+ .dialogCentered {
103
+ width: calc(100% - 16px);
97
104
  }
98
- 100% {
99
- opacity: 1;
105
+
106
+ .dialog__content {
107
+ padding: 12px;
100
108
  }
101
109
  }
@@ -9,9 +9,6 @@ import { Flex } from "../Flex"
9
9
  import { getClasses } from "@heliosgraphics/utils"
10
10
  import type { DialogProps } from "./Dialog.types"
11
11
 
12
- const ATTRIBUTE_SCROLL = "data-scroll" as const
13
- const POSITION_FIXED_CLASS = "fixed" as const
14
-
15
12
  export const Dialog: FC<DialogProps> = ({
16
13
  title,
17
14
  children,
@@ -24,60 +21,25 @@ export const Dialog: FC<DialogProps> = ({
24
21
  }) => {
25
22
  const dialogRef = useRef<HTMLDialogElement | null>(null)
26
23
  const triggerRef = useRef<Element | null>(null)
27
-
28
- const resetScroll = (): void => {
29
- globalThis?.requestAnimationFrame(() => {
30
- document.body.setAttribute(ATTRIBUTE_SCROLL, "")
31
- dialogRef?.current?.scrollTo?.(0, 0)
32
- })
33
- }
34
-
35
- const resetDocumentStyle = (scrollPosition: number): void => {
36
- globalThis?.requestAnimationFrame(() => {
37
- document.body.style.position = ""
38
- document.body.style.height = ""
39
- document.body.style.top = ""
40
- document.body.classList.remove(POSITION_FIXED_CLASS)
41
-
42
- return globalThis?.scrollTo?.(0, scrollPosition)
43
- })
44
- }
24
+ const onCloseRef = useRef<() => void>(onClose)
45
25
 
46
26
  useEffect(() => {
47
- return (): void => {
48
- const localPos: string = document?.body?.getAttribute(ATTRIBUTE_SCROLL) ?? "0"
49
- const scrollPosition: number = parseInt(localPos) ?? 0
50
-
51
- resetDocumentStyle(scrollPosition)
52
- resetScroll()
53
- }
54
- }, [title, isOpen])
27
+ onCloseRef.current = onClose
28
+ }, [onClose])
55
29
 
56
30
  useEffect(() => {
57
31
  dialogRef?.current?.scrollTo?.(0, 0)
58
32
 
59
33
  if (isOpen) {
60
34
  triggerRef.current = document.activeElement
35
+ document.documentElement.style.overflow = "hidden"
61
36
 
62
- const localPosition: number = document?.documentElement?.scrollTop ?? 0
63
-
64
- document.body.classList.add(POSITION_FIXED_CLASS)
65
- document.body.style.top = `-${localPosition}px`
66
- document.body.setAttribute(ATTRIBUTE_SCROLL, localPosition.toString())
67
-
68
- dialogRef?.current?.showModal?.()
69
-
70
- const dialog = dialogRef.current
71
-
72
- const handleCancel = (event: Event): void => {
73
- event.preventDefault()
74
- onClose()
37
+ if (!dialogRef.current?.open) {
38
+ dialogRef?.current?.showModal?.()
75
39
  }
76
40
 
77
- dialog?.addEventListener("cancel", handleCancel)
78
-
79
41
  return (): void => {
80
- dialog?.removeEventListener("cancel", handleCancel)
42
+ document.documentElement.style.overflow = ""
81
43
 
82
44
  const trigger = triggerRef.current
83
45
  if (trigger && trigger instanceof HTMLElement) {
@@ -85,20 +47,14 @@ export const Dialog: FC<DialogProps> = ({
85
47
  }
86
48
  triggerRef.current = null
87
49
  }
88
- } else if (!dialogRef?.current) {
89
- const localPos: string = document?.body?.getAttribute(ATTRIBUTE_SCROLL) ?? "0"
90
- const scrollPosition: number = parseInt(localPos) ?? 0
91
-
92
- document.body.classList.remove(POSITION_FIXED_CLASS)
93
-
94
- resetDocumentStyle(scrollPosition)
95
-
96
- dialogRef?.current?.close?.()
97
50
  }
98
-
99
- return undefined
100
51
  }, [isOpen])
101
52
 
53
+ const onCancel = (event: React.SyntheticEvent<HTMLDialogElement>): void => {
54
+ event.preventDefault()
55
+ onCloseRef.current()
56
+ }
57
+
102
58
  if (!isOpen) return null
103
59
 
104
60
  const onDialogClose = (event: MouseEvent<HTMLDialogElement>): boolean | void | Promise<void> => {
@@ -117,7 +73,14 @@ export const Dialog: FC<DialogProps> = ({
117
73
  })
118
74
 
119
75
  return (
120
- <dialog ref={dialogRef} className={dialogClasses} onClick={onDialogClose} role={role} data-ui-component="Dialog">
76
+ <dialog
77
+ ref={dialogRef}
78
+ className={dialogClasses}
79
+ onClick={onDialogClose}
80
+ onCancel={onCancel}
81
+ role={role}
82
+ data-ui-component="Dialog"
83
+ >
121
84
  {!!title && (
122
85
  <Flex isBetween={true} isYCentered={true} padding={8} className={dialogFlexClasses}>
123
86
  <Heading level={5} fontWeight="medium">
@@ -128,7 +91,9 @@ export const Dialog: FC<DialogProps> = ({
128
91
  </ButtonGroup>
129
92
  </Flex>
130
93
  )}
131
- {isOpen && <div className={dialogContentClasses}>{children}</div>}
94
+ <div className={dialogContentClasses}>{children}</div>
132
95
  </dialog>
133
96
  )
134
97
  }
98
+
99
+ Dialog.displayName = "Dialog"
@@ -1,3 +1,4 @@
1
+ import { getClasses } from "@heliosgraphics/utils"
1
2
  import { getDonutBorderSize } from "./Donut.utils"
2
3
  import { Flex } from "../Flex"
3
4
  import styles from "./Donut.module.css"
@@ -12,18 +13,14 @@ export const Donut: FC<DonutProps> = ({ children, size, percentage = 0, color })
12
13
  const dashSize = 100 - percentage
13
14
  const donutColor: string = `hsl(var(--${color}-hue), var(--${color}-saturation), 50%)`
14
15
 
16
+ const donutClasses: string = getClasses(styles.donut, "relative")
15
17
  const donutContainerStyle: object = {
16
18
  height: size + "px",
17
19
  width: size + "px",
18
20
  }
19
21
 
20
22
  return (
21
- <Flex
22
- style={donutContainerStyle}
23
- className={`${styles.donut} relative`}
24
- isCentered={true}
25
- data-ui-component="Donut"
26
- >
23
+ <Flex style={donutContainerStyle} className={donutClasses} isCentered={true} data-ui-component="Donut">
27
24
  <Flex className="absolute top-0 left-0 z-50" style={donutContainerStyle} isCentered={true}>
28
25
  {children}
29
26
  </Flex>
@@ -62,3 +59,5 @@ export const Donut: FC<DonutProps> = ({ children, size, percentage = 0, color })
62
59
  </Flex>
63
60
  )
64
61
  }
62
+
63
+ Donut.displayName = "Donut"
@@ -42,3 +42,5 @@ export const Dot: FC<DotProps> = ({ colorAccent, size = 8, color = "blue" }) =>
42
42
  </div>
43
43
  )
44
44
  }
45
+
46
+ Dot.displayName = "Dot"
@@ -5,6 +5,11 @@
5
5
  user-select: none;
6
6
  }
7
7
 
8
+ .dropdownOpen [data-ui-component="Icon"] {
9
+ transition: transform 96ms ease-in-out;
10
+ transform: rotate(180deg);
11
+ }
12
+
8
13
  .dropdownDisabled {
9
14
  cursor: not-allowed;
10
15
  pointer-events: none;
@@ -48,30 +53,40 @@
48
53
  position: absolute;
49
54
  z-index: var(--z-index-8);
50
55
 
56
+ display: none;
51
57
  min-width: 240px;
52
58
  opacity: 0;
53
59
 
54
60
  transition:
61
+ display 96ms ease-in-out allow-discrete,
55
62
  transform 96ms ease-in-out,
56
63
  opacity 96ms ease-in-out;
57
64
  pointer-events: none;
58
65
  }
59
66
 
60
- .dropdown__nav.dropdown__navActive {
61
- opacity: 1 !important;
67
+ .dropdown__navActive {
68
+ display: block;
69
+ opacity: 1;
62
70
 
63
- transform: translateY(0) !important;
71
+ transform: translateY(0);
64
72
 
65
73
  pointer-events: all;
66
- }
67
74
 
68
- @keyframes dropdownFade {
69
- 0% {
75
+ @starting-style {
70
76
  opacity: 0;
71
- /* transform: translateY(-8px); */
72
77
  }
73
- 100% {
74
- opacity: 1;
75
- /* transform: translateY(0); */
78
+ }
79
+
80
+ .dropdownBottomLeft .dropdown__navActive,
81
+ .dropdownBottomRight .dropdown__navActive {
82
+ @starting-style {
83
+ transform: translateY(-6px);
84
+ }
85
+ }
86
+
87
+ .dropdownTopLeft .dropdown__navActive,
88
+ .dropdownTopRight .dropdown__navActive {
89
+ @starting-style {
90
+ transform: translateY(6px);
76
91
  }
77
92
  }
@@ -1,22 +1,12 @@
1
1
  "use client"
2
2
 
3
- import {
4
- useEffect,
5
- useMemo,
6
- useState,
7
- useRef,
8
- cloneElement,
9
- Children as ReactChildren,
10
- type ReactElement,
11
- type FC,
12
- } from "react"
3
+ import { useEffect, useMemo, useState, useRef, type FC } from "react"
13
4
  import { ANIMATION_FAST } from "../../constants/animations"
14
5
  import { getClasses } from "@heliosgraphics/utils"
15
6
  import { ResultList } from "../shared/ResultList"
16
7
  import styles from "./Dropdown.module.css"
17
- import type { MouseEvent, ReactNode } from "react"
8
+ import type { KeyboardEvent, MouseEvent } from "react"
18
9
  import type { DropdownProps } from "./Dropdown.types"
19
- import type { HeliosIconType } from "../../types/icons"
20
10
 
21
11
  export const Dropdown: FC<DropdownProps> = ({ children, items, isDisabled, position = "bottom-left" }) => {
22
12
  const hoverStateRef = useRef<boolean>(false)
@@ -55,6 +45,7 @@ export const Dropdown: FC<DropdownProps> = ({ children, items, isDisabled, posit
55
45
  [styles.dropdownBottomRight]: position === "bottom-right",
56
46
  [styles.dropdownTopLeft]: position === "top-left",
57
47
  [styles.dropdownTopRight]: position === "top-right",
48
+ [styles.dropdownOpen]: isVisible,
58
49
  })
59
50
 
60
51
  const mouseEnter = (_event: MouseEvent<HTMLDivElement>): void => {
@@ -72,23 +63,16 @@ export const Dropdown: FC<DropdownProps> = ({ children, items, isDisabled, posit
72
63
  }, ANIMATION_FAST)
73
64
  }
74
65
 
75
- const renderChildren = ReactChildren.map(
76
- children as ReactElement<{ children: ReactNode; icon: HeliosIconType }>,
77
- (child: ReactElement<{ children: ReactNode; icon: HeliosIconType }>) => {
78
- const caretProps = {
79
- ...(child?.props?.icon === "caret-down" && isVisible && { icon: "caret-up" as HeliosIconType }),
80
- ...(child?.props?.icon === "chevron-down" && isVisible && { icon: "chevron-up" as HeliosIconType }),
81
- }
82
-
83
- return cloneElement(child, { ...child.props, ...caretProps })
84
- },
85
- )
86
-
87
66
  const onSetVisible = (): void => setVisible(!isVisible)
88
67
 
89
- const navClasses: string = getClasses(styles.dropdown__nav, {
90
- [styles.dropdown__navActive]: isVisible,
91
- })
68
+ const onKeyDown = (event: KeyboardEvent<HTMLDivElement>): void => {
69
+ if (event.key === "Enter" || event.key === " ") {
70
+ event.preventDefault()
71
+ onSetVisible()
72
+ }
73
+ }
74
+
75
+ const navClasses: string = getClasses(styles.dropdown__nav, isVisible && styles.dropdown__navActive)
92
76
 
93
77
  const itemsWithClose = useMemo(
94
78
  () =>
@@ -108,8 +92,15 @@ export const Dropdown: FC<DropdownProps> = ({ children, items, isDisabled, posit
108
92
 
109
93
  return (
110
94
  <div className={dropdownClasses} onMouseEnter={mouseEnter} onMouseLeave={mouseLeave} data-ui-component="Dropdown">
111
- <div onClick={onSetVisible} aria-expanded={isVisible} aria-haspopup="listbox">
112
- {renderChildren}
95
+ <div
96
+ role="button"
97
+ tabIndex={0}
98
+ onClick={onSetVisible}
99
+ onKeyDown={onKeyDown}
100
+ aria-expanded={isVisible}
101
+ aria-haspopup="listbox"
102
+ >
103
+ {children}
113
104
  </div>
114
105
  <nav
115
106
  className={navClasses}
@@ -122,3 +113,5 @@ export const Dropdown: FC<DropdownProps> = ({ children, items, isDisabled, posit
122
113
  </div>
123
114
  )
124
115
  }
116
+
117
+ Dropdown.displayName = "Dropdown"
@@ -10,3 +10,5 @@ export const Fieldset: FC<FieldsetProps> = ({ children, legend }) => {
10
10
  </fieldset>
11
11
  )
12
12
  }
13
+
14
+ Fieldset.displayName = "Fieldset"
@@ -25,6 +25,7 @@ export const meta: HeliosAttributeMeta<FlexBaseProps> = {
25
25
  isBetween: { type: "boolean", isOptional: true },
26
26
  isCentered: { type: "boolean", isOptional: true },
27
27
  isColumn: { type: "boolean", isOptional: true },
28
+ isFullHeight: { type: "boolean", isOptional: true },
28
29
  isFullWidth: { type: "boolean", isOptional: true },
29
30
  isInline: { type: "boolean", isOptional: true },
30
31
  isNoWrap: { type: "boolean", isOptional: true },
@@ -1,14 +1,35 @@
1
1
  import { getFlexUtility, getSafeFlexProps } from "../Flex/Flex.utils"
2
2
  import type { FlexProps } from "./Flex.types"
3
+ import type { FC, KeyboardEvent } from "react"
3
4
 
4
- export const Flex = (props: FlexProps) => {
5
+ export const Flex: FC<FlexProps> = (props) => {
5
6
  const { ref, ...restProps } = props
6
7
  const flexClasses: string = getFlexUtility(restProps)
7
8
  const safeProps = getSafeFlexProps(restProps)
8
9
 
10
+ const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
11
+ if (restProps.onClick && (event.key === "Enter" || event.key === " ")) {
12
+ event.preventDefault()
13
+ restProps.onClick(event as unknown as React.MouseEvent<HTMLDivElement>)
14
+ }
15
+ }
16
+
17
+ const a11yProps = restProps.onClick
18
+ ? { role: (restProps.role ?? "button") as string, tabIndex: restProps.tabIndex ?? 0, onKeyDown: handleKeyDown }
19
+ : {}
20
+
9
21
  return (
10
- <div {...safeProps} className={flexClasses} onClick={restProps.onClick} ref={ref}>
22
+ <div
23
+ {...safeProps}
24
+ className={flexClasses}
25
+ onClick={restProps.onClick}
26
+ ref={ref}
27
+ data-ui-component="Flex"
28
+ {...a11yProps}
29
+ >
11
30
  {restProps.children}
12
31
  </div>
13
32
  )
14
33
  }
34
+
35
+ Flex.displayName = "Flex"
@@ -14,6 +14,7 @@ export interface FlexBaseProps {
14
14
  isBetween?: boolean
15
15
  isCentered?: boolean
16
16
  isColumn?: boolean
17
+ isFullHeight?: boolean
17
18
  isFullWidth?: boolean
18
19
  isInline?: boolean
19
20
  isNoWrap?: boolean
@@ -40,6 +40,9 @@ describe("getFlexUtility", () => {
40
40
  it("Generates without duplicated classes", () =>
41
41
  expect(getFlexUtility(MOCK_FLEX_DUPLICATE)).toEqual(MOCK_FLEX_DUPLICATE_CLASSES))
42
42
 
43
+ it("Generates xAlign center class", () =>
44
+ expect(getFlexUtility({ children: null, xAlign: "center" })).toEqual("flex flex-x-center"))
45
+
43
46
  const MOCK_FLEX_RESPONSIVE_CLASSES = `flex mobile:p-0 tablet:p-8 p-16`
44
47
  const MOCK_FLEX_RESPONSIVE: FlexProps = {
45
48
  children: null,
@@ -60,7 +63,7 @@ describe("getFlexUtility", () => {
60
63
  })
61
64
 
62
65
  describe("getSafeFlexProps", () => {
63
- const MOCK_FLEX_ATTRIBUTES: unknown = {
66
+ const MOCK_FLEX_ATTRIBUTES: Record<string, unknown> = {
64
67
  children: null,
65
68
  className: "xo",
66
69
  draggable: true,
@@ -21,10 +21,12 @@ export const getFlexUtility = (props?: FlexProps): string => {
21
21
  if (props.yAlign === "start") flexClasses.add("align-flex-start")
22
22
  if (props.yAlign === "end") flexClasses.add("align-flex-end")
23
23
  if (props.xAlign === "start") flexClasses.add("justify-flex-start")
24
+ if (props.xAlign === "center") flexClasses.add("flex-x-center")
24
25
  if (props.xAlign === "end") flexClasses.add("justify-flex-end")
25
26
  if (props.isBetween) flexClasses.add("space-between")
26
27
  if (props.isAround) flexClasses.add("space-around")
27
28
  if (props.onClick) flexClasses.add("cursor-pointer")
29
+ if (props.isFullHeight) flexClasses.add("hp-100")
28
30
  if (props.isFullWidth) flexClasses.add("wp-100")
29
31
  if (props.isNoWrap) flexClasses.add("nowrap")
30
32
  if (props.elevation === "small") flexClasses.add("elevation-sm")
@@ -128,7 +130,7 @@ export const getRadius = (radiusValue?: ResponsiveRadiusType): string => {
128
130
  }
129
131
 
130
132
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
131
- export const getSafeFlexProps = (props: any): Partial<FlexProps> => {
133
+ export const getSafeFlexProps = (props: Record<string, any>): Record<string, unknown> => {
132
134
  const {
133
135
  gap: _gap,
134
136
  isAround: _isAround,
@@ -136,6 +138,7 @@ export const getSafeFlexProps = (props: any): Partial<FlexProps> => {
136
138
  isCentered: _isCentered,
137
139
  isColumn: _isColumn,
138
140
  isColumnCentered: _isColumnCentered,
141
+ isFullHeight: _isFullHeight,
139
142
  isFullWidth: _isFullWidth,
140
143
  isInline: _isInline,
141
144
  isStretch: _isStretch,
@@ -13,3 +13,5 @@ export const Grid: FC<GridProps> = ({ columns, children }) => {
13
13
  </div>
14
14
  )
15
15
  }
16
+
17
+ Grid.displayName = "Grid"
@@ -15,4 +15,9 @@ export const meta: HeliosAttributeMeta<HeadingBaseProps> = {
15
15
  level: {
16
16
  type: "0 | 1 | 2 | 3 | 4 | 5 | 6",
17
17
  },
18
+ lineHeight: {
19
+ type: "number",
20
+ isOptional: true,
21
+ description: "Overrides the default line-height",
22
+ },
18
23
  }
@@ -12,7 +12,7 @@ import type { HeadingProps } from "./Heading.types"
12
12
  import type { FC, CSSProperties } from "react"
13
13
 
14
14
  export const Heading: FC<HeadingProps> = (props) => {
15
- const { level, lineClamp, style, className, ...rest } = props
15
+ const { level, lineClamp, lineHeight, style, className, ...rest } = props
16
16
 
17
17
  const headingClasses: string = getClasses(className, styles.heading, {
18
18
  [styles.headingPrimary]: props.emphasis === "primary",
@@ -32,8 +32,12 @@ export const Heading: FC<HeadingProps> = (props) => {
32
32
  }
33
33
  : undefined
34
34
 
35
+ const lineHeightStyle: CSSProperties | undefined = lineHeight !== undefined ? { lineHeight } : undefined
36
+
35
37
  const mergedStyle: CSSProperties | undefined =
36
- style || lineClampStyle ? { ...(style || {}), ...(lineClampStyle || {}) } : undefined
38
+ style || lineClampStyle || lineHeightStyle
39
+ ? { ...(style || {}), ...(lineClampStyle || {}), ...(lineHeightStyle || {}) }
40
+ : undefined
37
41
 
38
42
  const allProps: Omit<HeadingProps, "level"> = {
39
43
  ...rest,
@@ -44,20 +48,22 @@ export const Heading: FC<HeadingProps> = (props) => {
44
48
 
45
49
  switch (level) {
46
50
  case 0:
47
- return <H0 {...allProps} />
51
+ return <H0 {...allProps} data-ui-component="Heading" />
48
52
  case 1:
49
- return <H1 {...allProps} />
53
+ return <H1 {...allProps} data-ui-component="Heading" />
50
54
  case 2:
51
- return <H2 {...allProps} />
55
+ return <H2 {...allProps} data-ui-component="Heading" />
52
56
  case 3:
53
- return <H3 {...allProps} />
57
+ return <H3 {...allProps} data-ui-component="Heading" />
54
58
  case 4:
55
- return <H4 {...allProps} />
59
+ return <H4 {...allProps} data-ui-component="Heading" />
56
60
  case 5:
57
- return <H5 {...allProps} />
61
+ return <H5 {...allProps} data-ui-component="Heading" />
58
62
  case 6:
59
- return <H6 {...allProps} />
63
+ return <H6 {...allProps} data-ui-component="Heading" />
60
64
  default:
61
65
  return null
62
66
  }
63
67
  }
68
+
69
+ Heading.displayName = "Heading"
@@ -3,6 +3,7 @@ import type { TextBaseProps } from "../Text/Text.types"
3
3
 
4
4
  export interface HeadingBaseProps {
5
5
  level: 0 | 1 | 2 | 3 | 4 | 5 | 6
6
+ lineHeight?: number
6
7
  }
7
8
 
8
9
  export type HeadingProps = HeadingBaseProps & Omit<TextBaseProps, "type"> & HTMLAttributes<HTMLHeadingElement>
@@ -8,3 +8,5 @@ export const H0: FC<H0Props> = (props) => {
8
8
 
9
9
  return <h1 {...props} className={h0Classes} data-ui-component="Heading.H0" />
10
10
  }
11
+
12
+ H0.displayName = "H0"
@@ -1,6 +1,9 @@
1
+ import { getClasses } from "@heliosgraphics/utils"
1
2
  import type { FC } from "react"
2
3
  import type { H1Props } from "./H1.types"
3
4
 
4
5
  export const H1: FC<H1Props> = (props) => {
5
- return <h1 {...props} className={`h1 ${props.className}`} data-ui-component="Heading.H1" />
6
+ return <h1 {...props} className={getClasses("h1", props.className)} data-ui-component="Heading.H1" />
6
7
  }
8
+
9
+ H1.displayName = "H1"
@@ -1,6 +1,9 @@
1
+ import { getClasses } from "@heliosgraphics/utils"
1
2
  import type { FC } from "react"
2
3
  import type { H2Props } from "./H2.types"
3
4
 
4
5
  export const H2: FC<H2Props> = (props) => {
5
- return <h2 {...props} className={`h2 ${props.className}`} data-ui-component="Heading.H2" />
6
+ return <h2 {...props} className={getClasses("h2", props.className)} data-ui-component="Heading.H2" />
6
7
  }
8
+
9
+ H2.displayName = "H2"
@@ -1,6 +1,9 @@
1
+ import { getClasses } from "@heliosgraphics/utils"
1
2
  import type { FC } from "react"
2
3
  import type { H3Props } from "./H3.types"
3
4
 
4
5
  export const H3: FC<H3Props> = (props) => {
5
- return <h3 {...props} className={`h3 ${props.className}`} data-ui-component="Heading.H3" />
6
+ return <h3 {...props} className={getClasses("h3", props.className)} data-ui-component="Heading.H3" />
6
7
  }
8
+
9
+ H3.displayName = "H3"