@torpor/ui 0.1.7 → 0.2.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 (276) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/Accordion/Accordion.torp +2 -2
  3. package/dist/Accordion/AccordionContent.torp +2 -2
  4. package/dist/Accordion/AccordionHeader.torp +2 -2
  5. package/dist/Accordion/AccordionItem.torp +2 -2
  6. package/dist/Accordion/AccordionTrigger.torp +2 -2
  7. package/dist/Accordion/index.d.ts +92 -0
  8. package/dist/Breadcrumb/Breadcrumb.torp +33 -0
  9. package/dist/Breadcrumb/BreadcrumbItem.torp +19 -0
  10. package/dist/Breadcrumb/BreadcrumbSeparator.torp +16 -0
  11. package/dist/Breadcrumb/index.d.ts +56 -0
  12. package/dist/Breadcrumb/index.js +14 -0
  13. package/dist/Calendar/Calendar.torp +265 -0
  14. package/dist/Calendar/CalendarDay.torp +67 -0
  15. package/dist/Calendar/CalendarTypes.ts +22 -0
  16. package/dist/Calendar/index.d.ts +38 -0
  17. package/dist/Calendar/index.js +10 -0
  18. package/dist/Calendar/index.js.map +1 -0
  19. package/dist/Dialog/Dialog.torp +3 -3
  20. package/dist/Dialog/DialogBody.torp +2 -2
  21. package/dist/Dialog/DialogButton.torp +2 -2
  22. package/dist/Dialog/DialogFooter.torp +2 -2
  23. package/dist/Dialog/DialogHeader.torp +2 -2
  24. package/dist/Dialog/index.d.ts +92 -0
  25. package/dist/Disclosure/Disclosure.torp +69 -0
  26. package/dist/Disclosure/DisclosureContent.torp +36 -0
  27. package/dist/Disclosure/DisclosureHeader.torp +34 -0
  28. package/dist/Disclosure/DisclosureTrigger.torp +36 -0
  29. package/dist/Disclosure/DisclosureTypes.ts +14 -0
  30. package/dist/Disclosure/index.d.ts +74 -0
  31. package/dist/Disclosure/index.js +18 -0
  32. package/dist/Disclosure/index.js.map +1 -0
  33. package/{src/Offscreen/Offscreen.torp → dist/Drawer/Drawer.torp} +10 -19
  34. package/dist/Drawer/index.d.ts +20 -0
  35. package/dist/Drawer/index.js +6 -0
  36. package/dist/Drawer/index.js.map +1 -0
  37. package/dist/Menu/Menu.torp +160 -0
  38. package/dist/Menu/MenuButton.torp +53 -0
  39. package/dist/Menu/MenuCheck.torp +70 -0
  40. package/dist/Menu/MenuGroup.torp +19 -0
  41. package/dist/Menu/MenuIndicator.torp +29 -0
  42. package/dist/Menu/MenuPopout.torp +71 -0
  43. package/dist/Menu/MenuPopoutContent.torp +74 -0
  44. package/dist/Menu/MenuPopoutTrigger.torp +117 -0
  45. package/dist/Menu/MenuRadio.torp +88 -0
  46. package/dist/Menu/MenuRadioGroup.torp +86 -0
  47. package/dist/Menu/MenuSeparator.torp +14 -0
  48. package/dist/Menu/MenuTypes.ts +58 -0
  49. package/dist/Menu/index.d.ts +200 -0
  50. package/dist/Menu/index.js +46 -0
  51. package/dist/Menu/index.js.map +1 -0
  52. package/dist/MenuBar/MenuBar.torp +150 -0
  53. package/dist/MenuBar/MenuBarItem.torp +104 -0
  54. package/dist/MenuBar/MenuBarItemContent.torp +108 -0
  55. package/dist/MenuBar/MenuBarItemTrigger.torp +89 -0
  56. package/dist/MenuBar/MenuBarTypes.ts +36 -0
  57. package/dist/MenuBar/index.d.ts +74 -0
  58. package/dist/MenuBar/index.js +18 -0
  59. package/dist/MenuBar/index.js.map +1 -0
  60. package/dist/NavMenu/NavMenu.torp +2 -2
  61. package/dist/NavMenu/NavMenuButton.torp +2 -2
  62. package/dist/NavMenu/NavMenuElement.torp +2 -2
  63. package/dist/NavMenu/NavMenuGroup.torp +2 -2
  64. package/dist/NavMenu/NavMenuGroupHeader.torp +2 -2
  65. package/dist/NavMenu/NavMenuLink.torp +2 -2
  66. package/dist/NavMenu/NavMenuPopout.torp +2 -2
  67. package/dist/NavMenu/NavMenuPopoutButton.torp +3 -3
  68. package/dist/NavMenu/NavMenuPopoutContent.torp +29 -27
  69. package/dist/NavMenu/NavMenuSeparator.torp +2 -2
  70. package/dist/NavMenu/index.d.ts +182 -0
  71. package/dist/Notification/Notification.torp +48 -0
  72. package/dist/Notification/NotificationClose.torp +28 -0
  73. package/dist/Notification/NotificationContainer.torp +14 -0
  74. package/dist/Notification/NotificationContent.torp +13 -0
  75. package/dist/Notification/NotificationTypes.ts +5 -0
  76. package/dist/Notification/index.d.ts +74 -0
  77. package/dist/Notification/index.js +36 -0
  78. package/dist/Notification/index.js.map +1 -0
  79. package/dist/Notification/showNotification.ts +30 -0
  80. package/dist/Popover/Popover.torp +2 -2
  81. package/dist/Popover/PopoverContent.torp +31 -28
  82. package/dist/Popover/PopoverContentAnchored.torp +39 -38
  83. package/dist/Popover/PopoverContentContextual.torp +39 -36
  84. package/dist/Popover/PopoverOverlay.torp +3 -3
  85. package/dist/Popover/PopoverTrigger.torp +2 -2
  86. package/dist/Popover/PopoverTriggerContextual.torp +2 -2
  87. package/dist/Popover/PopoverTriggerHover.torp +3 -3
  88. package/dist/Popover/index.d.ts +146 -0
  89. package/dist/TabGroup/TabContent.torp +2 -2
  90. package/dist/TabGroup/TabGroup.torp +2 -2
  91. package/dist/TabGroup/TabHeader.torp +2 -2
  92. package/dist/TabGroup/TabList.torp +2 -2
  93. package/dist/TabGroup/TabTrigger.torp +2 -2
  94. package/dist/TabGroup/index.d.ts +92 -0
  95. package/dist/icons/ArrowDown.torp +15 -0
  96. package/dist/icons/ArrowLeft.torp +15 -0
  97. package/dist/icons/ArrowRight.torp +15 -0
  98. package/dist/icons/ArrowUp.torp +15 -0
  99. package/dist/icons/Bars.torp +17 -0
  100. package/dist/icons/Check.torp +15 -0
  101. package/dist/icons/ChevronDown.torp +15 -0
  102. package/dist/icons/ChevronLeft.torp +15 -0
  103. package/dist/icons/ChevronRight.torp +15 -0
  104. package/dist/icons/ChevronUp.torp +15 -0
  105. package/dist/icons/Cross.torp +16 -0
  106. package/dist/icons/Loading.torp +16 -0
  107. package/dist/icons/Minus.torp +15 -0
  108. package/dist/icons/Plus.torp +16 -0
  109. package/dist/icons/index.d.ts +254 -0
  110. package/dist/icons/index.js +58 -0
  111. package/dist/icons/index.js.map +1 -0
  112. package/dist/{motion.d.cts → index.d.ts} +2 -2
  113. package/dist/metafile-esm.json +1 -1
  114. package/dist/motion/fade.ts +7 -0
  115. package/dist/motion/index.d.ts +2 -0
  116. package/dist/{motion.js → motion/index.js} +1 -1
  117. package/dist/motion/index.js.map +1 -0
  118. package/dist/mount/index.d.ts +2 -0
  119. package/dist/utils/dateUtils.ts +244 -0
  120. package/dist/utils/index.d.ts +2 -0
  121. package/dist/utils/setPopoverPosition.ts +4 -0
  122. package/package.json +50 -34
  123. package/scripts/postbuild.ts +42 -5
  124. package/src/Accordion/Accordion.torp +2 -2
  125. package/src/Accordion/AccordionContent.torp +2 -2
  126. package/src/Accordion/AccordionHeader.torp +2 -2
  127. package/src/Accordion/AccordionItem.torp +2 -2
  128. package/src/Accordion/AccordionTrigger.torp +2 -2
  129. package/src/Breadcrumb/Breadcrumb.torp +33 -0
  130. package/src/Breadcrumb/BreadcrumbItem.torp +19 -0
  131. package/src/Breadcrumb/BreadcrumbSeparator.torp +16 -0
  132. package/src/Breadcrumb/index.ts +5 -0
  133. package/src/Calendar/Calendar.torp +265 -0
  134. package/src/Calendar/CalendarDay.torp +67 -0
  135. package/src/Calendar/CalendarTypes.ts +22 -0
  136. package/src/Calendar/index.ts +4 -0
  137. package/src/Dialog/Dialog.torp +3 -3
  138. package/src/Dialog/DialogBody.torp +2 -2
  139. package/src/Dialog/DialogButton.torp +2 -2
  140. package/src/Dialog/DialogFooter.torp +2 -2
  141. package/src/Dialog/DialogHeader.torp +2 -2
  142. package/src/Disclosure/Disclosure.torp +69 -0
  143. package/src/Disclosure/DisclosureContent.torp +36 -0
  144. package/src/Disclosure/DisclosureHeader.torp +34 -0
  145. package/src/Disclosure/DisclosureTrigger.torp +36 -0
  146. package/src/Disclosure/DisclosureTypes.ts +14 -0
  147. package/src/Disclosure/index.ts +6 -0
  148. package/{dist/Offscreen/Offscreen.torp → src/Drawer/Drawer.torp} +10 -19
  149. package/src/Drawer/index.ts +3 -0
  150. package/src/Menu/Menu.torp +160 -0
  151. package/src/Menu/MenuButton.torp +53 -0
  152. package/src/Menu/MenuCheck.torp +70 -0
  153. package/src/Menu/MenuGroup.torp +19 -0
  154. package/src/Menu/MenuIndicator.torp +29 -0
  155. package/src/Menu/MenuPopout.torp +71 -0
  156. package/src/Menu/MenuPopoutContent.torp +74 -0
  157. package/src/Menu/MenuPopoutTrigger.torp +117 -0
  158. package/src/Menu/MenuRadio.torp +88 -0
  159. package/src/Menu/MenuRadioGroup.torp +86 -0
  160. package/src/Menu/MenuSeparator.torp +14 -0
  161. package/src/Menu/MenuTypes.ts +58 -0
  162. package/src/Menu/index.ts +25 -0
  163. package/src/MenuBar/MenuBar.torp +150 -0
  164. package/src/MenuBar/MenuBarItem.torp +104 -0
  165. package/src/MenuBar/MenuBarItemContent.torp +108 -0
  166. package/src/MenuBar/MenuBarItemTrigger.torp +89 -0
  167. package/src/MenuBar/MenuBarTypes.ts +36 -0
  168. package/src/MenuBar/index.ts +6 -0
  169. package/src/NavMenu/NavMenu.torp +2 -2
  170. package/src/NavMenu/NavMenuButton.torp +2 -2
  171. package/src/NavMenu/NavMenuElement.torp +2 -2
  172. package/src/NavMenu/NavMenuGroup.torp +2 -2
  173. package/src/NavMenu/NavMenuGroupHeader.torp +2 -2
  174. package/src/NavMenu/NavMenuLink.torp +2 -2
  175. package/src/NavMenu/NavMenuPopout.torp +2 -2
  176. package/src/NavMenu/NavMenuPopoutButton.torp +3 -3
  177. package/src/NavMenu/NavMenuPopoutContent.torp +29 -27
  178. package/src/NavMenu/NavMenuSeparator.torp +2 -2
  179. package/src/Notification/Notification.torp +48 -0
  180. package/src/Notification/NotificationClose.torp +28 -0
  181. package/src/Notification/NotificationContainer.torp +14 -0
  182. package/src/Notification/NotificationContent.torp +13 -0
  183. package/src/Notification/NotificationTypes.ts +5 -0
  184. package/src/Notification/index.ts +13 -0
  185. package/src/Notification/showNotification.ts +30 -0
  186. package/src/Popover/Popover.torp +2 -2
  187. package/src/Popover/PopoverContent.torp +31 -28
  188. package/src/Popover/PopoverContentAnchored.torp +39 -38
  189. package/src/Popover/PopoverContentContextual.torp +39 -36
  190. package/src/Popover/PopoverOverlay.torp +3 -3
  191. package/src/Popover/PopoverTrigger.torp +2 -2
  192. package/src/Popover/PopoverTriggerContextual.torp +2 -2
  193. package/src/Popover/PopoverTriggerHover.torp +3 -3
  194. package/src/Slider/Slider.torp +83 -0
  195. package/src/Slider/index.ts +3 -0
  196. package/src/TabGroup/TabContent.torp +2 -2
  197. package/src/TabGroup/TabGroup.torp +2 -2
  198. package/src/TabGroup/TabHeader.torp +2 -2
  199. package/src/TabGroup/TabList.torp +2 -2
  200. package/src/TabGroup/TabTrigger.torp +2 -2
  201. package/src/icons/ArrowDown.torp +15 -0
  202. package/src/icons/ArrowLeft.torp +15 -0
  203. package/src/icons/ArrowRight.torp +15 -0
  204. package/src/icons/ArrowUp.torp +15 -0
  205. package/src/icons/Bars.torp +17 -0
  206. package/src/icons/Check.torp +15 -0
  207. package/src/icons/ChevronDown.torp +15 -0
  208. package/src/icons/ChevronLeft.torp +15 -0
  209. package/src/icons/ChevronRight.torp +15 -0
  210. package/src/icons/ChevronUp.torp +15 -0
  211. package/src/icons/Cross.torp +16 -0
  212. package/src/icons/Loading.torp +16 -0
  213. package/src/icons/Minus.torp +15 -0
  214. package/src/icons/Plus.torp +16 -0
  215. package/src/icons/index.ts +31 -0
  216. package/src/motion/fade.ts +2 -2
  217. package/src/motion/index.ts +3 -0
  218. package/src/utils/dateUtils.ts +244 -0
  219. package/src/utils/setPopoverPosition.ts +4 -0
  220. package/test/Breadcrumb/accessibility.test.ts +29 -0
  221. package/test/Breadcrumb/basic.test.ts +17 -0
  222. package/test/Breadcrumb/components/BreadcrumbTest.torp +15 -0
  223. package/test/Calendar/components/CalendarTest.torp +9 -0
  224. package/test/Calendar/default.test.ts +18 -0
  225. package/test/Dialog/components/DialogAccessibility.torp +5 -5
  226. package/test/Disclosure/accessibility.test.ts +30 -0
  227. package/test/Disclosure/components/DisclosureTest.torp +17 -0
  228. package/test/Disclosure/default.test.ts +15 -0
  229. package/test/Disclosure/toggle.test.ts +19 -0
  230. package/test/Menu/accessibility.test.ts +82 -0
  231. package/test/Menu/basic.test.ts +57 -0
  232. package/test/Menu/components/MenuAccessibility.torp +59 -0
  233. package/test/Menu/components/MenuBasic.torp +58 -0
  234. package/test/MenuBar/accessibility.test.ts +86 -0
  235. package/test/MenuBar/basic.test.ts +21 -0
  236. package/test/MenuBar/components/MenuBarAccessibility.torp +34 -0
  237. package/test/MenuBar/components/MenuBarBasic.torp +25 -0
  238. package/test/Notification/accessibility.test.ts +21 -0
  239. package/test/Notification/components/CustomNotification.torp +14 -0
  240. package/test/Notification/components/NotificationAccessibility.torp +14 -0
  241. package/test/Notification/components/NotificationFunction.torp +20 -0
  242. package/test/Notification/components/NotificationVariable.torp +22 -0
  243. package/test/Notification/function.test.ts +25 -0
  244. package/test/Notification/variable.test.ts +25 -0
  245. package/test/Popover/components/CustomDialog.torp +25 -0
  246. package/test/Popover/components/PromptFunction.torp +19 -0
  247. package/test/Popover/components/PromptVariable.torp +38 -0
  248. package/test/Popover/function.test.ts +26 -0
  249. package/test/Popover/variable.test.ts +27 -0
  250. package/tsup.config.ts +12 -4
  251. package/dist/Accordion/index.cjs +0 -53
  252. package/dist/Accordion/index.cjs.map +0 -1
  253. package/dist/Dialog/index.cjs +0 -53
  254. package/dist/Dialog/index.cjs.map +0 -1
  255. package/dist/NavMenu/index.cjs +0 -78
  256. package/dist/NavMenu/index.cjs.map +0 -1
  257. package/dist/Offscreen/index.cjs +0 -33
  258. package/dist/Offscreen/index.cjs.map +0 -1
  259. package/dist/Offscreen/index.js +0 -6
  260. package/dist/Popover/index.cjs +0 -222
  261. package/dist/Popover/index.cjs.map +0 -1
  262. package/dist/TabGroup/index.cjs +0 -53
  263. package/dist/TabGroup/index.cjs.map +0 -1
  264. package/dist/metafile-cjs.json +0 -1
  265. package/dist/motion.cjs +0 -34
  266. package/dist/motion.cjs.map +0 -1
  267. package/dist/motion.d.ts +0 -6
  268. package/dist/motion.js.map +0 -1
  269. package/src/Accordion.ts +0 -7
  270. package/src/Dialog.ts +0 -7
  271. package/src/NavMenu.ts +0 -23
  272. package/src/Offscreen/index.ts +0 -3
  273. package/src/Popover.ts +0 -29
  274. package/src/TabGroup.ts +0 -7
  275. package/src/motion.ts +0 -3
  276. /package/dist/{Offscreen → Breadcrumb}/index.js.map +0 -0
