@factorialco/f0-react-native 0.19.1

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 (310) hide show
  1. package/README.md +54 -0
  2. package/package.json +84 -0
  3. package/src/components/Activity/ActivityItem/__snapshots__/index.spec.tsx.snap +62 -0
  4. package/src/components/Activity/ActivityItem/index.spec.tsx +103 -0
  5. package/src/components/Activity/ActivityItem/index.tsx +90 -0
  6. package/src/components/Avatars/Avatar.tsx +56 -0
  7. package/src/components/Avatars/BaseAvatar/index.tsx +126 -0
  8. package/src/components/Avatars/BaseAvatar/utils.ts +73 -0
  9. package/src/components/Avatars/CompanyAvatar/__snapshots__/index.spec.tsx.snap +25 -0
  10. package/src/components/Avatars/CompanyAvatar/index.spec.tsx +11 -0
  11. package/src/components/Avatars/CompanyAvatar/index.tsx +36 -0
  12. package/src/components/Avatars/DateAvatar/__snapshots__/index.spec.tsx.snap +18 -0
  13. package/src/components/Avatars/DateAvatar/index.spec.tsx +12 -0
  14. package/src/components/Avatars/DateAvatar/index.tsx +22 -0
  15. package/src/components/Avatars/EmojiAvatar/__snapshots__/index.spec.tsx.snap +37 -0
  16. package/src/components/Avatars/EmojiAvatar/index.spec.tsx +15 -0
  17. package/src/components/Avatars/EmojiAvatar/index.tsx +37 -0
  18. package/src/components/Avatars/FileAvatar/__snapshots__/index.spec.tsx.snap +261 -0
  19. package/src/components/Avatars/FileAvatar/index.spec.tsx +43 -0
  20. package/src/components/Avatars/FileAvatar/index.tsx +117 -0
  21. package/src/components/Avatars/FileAvatar/utils.ts +103 -0
  22. package/src/components/Avatars/IconAvatar/__snapshots__/index.spec.tsx.snap +256 -0
  23. package/src/components/Avatars/IconAvatar/index.spec.tsx +16 -0
  24. package/src/components/Avatars/IconAvatar/index.tsx +31 -0
  25. package/src/components/Avatars/ModuleAvatar/index.tsx +106 -0
  26. package/src/components/Avatars/ModuleAvatar/modules.ts +64 -0
  27. package/src/components/Avatars/PersonAvatar/__snapshots__/index.spec.tsx.snap +25 -0
  28. package/src/components/Avatars/PersonAvatar/index.spec.tsx +13 -0
  29. package/src/components/Avatars/PersonAvatar/index.tsx +38 -0
  30. package/src/components/Avatars/TeamAvatar/__snapshots__/index.spec.tsx.snap +25 -0
  31. package/src/components/Avatars/TeamAvatar/index.spec.tsx +11 -0
  32. package/src/components/Avatars/TeamAvatar/index.tsx +36 -0
  33. package/src/components/Avatars/exports.ts +9 -0
  34. package/src/components/Avatars/types.ts +15 -0
  35. package/src/components/Badge/index.tsx +47 -0
  36. package/src/components/Button/__snapshots__/index.spec.tsx.snap +517 -0
  37. package/src/components/Button/index.spec.tsx +124 -0
  38. package/src/components/Button/index.tsx +233 -0
  39. package/src/components/Counter/__snapshots__/index.spec.tsx.snap +113 -0
  40. package/src/components/Counter/index.spec.tsx +53 -0
  41. package/src/components/Counter/index.tsx +59 -0
  42. package/src/components/ExampleComponent.tsx +22 -0
  43. package/src/components/Icon/README.md +63 -0
  44. package/src/components/Icon/__tests__/Icon.spec.tsx +35 -0
  45. package/src/components/Icon/index.tsx +85 -0
  46. package/src/components/Navigation/PageHeader/__snapshots__/index.spec.tsx.snap +242 -0
  47. package/src/components/Navigation/PageHeader/index.spec.tsx +198 -0
  48. package/src/components/Navigation/PageHeader/index.tsx +53 -0
  49. package/src/components/OneChip/__snapshots__/index.spec.tsx.snap +366 -0
  50. package/src/components/OneChip/index.spec.tsx +69 -0
  51. package/src/components/OneChip/index.tsx +89 -0
  52. package/src/components/OnePreset/__snapshots__/index.spec.tsx.snap +149 -0
  53. package/src/components/OnePreset/index.spec.tsx +46 -0
  54. package/src/components/OnePreset/index.tsx +36 -0
  55. package/src/components/Tags/AlertTab/__snapshots__/index.spec.tsx.snap +292 -0
  56. package/src/components/Tags/AlertTab/index.spec.tsx +18 -0
  57. package/src/components/Tags/AlertTab/index.tsx +61 -0
  58. package/src/components/Tags/BaseTag/index.tsx +64 -0
  59. package/src/components/Tags/DotTag/__snapshots__/index.spec.tsx.snap +1081 -0
  60. package/src/components/Tags/DotTag/index.spec.tsx +14 -0
  61. package/src/components/Tags/DotTag/index.tsx +54 -0
  62. package/src/components/Tags/RawTag/__snapshots__/index.spec.tsx.snap +213 -0
  63. package/src/components/Tags/RawTag/index.spec.tsx +19 -0
  64. package/src/components/Tags/RawTag/index.tsx +41 -0
  65. package/src/components/Tags/Tag.tsx +32 -0
  66. package/src/components/Tags/exports.ts +3 -0
  67. package/src/components/__tests__/ExampleComponent.spec.tsx +16 -0
  68. package/src/components/experimental/Lists/DataList/ItemContainer.tsx +74 -0
  69. package/src/components/experimental/Lists/DataList/actions/CopyAction.tsx +72 -0
  70. package/src/components/experimental/Lists/DataList/actions/GenericAction.tsx +37 -0
  71. package/src/components/experimental/Lists/DataList/index.tsx +186 -0
  72. package/src/components/experimental/Lists/DetailsItem/__snapshots__/index.spec.tsx.snap +759 -0
  73. package/src/components/experimental/Lists/DetailsItem/index.spec.tsx +81 -0
  74. package/src/components/experimental/Lists/DetailsItem/index.tsx +68 -0
  75. package/src/components/experimental/Lists/DetailsItemsList/__snapshots__/index.spec.tsx.snap +671 -0
  76. package/src/components/experimental/Lists/DetailsItemsList/index.spec.tsx +73 -0
  77. package/src/components/experimental/Lists/DetailsItemsList/index.tsx +52 -0
  78. package/src/icons/app/AcademicCap.tsx +31 -0
  79. package/src/icons/app/Add.tsx +21 -0
  80. package/src/icons/app/Ai.tsx +24 -0
  81. package/src/icons/app/Alert.tsx +16 -0
  82. package/src/icons/app/AlertCircle.tsx +19 -0
  83. package/src/icons/app/AlertCircleLine.tsx +17 -0
  84. package/src/icons/app/AlignTextCenter.tsx +21 -0
  85. package/src/icons/app/AlignTextJustify.tsx +21 -0
  86. package/src/icons/app/AlignTextLeft.tsx +21 -0
  87. package/src/icons/app/AlignTextRight.tsx +21 -0
  88. package/src/icons/app/Appearance.tsx +33 -0
  89. package/src/icons/app/Archive.tsx +21 -0
  90. package/src/icons/app/ArchiveOpen.tsx +21 -0
  91. package/src/icons/app/ArrowCycle.tsx +27 -0
  92. package/src/icons/app/ArrowDown.tsx +27 -0
  93. package/src/icons/app/ArrowLeft.tsx +21 -0
  94. package/src/icons/app/ArrowRight.tsx +21 -0
  95. package/src/icons/app/ArrowUp.tsx +21 -0
  96. package/src/icons/app/Bank.tsx +20 -0
  97. package/src/icons/app/BarGraph.tsx +21 -0
  98. package/src/icons/app/Bell.tsx +24 -0
  99. package/src/icons/app/Bold.tsx +23 -0
  100. package/src/icons/app/BookOpen.tsx +21 -0
  101. package/src/icons/app/Briefcase.tsx +18 -0
  102. package/src/icons/app/Bucket.tsx +23 -0
  103. package/src/icons/app/Building.tsx +25 -0
  104. package/src/icons/app/Bullet.tsx +19 -0
  105. package/src/icons/app/Calculator.tsx +17 -0
  106. package/src/icons/app/Calendar.tsx +21 -0
  107. package/src/icons/app/CalendarArrowDown.tsx +21 -0
  108. package/src/icons/app/CalendarArrowLeft.tsx +21 -0
  109. package/src/icons/app/CalendarArrowRight.tsx +21 -0
  110. package/src/icons/app/CameraPlus.tsx +22 -0
  111. package/src/icons/app/ChartLine.tsx +26 -0
  112. package/src/icons/app/ChartPie.tsx +25 -0
  113. package/src/icons/app/Check.tsx +21 -0
  114. package/src/icons/app/CheckCircle.tsx +21 -0
  115. package/src/icons/app/CheckCircleLine.tsx +22 -0
  116. package/src/icons/app/CheckDouble.tsx +21 -0
  117. package/src/icons/app/ChevronDown.tsx +21 -0
  118. package/src/icons/app/ChevronLeft.tsx +21 -0
  119. package/src/icons/app/ChevronRight.tsx +21 -0
  120. package/src/icons/app/ChevronUp.tsx +21 -0
  121. package/src/icons/app/Circle.tsx +16 -0
  122. package/src/icons/app/Clock.tsx +22 -0
  123. package/src/icons/app/Code.tsx +21 -0
  124. package/src/icons/app/Coffee.tsx +24 -0
  125. package/src/icons/app/Comment.tsx +19 -0
  126. package/src/icons/app/Completed.tsx +21 -0
  127. package/src/icons/app/CreditCard.tsx +21 -0
  128. package/src/icons/app/Cross.tsx +21 -0
  129. package/src/icons/app/CrossedCircle.tsx +21 -0
  130. package/src/icons/app/Crown.tsx +21 -0
  131. package/src/icons/app/Delete.tsx +21 -0
  132. package/src/icons/app/Deny.tsx +21 -0
  133. package/src/icons/app/Desktop.tsx +26 -0
  134. package/src/icons/app/DollarBill.tsx +22 -0
  135. package/src/icons/app/DottedCircle.tsx +16 -0
  136. package/src/icons/app/Download.tsx +21 -0
  137. package/src/icons/app/DropdownDefault.tsx +25 -0
  138. package/src/icons/app/DropdownOpen.tsx +25 -0
  139. package/src/icons/app/Ellipsis.tsx +36 -0
  140. package/src/icons/app/EllipsisHorizontal.tsx +18 -0
  141. package/src/icons/app/Envelope.tsx +21 -0
  142. package/src/icons/app/EnvelopeOpen.tsx +23 -0
  143. package/src/icons/app/Exit.tsx +21 -0
  144. package/src/icons/app/ExternalLink.tsx +21 -0
  145. package/src/icons/app/EyeInvisible.tsx +26 -0
  146. package/src/icons/app/EyeVisible.tsx +21 -0
  147. package/src/icons/app/FaceNegative.tsx +21 -0
  148. package/src/icons/app/FaceNeutral.tsx +21 -0
  149. package/src/icons/app/FacePositive.tsx +21 -0
  150. package/src/icons/app/FaceSuperNegative.tsx +21 -0
  151. package/src/icons/app/FaceSuperPositive.tsx +21 -0
  152. package/src/icons/app/Feed.tsx +21 -0
  153. package/src/icons/app/File.tsx +21 -0
  154. package/src/icons/app/FileFilled.tsx +21 -0
  155. package/src/icons/app/Filter.tsx +19 -0
  156. package/src/icons/app/Flag.tsx +21 -0
  157. package/src/icons/app/Folder.tsx +19 -0
  158. package/src/icons/app/Folders.tsx +24 -0
  159. package/src/icons/app/Globe.tsx +26 -0
  160. package/src/icons/app/Graph.tsx +26 -0
  161. package/src/icons/app/Handshake.tsx +29 -0
  162. package/src/icons/app/Heading1.tsx +21 -0
  163. package/src/icons/app/Heading2.tsx +21 -0
  164. package/src/icons/app/Heading3.tsx +21 -0
  165. package/src/icons/app/Heart.tsx +21 -0
  166. package/src/icons/app/HoldHeart.tsx +21 -0
  167. package/src/icons/app/Home.tsx +20 -0
  168. package/src/icons/app/Hub.tsx +51 -0
  169. package/src/icons/app/Image.tsx +21 -0
  170. package/src/icons/app/InProgressTask.tsx +17 -0
  171. package/src/icons/app/Inbox.tsx +26 -0
  172. package/src/icons/app/Info.tsx +21 -0
  173. package/src/icons/app/InfoCircle.tsx +21 -0
  174. package/src/icons/app/InfoCircleLine.tsx +28 -0
  175. package/src/icons/app/Italic.tsx +21 -0
  176. package/src/icons/app/Kanban.tsx +21 -0
  177. package/src/icons/app/Laptop.tsx +20 -0
  178. package/src/icons/app/LayersFront.tsx +26 -0
  179. package/src/icons/app/Lightbulb.tsx +26 -0
  180. package/src/icons/app/Link.tsx +25 -0
  181. package/src/icons/app/LinkRemove.tsx +20 -0
  182. package/src/icons/app/List.tsx +25 -0
  183. package/src/icons/app/LockLocked.tsx +21 -0
  184. package/src/icons/app/LockUnlocked.tsx +21 -0
  185. package/src/icons/app/LogoAvatar.tsx +23 -0
  186. package/src/icons/app/LogoEruditai.tsx +19 -0
  187. package/src/icons/app/LogoTravelperk.tsx +27 -0
  188. package/src/icons/app/Masonry.tsx +19 -0
  189. package/src/icons/app/Maximize.tsx +21 -0
  190. package/src/icons/app/Megaphone.tsx +21 -0
  191. package/src/icons/app/Menu.tsx +21 -0
  192. package/src/icons/app/MessageFrown.tsx +25 -0
  193. package/src/icons/app/MessageHeart.tsx +25 -0
  194. package/src/icons/app/Messages.tsx +21 -0
  195. package/src/icons/app/Microphone.tsx +26 -0
  196. package/src/icons/app/MicrophoneNegative.tsx +26 -0
  197. package/src/icons/app/Minimize.tsx +21 -0
  198. package/src/icons/app/Minus.tsx +21 -0
  199. package/src/icons/app/Mobile.tsx +21 -0
  200. package/src/icons/app/Money.tsx +26 -0
  201. package/src/icons/app/MoneyBag.tsx +24 -0
  202. package/src/icons/app/MoveDown.tsx +44 -0
  203. package/src/icons/app/MoveTop.tsx +34 -0
  204. package/src/icons/app/MoveUp.tsx +42 -0
  205. package/src/icons/app/Office.tsx +31 -0
  206. package/src/icons/app/OlList.tsx +21 -0
  207. package/src/icons/app/PalmTree.tsx +20 -0
  208. package/src/icons/app/Paperclip.tsx +20 -0
  209. package/src/icons/app/PartiallyCompleted.tsx +21 -0
  210. package/src/icons/app/PauseCircle.tsx +21 -0
  211. package/src/icons/app/Pencil.tsx +21 -0
  212. package/src/icons/app/People.tsx +27 -0
  213. package/src/icons/app/Person.tsx +22 -0
  214. package/src/icons/app/Phone.tsx +20 -0
  215. package/src/icons/app/Pin.tsx +22 -0
  216. package/src/icons/app/PixBrazil.tsx +19 -0
  217. package/src/icons/app/Placeholder.tsx +21 -0
  218. package/src/icons/app/Plane.tsx +21 -0
  219. package/src/icons/app/Plus.tsx +21 -0
  220. package/src/icons/app/Present.tsx +31 -0
  221. package/src/icons/app/Printer.tsx +26 -0
  222. package/src/icons/app/Proyector.tsx +22 -0
  223. package/src/icons/app/Question.tsx +22 -0
  224. package/src/icons/app/Quote.tsx +21 -0
  225. package/src/icons/app/Reaction.tsx +32 -0
  226. package/src/icons/app/Receipt.tsx +20 -0
  227. package/src/icons/app/Record.tsx +17 -0
  228. package/src/icons/app/RemoveFavorite.tsx +21 -0
  229. package/src/icons/app/Replace.tsx +21 -0
  230. package/src/icons/app/Reset.tsx +27 -0
  231. package/src/icons/app/Rocket.tsx +26 -0
  232. package/src/icons/app/Salad.tsx +21 -0
  233. package/src/icons/app/Save.tsx +27 -0
  234. package/src/icons/app/Schedule.tsx +32 -0
  235. package/src/icons/app/Search.tsx +17 -0
  236. package/src/icons/app/SearchPerson.tsx +24 -0
  237. package/src/icons/app/Settings.tsx +20 -0
  238. package/src/icons/app/Share.tsx +27 -0
  239. package/src/icons/app/Sliders.tsx +22 -0
  240. package/src/icons/app/SolidPause.tsx +19 -0
  241. package/src/icons/app/SolidPlay.tsx +19 -0
  242. package/src/icons/app/SolidStop.tsx +19 -0
  243. package/src/icons/app/Sort.tsx +21 -0
  244. package/src/icons/app/Sparkles.tsx +25 -0
  245. package/src/icons/app/Spinner.tsx +20 -0
  246. package/src/icons/app/Split.tsx +21 -0
  247. package/src/icons/app/Star.tsx +21 -0
  248. package/src/icons/app/StarFilled.tsx +19 -0
  249. package/src/icons/app/Strikethrough.tsx +21 -0
  250. package/src/icons/app/Suitcase.tsx +22 -0
  251. package/src/icons/app/Table.tsx +21 -0
  252. package/src/icons/app/Target.tsx +31 -0
  253. package/src/icons/app/TextSize.tsx +21 -0
  254. package/src/icons/app/Timer.tsx +27 -0
  255. package/src/icons/app/Underline.tsx +26 -0
  256. package/src/icons/app/Upload.tsx +21 -0
  257. package/src/icons/app/Video.tsx +23 -0
  258. package/src/icons/app/VideoRecorder.tsx +31 -0
  259. package/src/icons/app/VideoRecorderNegative.tsx +22 -0
  260. package/src/icons/app/Wallet.tsx +26 -0
  261. package/src/icons/app/Warning.tsx +21 -0
  262. package/src/icons/app/WhatsappChat.tsx +23 -0
  263. package/src/icons/app/Windows.tsx +21 -0
  264. package/src/icons/app/index.ts +186 -0
  265. package/src/icons/index.ts +5 -0
  266. package/src/icons/modules/Benefits.tsx +25 -0
  267. package/src/icons/modules/Calendar.tsx +21 -0
  268. package/src/icons/modules/Cards.tsx +25 -0
  269. package/src/icons/modules/ClockIn.tsx +21 -0
  270. package/src/icons/modules/Discover.tsx +25 -0
  271. package/src/icons/modules/Documents.tsx +19 -0
  272. package/src/icons/modules/Engagement.tsx +21 -0
  273. package/src/icons/modules/Finance.tsx +21 -0
  274. package/src/icons/modules/Goals.tsx +19 -0
  275. package/src/icons/modules/Home.tsx +19 -0
  276. package/src/icons/modules/Hub.tsx +20 -0
  277. package/src/icons/modules/Inbox.tsx +19 -0
  278. package/src/icons/modules/Kudos.tsx +19 -0
  279. package/src/icons/modules/MyDocuments.tsx +21 -0
  280. package/src/icons/modules/Organization.tsx +19 -0
  281. package/src/icons/modules/Overviews.tsx +23 -0
  282. package/src/icons/modules/Payroll.tsx +25 -0
  283. package/src/icons/modules/Performance.tsx +19 -0
  284. package/src/icons/modules/Profile.tsx +19 -0
  285. package/src/icons/modules/Projects.tsx +21 -0
  286. package/src/icons/modules/Recruitment.tsx +21 -0
  287. package/src/icons/modules/Reports.tsx +21 -0
  288. package/src/icons/modules/Sales.tsx +27 -0
  289. package/src/icons/modules/Settings.tsx +21 -0
  290. package/src/icons/modules/Shifts.tsx +27 -0
  291. package/src/icons/modules/Social.tsx +21 -0
  292. package/src/icons/modules/Software.tsx +19 -0
  293. package/src/icons/modules/Spaces.tsx +21 -0
  294. package/src/icons/modules/Spending.tsx +21 -0
  295. package/src/icons/modules/Tasks.tsx +21 -0
  296. package/src/icons/modules/TimeOff.tsx +21 -0
  297. package/src/icons/modules/TimeTracking.tsx +25 -0
  298. package/src/icons/modules/Trainings.tsx +19 -0
  299. package/src/icons/modules/Treasury.tsx +19 -0
  300. package/src/icons/modules/Workflows.tsx +19 -0
  301. package/src/icons/modules/index.ts +35 -0
  302. package/src/icons/types.ts +9 -0
  303. package/src/index.ts +18 -0
  304. package/src/lib/date.ts +102 -0
  305. package/src/lib/emojis.tsx +47 -0
  306. package/src/lib/iconWithClassName.ts +17 -0
  307. package/src/lib/text.ts +31 -0
  308. package/src/lib/utils.ts +6 -0
  309. package/src/ui/avatar.tsx +113 -0
  310. package/tailwind.config.ts +19 -0
