@seed-design/lynx-react 0.7.1 → 0.9.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 (265) 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 +42 -16
  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/context.d.ts +2 -1
  17. package/lib/components/AppBar/context.d.ts.map +1 -1
  18. package/lib/components/AppBar/index.d.ts +1 -1
  19. package/lib/components/AppBar/index.d.ts.map +1 -1
  20. package/lib/components/AppBar/useAppBar.d.ts +2 -1
  21. package/lib/components/AppBar/useAppBar.d.ts.map +1 -1
  22. package/lib/components/AppBar/useAppBar.js +17 -15
  23. package/lib/components/AttachmentDisplay/AttachmentDisplay.d.ts +182 -0
  24. package/lib/components/AttachmentDisplay/AttachmentDisplay.d.ts.map +1 -0
  25. package/lib/components/AttachmentDisplay/AttachmentDisplay.jsx +444 -0
  26. package/lib/components/AttachmentDisplay/index.d.ts +2 -0
  27. package/lib/components/AttachmentDisplay/index.d.ts.map +1 -0
  28. package/lib/components/AttachmentInput/AttachmentInput.d.ts +179 -0
  29. package/lib/components/AttachmentInput/AttachmentInput.d.ts.map +1 -0
  30. package/lib/components/AttachmentInput/AttachmentInput.jsx +520 -0
  31. package/lib/components/AttachmentInput/index.d.ts +2 -0
  32. package/lib/components/AttachmentInput/index.d.ts.map +1 -0
  33. package/lib/components/BottomSheet/BottomSheet.d.ts +1 -1
  34. package/lib/components/BottomSheet/BottomSheet.d.ts.map +1 -1
  35. package/lib/components/BottomSheet/BottomSheet.jsx +1 -7
  36. package/lib/components/Box/Box.d.ts +3 -3
  37. package/lib/components/Box/Box.d.ts.map +1 -1
  38. package/lib/components/Callout/Callout.jsx +2 -2
  39. package/lib/components/Checkbox/Checkbox.jsx +2 -2
  40. package/lib/components/Chip/Chip.jsx +2 -2
  41. package/lib/components/ChipTabs/ChipTabs.d.ts +41 -0
  42. package/lib/components/ChipTabs/ChipTabs.d.ts.map +1 -0
  43. package/lib/components/ChipTabs/ChipTabs.jsx +39 -0
  44. package/lib/components/ChipTabs/ChipTabs.namespace.d.ts +2 -0
  45. package/lib/components/ChipTabs/ChipTabs.namespace.d.ts.map +1 -0
  46. package/lib/components/ChipTabs/ChipTabs.namespace.js +13 -0
  47. package/lib/components/ChipTabs/index.d.ts +3 -0
  48. package/lib/components/ChipTabs/index.d.ts.map +1 -0
  49. package/lib/components/ContextualFloatingButton/ContextualFloatingButton.d.ts +18 -0
  50. package/lib/components/ContextualFloatingButton/ContextualFloatingButton.d.ts.map +1 -0
  51. package/lib/components/ContextualFloatingButton/ContextualFloatingButton.jsx +113 -0
  52. package/lib/components/ContextualFloatingButton/index.d.ts +2 -0
  53. package/lib/components/ContextualFloatingButton/index.d.ts.map +1 -0
  54. package/lib/components/Dialog/Dialog.d.ts +87 -0
  55. package/lib/components/Dialog/Dialog.d.ts.map +1 -0
  56. package/lib/components/Dialog/Dialog.jsx +147 -0
  57. package/lib/components/Dialog/Dialog.namespace.d.ts +2 -0
  58. package/lib/components/Dialog/Dialog.namespace.d.ts.map +1 -0
  59. package/lib/components/Dialog/Dialog.namespace.js +18 -0
  60. package/lib/components/Dialog/index.d.ts +3 -0
  61. package/lib/components/Dialog/index.d.ts.map +1 -0
  62. package/lib/components/FloatingActionButton/FloatingActionButton.d.ts +23 -0
  63. package/lib/components/FloatingActionButton/FloatingActionButton.d.ts.map +1 -0
  64. package/lib/components/FloatingActionButton/FloatingActionButton.jsx +76 -0
  65. package/lib/components/FloatingActionButton/FloatingActionButton.namespace.d.ts +2 -0
  66. package/lib/components/FloatingActionButton/FloatingActionButton.namespace.d.ts.map +1 -0
  67. package/lib/components/FloatingActionButton/FloatingActionButton.namespace.js +10 -0
  68. package/lib/components/FloatingActionButton/index.d.ts +3 -0
  69. package/lib/components/FloatingActionButton/index.d.ts.map +1 -0
  70. package/lib/components/HelpBubble/HelpBubble.d.ts +67 -0
  71. package/lib/components/HelpBubble/HelpBubble.d.ts.map +1 -0
  72. package/lib/components/HelpBubble/HelpBubble.jsx +600 -0
  73. package/lib/components/HelpBubble/HelpBubble.namespace.d.ts +2 -0
  74. package/lib/components/HelpBubble/HelpBubble.namespace.d.ts.map +1 -0
  75. package/lib/components/HelpBubble/HelpBubble.namespace.js +18 -0
  76. package/lib/components/HelpBubble/index.d.ts +3 -0
  77. package/lib/components/HelpBubble/index.d.ts.map +1 -0
  78. package/lib/components/Icon/Icon.d.ts +1 -0
  79. package/lib/components/Icon/Icon.d.ts.map +1 -1
  80. package/lib/components/Icon/Icon.jsx +2 -1
  81. package/lib/components/IdentityPlaceholder/IdentityPlaceholder.d.ts +28 -0
  82. package/lib/components/IdentityPlaceholder/IdentityPlaceholder.d.ts.map +1 -0
  83. package/lib/components/IdentityPlaceholder/IdentityPlaceholder.jsx +37 -0
  84. package/lib/components/IdentityPlaceholder/IdentityPlaceholder.namespace.d.ts +2 -0
  85. package/lib/components/IdentityPlaceholder/IdentityPlaceholder.namespace.d.ts.map +1 -0
  86. package/lib/components/IdentityPlaceholder/IdentityPlaceholder.namespace.js +9 -0
  87. package/lib/components/IdentityPlaceholder/identity-placeholder-business.js +4 -0
  88. package/lib/components/IdentityPlaceholder/identity-placeholder-person.js +4 -0
  89. package/lib/components/IdentityPlaceholder/index.d.ts +3 -0
  90. package/lib/components/IdentityPlaceholder/index.d.ts.map +1 -0
  91. package/lib/components/InputButton/InputButton.d.ts +2 -0
  92. package/lib/components/InputButton/InputButton.d.ts.map +1 -1
  93. package/lib/components/InputButton/InputButton.jsx +40 -8
  94. package/lib/components/List/List.d.ts +1 -1
  95. package/lib/components/List/List.d.ts.map +1 -1
  96. package/lib/components/List/List.jsx +26 -12
  97. package/lib/components/Menu/Menu.d.ts.map +1 -1
  98. package/lib/components/Menu/Menu.jsx +15 -5
  99. package/lib/components/PageBanner/PageBanner.d.ts +3 -0
  100. package/lib/components/PageBanner/PageBanner.d.ts.map +1 -1
  101. package/lib/components/PageBanner/PageBanner.jsx +49 -11
  102. package/lib/components/QuantityPicker/QuantityPicker.d.ts +53 -0
  103. package/lib/components/QuantityPicker/QuantityPicker.d.ts.map +1 -0
  104. package/lib/components/QuantityPicker/QuantityPicker.jsx +282 -0
  105. package/lib/components/QuantityPicker/QuantityPicker.namespace.d.ts +2 -0
  106. package/lib/components/QuantityPicker/QuantityPicker.namespace.d.ts.map +1 -0
  107. package/lib/components/QuantityPicker/QuantityPicker.namespace.js +11 -0
  108. package/lib/components/QuantityPicker/index.d.ts +3 -0
  109. package/lib/components/QuantityPicker/index.d.ts.map +1 -0
  110. package/lib/components/RadioGroup/RadioGroup.jsx +2 -2
  111. package/lib/components/ReactionButton/ReactionButton.d.ts +1 -1
  112. package/lib/components/ReactionButton/ReactionButton.d.ts.map +1 -1
  113. package/lib/components/ReactionButton/ReactionButton.jsx +22 -13
  114. package/lib/components/ScrollFog/ScrollFog.d.ts +41 -0
  115. package/lib/components/ScrollFog/ScrollFog.d.ts.map +1 -0
  116. package/lib/components/ScrollFog/ScrollFog.jsx +65 -0
  117. package/lib/components/ScrollFog/index.d.ts +2 -0
  118. package/lib/components/ScrollFog/index.d.ts.map +1 -0
  119. package/lib/components/SegmentedControl/SegmentedControl.jsx +2 -2
  120. package/lib/components/Select/Select.d.ts +112 -0
  121. package/lib/components/Select/Select.d.ts.map +1 -0
  122. package/lib/components/Select/Select.jsx +885 -0
  123. package/lib/components/Select/Select.namespace.d.ts +2 -0
  124. package/lib/components/Select/Select.namespace.d.ts.map +1 -0
  125. package/lib/components/Select/Select.namespace.js +23 -0
  126. package/lib/components/Select/index.d.ts +3 -0
  127. package/lib/components/Select/index.d.ts.map +1 -0
  128. package/lib/components/Skeleton/Skeleton.d.ts +15 -0
  129. package/lib/components/Skeleton/Skeleton.d.ts.map +1 -0
  130. package/lib/components/Skeleton/Skeleton.jsx +18 -0
  131. package/lib/components/Skeleton/index.d.ts +2 -0
  132. package/lib/components/Skeleton/index.d.ts.map +1 -0
  133. package/lib/components/Slider/Slider.d.ts +101 -0
  134. package/lib/components/Slider/Slider.d.ts.map +1 -0
  135. package/lib/components/Slider/Slider.jsx +573 -0
  136. package/lib/components/Slider/Slider.namespace.d.ts +2 -0
  137. package/lib/components/Slider/Slider.namespace.d.ts.map +1 -0
  138. package/lib/components/Slider/Slider.namespace.js +19 -0
  139. package/lib/components/Slider/Slider.utils.d.ts +19 -0
  140. package/lib/components/Slider/Slider.utils.d.ts.map +1 -0
  141. package/lib/components/Slider/Slider.utils.js +106 -0
  142. package/lib/components/Slider/index.d.ts +3 -0
  143. package/lib/components/Slider/index.d.ts.map +1 -0
  144. package/lib/components/Stack/Stack.d.ts +7 -9
  145. package/lib/components/Stack/Stack.d.ts.map +1 -1
  146. package/lib/components/SwipeableMenuSheet/SwipeableMenuSheet.d.ts.map +1 -1
  147. package/lib/components/SwipeableMenuSheet/SwipeableMenuSheet.jsx +22 -8
  148. package/lib/components/Switch/Switch.d.ts.map +1 -1
  149. package/lib/components/Switch/Switch.jsx +5 -3
  150. package/lib/components/Tabs/Tabs.d.ts +34 -2
  151. package/lib/components/Tabs/Tabs.d.ts.map +1 -1
  152. package/lib/components/Tabs/Tabs.jsx +186 -56
  153. package/lib/components/Tabs/Tabs.utils.d.ts +14 -1
  154. package/lib/components/Tabs/Tabs.utils.d.ts.map +1 -1
  155. package/lib/components/Tabs/Tabs.utils.js +24 -3
  156. package/lib/components/Tabs/index.d.ts +1 -1
  157. package/lib/components/Tabs/index.d.ts.map +1 -1
  158. package/lib/components/index.d.ts +14 -0
  159. package/lib/components/index.d.ts.map +1 -1
  160. package/lib/hooks/useControllableState.d.ts +1 -14
  161. package/lib/hooks/useControllableState.d.ts.map +1 -1
  162. package/lib/hooks/usePressTap.d.ts +1 -27
  163. package/lib/hooks/usePressTap.d.ts.map +1 -1
  164. package/lib/hooks/useSafeArea.d.ts +8 -4
  165. package/lib/hooks/useSafeArea.d.ts.map +1 -1
  166. package/lib/hooks/useSafeArea.js +22 -20
  167. package/lib/index.js +29 -6
  168. package/lib/{hooks/useControllableState.js → packages/lynx-react-headless/use-controllable-state/lib/index.js} +2 -3
  169. package/lib/{hooks/usePressTap.js → packages/lynx-react-headless/use-press-tap/lib/index.js} +13 -10
  170. package/lib/types.d.ts +1 -0
  171. package/lib/types.d.ts.map +1 -1
  172. package/lib/utils/styled.d.ts +46 -6
  173. package/lib/utils/styled.d.ts.map +1 -1
  174. package/lib/utils/styled.js +19 -4
  175. package/package.json +9 -5
  176. package/src/components/AlertDialog/AlertDialog.namespace.ts +22 -0
  177. package/src/components/AlertDialog/AlertDialog.test.tsx +226 -0
  178. package/src/components/AlertDialog/AlertDialog.tsx +374 -0
  179. package/src/components/AlertDialog/index.ts +24 -0
  180. package/src/components/AppBar/AppBar.namespace.ts +1 -0
  181. package/src/components/AppBar/AppBar.test.tsx +252 -3
  182. package/src/components/AppBar/AppBar.tsx +108 -25
  183. package/src/components/AppBar/context.ts +3 -1
  184. package/src/components/AppBar/index.ts +1 -0
  185. package/src/components/AppBar/useAppBar.ts +21 -15
  186. package/src/components/AttachmentDisplay/AttachmentDisplay.tsx +886 -0
  187. package/src/components/AttachmentDisplay/index.ts +48 -0
  188. package/src/components/AttachmentInput/AttachmentInput.tsx +1001 -0
  189. package/src/components/AttachmentInput/index.ts +49 -0
  190. package/src/components/BottomSheet/BottomSheet.test.tsx +22 -7
  191. package/src/components/BottomSheet/BottomSheet.tsx +1 -19
  192. package/src/components/Box/Box.test.tsx +104 -0
  193. package/src/components/Box/Box.tsx +9 -6
  194. package/src/components/ChipTabs/ChipTabs.namespace.ts +14 -0
  195. package/src/components/ChipTabs/ChipTabs.tsx +119 -0
  196. package/src/components/ChipTabs/index.ts +16 -0
  197. package/src/components/ContextualFloatingButton/ContextualFloatingButton.test.tsx +63 -0
  198. package/src/components/ContextualFloatingButton/ContextualFloatingButton.tsx +232 -0
  199. package/src/components/ContextualFloatingButton/index.ts +4 -0
  200. package/src/components/Dialog/Dialog.namespace.ts +24 -0
  201. package/src/components/Dialog/Dialog.test.tsx +172 -0
  202. package/src/components/Dialog/Dialog.tsx +373 -0
  203. package/src/components/Dialog/index.ts +26 -0
  204. package/src/components/FloatingActionButton/FloatingActionButton.namespace.ts +8 -0
  205. package/src/components/FloatingActionButton/FloatingActionButton.test.tsx +59 -0
  206. package/src/components/FloatingActionButton/FloatingActionButton.tsx +207 -0
  207. package/src/components/FloatingActionButton/index.ts +10 -0
  208. package/src/components/HelpBubble/HelpBubble.namespace.ts +24 -0
  209. package/src/components/HelpBubble/HelpBubble.tsx +998 -0
  210. package/src/components/HelpBubble/index.ts +26 -0
  211. package/src/components/Icon/Icon.test.tsx +0 -3
  212. package/src/components/Icon/Icon.tsx +11 -1
  213. package/src/components/IdentityPlaceholder/IdentityPlaceholder.namespace.ts +6 -0
  214. package/src/components/IdentityPlaceholder/IdentityPlaceholder.tsx +103 -0
  215. package/src/components/IdentityPlaceholder/assets.d.ts +4 -0
  216. package/src/components/IdentityPlaceholder/identity-placeholder-business.webp +0 -0
  217. package/src/components/IdentityPlaceholder/identity-placeholder-person.webp +0 -0
  218. package/src/components/IdentityPlaceholder/index.ts +8 -0
  219. package/src/components/InputButton/InputButton.test.tsx +128 -1
  220. package/src/components/InputButton/InputButton.tsx +60 -9
  221. package/src/components/List/List.test.tsx +54 -2
  222. package/src/components/List/List.tsx +43 -10
  223. package/src/components/Menu/Menu.tsx +14 -4
  224. package/src/components/Menu/__tests__/Menu.test.tsx +34 -0
  225. package/src/components/PageBanner/PageBanner.test.tsx +110 -3
  226. package/src/components/PageBanner/PageBanner.tsx +83 -19
  227. package/src/components/QuantityPicker/QuantityPicker.namespace.ts +10 -0
  228. package/src/components/QuantityPicker/QuantityPicker.test.tsx +235 -0
  229. package/src/components/QuantityPicker/QuantityPicker.tsx +605 -0
  230. package/src/components/QuantityPicker/index.ts +14 -0
  231. package/src/components/ReactionButton/ReactionButton.feedback.test.tsx +50 -0
  232. package/src/components/ReactionButton/ReactionButton.test.tsx +5 -3
  233. package/src/components/ReactionButton/ReactionButton.tsx +20 -12
  234. package/src/components/ScrollFog/ScrollFog.test.tsx +222 -0
  235. package/src/components/ScrollFog/ScrollFog.tsx +172 -0
  236. package/src/components/ScrollFog/index.ts +1 -0
  237. package/src/components/Select/Select.namespace.ts +34 -0
  238. package/src/components/Select/Select.test.tsx +297 -0
  239. package/src/components/Select/Select.tsx +1483 -0
  240. package/src/components/Select/index.ts +39 -0
  241. package/src/components/Skeleton/Skeleton.test.tsx +64 -0
  242. package/src/components/Skeleton/Skeleton.tsx +45 -0
  243. package/src/components/Skeleton/index.ts +1 -0
  244. package/src/components/Slider/Slider.namespace.ts +26 -0
  245. package/src/components/Slider/Slider.test.tsx +260 -0
  246. package/src/components/Slider/Slider.tsx +897 -0
  247. package/src/components/Slider/Slider.utils.ts +167 -0
  248. package/src/components/Slider/index.ts +30 -0
  249. package/src/components/Stack/Stack.test.tsx +7 -0
  250. package/src/components/Stack/Stack.tsx +11 -10
  251. package/src/components/SwipeableMenuSheet/SwipeableMenuSheet.test.tsx +21 -1
  252. package/src/components/SwipeableMenuSheet/SwipeableMenuSheet.tsx +30 -7
  253. package/src/components/Switch/Switch.tsx +14 -13
  254. package/src/components/Tabs/Tabs.test.tsx +218 -1
  255. package/src/components/Tabs/Tabs.tsx +346 -75
  256. package/src/components/Tabs/Tabs.utils.ts +51 -1
  257. package/src/components/Tabs/index.ts +17 -1
  258. package/src/components/index.ts +14 -0
  259. package/src/hooks/useControllableState.ts +1 -43
  260. package/src/hooks/usePressTap.test.ts +23 -11
  261. package/src/hooks/usePressTap.ts +1 -60
  262. package/src/hooks/useSafeArea.test.ts +13 -1
  263. package/src/hooks/useSafeArea.ts +25 -23
  264. package/src/types.ts +1 -0
  265. package/src/utils/styled.ts +127 -9