@@ -0,0 +1,244 @@
1
+ // TODO: Can we get these from the locale?
2
+ const monthNames: string[] = [
3
+ "January",
4
+ "February",
5
+ "March",
6
+ "April",
7
+ "May",
8
+ "June",
9
+ "July",
10
+ "August",
11
+ "September",
12
+ "October",
13
+ "November",
14
+ "December",
15
+ ];
16
+ const shortMonthNames: string[] = monthNames.map((n) => n.substring(0, 3).toLowerCase());
17
+
18
+ const dayNames: string[] = [
19
+ "Sunday",
20
+ "Monday",
21
+ "Tuesday",
22
+ "Wednesday",
23
+ "Thursday",
24
+ "Friday",
25
+ "Saturday",
26
+ "Sunday",
27
+ ];
28
+ const shortDayNames: string[] = dayNames.map((n) => n.substring(0, 3).toLowerCase());
29
+
30
+ export { monthNames, shortMonthNames, dayNames, shortDayNames };
31
+
32
+ export function areDatesEqual(date1: Date, date2: Date): boolean {
33
+ if ((date1 && !date2) || (date2 && !date1)) {
34
+ return false;
35
+ }
36
+ return (
37
+ date1.getFullYear() === date2.getFullYear() &&
38
+ date1.getMonth() === date2.getMonth() &&
39
+ date1.getDate() === date2.getDate()
40
+ );
41
+ }
42
+
43
+ export function parseDateTime(text: string, dateOrder: string): Date {
44
+ const parts = text
45
+ .toString()
46
+ .toLowerCase()
47
+ .split(/[.,/ :-]/);
48
+
49
+ const today = new Date();
50
+ let day = today.getDate();
51
+ let month = today.getMonth();
52
+ let year = today.getFullYear();
53
+ let hour = 9;
54
+ let minute = 0;
55
+ //let ampm = "";
56
+
57
+ let dayPart = -1;
58
+ let monthPart = -1;
59
+ let yearPart = -1;
60
+ //let hourPart = -1;
61
+
62
+ // Try to get the month if it's been supplied as a word
63
+ for (let i = 0; i < parts.length; i++) {
64
+ const index = shortMonthNames.indexOf(parts[i].substring(0, 3));
65
+ if (index !== -1) {
66
+ month = index;
67
+ monthPart = i;
68
+ break;
69
+ }
70
+ }
71
+
72
+ // Get each date part based on the dateOrder supplied
73
+ const dateOrderLower = dateOrder.toLowerCase();
74
+ for (let i = 0; i < dateOrder.length; i++) {
75
+ if (dateOrderLower[i] === "d") {
76
+ const dayResult = getDayPart(parts, monthPart, yearPart);
77
+ if (dayResult) {
78
+ day = dayResult.value;
79
+ dayPart = dayResult.part;
80
+ }
81
+ }
82
+ if (dateOrderLower[i] === "m" && monthPart === -1) {
83
+ const monthResult = getMonthPart(parts, dayPart, yearPart);
84
+ if (monthResult) {
85
+ month = monthResult.value;
86
+ monthPart = monthResult.part;
87
+ }
88
+ }
89
+ if (dateOrderLower[i] === "y") {
90
+ const yearResult = getYearPart(parts, dayPart, monthPart);
91
+ if (yearResult) {
92
+ year = yearResult.value;
93
+ yearPart = yearResult.part;
94
+ }
95
+ }
96
+ }
97
+
98
+ // TODO: Time...
99
+ //if (includeTime) {
100
+ // // Hour part is the first part that isn't a date part
101
+ // for (let i = 0; i < parts.length; i++) {
102
+ // if (i !== yearPart && i !== monthPart && i !== dayPart) {
103
+ // const maybehour = parseInt(parts[i])
104
+ // if (!isNaN(maybehour)) {
105
+ // hour = maybehour
106
+ // hourPart = i
107
+ // break
108
+ // }
109
+ // }
110
+ // }
111
+ //
112
+ // // Minute part is the first part that isn't a date part or the hour part
113
+ // for (let i = 0; i < parts.length; i++) {
114
+ // if (i !== yearPart && i !== monthPart && i !== dayPart && i !== hourPart) {
115
+ // const maybeminute = parseInt(parts[i])
116
+ // if (!isNaN(maybeminute)) {
117
+ // minute = maybeminute
118
+ // break
119
+ // }
120
+ // }
121
+ // }
122
+ //
123
+ // // Look for AM or PM
124
+ // for (let i = 0; i < parts.length; i++) {
125
+ // if (parts[i] === 'am' || parts[i] === 'pm') {
126
+ // ampm = parts[i].toString().toUpperCase()
127
+ // if (ampm === 'PM') {
128
+ // hour = hour + 12
129
+ // }
130
+ // break
131
+ // }
132
+ // }
133
+ //}
134
+
135
+ return new Date(year, month, day, hour, minute);
136
+ }
137
+
138
+ interface DatePart {
139
+ value: number;
140
+ part: number;
141
+ }
142
+
143
+ function getDayPart(parts: string[], monthPart: number, yearPart: number): DatePart | undefined {
144
+ for (let i = 0; i < parts.length; i++) {
145
+ if (i !== monthPart && i !== yearPart) {
146
+ const maybeday = parseInt(parts[i]);
147
+ if (!isNaN(maybeday)) {
148
+ const value = maybeday;
149
+ const part = i;
150
+ return { value, part };
151
+ }
152
+ }
153
+ }
154
+ }
155
+
156
+ function getMonthPart(parts: string[], dayPart: number, yearPart: number): DatePart | undefined {
157
+ for (let i = 0; i < parts.length; i++) {
158
+ if (i !== dayPart && i !== yearPart) {
159
+ const maybemonth = parseInt(parts[i]);
160
+ if (!isNaN(maybemonth)) {
161
+ const value = maybemonth - 1;
162
+ const part = i;
163
+ return { value, part };
164
+ }
165
+ }
166
+ }
167
+ }
168
+
169
+ function getYearPart(parts: string[], dayPart: number, monthPart: number): DatePart | undefined {
170
+ for (let i = 0; i < parts.length; i++) {
171
+ if (i !== monthPart && i !== dayPart) {
172
+ const maybeyear = parseInt(parts[i]);
173
+ if (!isNaN(maybeyear)) {
174
+ const value = maybeyear < 100 ? maybeyear + 2000 : maybeyear;
175
+ const part = i;
176
+ return { value, part };
177
+ }
178
+ }
179
+ }
180
+ }
181
+
182
+ export function formatDate(value: Date, dateFormat: string): string {
183
+ if (!value) {
184
+ return "";
185
+ }
186
+
187
+ const date = new Date(value);
188
+
189
+ // Build the text that will be displayed
190
+ return dateFormat.replace(/(d+)|(M+)|(y+)/g, (m) => {
191
+ if (m.indexOf("d") >= 0) {
192
+ return formatDay(date, m);
193
+ } else if (m.indexOf("M") >= 0) {
194
+ return formatMonth(date, m);
195
+ } else if (m.indexOf("y") >= 0) {
196
+ return formatYear(date, m);
197
+ } else {
198
+ throw new Error("Invalid date format string");
199
+ }
200
+ });
201
+
202
+ // TODO: Time
203
+ //if (includeTime) {
204
+ // const ampm = hour >= 12 ? 'PM' : 'AM'
205
+ // hour = hour % 12
206
+ // hour = hour || 12 // the hour '0' should be '12'
207
+ //
208
+ // minute = minute < 10 ? '0' + minute : minute
209
+ // text = text + ' ' + hour + ':' + minute + ' ' + ampm
210
+ //}
211
+ }
212
+
213
+ function formatDay(date: Date, d: string): string {
214
+ if (d.length >= 4) {
215
+ return dayNames[date.getDay()];
216
+ } else if (d.length === 3) {
217
+ return dayNames[date.getDay()].substring(0, 3);
218
+ } else if (d.length == 2) {
219
+ let text = date.getDate().toString();
220
+ return text.length < 2 ? "0" + text : text;
221
+ } else {
222
+ return date.getDate().toString();
223
+ }
224
+ }
225
+
226
+ function formatMonth(date: Date, m: string): string {
227
+ if (m.length >= 4) {
228
+ return monthNames[date.getMonth()];
229
+ } else if (m.length === 3) {
230
+ return monthNames[date.getMonth()].substring(0, 3);
231
+ } else if (m.length == 2) {
232
+ let text = date.getMonth().toString();
233
+ return text.length < 2 ? "0" + text : text;
234
+ } else {
235
+ return date.getMonth().toString();
236
+ }
237
+ }
238
+
239
+ function formatYear(date: Date, y: string): string {
240
+ return date
241
+ .getFullYear()
242
+ .toString()
243
+ .substring(4 - y.length, y.length);
244
+ }
@@ -42,6 +42,7 @@ export default function setPopoverPosition(
42
42
  break;
43
43
  }
