@seed-design/lynx-react 0.7.0 → 0.8.0

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 (245) hide show
  1. package/lib/components/Accordion/Accordion.jsx +2 -2
  2. package/lib/components/ActionButton/ActionButton.jsx +1 -1
  3. package/lib/components/AlertDialog/AlertDialog.d.ts +64 -0
  4. package/lib/components/AlertDialog/AlertDialog.d.ts.map +1 -0
  5. package/lib/components/AlertDialog/AlertDialog.jsx +138 -0
  6. package/lib/components/AlertDialog/AlertDialog.namespace.d.ts +2 -0
  7. package/lib/components/AlertDialog/AlertDialog.namespace.d.ts.map +1 -0
  8. package/lib/components/AlertDialog/AlertDialog.namespace.js +17 -0
  9. package/lib/components/AlertDialog/index.d.ts +3 -0
  10. package/lib/components/AlertDialog/index.d.ts.map +1 -0
  11. package/lib/components/AppBar/AppBar.d.ts +14 -1
  12. package/lib/components/AppBar/AppBar.d.ts.map +1 -1
  13. package/lib/components/AppBar/AppBar.jsx +46 -10
  14. package/lib/components/AppBar/AppBar.namespace.d.ts +1 -1
  15. package/lib/components/AppBar/AppBar.namespace.d.ts.map +1 -1
  16. package/lib/components/AppBar/index.d.ts +1 -1
  17. package/lib/components/AppBar/index.d.ts.map +1 -1
  18. package/lib/components/AttachmentDisplay/AttachmentDisplay.d.ts +182 -0
  19. package/lib/components/AttachmentDisplay/AttachmentDisplay.d.ts.map +1 -0
  20. package/lib/components/AttachmentDisplay/AttachmentDisplay.jsx +444 -0
  21. package/lib/components/AttachmentDisplay/index.d.ts +2 -0
  22. package/lib/components/AttachmentDisplay/index.d.ts.map +1 -0
  23. package/lib/components/AttachmentInput/AttachmentInput.d.ts +179 -0
  24. package/lib/components/AttachmentInput/AttachmentInput.d.ts.map +1 -0
  25. package/lib/components/AttachmentInput/AttachmentInput.jsx +520 -0
  26. package/lib/components/AttachmentInput/index.d.ts +2 -0
  27. package/lib/components/AttachmentInput/index.d.ts.map +1 -0
  28. package/lib/components/BottomSheet/BottomSheet.d.ts +1 -1
  29. package/lib/components/BottomSheet/BottomSheet.d.ts.map +1 -1
  30. package/lib/components/BottomSheet/BottomSheet.jsx +1 -7
  31. package/lib/components/Callout/Callout.jsx +2 -2
  32. package/lib/components/Checkbox/Checkbox.jsx +2 -2
  33. package/lib/components/Chip/Chip.jsx +2 -2
  34. package/lib/components/ChipTabs/ChipTabs.d.ts +41 -0
  35. package/lib/components/ChipTabs/ChipTabs.d.ts.map +1 -0
  36. package/lib/components/ChipTabs/ChipTabs.jsx +39 -0
  37. package/lib/components/ChipTabs/ChipTabs.namespace.d.ts +2 -0
  38. package/lib/components/ChipTabs/ChipTabs.namespace.d.ts.map +1 -0
  39. package/lib/components/ChipTabs/ChipTabs.namespace.js +13 -0
  40. package/lib/components/ChipTabs/index.d.ts +3 -0
  41. package/lib/components/ChipTabs/index.d.ts.map +1 -0
  42. package/lib/components/ContextualFloatingButton/ContextualFloatingButton.d.ts +18 -0
  43. package/lib/components/ContextualFloatingButton/ContextualFloatingButton.d.ts.map +1 -0
  44. package/lib/components/ContextualFloatingButton/ContextualFloatingButton.jsx +113 -0
  45. package/lib/components/ContextualFloatingButton/index.d.ts +2 -0
  46. package/lib/components/ContextualFloatingButton/index.d.ts.map +1 -0
  47. package/lib/components/Dialog/Dialog.d.ts +87 -0
  48. package/lib/components/Dialog/Dialog.d.ts.map +1 -0
  49. package/lib/components/Dialog/Dialog.jsx +147 -0
  50. package/lib/components/Dialog/Dialog.namespace.d.ts +2 -0
  51. package/lib/components/Dialog/Dialog.namespace.d.ts.map +1 -0
  52. package/lib/components/Dialog/Dialog.namespace.js +18 -0
  53. package/lib/components/Dialog/index.d.ts +3 -0
  54. package/lib/components/Dialog/index.d.ts.map +1 -0
  55. package/lib/components/FloatingActionButton/FloatingActionButton.d.ts +23 -0
  56. package/lib/components/FloatingActionButton/FloatingActionButton.d.ts.map +1 -0
  57. package/lib/components/FloatingActionButton/FloatingActionButton.jsx +76 -0
  58. package/lib/components/FloatingActionButton/FloatingActionButton.namespace.d.ts +2 -0
  59. package/lib/components/FloatingActionButton/FloatingActionButton.namespace.d.ts.map +1 -0
  60. package/lib/components/FloatingActionButton/FloatingActionButton.namespace.js +10 -0
  61. package/lib/components/FloatingActionButton/index.d.ts +3 -0
  62. package/lib/components/FloatingActionButton/index.d.ts.map +1 -0
  63. package/lib/components/HelpBubble/HelpBubble.d.ts +67 -0
  64. package/lib/components/HelpBubble/HelpBubble.d.ts.map +1 -0
  65. package/lib/components/HelpBubble/HelpBubble.jsx +600 -0
  66. package/lib/components/HelpBubble/HelpBubble.namespace.d.ts +2 -0
  67. package/lib/components/HelpBubble/HelpBubble.namespace.d.ts.map +1 -0
  68. package/lib/components/HelpBubble/HelpBubble.namespace.js +18 -0
  69. package/lib/components/HelpBubble/index.d.ts +3 -0
  70. package/lib/components/HelpBubble/index.d.ts.map +1 -0
  71. package/lib/components/Icon/Icon.d.ts +1 -0
  72. package/lib/components/Icon/Icon.d.ts.map +1 -1
  73. package/lib/components/Icon/Icon.jsx +3 -2
  74. package/lib/components/IdentityPlaceholder/IdentityPlaceholder.d.ts +28 -0
  75. package/lib/components/IdentityPlaceholder/IdentityPlaceholder.d.ts.map +1 -0
  76. package/lib/components/IdentityPlaceholder/IdentityPlaceholder.jsx +37 -0
  77. package/lib/components/IdentityPlaceholder/IdentityPlaceholder.namespace.d.ts +2 -0
  78. package/lib/components/IdentityPlaceholder/IdentityPlaceholder.namespace.d.ts.map +1 -0
  79. package/lib/components/IdentityPlaceholder/IdentityPlaceholder.namespace.js +9 -0
  80. package/lib/components/IdentityPlaceholder/identity-placeholder-business.js +4 -0
  81. package/lib/components/IdentityPlaceholder/identity-placeholder-person.js +4 -0
  82. package/lib/components/IdentityPlaceholder/index.d.ts +3 -0
  83. package/lib/components/IdentityPlaceholder/index.d.ts.map +1 -0
  84. package/lib/components/InputButton/InputButton.d.ts +2 -0
  85. package/lib/components/InputButton/InputButton.d.ts.map +1 -1
  86. package/lib/components/InputButton/InputButton.jsx +40 -8
  87. package/lib/components/List/List.d.ts +1 -1
  88. package/lib/components/List/List.d.ts.map +1 -1
  89. package/lib/components/List/List.jsx +26 -12
  90. package/lib/components/Menu/Menu.d.ts.map +1 -1
  91. package/lib/components/Menu/Menu.jsx +15 -5
  92. package/lib/components/PageBanner/PageBanner.d.ts +3 -0
  93. package/lib/components/PageBanner/PageBanner.d.ts.map +1 -1
  94. package/lib/components/PageBanner/PageBanner.jsx +49 -11
  95. package/lib/components/QuantityPicker/QuantityPicker.d.ts +53 -0
  96. package/lib/components/QuantityPicker/QuantityPicker.d.ts.map +1 -0
  97. package/lib/components/QuantityPicker/QuantityPicker.jsx +282 -0
  98. package/lib/components/QuantityPicker/QuantityPicker.namespace.d.ts +2 -0
  99. package/lib/components/QuantityPicker/QuantityPicker.namespace.d.ts.map +1 -0
  100. package/lib/components/QuantityPicker/QuantityPicker.namespace.js +11 -0
  101. package/lib/components/QuantityPicker/index.d.ts +3 -0
  102. package/lib/components/QuantityPicker/index.d.ts.map +1 -0
  103. package/lib/components/RadioGroup/RadioGroup.jsx +2 -2
  104. package/lib/components/ReactionButton/ReactionButton.d.ts +1 -1
  105. package/lib/components/ReactionButton/ReactionButton.d.ts.map +1 -1
  106. package/lib/components/ReactionButton/ReactionButton.jsx +22 -13
  107. package/lib/components/ScrollFog/ScrollFog.d.ts +41 -0
  108. package/lib/components/ScrollFog/ScrollFog.d.ts.map +1 -0
  109. package/lib/components/ScrollFog/ScrollFog.jsx +65 -0
  110. package/lib/components/ScrollFog/index.d.ts +2 -0
  111. package/lib/components/ScrollFog/index.d.ts.map +1 -0
  112. package/lib/components/SegmentedControl/SegmentedControl.jsx +2 -2
  113. package/lib/components/Select/Select.d.ts +112 -0
  114. package/lib/components/Select/Select.d.ts.map +1 -0
  115. package/lib/components/Select/Select.jsx +885 -0
  116. package/lib/components/Select/Select.namespace.d.ts +2 -0
  117. package/lib/components/Select/Select.namespace.d.ts.map +1 -0
  118. package/lib/components/Select/Select.namespace.js +23 -0
  119. package/lib/components/Select/index.d.ts +3 -0
  120. package/lib/components/Select/index.d.ts.map +1 -0
  121. package/lib/components/Skeleton/Skeleton.d.ts +15 -0
  122. package/lib/components/Skeleton/Skeleton.d.ts.map +1 -0
  123. package/lib/components/Skeleton/Skeleton.jsx +18 -0
  124. package/lib/components/Skeleton/index.d.ts +2 -0
  125. package/lib/components/Skeleton/index.d.ts.map +1 -0
  126. package/lib/components/Slider/Slider.d.ts +101 -0
  127. package/lib/components/Slider/Slider.d.ts.map +1 -0
  128. package/lib/components/Slider/Slider.jsx +573 -0
  129. package/lib/components/Slider/Slider.namespace.d.ts +2 -0
  130. package/lib/components/Slider/Slider.namespace.d.ts.map +1 -0
  131. package/lib/components/Slider/Slider.namespace.js +19 -0
  132. package/lib/components/Slider/Slider.utils.d.ts +19 -0
  133. package/lib/components/Slider/Slider.utils.d.ts.map +1 -0
  134. package/lib/components/Slider/Slider.utils.js +106 -0
  135. package/lib/components/Slider/index.d.ts +3 -0
  136. package/lib/components/Slider/index.d.ts.map +1 -0
  137. package/lib/components/SwipeableMenuSheet/SwipeableMenuSheet.d.ts.map +1 -1
  138. package/lib/components/SwipeableMenuSheet/SwipeableMenuSheet.jsx +22 -8
  139. package/lib/components/Switch/Switch.d.ts.map +1 -1
  140. package/lib/components/Switch/Switch.jsx +5 -3
  141. package/lib/components/Tabs/Tabs.d.ts +34 -2
  142. package/lib/components/Tabs/Tabs.d.ts.map +1 -1
  143. package/lib/components/Tabs/Tabs.jsx +186 -56
  144. package/lib/components/Tabs/Tabs.utils.d.ts +14 -1
  145. package/lib/components/Tabs/Tabs.utils.d.ts.map +1 -1
  146. package/lib/components/Tabs/Tabs.utils.js +24 -3
  147. package/lib/components/Tabs/index.d.ts +1 -1
  148. package/lib/components/Tabs/index.d.ts.map +1 -1
  149. package/lib/components/index.d.ts +14 -0
  150. package/lib/components/index.d.ts.map +1 -1
  151. package/lib/hooks/useControllableState.d.ts +1 -14
  152. package/lib/hooks/useControllableState.d.ts.map +1 -1
  153. package/lib/hooks/usePressTap.d.ts +1 -27
  154. package/lib/hooks/usePressTap.d.ts.map +1 -1
  155. package/lib/hooks/useScaleFeedback.d.ts +1 -1
  156. package/lib/hooks/useScaleFeedback.js +1 -1
  157. package/lib/index.js +29 -6
  158. package/lib/{hooks/useControllableState.js → packages/lynx-react-headless/use-controllable-state/lib/index.js} +2 -3
  159. package/lib/{hooks/usePressTap.js → packages/lynx-react-headless/use-press-tap/lib/index.js} +13 -10
  160. package/lib/types.d.ts +1 -0
  161. package/lib/types.d.ts.map +1 -1
  162. package/package.json +9 -5
  163. package/src/components/ActionButton/ActionButton.test.tsx +7 -7
  164. package/src/components/AlertDialog/AlertDialog.namespace.ts +22 -0
  165. package/src/components/AlertDialog/AlertDialog.test.tsx +226 -0
  166. package/src/components/AlertDialog/AlertDialog.tsx +374 -0
  167. package/src/components/AlertDialog/index.ts +24 -0
  168. package/src/components/AppBar/AppBar.namespace.ts +1 -0
  169. package/src/components/AppBar/AppBar.test.tsx +257 -0
  170. package/src/components/AppBar/AppBar.tsx +109 -14
  171. package/src/components/AppBar/index.ts +1 -0
  172. package/src/components/AttachmentDisplay/AttachmentDisplay.tsx +886 -0
  173. package/src/components/AttachmentDisplay/index.ts +48 -0
  174. package/src/components/AttachmentInput/AttachmentInput.tsx +1001 -0
  175. package/src/components/AttachmentInput/index.ts +49 -0
  176. package/src/components/BottomSheet/BottomSheet.test.tsx +22 -7
  177. package/src/components/BottomSheet/BottomSheet.tsx +1 -19
  178. package/src/components/ChipTabs/ChipTabs.namespace.ts +14 -0
  179. package/src/components/ChipTabs/ChipTabs.tsx +119 -0
  180. package/src/components/ChipTabs/index.ts +16 -0
  181. package/src/components/ContextualFloatingButton/ContextualFloatingButton.test.tsx +63 -0
  182. package/src/components/ContextualFloatingButton/ContextualFloatingButton.tsx +232 -0
  183. package/src/components/ContextualFloatingButton/index.ts +4 -0
  184. package/src/components/Dialog/Dialog.namespace.ts +24 -0
  185. package/src/components/Dialog/Dialog.test.tsx +172 -0
  186. package/src/components/Dialog/Dialog.tsx +373 -0
  187. package/src/components/Dialog/index.ts +26 -0
  188. package/src/components/FloatingActionButton/FloatingActionButton.namespace.ts +8 -0
  189. package/src/components/FloatingActionButton/FloatingActionButton.test.tsx +59 -0
  190. package/src/components/FloatingActionButton/FloatingActionButton.tsx +207 -0
  191. package/src/components/FloatingActionButton/index.ts +10 -0
  192. package/src/components/HelpBubble/HelpBubble.namespace.ts +24 -0
  193. package/src/components/HelpBubble/HelpBubble.tsx +998 -0
  194. package/src/components/HelpBubble/index.ts +26 -0
  195. package/src/components/Icon/Icon.test.tsx +57 -5
  196. package/src/components/Icon/Icon.tsx +16 -2
  197. package/src/components/IdentityPlaceholder/IdentityPlaceholder.namespace.ts +6 -0
  198. package/src/components/IdentityPlaceholder/IdentityPlaceholder.tsx +103 -0
  199. package/src/components/IdentityPlaceholder/assets.d.ts +4 -0
  200. package/src/components/IdentityPlaceholder/identity-placeholder-business.webp +0 -0
  201. package/src/components/IdentityPlaceholder/identity-placeholder-person.webp +0 -0
  202. package/src/components/IdentityPlaceholder/index.ts +8 -0
  203. package/src/components/InputButton/InputButton.test.tsx +128 -1
  204. package/src/components/InputButton/InputButton.tsx +60 -9
  205. package/src/components/List/List.test.tsx +54 -2
  206. package/src/components/List/List.tsx +43 -10
  207. package/src/components/Menu/Menu.tsx +14 -4
  208. package/src/components/Menu/__tests__/Menu.test.tsx +34 -0
  209. package/src/components/PageBanner/PageBanner.test.tsx +110 -3
  210. package/src/components/PageBanner/PageBanner.tsx +83 -19
  211. package/src/components/QuantityPicker/QuantityPicker.namespace.ts +10 -0
  212. package/src/components/QuantityPicker/QuantityPicker.test.tsx +235 -0
  213. package/src/components/QuantityPicker/QuantityPicker.tsx +605 -0
  214. package/src/components/QuantityPicker/index.ts +14 -0
  215. package/src/components/ReactionButton/ReactionButton.feedback.test.tsx +50 -0
  216. package/src/components/ReactionButton/ReactionButton.test.tsx +6 -4
  217. package/src/components/ReactionButton/ReactionButton.tsx +20 -12
  218. package/src/components/ScrollFog/ScrollFog.test.tsx +222 -0
  219. package/src/components/ScrollFog/ScrollFog.tsx +172 -0
  220. package/src/components/ScrollFog/index.ts +1 -0
  221. package/src/components/Select/Select.namespace.ts +34 -0
  222. package/src/components/Select/Select.test.tsx +297 -0
  223. package/src/components/Select/Select.tsx +1483 -0
  224. package/src/components/Select/index.ts +39 -0
  225. package/src/components/Skeleton/Skeleton.test.tsx +64 -0
  226. package/src/components/Skeleton/Skeleton.tsx +45 -0
  227. package/src/components/Skeleton/index.ts +1 -0
  228. package/src/components/Slider/Slider.namespace.ts +26 -0
  229. package/src/components/Slider/Slider.test.tsx +260 -0
  230. package/src/components/Slider/Slider.tsx +897 -0
  231. package/src/components/Slider/Slider.utils.ts +167 -0
  232. package/src/components/Slider/index.ts +30 -0
  233. package/src/components/SwipeableMenuSheet/SwipeableMenuSheet.test.tsx +21 -1
  234. package/src/components/SwipeableMenuSheet/SwipeableMenuSheet.tsx +30 -7
  235. package/src/components/Switch/Switch.tsx +14 -13
  236. package/src/components/Tabs/Tabs.test.tsx +218 -1
  237. package/src/components/Tabs/Tabs.tsx +346 -75
  238. package/src/components/Tabs/Tabs.utils.ts +51 -1
  239. package/src/components/Tabs/index.ts +17 -1
  240. package/src/components/index.ts +14 -0
  241. package/src/hooks/useControllableState.ts +1 -43
  242. package/src/hooks/usePressTap.test.ts +23 -11
  243. package/src/hooks/usePressTap.ts +1 -60
  244. package/src/hooks/useScaleFeedback.ts +1 -1
  245. package/src/types.ts +1 -0