@@ -0,0 +1,297 @@
1
+ import "@testing-library/jest-dom";
2
+ import { act, fireEvent, render, waitFor } from "@lynx-js/react/testing-library";
3
+ import * as React from "@lynx-js/react";
4
+ import type * as LynxUiCommon from "@lynx-js/lynx-ui-common";
5
+
6
+ import { afterEach, describe, expect, it, vi } from "vitest";
7
+
8
+ import { Select } from "./index";
9
+
10
+ const geometry = vi.hoisted(() => ({
11
+ computePosition: vi.fn(),
12
+ getRectByRef: vi.fn(),
13
+ }));
14
+
15
+ vi.mock("@lynx-js/lynx-ui-common", async (importOriginal) => {
16
+ const actual = await importOriginal<typeof LynxUiCommon>();
17
+ return { ...actual, getRectByRef: geometry.getRectByRef };
18
+ });
19
+
20
+ vi.mock("../private/Positioning", () => ({
21
+ computePosition: geometry.computePosition,
22
+ }));
23
+
24
+ interface GeometryRect {
25
+ left: number;
26
+ top: number;
27
+ right: number;
28
+ bottom: number;
29
+ width: number;
30
+ height: number;
31
+ }
32
+
33
+ function createRect(left: number, top: number, width: number, height: number): GeometryRect {
34
+ return { left, top, right: left + width, bottom: top + height, width, height };
35
+ }
36
+
37
+ function deferred<T>() {
38
+ let resolve!: (value: T) => void;
39
+ const promise = new Promise<T>((resolvePromise) => {
40
+ resolve = resolvePromise;
41
+ });
42
+ return { promise, resolve };
43
+ }
44
+ function installRootSelectorQuery() {
45
+ const createSelectorQuery = () => ({
46
+ selectRoot: () => ({}),
47
+ });
48
+ vi.stubGlobal("lynx", {
49
+ ...globalThis.lynx,
50
+ createSelectorQuery,
51
+ });
52
+ for (const testGlobal of [
53
+ lynxTestingEnv.backgroundThread.globalThis,
54
+ lynxTestingEnv.mainThread.globalThis,
55
+ ]) {
56
+ testGlobal["lynx"] = {
57
+ ...testGlobal["lynx"],
58
+ createSelectorQuery,
59
+ };
60
+ }
61
+ }
62
+
63
+ function FloatingSelect({
64
+ contentRef,
65
+ styleOrder = "normal",
66
+ }: {
67
+ contentRef?: React.Ref<unknown>;
68
+ styleOrder?: "normal" | "reversed";
69
+ }) {
70
+ const style =
71
+ styleOrder === "normal"
72
+ ? { paddingTop: "4px", paddingBottom: "8px" }
73
+ : { paddingBottom: "8px", paddingTop: "4px" };
74
+ return (
75
+ <Select.Root defaultOpen>
76
+ <Select.Trigger accessibility-label="열기" />
77
+ <Select.Content ref={contentRef} style={style} accessibility-label="floating-content">
78
+ <Select.Group>
79
+ <Select.Item value="apple" label="사과">
80
+ <Select.ItemLabel />
81
+ </Select.Item>
82
+ </Select.Group>
83
+ </Select.Content>
84
+ </Select.Root>
85
+ );
86
+ }
87
+
88
+ function getFloatingContent() {
89
+ const content = getRenderedRoot().querySelector<HTMLElement>(
90
+ '[accessibility-label="floating-content"]',
91
+ );
92
+ if (!content) throw new Error("Expected floating Select content to exist.");
93
+ return content;
94
+ }
95
+
96
+ function getRenderedRoot() {
97
+ const root = elementTree.root;
98
+
99
+ if (!root) {
100
+ throw new Error("Expected Lynx render root to exist.");
101
+ }
102
+
103
+ return root;
104
+ }
105
+
106
+ function TestSelect() {
107
+ return (
108
+ <Select.Root defaultValue={["apple"]}>
109
+ <Select.Trigger className="select-trigger" accessibility-label="과일" />
110
+ <Select.Content>
111
+ <Select.Group>
112
+ <Select.Item value="apple" label="사과">
113
+ <Select.ItemLabel />
114
+ </Select.Item>
115
+ <Select.Item value="banana" label="바나나">
116
+ <Select.ItemLabel />
117
+ </Select.Item>
118
+ </Select.Group>
119
+ </Select.Content>
120
+ </Select.Root>
121
+ );
122
+ }
123
+
124
+ describe("Select", () => {
125
+ afterEach(() => {
126
+ geometry.computePosition.mockReset();
127
+ geometry.getRectByRef.mockReset();
128
+ vi.unstubAllGlobals();
129
+ });
130
+
131
+ it("shows the initially selected label and updates it after a single selection", () => {
132
+ render(<TestSelect />);
133
+
134
+ const root = getRenderedRoot();
135
+ const trigger = root.querySelector<HTMLElement>(".select-trigger");
136
+ const banana = Array.from(root.querySelectorAll<HTMLElement>("[accessibility-label]")).find(
137
+ (element) => element.getAttribute("accessibility-label") === "바나나",
138
+ );
139
+
140
+ expect(trigger).not.toBeNull();
141
+ expect(banana).toBeDefined();
142
+ expect(trigger).toHaveTextContent("사과");
143
+
144
+ fireEvent.tap(trigger as HTMLElement);
145
+ expect(trigger).toHaveAttribute("accessibility-value", "expanded");
146
+
147
+ fireEvent.tap(banana as HTMLElement);
148
+ expect(trigger).toHaveTextContent("바나나");
149
+ expect(trigger).toHaveAttribute("accessibility-value", "collapsed");
150
+ });
151
+
152
+ it("preserves controlled open state after splitting recipe variants", () => {
153
+ const onOpenChange = vi.fn();
154
+ const { rerender } = render(
155
+ <Select.Root open onOpenChange={onOpenChange}>
156
+ <Select.Trigger className="controlled-trigger" accessibility-label="과일" />
157
+ </Select.Root>,
158
+ );
159
+ const trigger = getRenderedRoot().querySelector<HTMLElement>(".controlled-trigger");
160
+ if (!trigger) throw new Error("Expected the controlled Select trigger.");
161
+
162
+ expect(trigger).toHaveAttribute("accessibility-value", "expanded");
163
+ fireEvent.tap(trigger);
164
+ expect(onOpenChange).toHaveBeenCalledWith(
165
+ false,
166
+ expect.objectContaining({ reason: "trigger" }),
167
+ );
168
+ expect(trigger).toHaveAttribute("accessibility-value", "expanded");
169
+
170
+ rerender(
171
+ <Select.Root open={false} onOpenChange={onOpenChange}>
172
+ <Select.Trigger className="controlled-trigger" accessibility-label="과일" />
173
+ </Select.Root>,
174
+ );
175
+ expect(trigger).toHaveAttribute("accessibility-value", "collapsed");
176
+ });
177
+
178
+ it("positions content after a public ref patch resolves deferred geometry", async () => {
179
+ installRootSelectorQuery();
180
+ const pendingGeometry = deferred<GeometryRect>();
181
+ const intrinsic = createRect(0, 0, 240, 480);
182
+
183
+ geometry.getRectByRef.mockImplementation(
184
+ (_ref: unknown, _relative: boolean, scrollAreaId?: string) =>
185
+ scrollAreaId ? Promise.resolve(intrinsic) : pendingGeometry.promise,
186
+ );
187
+ geometry.computePosition.mockImplementation(async ({ width }: { width: number }) => ({
188
+ availableWidth: width,
189
+ availableHeight: 480,
190
+ left: 144,
191
+ top: 284,
192
+ width,
193
+ height: 320,
194
+ placement: "bottom",
195
+ transformOrigin: "50% 0%",
196
+ }));
197
+
198
+ const initialRef = vi.fn();
199
+ const patchedRef = vi.fn();
200
+ const { rerender } = render(<FloatingSelect contentRef={initialRef} />);
201
+
202
+ await waitFor(() => {
203
+ expect(
204
+ geometry.getRectByRef.mock.calls.some(([, , scrollAreaId]) => scrollAreaId === undefined),
205
+ ).toBe(true);
206
+ });
207
+
208
+ rerender(<FloatingSelect contentRef={patchedRef} />);
209
+ await act(async () => {
210
+ pendingGeometry.resolve(createRect(72, 112, 240, 52));
211
+ await pendingGeometry.promise;
212
+ });
213
+
214
+ await waitFor(() => {
215
+ expect(getFloatingContent()).toHaveStyle({
216
+ left: "72px",
217
+ top: "172px",
218
+ width: "240px",
219
+ });
220
+ });
221
+ });
222
+
223
+ it("keeps positioned content visible when an equivalent inline style is recreated", async () => {
224
+ installRootSelectorQuery();
225
+ const intrinsic = createRect(0, 0, 240, 480);
226
+ const repeatedMeasurement = deferred<GeometryRect>();
227
+ let blockIntrinsicMeasurement = false;
228
+
229
+ geometry.getRectByRef.mockImplementation(
230
+ (_ref: unknown, _relative: boolean, scrollAreaId?: string) =>
231
+ scrollAreaId && blockIntrinsicMeasurement
232
+ ? repeatedMeasurement.promise
233
+ : Promise.resolve(scrollAreaId ? intrinsic : createRect(72, 112, 240, 52)),
234
+ );
235
+ geometry.computePosition.mockImplementation(async ({ width }: { width: number }) => ({
236
+ availableWidth: width,
237
+ availableHeight: 480,
238
+ left: 144,
239
+ top: 284,
240
+ width,
241
+ height: 320,
242
+ placement: "bottom",
243
+ transformOrigin: "50% 0%",
244
+ }));
245
+
246
+ const { rerender } = render(<FloatingSelect />);
247
+ await waitFor(() => {
248
+ expect(getFloatingContent()).toHaveStyle({
249
+ left: "72px",
250
+ top: "172px",
251
+ width: "240px",
252
+ });
253
+ });
254
+ blockIntrinsicMeasurement = true;
255
+
256
+ rerender(<FloatingSelect styleOrder="reversed" />);
257
+ await act(async () => {
258
+ await Promise.resolve();
259
+ });
260
+
261
+ expect(getFloatingContent()).toHaveStyle({
262
+ left: "72px",
263
+ top: "172px",
264
+ width: "240px",
265
+ });
266
+ });
267
+
268
+ it("remeasures and positions after width clamping without another layout change", async () => {
269
+ installRootSelectorQuery();
270
+ const intrinsic = createRect(0, 0, 240, 480);
271
+
272
+ geometry.getRectByRef.mockImplementation(
273
+ (_ref: unknown, _relative: boolean, scrollAreaId?: string) =>
274
+ Promise.resolve(scrollAreaId ? intrinsic : createRect(72, 112, 240, 52)),
275
+ );
276
+ geometry.computePosition.mockImplementation(async ({ width }: { width: number }) => ({
277
+ availableWidth: width === 240 ? 180 : width,
278
+ availableHeight: 480,
279
+ left: 144,
280
+ top: 284,
281
+ width,
282
+ height: 320,
283
+ placement: "bottom",
284
+ transformOrigin: "50% 0%",
285
+ }));
286
+
287
+ render(<FloatingSelect />);
288
+
289
+ await waitFor(() => {
290
+ expect(getFloatingContent()).toHaveStyle({
291
+ left: "72px",
292
+ top: "172px",
293
+ width: "180px",
294
+ });
295
+ });
296
+ });
297
+ });