44
44
  default: {
45
+ // eslint-disable-next-line restrict-template-expressions
45
46
  throw new Error(`Invalid MenuPopout side: ${side}`);
46
47
  }
47
48
  }
@@ -68,6 +69,7 @@ export default function setPopoverPosition(
68
69
  break;
69
70
  }
70
71
  default: {
72
+ // eslint-disable-next-line restrict-template-expressions
71
73
  throw new Error(`Invalid MenuPopout side: ${side}`);
72
74
  }
73
75
  }
@@ -94,6 +96,7 @@ function setVerticalAlignment(
94
96
  break;
95
97
  }
96
98
  default: {
99
+ // eslint-disable-next-line restrict-template-expressions
97
100
  throw new Error(`Invalid Popout alignment: ${alignment}`);
98
101
  }
99
102
  }
@@ -126,6 +129,7 @@ function setHorizontalAlignment(
126
129
  break;
127
130
  }
128
131
  default: {
132
+ // eslint-disable-next-line restrict-template-expressions
129
133
  throw new Error(`Invalid Popout alignment: ${alignment}`);
130
134
  }
131
135
  }
@@ -0,0 +1,29 @@
1
+ import { queryByText } from "@testing-library/dom";
2
+ import "@testing-library/jest-dom/vitest";
3
+ import { mount } from "@torpor/view";
4
+ import { describe, expect, it } from "vitest";
5
+ import BreadcrumbTest from "./components/BreadcrumbTest.torp";
6
+
7
+ describe("Breadcrumb", () => {
8
+ it("Accessibility", async () => {
9
+ const container = document.createElement("div");
10
+ document.body.appendChild(container);
11
+ mount(container, BreadcrumbTest);
12
+
13
+ // Tests from https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/
14
+
15
+ // Breadcrumb trail is contained within a navigation landmark region
16
+ expect(
17
+ queryByText(container, "Main page")?.parentElement?.parentElement?.tagName.toLowerCase(),
18
+ ).toMatch("nav");
19
+
20
+ // The landmark region is labelled via aria-label or aria-labelledby
21
+ expect(queryByText(container, "Main page")?.parentElement?.parentElement).toHaveAttribute(
22
+ "aria-label",
23
+ );
24
+
25
+ // The link to the current page has aria-current set to page. If the element representing the
26
+ // current page is not a link, aria-current is optional
27
+ expect(queryByText(container, "Current page")).toHaveAttribute("aria-current", "page");
28
+ });
29
+ });
@@ -0,0 +1,17 @@
1
+ import { queryByText } from "@testing-library/dom";
2
+ import "@testing-library/jest-dom/vitest";
3
+ import { mount } from "@torpor/view";
4
+ import { describe, expect, it } from "vitest";
5
+ import BreadcrumbTest from "./components/BreadcrumbTest.torp";
6
+
7
+ describe("Breadcrumb", () => {
8
+ it("Basic", async () => {
9
+ const container = document.createElement("div");
10
+ document.body.appendChild(container);
11
+ mount(container, BreadcrumbTest);
12
+
13
+ expect(queryByText(container, "Main page")).toBeInTheDocument();
14
+ expect(queryByText(container, "Sub page")).toBeInTheDocument();
15
+ expect(queryByText(container, "Current page")).toBeInTheDocument();
16
+ });
17
+ });
@@ -0,0 +1,15 @@
1
+ import { Breadcrumb, BreadcrumbItem } from "../../../src/Breadcrumb/index";
2
+
3
+ export default function BreadcrumbAccessibility() {
4
+ @render {
5
+ <Breadcrumb ariaLabel="Breadcrumb">
6
+ <BreadcrumbItem>
7
+ <a href={location + "#mainpage"}>Main page</a>
8
+ </BreadcrumbItem>
9
+ <BreadcrumbItem>
10
+ <a href={location + "#subpage"}>Sub page</a>
11
+ </BreadcrumbItem>
12
+ <BreadcrumbItem current>Current page</BreadcrumbItem>
13
+ </Breadcrumb>
14
+ }
15
+ }
@@ -0,0 +1,9 @@
1
+ import {
2
+ Calendar,
3
+ } from "../../../src/Calendar/index";
4
+
5
+ export default function CalendarTest() {
6
+ @render {
7
+ <Calendar {$props.selectable} {$props.value} />
8
+ }
9
+ }
@@ -0,0 +1,18 @@
1
+ import { queryByText } from "@testing-library/dom";
2
+ import "@testing-library/jest-dom/vitest";
3
+ import { mount } from "@torpor/view";
4
+ import { describe, expect, it } from "vitest";
5
+ import CalendarTest from "./components/CalendarTest.torp";
6
+
7
+ describe("Calendar", () => {
8
+ it("Default value", async () => {
9
+ const container = document.createElement("div");
10
+ document.body.appendChild(container);
11
+ mount(container, CalendarTest, { selectable: true, value: new Date(2025, 8, 15) });
12
+
13
+ expect(queryByText(container, "September 2025")).toBeInTheDocument();
14
+
15
+ expect(queryByText(container, "15")).toBeInTheDocument();
16
+ expect(queryByText(container, "15")!.parentElement!.parentElement).toHaveClass("active");
17
+ });
18
+ });
@@ -1,18 +1,18 @@
1
- import { Popover, PopoverContent } from '../../../src/Popover/index';
1
+ import { Popover, PopoverContent } from "../../../src/Popover/index";
2
2
  import {
3
3
  Dialog,
4
4
  DialogHeader,
5
5
  DialogBody,
6
6
  DialogFooter,
7
7
  DialogButton
8
- } from '../../../src/Dialog/index';
8
+ } from "../../../src/Dialog/index";
9
9
 