@@ -0,0 +1,39 @@
1
+ export {
2
+ SelectRoot,
3
+ SelectTrigger,
4
+ SelectValue,
5
+ SelectPlaceholder,
6
+ SelectPrefixIcon,
7
+ SelectSuffixIcon,
8
+ SelectContent,
9
+ SelectScrollArea,
10
+ SelectGroup,
11
+ SelectGroupLabel,
12
+ SelectItem,
13
+ SelectItemPrefixIcon,
14
+ SelectItemBody,
15
+ SelectItemLabel,
16
+ SelectItemDescription,
17
+ SelectItemIndicator,
18
+ type SelectRootProps,
19
+ type SelectTriggerProps,
20
+ type SelectValueProps,
21
+ type SelectPlaceholderProps,
22
+ type SelectPrefixIconProps,
23
+ type SelectSuffixIconProps,
24
+ type SelectContentProps,
25
+ type SelectScrollAreaProps,
26
+ type SelectGroupProps,
27
+ type SelectGroupLabelProps,
28
+ type SelectItemProps,
29
+ type SelectItemPrefixIconProps,
30
+ type SelectItemBodyProps,
31
+ type SelectItemLabelProps,
32
+ type SelectItemDescriptionProps,
33
+ type SelectItemIndicatorProps,
34
+ type SelectOpenChangeDetails,
35
+ type SelectOpenChangeReason,
36
+ type SelectSelectedItem,
37
+ } from "./Select";
38
+
39
+ export * as Select from "./Select.namespace";
@@ -0,0 +1,64 @@
1
+ import "@testing-library/jest-dom";
2
+ import { render } from "@lynx-js/react/testing-library";
3
+ import { describe, expect, it } from "vitest";
4
+
5
+ import { Skeleton } from "./Skeleton";
6
+
7
+ function getRenderedRoot() {
8
+ const root = elementTree.root;
9
+
10
+ if (!root) {
11
+ throw new Error("Expected Lynx render root to exist.");
12
+ }
13
+
14
+ return root;
15
+ }
16
+
17
+ function getSkeletonRoot() {
18
+ const root = getRenderedRoot().querySelector<HTMLElement>(".seed-skeleton__root");
19
+
20
+ if (!root) {
21
+ throw new Error("Expected Skeleton root to exist.");
22
+ }
23
+
24
+ return root;
25
+ }
26
+
27
+ describe("Skeleton", () => {
28
+ it("renders root and shimmer slots with default variant classes", () => {
29
+ render(<Skeleton />);
30
+
31
+ const root = getSkeletonRoot();
32
+ const shimmer = root.querySelector<HTMLElement>(".seed-skeleton__shimmer");
33
+
34
+ expect(root.tagName.toLowerCase()).toBe("view");
35
+ expect(root).toHaveClass("seed-skeleton__root--radius_8");
36
+ expect(root).toHaveClass("seed-skeleton__root--tone_neutral");
37
+ expect(shimmer).toBeInTheDocument();
38
+ expect(shimmer?.tagName.toLowerCase()).toBe("view");
39
+ expect(shimmer).toHaveClass("seed-skeleton__shimmer--tone_neutral");
40
+ });
41
+
42
+ it("applies radius and tone variants and preserves user className", () => {
43
+ render(<Skeleton className="custom-skeleton" radius="full" tone="magic" />);
44
+
45
+ const root = getSkeletonRoot();
46
+ const shimmer = root.querySelector<HTMLElement>(".seed-skeleton__shimmer");
47
+
48
+ expect(root).toHaveClass("custom-skeleton");
49
+ expect(root).toHaveClass("seed-skeleton__root--radius_full");
50
+ expect(root).toHaveClass("seed-skeleton__root--tone_magic");
51
+ expect(shimmer).toHaveClass("seed-skeleton__shimmer--tone_magic");
52
+ });
53
+
54
+ it("preserves width, height, style, and decorative accessibility semantics", () => {
55
+ render(<Skeleton width="48px" height="16px" style={{ opacity: 0.5 }} />);
56
+
57
+ const root = getSkeletonRoot();
58
+ const shimmer = root.querySelector<HTMLElement>(".seed-skeleton__shimmer");
59
+
60
+ expect(root).toHaveStyle({ width: "48px", height: "16px", opacity: "0.5" });
61
+ expect(root).toHaveAttribute("accessibility-elements-hidden", "true");
62
+ expect(shimmer).toHaveAttribute("accessibility-elements-hidden", "true");
63
+ });
64
+ });
@@ -0,0 +1,45 @@
1
+ import { skeleton, type SkeletonVariantProps } from "@seed-design/lynx-css/recipes/skeleton";
2
+ import clsx from "clsx";
3
+ import * as React from "@lynx-js/react";
4
+
5
+ import type { LynxStyledElementProps, LynxViewRef } from "../../types";
6
+ import { mergeProps } from "../../utils/merge-props";
7
+ import { useStyleProps, type StyleProps } from "../../utils/styled";
8
+
9
+ /**
10
+ * @platform Lynx
11
+ *
12
+ * 웹 Skeleton의 정적 placeholder를 native `<view>` root와 shimmer child로
13
+ * 렌더링합니다. 장식 요소이므로 접근성 트리에서 숨겨지며, children이나 이벤트·상태
14
+ * API를 노출하지 않습니다.
15
+ */
16
+ export interface SkeletonProps
17
+ extends SkeletonVariantProps,
18
+ Omit<LynxStyledElementProps, "children">,
19
+ Pick<StyleProps, "width" | "height"> {}
20
+
21
+ export const Skeleton = React.forwardRef<unknown, SkeletonProps>((props, ref) => {
22
+ const [variantProps, otherProps] = skeleton.splitVariantProps(props);
23
+ const { style, restProps } = useStyleProps(otherProps);
24
+ const {
25
+ children: _children,
26
+ className,
27
+ ...nativeProps
28
+ } = restProps as typeof restProps & {
29
+ children?: unknown;
30
+ };
31
+ const classes = skeleton(variantProps);
32
+
33
+ return (
34
+ <view
35
+ {...mergeProps(ref ? { ref: ref as LynxViewRef } : {}, nativeProps)}
36
+ className={clsx(classes.root, className)}
37
+ style={style}
38
+ accessibility-elements-hidden={true}
39
+ >
40
+ <view className={classes.shimmer} accessibility-elements-hidden={true} />
41
+ </view>
42
+ );
43
+ });
44
+
45
+ Skeleton.displayName = "Skeleton";
@@ -0,0 +1 @@
1
+ export { Skeleton, type SkeletonProps } from "./Skeleton";
@@ -0,0 +1,26 @@
1
+ export {
2
+ SliderRoot as Root,
3
+ SliderControl as Control,
4
+ SliderTrack as Track,
5
+ SliderRange as Range,
6
+ SliderThumb as Thumb,
7
+ SliderTick as Tick,
8
+ SliderMarkers as Markers,
9
+ SliderMarker as Marker,
10
+ SliderValueIndicatorRoot as ValueIndicatorRoot,
11
+ SliderValueIndicatorArrow as ValueIndicatorArrow,
12
+ SliderValueIndicatorArrowTip as ValueIndicatorArrowTip,
13
+ SliderValueIndicatorLabel as ValueIndicatorLabel,
14
+ type SliderRootProps as RootProps,
15
+ type SliderControlProps as ControlProps,
16
+ type SliderTrackProps as TrackProps,
17
+ type SliderRangeProps as RangeProps,
18
+ type SliderThumbProps as ThumbProps,
19
+ type SliderTickProps as TickProps,
20
+ type SliderMarkersProps as MarkersProps,
21
+ type SliderMarkerProps as MarkerProps,
22
+ type SliderValueIndicatorRootProps as ValueIndicatorRootProps,
23
+ type SliderValueIndicatorArrowProps as ValueIndicatorArrowProps,
24
+ type SliderValueIndicatorArrowTipProps as ValueIndicatorArrowTipProps,
25
+ type SliderValueIndicatorLabelProps as ValueIndicatorLabelProps,
26
+ } from "./Slider";
@@ -0,0 +1,260 @@
1
+ import "@testing-library/jest-dom";
2
+ import { fireEvent, render, waitSchedule } from "@lynx-js/react/testing-library";
3
+ import { describe, expect, it, vi } from "vitest";
4
+ import { getRectByRef } from "@lynx-js/lynx-ui-common";
5
+ import type * as LynxUiCommon from "@lynx-js/lynx-ui-common";
6
+
7
+ import * as Slider from "./Slider.namespace";
8
+ import {
9
+ hasMinimumSteps,
10
+ normalizeValue,
11
+ normalizeValues,
12
+ percentageForValue,
13
+ valueForPercentage,
14
+ } from "./Slider.utils";
15
+
16
+ vi.mock("@lynx-js/lynx-ui-common", async (importOriginal) => {
17
+ const actual = await importOriginal<typeof LynxUiCommon>();
18
+ return {
19
+ ...actual,
20
+ getRectByRef: vi.fn(() => Promise.resolve({ left: 10, width: 100 })),
21
+ };
22
+ });
23
+
24
+ describe("Slider utilities", () => {
25
+ it("normalizes to bounds and step increments", () => {
26
+ expect(normalizeValue(14, 0, 100, 10)).toBe(10);
27
+ expect(normalizeValue(140, 0, 100, 10)).toBe(100);
28
+ expect(normalizeValues([70, -2, 31], 0, 100, 10)).toEqual([0, 30, 70]);
29
+ });
30
+
31
+ it("uses allowed values instead of step snapping", () => {
32
+ expect(normalizeValue(34, 0, 100, 10, [5, 35, 90])).toBe(35);
33
+ expect(normalizeValues([89, 6], 0, 100, 1, [5, 35, 90])).toEqual([5, 90]);
34
+ expect(normalizeValue(-8, 0, 100, 10, [-10, 50])).toBe(-10);
35
+ });
36
+
37
+ it("enforces minimum steps and converts RTL geometry", () => {
38
+ expect(hasMinimumSteps([10, 30], 2, 10)).toBe(true);
39
+ expect(hasMinimumSteps([10, 20], 2, 10)).toBe(false);
40
+ expect(hasMinimumSteps([10, 10], 1, 0)).toBe(false);
41
+ expect(hasMinimumSteps([0.1, 0.3], 2, 0.1)).toBe(true);
42
+ expect(valueForPercentage(25, 0, 100, "ltr")).toBe(25);
43
+ expect(valueForPercentage(25, 0, 100, "rtl")).toBe(75);
44
+ expect(percentageForValue(25, 0, 100)).toBe(25);
45
+ });
46
+ });
47
+
48
+ describe("Slider", () => {
49
+ it("exposes adjustable thumb accessibility and indicator labels", () => {
50
+ const { container } = render(
51
+ <Slider.Root defaultValues={[40]} getAccessibilityLabel={(index) => `Price ${index + 1}`}>
52
+ <Slider.Control>
53
+ <Slider.Track>
54
+ <Slider.Range />
55
+ <Slider.Thumb />
56
+ <Slider.ValueIndicatorRoot>
57
+ <Slider.ValueIndicatorLabel />
58
+ </Slider.ValueIndicatorRoot>
59
+ </Slider.Track>
60
+ </Slider.Control>
61
+ </Slider.Root>,
62
+ );
63
+ const thumb = container.querySelector(".seed-slider__thumb");
64
+ const label = container.querySelector("text");
65
+ expect(thumb).toHaveAttribute("accessibility-role-description", "adjustable");
66
+ expect(thumb).toHaveAttribute("accessibility-label", "Price 1");
67
+ expect(thumb).toHaveAttribute("accessibility-value", "minimum 0, maximum 100, current 40");
68
+ expect(label).toHaveTextContent("40");
69
+ });
70
+
71
+ it("reports controlled touch updates and commits once at touch end", async () => {
72
+ const onValuesChange = vi.fn();
73
+ const onValuesCommit = vi.fn();
74
+ const view = render(
75
+ <Slider.Root values={[20]} onValuesChange={onValuesChange} onValuesCommit={onValuesCommit}>
76
+ <Slider.Control>
77
+ <Slider.Track>
78
+ <Slider.Range />
79
+ <Slider.Thumb />
80
+ </Slider.Track>
81
+ </Slider.Control>
82
+ </Slider.Root>,
83
+ );
84
+ await waitSchedule();
85
+ const root = view.container.querySelector(".seed-slider__root");
86
+ if (!root) throw new Error("Expected slider root");
87
+ fireEvent.touchstart(root, { eventType: "catchEvent", touches: [{ pageX: 60 }] });
88
+ await waitSchedule();
89
+ fireEvent.touchmove(root, { eventType: "catchEvent", touches: [{ pageX: 80 }] });
90
+ fireEvent.touchend(root, { eventType: "catchEvent", changedTouches: [{ pageX: 80 }] });
91
+ await waitSchedule();
92
+ expect(onValuesChange).toHaveBeenCalled();
93
+ expect(onValuesCommit).toHaveBeenCalledTimes(1);
94
+ expect(onValuesCommit).toHaveBeenLastCalledWith([70]);
95
+ });
96
+
97
+ it("coalesces touch moves to the latest value once per frame", async () => {
98
+ const onValuesChange = vi.fn();
99
+ const view = render(
100
+ <Slider.Root defaultValues={[20]} onValuesChange={onValuesChange}>
101
+ <Slider.Control>
102
+ <Slider.Track>
103
+ <Slider.Thumb />
104
+ </Slider.Track>
105
+ </Slider.Control>
106
+ </Slider.Root>,
107
+ );
108
+ await waitSchedule();
109
+ const root = view.container.querySelector(".seed-slider__root");
110
+ if (!root) throw new Error("Expected slider root");
111
+ fireEvent.touchstart(root, { eventType: "catchEvent", touches: [{ pageX: 30 }] });
112
+ fireEvent.touchmove(root, { eventType: "catchEvent", touches: [{ pageX: 40 }] });
113
+ fireEvent.touchmove(root, { eventType: "catchEvent", touches: [{ pageX: 80 }] });
114
+
115
+ expect(onValuesChange).not.toHaveBeenCalled();
116
+ await waitSchedule();
117
+ expect(onValuesChange).toHaveBeenCalledTimes(1);
118
+ expect(onValuesChange).toHaveBeenLastCalledWith([70]);
119
+ });
120
+
121
+ it("commits a quick first tap after track measurement", async () => {
122
+ const onValuesChange = vi.fn();
123
+ const onValuesCommit = vi.fn();
124
+ const view = render(
125
+ <Slider.Root
126
+ defaultValues={[20]}
127
+ onValuesChange={onValuesChange}
128
+ onValuesCommit={onValuesCommit}
129
+ >
130
+ <Slider.Control>
131
+ <Slider.Track>
132
+ <Slider.Thumb />
133
+ </Slider.Track>
134
+ </Slider.Control>
135
+ </Slider.Root>,
136
+ );
137
+ const root = view.container.querySelector(".seed-slider__root");
138
+ if (!root) throw new Error("Expected slider root");
139
+ fireEvent.touchstart(root, { eventType: "catchEvent", touches: [{ pageX: 80 }] });
140
+ fireEvent.touchend(root, { eventType: "catchEvent", changedTouches: [{ pageX: 80 }] });
141
+ await waitSchedule();
142
+ expect(onValuesChange).toHaveBeenLastCalledWith([70]);
143
+ expect(onValuesCommit).toHaveBeenCalledTimes(1);
144
+ expect(onValuesCommit).toHaveBeenLastCalledWith([70]);
145
+ });
146
+
147
+ it("clears a finished interaction when measurement fails", async () => {
148
+ const getRect = vi.mocked(getRectByRef);
149
+ const onValuesChange = vi.fn();
150
+ const onValuesCommit = vi.fn();
151
+ getRect.mockRejectedValue(new Error("measurement failed"));
152
+
153
+ try {
154
+ const view = render(
155
+ <Slider.Root values={[20]} onValuesChange={onValuesChange} onValuesCommit={onValuesCommit}>
156
+ <Slider.Control>
157
+ <Slider.Track>
158
+ <Slider.Thumb />
159
+ </Slider.Track>
160
+ </Slider.Control>
161
+ </Slider.Root>,
162
+ );
163
+ const root = view.container.querySelector(".seed-slider__root");
164
+ if (!root) throw new Error("Expected slider root");
165
+ fireEvent.touchstart(root, { eventType: "catchEvent", touches: [{ pageX: 80 }] });
166
+ fireEvent.touchend(root, { eventType: "catchEvent", changedTouches: [{ pageX: 80 }] });
167
+ await waitSchedule();
168
+
169
+ getRect.mockResolvedValue({
170
+ left: 10,
171
+ top: 0,
172
+ width: 100,
173
+ height: 20,
174
+ right: 110,
175
+ bottom: 20,
176
+ });
177
+ view.rerender(
178
+ <Slider.Root
179
+ values={[20, 40]}
180
+ onValuesChange={onValuesChange}
181
+ onValuesCommit={onValuesCommit}
182
+ >
183
+ <Slider.Control>
184
+ <Slider.Track>
185
+ <Slider.Thumb />
186
+ </Slider.Track>
187
+ </Slider.Control>
188
+ </Slider.Root>,
189
+ );
190
+ await waitSchedule();
191
+
192
+ expect(onValuesChange).not.toHaveBeenCalled();
193
+ expect(onValuesCommit).not.toHaveBeenCalled();
194
+ } finally {
195
+ getRect.mockReset();
196
+ getRect.mockResolvedValue({
197
+ left: 10,
198
+ top: 0,
199
+ width: 100,
200
+ height: 20,
201
+ right: 110,
202
+ bottom: 20,
203
+ });
204
+ }
205
+ });
206
+
207
+ it("cancels an unmeasured interaction without committing delayed updates", async () => {
208
+ const onValuesChange = vi.fn();
209
+ const onValuesCommit = vi.fn();
210
+ const view = render(
211
+ <Slider.Root
212
+ defaultValues={[20]}
213
+ onValuesChange={onValuesChange}
214
+ onValuesCommit={onValuesCommit}
215
+ >
216
+ <Slider.Control>
217
+ <Slider.Track>
218
+ <Slider.Thumb />
219
+ </Slider.Track>
220
+ </Slider.Control>
221
+ </Slider.Root>,
222
+ );
223
+ const root = view.container.querySelector(".seed-slider__root");
224
+ if (!root) throw new Error("Expected slider root");
225
+ fireEvent.touchstart(root, { eventType: "catchEvent", touches: [{ pageX: 80 }] });
226
+ fireEvent.touchcancel(root, { eventType: "catchEvent" });
227
+ await waitSchedule();
228
+ expect(onValuesChange).not.toHaveBeenCalled();
229
+ expect(onValuesCommit).not.toHaveBeenCalled();
230
+ });
231
+
232
+ it("does not update or commit a disabled slider", () => {
233
+ const onValuesChange = vi.fn();
234
+ const onValuesCommit = vi.fn();
235
+ const { container } = render(
236
+ <Slider.Root
237
+ defaultValues={[20]}
238
+ disabled
239
+ onValuesChange={onValuesChange}
240
+ onValuesCommit={onValuesCommit}
241
+ >
242
+ <Slider.Control>
243
+ <Slider.Track>
244
+ <Slider.Thumb />
245
+ </Slider.Track>
246
+ </Slider.Control>
247
+ </Slider.Root>,
248
+ );
249
+ const root = container.querySelector(".seed-slider__root");
250
+ if (!root) throw new Error("Expected slider root");
251
+ fireEvent.touchstart(root, { eventType: "catchEvent", touches: [{ pageX: 80 }] });
252
+ fireEvent.touchend(root, { eventType: "catchEvent", changedTouches: [{ pageX: 80 }] });
253
+ expect(onValuesChange).not.toHaveBeenCalled();
254
+ expect(onValuesCommit).not.toHaveBeenCalled();
255
+ expect(container.querySelector(".seed-slider__thumb")).toHaveAttribute(
256
+ "accessibility-traits",
257
+ "disabled",
258
+ );
259
+ });
260
+ });