@@ -0,0 +1,124 @@
1
+ import { render, fireEvent, screen } from "@testing-library/react-native";
2
+ import React from "react";
3
+ import { Button } from "./";
4
+ import { IconType } from "../Icon";
5
+
6
+ // Mock the Icon component
7
+ jest.mock("../Icon", () => ({
8
+ Icon: () => null,
9
+ }));
10
+
11
+ // Mock dependencies
12
+ const mockIcon: IconType = "check" as unknown as IconType;
13
+ const mockOnPress = jest.fn();
14
+
15
+ describe("Button", () => {
16
+ const defaultProps = {
17
+ label: "Test Button",
18
+ onPress: mockOnPress,
19
+ };
20
+
21
+ beforeEach(() => {
22
+ jest.clearAllMocks();
23
+ });
24
+
25
+ it("Snapshot - default button", () => {
26
+ const { toJSON } = render(<Button {...defaultProps} />);
27
+
28
+ expect(toJSON()).toMatchSnapshot();
29
+ });
30
+
31
+ it("Snapshot - outline variant", () => {
32
+ const { toJSON } = render(<Button {...defaultProps} variant="outline" />);
33
+
34
+ expect(toJSON()).toMatchSnapshot();
35
+ });
36
+
37
+ it("Snapshot - critical variant", () => {
38
+ const { toJSON } = render(<Button {...defaultProps} variant="critical" />);
39
+
40
+ expect(toJSON()).toMatchSnapshot();
41
+ });
42
+
43
+ it("Snapshot - with icon", () => {
44
+ const { toJSON } = render(<Button {...defaultProps} icon={mockIcon} />);
45
+
46
+ expect(toJSON()).toMatchSnapshot();
47
+ });
48
+
49
+ it("Snapshot - with emoji", () => {
50
+ const { toJSON } = render(<Button {...defaultProps} emoji="👋" />);
51
+
52
+ expect(toJSON()).toMatchSnapshot();
53
+ });
54
+
55
+ it("Snapshot - disabled state", () => {
56
+ const { toJSON } = render(<Button {...defaultProps} disabled />);
57
+
58
+ expect(toJSON()).toMatchSnapshot();
59
+ });
60
+
61
+ it("Snapshot - loading state", () => {
62
+ const { toJSON } = render(<Button {...defaultProps} loading />);
63
+
64
+ expect(toJSON()).toMatchSnapshot();
65
+ });
66
+
67
+ it("Snapshot - different sizes", () => {
68
+ const sizes = ["sm", "md", "lg"] as const;
69
+
70
+ sizes.forEach((size) => {
71
+ const { toJSON } = render(<Button {...defaultProps} size={size} />);
72
+ expect(toJSON()).toMatchSnapshot();
73
+ });
74
+ });
75
+
76
+ it("Snapshot - round button with hidden label", () => {
77
+ const { toJSON } = render(<Button {...defaultProps} round hideLabel />);
78
+
79
+ expect(toJSON()).toMatchSnapshot();
80
+ });
81
+
82
+ // Regular component tests
83
+ it("renders correctly with required props", () => {
84
+ render(<Button {...defaultProps} />);
85
+
86
+ const button = screen.getByText("Test Button");
87
+
88
+ expect(button).toBeDefined();
89
+ });
90
+
91
+ it("handles press events", () => {
92
+ render(<Button {...defaultProps} />);
93
+
94
+ fireEvent.press(screen.getByRole("button"));
95
+
96
+ expect(mockOnPress).toHaveBeenCalled();
97
+ });
98
+
99
+ it("does not call onPress when disabled", () => {
100
+ render(<Button {...defaultProps} disabled />);
101
+
102
+ fireEvent.press(screen.getByRole("button"));
103
+
104
+ expect(mockOnPress).not.toHaveBeenCalled();
105
+ });
106
+
107
+ it("shows correct accessibility label when disabled", () => {
108
+ render(<Button {...defaultProps} disabled />);
109
+
110
+ const button = screen.getByRole("button");
111
+
112
+ expect(button.props.accessibilityLabel).toBe("Test Button, disabled");
113
+ });
114
+
115
+ it("shows correct accessibility label when loading", () => {
116
+ render(<Button {...defaultProps} loading />);
117
+
118
+ const button = screen.getByRole("button");
119
+
120
+ expect(button.props.accessibilityLabel).toBe(
121
+ "Test Button, disabled, loading",
122
+ );
123
+ });
124
+ });
@@ -0,0 +1,233 @@
1
+ import { cva, type VariantProps } from "cva";
2
+ import React, { forwardRef, useState } from "react";
3
+ import { Pressable, Text, View } from "react-native";
4
+ import { cn } from "../../lib/utils";
5
+ import { Icon, type IconType } from "../Icon";
6
+
7
+ export const variants = [
8
+ "default",
9
+ "outline",
10
+ "critical",
11
+ "neutral",
12
+ "ghost",
13
+ "promote",
14
+ ] as const;
15
+ export type ButtonVariant = (typeof variants)[number];
16
+
17
+ export const sizes = ["sm", "md", "lg"] as const;
18
+ export type ButtonSize = (typeof sizes)[number];
19
+
20
+ const buttonVariants = cva({
21
+ base: "flex-row items-center justify-center rounded border-none grow-0",
22
+ variants: {
23
+ variant: {
24
+ default: "bg-f1-background-accent-bold",
25
+ outline: "bg-f1-background-inverse-secondary border border-f1-border",
26
+ neutral: "bg-f1-background-secondary",
27
+ critical: "bg-f1-background-secondary border border-f1-border",
28
+ ghost: "bg-transparent",
29
+ promote: "bg-f1-background-promote border border-f1-border-promote",
30
+ },
31
+ size: {
32
+ sm: "h-6 rounded-sm",
33
+ md: "h-8 rounded",
34
+ lg: "h-10 rounded-md",
35
+ },
36
+ disabled: {
37
+ true: "opacity-50",
38
+ false: "",
39
+ },
40
+ round: {
41
+ true: "aspect-square p-0",
42
+ false: "gap-1 px-2 sm:px-3 lg:px-4",
43
+ },
44
+ },
45
+ defaultVariants: {
46
+ variant: "default",
47
+ size: "md",
48
+ disabled: false,
49
+ round: false,
50
+ },
51
+ });
52
+
53
+ const pressedVariants = cva({
54
+ base: "",
55
+ variants: {
56
+ variant: {
57
+ default: "bg-f1-background-accent-bold-hover",
58
+ outline: "bg-f1-background-tertiary border-opacity-70",
59
+ neutral: "bg-f1-background-secondary-hover",
60
+ critical: "bg-f1-background-critical-bold border-transparent",
61
+ ghost: "bg-f1-background-secondary-hover",
62
+ promote: "bg-f1-background-promote-hover",
63
+ },
64
+ },
65
+ defaultVariants: {
66
+ variant: "default",
67
+ },
68
+ });
69
+
70
+ const getIconColor = (variant: ButtonVariant, isPressed: boolean) => {
71
+ switch (variant) {
72
+ case "default":
73
+ return "text-f1-icon-inverse";
74
+ case "critical":
75
+ return isPressed ? "text-f1-icon-inverse" : "text-f1-icon-critical-bold";
76
+ default:
77
+ return "text-f1-icon";
78
+ }
79
+ };
80
+
81
+ const getIconOnlyColor = (variant: ButtonVariant, isPressed: boolean) => {
82
+ switch (variant) {
83
+ case "critical":
84
+ return isPressed ? "text-f1-icon-inverse" : "text-f1-icon-critical-bold";
85
+ case "default":
86
+ return "text-f1-icon-inverse";
87
+ case "outline":
88
+ case "neutral":
89
+ case "ghost":
90
+ case "promote":
91
+ default:
92
+ return "text-f1-icon-bold";
93
+ }
94
+ };
95
+
96
+ const getTextColorClass = (variant: ButtonVariant, isPressed: boolean) => {
97
+ if (isPressed && variant === "critical") {
98
+ return "text-f1-foreground-inverse";
99
+ }
100
+
101
+ switch (variant) {
102
+ case "default":
103
+ return "text-f1-foreground-inverse";
104
+ case "critical":
105
+ return "text-f1-foreground-critical";
106
+ default:
107
+ return "text-f1-foreground";
108
+ }
109
+ };
110
+
111
+ export interface ButtonProps extends VariantProps<typeof buttonVariants> {
112
+ label: string;
113
+ onPress?: () => void | Promise<unknown>;
114
+ disabled?: boolean;
115
+ loading?: boolean;
116
+ icon?: IconType;
117
+ emoji?: string;
118
+ hideLabel?: boolean;
119
+ className?: string;
120
+ accessibilityHint?: string;
121
+ showBadge?: boolean;
122
+ fullWidth?: boolean;
123
+ }
124
+
125
+ export const Button = forwardRef<View, ButtonProps>(function Button(
126
+ {
127
+ label,
128
+ onPress,
129
+ disabled = false,
130
+ loading = false,
131
+ icon,
132
+ emoji,
133
+ hideLabel = false,
134
+ variant = "default",
135
+ size = "md",
136
+ round = false,
137
+ className,
138
+ accessibilityHint,
139
+ showBadge = false,
140
+ fullWidth = false,
141
+ },
142
+ ref,
143
+ ) {
144
+ const [isLoading, setIsLoading] = useState(false);
145
+ const [isPressed, setIsPressed] = useState(false);
146
+
147
+ const handlePress = async () => {
148
+ if (!onPress || disabled || loading || isLoading) return;
149
+
150
+ const result = onPress();
151
+
152
+ if (result instanceof Promise) {
153
+ setIsLoading(true);
154
+ try {
155
+ await result;
156
+ } finally {
157
+ setIsLoading(false);
158
+ }
159
+ }
160
+ };
161
+
162
+ const isDisabled = disabled || loading || isLoading;
163
+ const accessibilityLabel = `${label}${isDisabled ? ", disabled" : ""}${loading || isLoading ? ", loading" : ""}`;
164
+ const shouldShowPressed = isPressed && !isDisabled;
165
+
166
+ return (
167
+ <View className={`flex ${fullWidth ? "flex-1" : "item-start"}`}>
168
+ <Pressable
169
+ ref={ref}
170
+ disabled={isDisabled}
171
+ onPressIn={() => setIsPressed(true)}
172
+ onPressOut={() => setIsPressed(false)}
173
+ onPress={handlePress}
174
+ className={cn(
175
+ buttonVariants({
176
+ variant,
177
+ size,
178
+ disabled: isDisabled,
179
+ round: hideLabel && round,
180
+ }),
181
+ shouldShowPressed && pressedVariants({ variant }),
182
+ className,
183
+ )}
184
+ accessibilityLabel={accessibilityLabel}
185
+ accessibilityRole="button"
186
+ accessibilityState={{
187
+ disabled: isDisabled,
188
+ busy: loading || isLoading,
189
+ }}
190
+ accessibilityHint={accessibilityHint}
191
+ >
192
+ {icon && (
193
+ <Icon
194
+ icon={icon}
195
+ size={size === "sm" ? "sm" : "md"}
196
+ className={cn(
197
+ hideLabel && round ? undefined : "-ml-0.5",
198
+ hideLabel && round
199
+ ? getIconOnlyColor(variant, shouldShowPressed)
200
+ : getIconColor(variant, shouldShowPressed),
201
+ )}
202
+ />
203
+ )}
204
+ {emoji && (
205
+ <Text
206
+ className={cn(
207
+ "text-base font-medium",
208
+ getTextColorClass(variant, shouldShowPressed),
209
+ )}
210
+ >
211
+ {emoji}
212
+ </Text>
213
+ )}
214
+ {!hideLabel && (
215
+ <Text
216
+ className={cn(
217
+ "text-base font-medium",
218
+ getTextColorClass(variant, shouldShowPressed),
219
+ )}
220
+ >
221
+ {label}
222
+ </Text>
223
+ )}
224
+ </Pressable>
225
+ {showBadge && variant === "outline" && (
226
+ <View
227
+ accessibilityLabel="Notification Badge"
228
+ className="absolute right-1.5 top-1.5 h-1.5 w-1.5 rounded-full bg-f1-icon-accent"
229
+ />
230
+ )}
231
+ </View>
232
+ );
233
+ });
@@ -0,0 +1,113 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Counter Snapshot 1`] = `
4
+ <View
5
+ className="flex items-start"
6
+ >
7
+ <View
8
+ className="flex items-center justify-center rounded-xs grow-0 px-0.5 min-w-5 h-5 bg-f1-background-secondary border border-f1-border"
9
+ >
10
+ <Text
11
+ className="text-center text-sm font-medium tabular-nums whitespace-nowrap text-f1-foreground"
12
+ >
13
+ 42
14
+ </Text>
15
+ </View>
16
+ </View>
17
+ `;
18
+
19
+ exports[`Counter Snapshot vairan bold types 1`] = `
20
+ <View
21
+ className="flex items-start"
22
+ >
23
+ <View
24
+ className="flex items-center justify-center rounded-xs grow-0 px-0.5 min-w-5 h-5 bg-f1-background-accent-bold"
25
+ >
26
+ <Text
27
+ className="text-center text-sm font-medium tabular-nums whitespace-nowrap text-f1-foreground-inverse"
28
+ >
29
+ 42
30
+ </Text>
31
+ </View>
32
+ </View>
33
+ `;
34
+
35
+ exports[`Counter Snapshot vairan defauly types 1`] = `
36
+ <View
37
+ className="flex items-start"
38
+ >
39
+ <View
40
+ className="flex items-center justify-center rounded-xs grow-0 px-0.5 min-w-5 h-5 bg-f1-background-secondary border border-f1-border"
41
+ >
42
+ <Text
43
+ className="text-center text-sm font-medium tabular-nums whitespace-nowrap text-f1-foreground"
44
+ >
45
+ 42
46
+ </Text>
47
+ </View>
48
+ </View>
49
+ `;
50
+
51
+ exports[`Counter Snapshot vairan defauly types 2`] = `
52
+ <View
53
+ className="flex items-start"
54
+ >
55
+ <View
56
+ className="flex items-center justify-center rounded-xs grow-0 px-0.5 min-w-5 h-5 bg-f1-background-secondary border border-f1-border"
57
+ >
58
+ <Text
59
+ className="text-center text-sm font-medium tabular-nums whitespace-nowrap text-f1-foreground"
60
+ >
61
+ 42
62
+ </Text>
63
+ </View>
64
+ </View>
65
+ `;
66
+
67
+ exports[`Counter Snapshot vairan md size 1`] = `
68
+ <View
69
+ className="flex items-start"
70
+ >
71
+ <View
72
+ className="flex items-center justify-center rounded-xs grow-0 px-0.5 min-w-5 h-5 bg-f1-background-secondary border border-f1-border"
73
+ >
74
+ <Text
75
+ className="text-center text-sm font-medium tabular-nums whitespace-nowrap text-f1-foreground"
76
+ >
77
+ 42
78
+ </Text>
79
+ </View>
80
+ </View>
81
+ `;
82
+
83
+ exports[`Counter Snapshot vairan selected types 1`] = `
84
+ <View
85
+ className="flex items-start"
86
+ >
87
+ <View
88
+ className="flex items-center justify-center rounded-xs grow-0 px-0.5 min-w-5 h-5 bg-f1-background-selected-bold outline-f1-border-selected"
89
+ >
90
+ <Text
91
+ className="text-center text-sm font-medium tabular-nums whitespace-nowrap text-f1-foreground-inverse"
92
+ >
93
+ 42
94
+ </Text>
95
+ </View>
96
+ </View>
97
+ `;
98
+
99
+ exports[`Counter Snapshot vairan sm size 1`] = `
100
+ <View
101
+ className="flex items-start"
102
+ >
103
+ <View
104
+ className="flex items-center justify-center rounded-xs grow-0 px-0.5 min-w-4 h-4 bg-f1-background-secondary border border-f1-border"
105
+ >
106
+ <Text
107
+ className="text-center text-sm font-medium tabular-nums whitespace-nowrap leading-none py-0.5 text-f1-foreground"
108
+ >
109
+ 42
110
+ </Text>
111
+ </View>
112
+ </View>
113
+ `;
@@ -0,0 +1,53 @@
1
+ import { render, screen } from "@testing-library/react-native";
2
+ import React from "react";
3
+ import { Counter } from ".";
4
+
5
+ describe("Counter", () => {
6
+ const defaultProps = {
7
+ value: 42,
8
+ types: "default",
9
+ };
10
+
11
+ it("Snapshot", () => {
12
+ const { toJSON } = render(<Counter {...defaultProps} />);
13
+ expect(toJSON()).toMatchSnapshot();
14
+ });
15
+
16
+ it("Snapshot vairan defauly types", () => {
17
+ const { toJSON } = render(<Counter {...defaultProps} />);
18
+ expect(toJSON()).toMatchSnapshot();
19
+ });
20
+
21
+ it("Snapshot vairan defauly types", () => {
22
+ const { toJSON } = render(<Counter {...defaultProps} type="default" />);
23
+ expect(toJSON()).toMatchSnapshot();
24
+ });
25
+
26
+ it("Snapshot vairan bold types", () => {
27
+ const { toJSON } = render(<Counter {...defaultProps} type="bold" />);
28
+ expect(toJSON()).toMatchSnapshot();
29
+ });
30
+
31
+ it("Snapshot vairan selected types", () => {
32
+ const { toJSON } = render(<Counter {...defaultProps} type="selected" />);
33
+ expect(toJSON()).toMatchSnapshot();
34
+ });
35
+
36
+ it("Snapshot vairan md size", () => {
37
+ const { toJSON } = render(<Counter {...defaultProps} size="md" />);
38
+ expect(toJSON()).toMatchSnapshot();
39
+ });
40
+
41
+ it("Snapshot vairan sm size", () => {
42
+ const { toJSON } = render(<Counter {...defaultProps} size="sm" />);
43
+ expect(toJSON()).toMatchSnapshot();
44
+ });
45
+
46
+ it("renders correctly with required props", () => {
47
+ render(<Counter {...defaultProps} />);
48
+
49
+ const number = screen.getByText("42");
50
+
51
+ expect(number).toBeDefined();
52
+ });
53
+ });
@@ -0,0 +1,59 @@
1
+ import { cva, type VariantProps } from "cva";
2
+ import { Text, View } from "react-native";
3
+ import { cn } from "../../lib/utils";
4
+
5
+ const counterContainerVariants = cva({
6
+ base: "flex items-center justify-center rounded-xs grow-0 px-0.5",
7
+ variants: {
8
+ size: {
9
+ md: "min-w-5 h-5",
10
+ sm: "min-w-4 h-4",
11
+ },
12
+ type: {
13
+ default: "bg-f1-background-secondary border border-f1-border",
14
+ selected: "bg-f1-background-selected-bold outline-f1-border-selected",
15
+ bold: "bg-f1-background-accent-bold",
16
+ },
17
+ },
18
+ defaultVariants: {
19
+ size: "md",
20
+ type: "default",
21
+ },
22
+ });
23
+
24
+ const counterTextVariants = cva({
25
+ base: "text-center text-sm font-medium tabular-nums whitespace-nowrap",
26
+ variants: {
27
+ size: {
28
+ md: "",
29
+ sm: "leading-none py-0.5",
30
+ },
31
+ type: {
32
+ default: "text-f1-foreground",
33
+ selected: "text-f1-foreground-inverse",
34
+ bold: "text-f1-foreground-inverse",
35
+ },
36
+ },
37
+ defaultVariants: {
38
+ type: "default",
39
+ },
40
+ });
41
+
42
+ type CounterProps = {
43
+ value: number;
44
+ maxValue?: number;
45
+ } & VariantProps<typeof counterContainerVariants>;
46
+
47
+ export function Counter({ size, type, value, maxValue }: CounterProps) {
48
+ const displayValue = maxValue && value > maxValue ? `+${maxValue}` : value;
49
+
50
+ return (
51
+ <View className="flex items-start">
52
+ <View className={cn(counterContainerVariants({ size, type }))}>
53
+ <Text className={cn(counterTextVariants({ type, size }))}>
54
+ {displayValue}
55
+ </Text>
56
+ </View>
57
+ </View>
58
+ );
59
+ }
@@ -0,0 +1,22 @@
1
+ import React from "react";
2
+ import { Text, View } from "react-native";
3
+
4
+ export interface ExampleComponentProps {
5
+ /**
6
+ * Optional custom text to display
7
+ */
8
+ text?: string;
9
+ }
10
+
11
+ /**
12
+ * An example component that demonstrates system-based dark/light mode functionality
13
+ */
14
+ export const ExampleComponent: React.FC<ExampleComponentProps> = ({
15
+ text = "Hello World",
16
+ }) => {
17
+ return (
18
+ <View className="rounded-lg bg-f1-background p-4">
19
+ <Text className="text-base font-medium text-f1-foreground">{text}</Text>
20
+ </View>
21
+ );
22
+ };
@@ -0,0 +1,63 @@
1
+ # Icon Component
2
+
3
+ The Icon component is used to render SVG icons from the Factorial One Design System in React Native applications.
4
+
5
+ ## Import Pattern
6
+
7
+ ```tsx
8
+ // Import the Icon component
9
+ import { Icon } from "@factorialco/f0-react-native";
10
+
11
+ // Import specific icons from app or modules directories
12
+ import { AppIcons, ModuleIcons } from "@factorialco/f0-react-native";
13
+
14
+ // Use the Icon component with the icon as a prop
15
+ <Icon icon={AppIcons.Archive} size="md" />
16
+ <Icon icon={ModuleIcons.Home} size="lg" />
17
+ ```
18
+
19
+ ## Props
20
+
21
+ | Prop | Type | Default | Description |
22
+ | --------- | ---------------------------- | ------- | --------------------------------------- |
23
+ | icon | IconType | | The icon component to render |
24
+ | size | "xs" \| "sm" \| "md" \| "lg" | "md" | The size of the icon |
25
+ | className | string | | Additional classes for styling the icon |
26
+ | testID | string | | Test ID for testing |
27
+ | ...props | SvgProps | | Additional props for the SVG component |
28
+
29
+ ## Styling with NativeWind
30
+
31
+ The Icon component is designed to work with NativeWind, allowing you to style icons using Tailwind CSS classes:
32
+
33
+ ```tsx
34
+ // Style using Tailwind CSS classes
35
+ <Icon icon={AppIcons.Archive} className="text-f1-icon-secondary" />
36
+ ```
37
+
38
+ ## Available Icons
39
+
40
+ The library includes two sets of icons:
41
+
42
+ 1. **App Icons** - General purpose icons used throughout the application
43
+ 2. **Module Icons** - Icons representing specific modules in the Factorial application
44
+
45
+ ## Examples
46
+
47
+ ```tsx
48
+ import { Icon } from "@factorialco/f0-react-native";
49
+ import { AppIcons, ModuleIcons } from "@factorialco/f0-react-native";
50
+
51
+ // Basic usage
52
+ <Icon icon={AppIcons.Calendar} />
53
+
54
+ // With size variant
55
+ <Icon icon={AppIcons.ChevronDown} size="xs" />
56
+ <Icon icon={AppIcons.Check} size="sm" />
57
+ <Icon icon={ModuleIcons.Home} size="md" />
58
+ <Icon icon={ModuleIcons.Settings} size="lg" />
59
+
60
+ // With color styling
61
+ <Icon icon={AppIcons.Heart} className="text-red-500" />
62
+ <Icon icon={AppIcons.InfoCircle} className="text-blue-500" />
63
+ ```
@@ -0,0 +1,35 @@
1
+ import { render } from "@testing-library/react-native";
2
+ import React from "react";
3
+ import { Icon } from "../index";
4
+ import { Archive } from "../../../icons/app";
5
+ import { Home } from "../../../icons/modules";
6
+
7
+ describe("Icon", () => {
8
+ it("renders correctly with an app icon", () => {
9
+ const { getByTestId } = render(<Icon icon={Archive} testID="icon" />);
10
+ expect(getByTestId("icon")).toBeTruthy();
11
+ });
12
+
13
+ it("renders correctly with a module icon", () => {
14
+ const { getByTestId } = render(<Icon icon={Home} testID="icon" />);
15
+ expect(getByTestId("icon")).toBeTruthy();
16
+ });
17
+
18
+ it("applies the correct size class", () => {
19
+ const { getByTestId } = render(
20
+ <Icon icon={Archive} size="lg" testID="icon" />,
21
+ );
22
+ // In a real test environment, we could check the actual style props applied
23
+ // but for now we just ensure it renders
24
+ expect(getByTestId("icon")).toBeTruthy();
25
+ });
26
+
27
+ it("applies custom className correctly", () => {
28
+ const { getByTestId } = render(
29
+ <Icon icon={Archive} className="text-red-500" testID="icon" />,
30
+ );
31
+ // In a real test environment, we could check the actual style props applied
32
+ // but for now we just ensure it renders
33
+ expect(getByTestId("icon")).toBeTruthy();
34
+ });
35
+ });