10
10
  export default function DialogAccessibility($props: any) {
11
11
  $props.modal ??= false;
12
12
  $props.alert ??= false;
13
13
  $props.ariaAnnounce ??= true;
14
14
 
15
- let color = '';
15
+ let color = "";
16
16
 
17
17
  @render {
18
18
  <Popover visible modal={$props.modal}>
@@ -21,8 +21,8 @@ export default function DialogAccessibility($props: any) {
21
21
  <DialogHeader>Color preference</DialogHeader>
22
22
  <DialogBody ariaAnnounce={$props.ariaAnnounce}>Which color do you prefer?</DialogBody>
23
23
  <DialogFooter>
24
- <DialogButton value="blue" onclick={() => (color = 'blue')}>Blue</DialogButton>
25
- <DialogButton value="red" onclick={() => (color = 'red')}>Red</DialogButton>
24
+ <DialogButton value="blue" onclick={() => (color = "blue")}>Blue</DialogButton>
25
+ <DialogButton value="red" onclick={() => (color = "red")}>Red</DialogButton>
26
26
  </DialogFooter>
27
27
  </Dialog>
28
28
  </PopoverContent>
@@ -0,0 +1,30 @@
1
+ import { fireEvent, getByText, queryByText } from "@testing-library/dom";
2
+ import "@testing-library/jest-dom/vitest";
3
+ import { mount } from "@torpor/view";
4
+ import { describe, expect, it } from "vitest";
5
+ import DisclosureTest from "./components/DisclosureTest.torp";
6
+
7
+ describe("Disclosure", () => {
8
+ it("Accessibility", async () => {
9
+ const container = document.createElement("div");
10
+ document.body.appendChild(container);
11
+ mount(container, DisclosureTest);
12
+
13
+ // Tests from https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/
14
+
15
+ // The element that shows and hides the content has role button
16
+ expect(queryByText(container, "Disclosure header")?.tagName.toLowerCase()).toMatch("button");
17
+
18
+ // When the content is hidden, the element with role button has aria-expanded set to false
19
+ expect(queryByText(container, "Disclosure header")).toHaveAttribute("aria-expanded", "false");
20
+
21
+ // When the content area is visible, it is set to true
22
+ fireEvent.click(getByText(container, "Disclosure header"));
23
+ expect(queryByText(container, "Disclosure header")).toHaveAttribute("aria-expanded", "true");
24
+
25
+ // Optionally, the element with role button has a value specified for aria-controls that refers
26
+ // to the element that contains all the content that is shown or hidden
27
+ const contentId = queryByText(container, "Disclosure content")?.id;
28
+ expect(queryByText(container, "Disclosure header")).toHaveAttribute("aria-controls", contentId);
29
+ });
30
+ });
@@ -0,0 +1,17 @@
1
+ import {
2
+ Disclosure,
3
+ DisclosureHeader,
4
+ DisclosureTrigger,
5
+ DisclosureContent
6
+ } from "../../../src/Disclosure/index";
7
+
8
+ export default function DisclosureAccessibility($props: { expanded: boolean }) {
9
+ @render {
10
+ <Disclosure {$props.expanded}>
11
+ <DisclosureHeader as="h4">
12
+ <DisclosureTrigger>Disclosure header</DisclosureTrigger>
13
+ </DisclosureHeader>
14
+ <DisclosureContent>Disclosure content</DisclosureContent>
15
+ </Disclosure>
16
+ }
17
+ }
@@ -0,0 +1,15 @@
1
+ import { queryByText } from "@testing-library/dom";
2
+ import "@testing-library/jest-dom/vitest";
3
+ import { mount } from "@torpor/view";
4
+ import { describe, expect, it } from "vitest";
5
+ import DisclosureTest from "./components/DisclosureTest.torp";
6
+
7
+ describe("Disclosure", () => {
8
+ it("Default value", async () => {
9
+ const container = document.createElement("div");
10
+ document.body.appendChild(container);
11
+ mount(container, DisclosureTest, { expanded: true });
12
+
13
+ expect(queryByText(container, "Disclosure content")).toBeInTheDocument();
14
+ });
15
+ });
@@ -0,0 +1,19 @@
1
+ import { fireEvent, getByText, queryByText } from "@testing-library/dom";
2
+ import "@testing-library/jest-dom/vitest";
3
+ import { mount } from "@torpor/view";
4
+ import { describe, expect, it } from "vitest";
5
+ import DisclosureTest from "./components/DisclosureTest.torp";
6
+
7
+ describe("Disclosure", () => {
8
+ it("Toggle", async () => {
9
+ const container = document.createElement("div");
10
+ document.body.appendChild(container);
11
+ mount(container, DisclosureTest);
12
+
13
+ expect(queryByText(container, "Disclosure content")).not.toBeInTheDocument();
14
+
15
+ // Clicking the header should reveal the body
16
+ fireEvent.click(getByText(container, "Disclosure header"));
17
+ expect(queryByText(container, "Disclosure content")).toBeInTheDocument();
18
+ });
19
+ });
@@ -0,0 +1,82 @@
1
+ import { fireEvent, getByText, queryByText } from "@testing-library/dom";
2
+ import "@testing-library/jest-dom/vitest";
3
+ import { mount } from "@torpor/view";
4
+ import { describe, expect, it } from "vitest";
5
+ import MenuAccessibility from "./components/MenuAccessibility.torp";
6
+
7
+ describe("Menu", () => {
8
+ it("Accessibility", async () => {
9
+ const container = document.createElement("div");
10
+ document.body.appendChild(container);
11
+ mount(container, MenuAccessibility);
12
+
13
+ // Tests from https://www.w3.org/WAI/ARIA/apg/patterns/menu/
14
+
15
+ // A menu is a container of items that represent choices. The element serving as the menu has
16
+ // a role of either menu or menubar
17
+ expect(queryByText(container, "Menu button")?.parentElement).toHaveAttribute("role", "menu");
18
+
19
+ // The items contained in a menu are child elements of the containing menu or menubar and have
20
+ // any of the following roles:
21
+ // * menuitem
22
+ // * menuitemcheckbox
23
+ // * menuitemradio
24
+ expect(queryByText(container, "Menu button")).toHaveAttribute("role", "menuitem");
25
+ expect(queryByText(container, "Check popout button")).toHaveAttribute("role", "menuitem");
26
+
27
+ // If activating a menuitem opens a submenu, the menuitem is known as a parent menuitem. A submenu's
28
+ // menu element is:
29
+ // * Contained inside the same menu element as its parent menuitem
30
+ // * Is the sibling element immediately following its parent menuitem
31
+
32
+ // A parent menuitem has aria-haspopup set to either menu or true
33
+ expect(queryByText(container, "Check popout button")).toHaveAttribute("aria-haspopup", "true");
34
+
35
+ // A parent menuitem has aria-expanded set to false when its child menu is not visible and set
36
+ // to true when the child menu is visible
37
+ expect(queryByText(container, "Check popout button")).toHaveAttribute("aria-expanded", "false");
38
+ fireEvent.click(getByText(container, "Check popout button"));
39
+ expect(queryByText(container, "Check popout button")).toHaveAttribute("aria-expanded", "true");
40
+
41
+ // One of the following approaches is used to enable scripts to move focus among items in a
42
+ // menu as described in Keyboard Navigation Inside Components:
43
+ // * The menu container has tabindex set to -1 or 0 and aria-activedescendant set to the ID
44
+ // of the focused item
45
+ // * Each item in the menu has tabindex set to -1, except in a menubar, where the first item
46
+ // has tabindex set to 0
47
+
48
+ // When a menuitemcheckbox or menuitemradio is checked, aria-checked is set to true
49
+ // NOTE: We don't (currently?) have checkboxes or radios in Menus
50
+
51
+ // When a menu item is disabled, aria-disabled is set to true
52
+ expect(queryByText(container, "Menu button")).not.toHaveAttribute("aria-disabled");
53
+ expect(queryByText(container, "Disabled button")).toHaveAttribute("aria-disabled", "true");
54
+
55
+ // Items in a menu may be divided into groups by placing an element with a role of separator
56
+ // between groups. For example, this technique should be used when a menu contains a set of
57
+ // menuitemradio items
58
+ expect(queryByText(container, "---")).toHaveAttribute("role", "separator");
59
+
60
+ // All separators should have aria-orientation consistent with the separator's orientation
61
+ // NOTE: What does this mean? None of the example separators have aria-orientation set
62
+
63
+ // An element with role menu either has:
64
+ // * aria-labelledby set to a value that refers to the menuitem or button that controls its
65
+ // display
66
+ // * A label provided by aria-label
67
+ // NOTE: We only support aria-label and it's required
68
+ expect(queryByText(container, "Menu button")?.parentElement).toHaveAttribute(
69
+ "aria-label",
70
+ "A menu",
71
+ );
72
+
73
+ // If a menu is horizontally oriented, it has aria-orientation set to horizontal. The default
74
+ // value of aria-orientation for a menu is vertical
75
+ // NOTE: For these menus, the only orientation is vertical -- can't really think of a situation
76
+ // where horizontal would make sense?
77
+ expect(queryByText(container, "Menu button")?.parentElement).toHaveAttribute(
78
+ "aria-orientation",
79
+ "vertical",
80
+ );
81
+ });
82
+ });
@@ -0,0 +1,57 @@
1
+ import { fireEvent, getByText, queryByText } from "@testing-library/dom";
2
+ import "@testing-library/jest-dom/vitest";
3
+ import { mount } from "@torpor/view";
4
+ import { describe, expect, it } from "vitest";
5
+ import MenuBasic from "./components/MenuBasic.torp";
6
+
7
+ describe("Menu", () => {
8
+ it("Basic operation", async () => {
9
+ const container = document.createElement("div");
10
+ document.body.appendChild(container);
11
+ mount(container, MenuBasic);
12
+
13
+ // By default, the top-level buttons should be displayed
14
+ expect(queryByText(container, "Menu button")).toBeInTheDocument();
15
+ expect(queryByText(container, "Check popout button")).toBeInTheDocument();
16
+ expect(queryByText(container, "Radio popout button")).toBeInTheDocument();
17
+
18
+ // Submenus should not be displayed
19
+ expect(queryByText(container, "Checked 1")).not.toBeInTheDocument();
20
+ expect(queryByText(container, "Radio 1")).not.toBeInTheDocument();
21
+
22
+ // Clicking a popout button should show the appropriate menu
23
+ fireEvent.click(getByText(container, "Check popout button"));
24
+ expect(queryByText(container, "Checked 1")).toBeInTheDocument();
25
+ expect(queryByText(container, "Radio 1")).not.toBeInTheDocument();
26
+
27
+ // Clicking another popout button should show that menu instead
28
+ fireEvent.click(getByText(container, "Radio popout button"));
29
+ //expect(queryByText(container, 'Checked 1')).not.toBeInTheDocument();
30
+ expect(queryByText(container, "Radio 1")).toBeInTheDocument();
31
+
32
+ // Checking a box should show or hide the indicator
33
+ fireEvent.click(getByText(container, "Check popout button"));
34
+ expect(queryByText(container, "Tick 1")).not.toBeInTheDocument();
35
+ expect(queryByText(container, "Tick 2")).not.toBeInTheDocument();
36
+ fireEvent.click(getByText(container, "Checked 1"));
37
+ expect(queryByText(container, "Tick 1")).toBeInTheDocument();
38
+ expect(queryByText(container, "Tick 2")).not.toBeInTheDocument();
39
+ fireEvent.click(getByText(container, "Checked 2"));
40
+ expect(queryByText(container, "Tick 1")).toBeInTheDocument();
41
+ expect(queryByText(container, "Tick 2")).toBeInTheDocument();
42
+ fireEvent.click(getByText(container, "Checked 1"));
43
+ expect(queryByText(container, "Tick 1")).not.toBeInTheDocument();
44
+ expect(queryByText(container, "Tick 2")).toBeInTheDocument();
45
+
46
+ // Checking a radio should show the indicator, and hide another indicator if already selected
47
+ fireEvent.click(getByText(container, "Radio popout button"));
48
+ expect(queryByText(container, "Circle 1")).not.toBeInTheDocument();
49
+ expect(queryByText(container, "Circle 2")).not.toBeInTheDocument();
50
+ fireEvent.click(getByText(container, "Radio 1"));
51
+ expect(queryByText(container, "Circle 1")).toBeInTheDocument();
52
+ expect(queryByText(container, "Circle 2")).not.toBeInTheDocument();
53
+ fireEvent.click(getByText(container, "Radio 2"));
54
+ expect(queryByText(container, "Circle 1")).not.toBeInTheDocument();
55
+ expect(queryByText(container, "Circle 2")).toBeInTheDocument();
56
+ });
57
+ });