@blotch/dolan 22.2.3 → 22.2.5

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 (227) hide show
  1. package/dist/commands/add.js +9 -1
  2. package/dist/commands/add.js.map +1 -1
  3. package/dist/commands/add.test.js +38 -3
  4. package/dist/commands/add.test.js.map +1 -1
  5. package/dist/commands/doctor.js +258 -21
  6. package/dist/commands/doctor.js.map +1 -1
  7. package/dist/commands/doctor.test.js +425 -11
  8. package/dist/commands/doctor.test.js.map +1 -1
  9. package/dist/commands/init.d.ts +2 -2
  10. package/dist/commands/init.js +38 -18
  11. package/dist/commands/init.js.map +1 -1
  12. package/dist/commands/init.test.js +105 -2
  13. package/dist/commands/init.test.js.map +1 -1
  14. package/dist/commands/theme.js +1 -1
  15. package/dist/commands/theme.js.map +1 -1
  16. package/dist/commands/update.d.ts +7 -0
  17. package/dist/commands/update.js +194 -50
  18. package/dist/commands/update.js.map +1 -1
  19. package/dist/commands/update.test.js +570 -3
  20. package/dist/commands/update.test.js.map +1 -1
  21. package/dist/lockfile.d.ts +17 -0
  22. package/dist/lockfile.js +56 -1
  23. package/dist/lockfile.js.map +1 -1
  24. package/dist/lockfile.test.d.ts +1 -0
  25. package/dist/lockfile.test.js +65 -0
  26. package/dist/lockfile.test.js.map +1 -0
  27. package/dist/registry.d.ts +6 -0
  28. package/dist/registry.js +119 -11
  29. package/dist/registry.js.map +1 -1
  30. package/dist/registry.test.js +61 -6
  31. package/dist/registry.test.js.map +1 -1
  32. package/dist/test-utils.d.ts +27 -0
  33. package/dist/test-utils.js +45 -1
  34. package/dist/test-utils.js.map +1 -1
  35. package/dist/testing/fire-lynx-event.d.ts +14 -0
  36. package/dist/testing/fire-lynx-event.js +19 -0
  37. package/dist/testing/fire-lynx-event.js.map +1 -0
  38. package/dist/testing/fire-lynx-event.test.d.ts +1 -0
  39. package/dist/testing/fire-lynx-event.test.js +44 -0
  40. package/dist/testing/fire-lynx-event.test.js.map +1 -0
  41. package/dist/testing/lynx-main-thread.d.ts +22 -0
  42. package/dist/testing/lynx-main-thread.js +11 -0
  43. package/dist/testing/lynx-main-thread.js.map +1 -0
  44. package/dist/testing/lynx-main-thread.test.d.ts +1 -0
  45. package/dist/testing/lynx-main-thread.test.js +24 -0
  46. package/dist/testing/lynx-main-thread.test.js.map +1 -0
  47. package/dist/testing/set-input-signal.d.ts +5 -4
  48. package/dist/testing/set-input-signal.js +5 -4
  49. package/dist/testing/set-input-signal.js.map +1 -1
  50. package/dist/ui/boolean-inputs.test.ts +76 -0
  51. package/dist/ui/components/accordion/accordion.test.ts +158 -144
  52. package/dist/ui/components/accordion/accordion.ts +19 -14
  53. package/dist/ui/components/action-sheet/action-sheet.test.ts +637 -183
  54. package/dist/ui/components/action-sheet/action-sheet.ts +339 -55
  55. package/dist/ui/components/action-sheet/index.ts +1 -0
  56. package/dist/ui/components/alert/alert.test.ts +67 -19
  57. package/dist/ui/components/alert/alert.ts +14 -16
  58. package/dist/ui/components/alert-dialog/alert-dialog.test.ts +740 -129
  59. package/dist/ui/components/alert-dialog/alert-dialog.ts +672 -58
  60. package/dist/ui/components/alert-dialog/index.ts +5 -0
  61. package/dist/ui/components/area-chart/area-chart.test.ts +81 -1
  62. package/dist/ui/components/area-chart/area-chart.ts +3 -2
  63. package/dist/ui/components/avatar/avatar.test.ts +89 -45
  64. package/dist/ui/components/badge/badge.test.ts +57 -23
  65. package/dist/ui/components/badge/badge.ts +14 -15
  66. package/dist/ui/components/bar-chart/bar-chart.test.ts +82 -1
  67. package/dist/ui/components/bottom-sheet/bottom-sheet.test.ts +1314 -273
  68. package/dist/ui/components/bottom-sheet/bottom-sheet.ts +1046 -119
  69. package/dist/ui/components/bottom-sheet/index.ts +7 -1
  70. package/dist/ui/components/button/button.test.ts +646 -2
  71. package/dist/ui/components/button/button.ts +443 -64
  72. package/dist/ui/components/button/index.ts +2 -0
  73. package/dist/ui/components/button-group/button-group.test.ts +528 -2
  74. package/dist/ui/components/button-group/button-group.ts +428 -70
  75. package/dist/ui/components/button-group/index.ts +3 -0
  76. package/dist/ui/components/candlestick-chart/candlestick-chart.test.ts +82 -1
  77. package/dist/ui/components/card/card.test.ts +97 -61
  78. package/dist/ui/components/card/card.ts +2 -1
  79. package/dist/ui/components/chart/chart-presets.test.ts +197 -1
  80. package/dist/ui/components/chart/chart-presets.ts +14 -7
  81. package/dist/ui/components/chart/chart-theme.ts +12 -0
  82. package/dist/ui/components/chart/chart.test.ts +1031 -0
  83. package/dist/ui/components/chart/chart.ts +66 -46
  84. package/dist/ui/components/checkbox/checkbox.test.ts +124 -42
  85. package/dist/ui/components/checkbox/checkbox.ts +25 -24
  86. package/dist/ui/components/collapsible/collapsible.test.ts +158 -81
  87. package/dist/ui/components/collapsible/collapsible.ts +21 -15
  88. package/dist/ui/components/context-menu/context-menu.test.ts +224 -0
  89. package/dist/ui/components/context-menu/context-menu.ts +110 -0
  90. package/dist/ui/components/context-menu/index.ts +1 -0
  91. package/dist/ui/components/dialog/dialog.test.ts +210 -152
  92. package/dist/ui/components/dialog/dialog.ts +35 -29
  93. package/dist/ui/components/dock/dock-scroll.test.ts +130 -0
  94. package/dist/ui/components/dock/dock-scroll.ts +142 -0
  95. package/dist/ui/components/dock/dock-search.test.ts +594 -0
  96. package/dist/ui/components/dock/dock.test.ts +752 -0
  97. package/dist/ui/components/dock/dock.ts +1438 -0
  98. package/dist/ui/components/dock/index.ts +2 -0
  99. package/dist/ui/components/dynamic-color/dynamic-color.test.ts +144 -0
  100. package/dist/ui/components/dynamic-color/dynamic-color.ts +146 -0
  101. package/dist/ui/components/dynamic-color/index.ts +5 -0
  102. package/dist/ui/components/empty-state/empty-state.test.ts +28 -39
  103. package/dist/ui/components/glass/concentric-radius.test.ts +13 -0
  104. package/dist/ui/components/glass/concentric-radius.ts +17 -0
  105. package/dist/ui/components/glass/glass-design.test.ts +78 -0
  106. package/dist/ui/components/glass/glass-design.ts +119 -0
  107. package/dist/ui/components/glass/glass.test.ts +376 -0
  108. package/dist/ui/components/glass/glass.ts +430 -0
  109. package/dist/ui/components/glass/index.ts +19 -0
  110. package/dist/ui/components/icon/icon-theme.test.ts +64 -0
  111. package/dist/ui/components/icon/icon-theme.ts +70 -16
  112. package/dist/ui/components/icon/icon.test.ts +52 -41
  113. package/dist/ui/components/input/input.test.ts +178 -101
  114. package/dist/ui/components/input/input.ts +17 -13
  115. package/dist/ui/components/label/label.test.ts +11 -24
  116. package/dist/ui/components/label/label.ts +8 -2
  117. package/dist/ui/components/line-chart/line-chart.test.ts +76 -1
  118. package/dist/ui/components/line-chart/line-chart.ts +3 -2
  119. package/dist/ui/components/list/list.test.ts +80 -49
  120. package/dist/ui/components/list/list.ts +5 -4
  121. package/dist/ui/components/menu/index.ts +25 -0
  122. package/dist/ui/components/menu/menu-placement.test.ts +153 -0
  123. package/dist/ui/components/menu/menu-placement.ts +159 -0
  124. package/dist/ui/components/menu/menu-popup.test.ts +636 -0
  125. package/dist/ui/components/menu/menu-popup.ts +565 -0
  126. package/dist/ui/components/menu/menu.test.ts +523 -0
  127. package/dist/ui/components/menu/menu.ts +455 -0
  128. package/dist/ui/components/nav-drawer/nav-drawer.test.ts +425 -260
  129. package/dist/ui/components/nav-drawer/nav-drawer.ts +75 -58
  130. package/dist/ui/components/navigation-bar/index.ts +1 -0
  131. package/dist/ui/components/navigation-bar/navigation-bar.test.ts +425 -0
  132. package/dist/ui/components/navigation-bar/navigation-bar.ts +411 -0
  133. package/dist/ui/components/pagination/pagination.test.ts +136 -86
  134. package/dist/ui/components/popover/index.ts +9 -0
  135. package/dist/ui/components/popover/popover-panel.test.ts +735 -0
  136. package/dist/ui/components/popover/popover-panel.ts +603 -0
  137. package/dist/ui/components/popover/popover.test.ts +286 -0
  138. package/dist/ui/components/popover/popover.ts +207 -0
  139. package/dist/ui/components/progress/progress.test.ts +102 -46
  140. package/dist/ui/components/progress/progress.ts +54 -55
  141. package/dist/ui/components/radio-group/radio-group.test.ts +128 -83
  142. package/dist/ui/components/radio-group/radio-group.ts +26 -25
  143. package/dist/ui/components/scatter-chart/scatter-chart.test.ts +74 -1
  144. package/dist/ui/components/scroll-area/scroll-area.test.ts +72 -36
  145. package/dist/ui/components/scroll-area/scroll-area.ts +4 -3
  146. package/dist/ui/components/search-field/index.ts +15 -0
  147. package/dist/ui/components/search-field/keyboard.test.ts +147 -0
  148. package/dist/ui/components/search-field/keyboard.ts +123 -0
  149. package/dist/ui/components/search-field/search-field.test.ts +549 -0
  150. package/dist/ui/components/search-field/search-field.ts +628 -0
  151. package/dist/ui/components/segmented-control/index.ts +8 -0
  152. package/dist/ui/components/segmented-control/segmented-control.test.ts +861 -0
  153. package/dist/ui/components/segmented-control/segmented-control.ts +884 -0
  154. package/dist/ui/components/select/select.test.ts +138 -78
  155. package/dist/ui/components/select/select.ts +19 -12
  156. package/dist/ui/components/separator/separator.test.ts +18 -17
  157. package/dist/ui/components/sheet/sheet.test.ts +151 -11
  158. package/dist/ui/components/sheet/sheet.ts +68 -5
  159. package/dist/ui/components/skeleton/skeleton.test.ts +24 -7
  160. package/dist/ui/components/skeleton/skeleton.ts +10 -16
  161. package/dist/ui/components/slider/index.ts +1 -0
  162. package/dist/ui/components/slider/slider.test.ts +695 -0
  163. package/dist/ui/components/slider/slider.ts +901 -0
  164. package/dist/ui/components/spinner/spinner.test.ts +64 -46
  165. package/dist/ui/components/spinner/spinner.ts +16 -19
  166. package/dist/ui/components/switch/index.ts +1 -1
  167. package/dist/ui/components/switch/switch.test.ts +534 -48
  168. package/dist/ui/components/switch/switch.ts +613 -48
  169. package/dist/ui/components/tab-bar/index.ts +1 -0
  170. package/dist/ui/components/tab-bar/tab-bar.test.ts +280 -0
  171. package/dist/ui/components/tab-bar/tab-bar.ts +230 -0
  172. package/dist/ui/components/tabs/index.ts +7 -1
  173. package/dist/ui/components/tabs/tabs.test.ts +484 -150
  174. package/dist/ui/components/tabs/tabs.ts +344 -149
  175. package/dist/ui/components/textarea/textarea.test.ts +206 -100
  176. package/dist/ui/components/textarea/textarea.ts +16 -13
  177. package/dist/ui/components/toast/toast.test.ts +434 -350
  178. package/dist/ui/components/toast/toast.ts +42 -51
  179. package/dist/ui/components/toggle/index.ts +1 -0
  180. package/dist/ui/components/toggle/toggle.test.ts +559 -146
  181. package/dist/ui/components/toggle/toggle.ts +241 -26
  182. package/dist/ui/components/toolbar/bar-scroll.test.ts +310 -0
  183. package/dist/ui/components/toolbar/bar-scroll.ts +372 -0
  184. package/dist/ui/components/toolbar/index.ts +12 -0
  185. package/dist/ui/components/toolbar/toolbar.test.ts +438 -0
  186. package/dist/ui/components/toolbar/toolbar.ts +535 -0
  187. package/dist/ui/theme/chart-colors.test.ts +8 -3
  188. package/dist/ui/theme/dark.css +38 -2
  189. package/dist/ui/theme/default.css +55 -7
  190. package/dist/ui/theme/icon-colors.test.ts +8 -3
  191. package/dist/ui/theme/rgb-channels.test.ts +84 -0
  192. package/dist/ui/theme/tailwind-build.test.ts +114 -0
  193. package/dist/ui/theme/tailwind-css.test.ts +83 -0
  194. package/dist/ui/theme/tailwind-plugin.test.ts +42 -2
  195. package/dist/ui/theme/tailwind-plugin.ts +93 -29
  196. package/dist/ui/theme/tailwind.css +113 -0
  197. package/dist/ui/utils/animate.test.ts +13 -1
  198. package/dist/utils/analyze.d.ts +19 -0
  199. package/dist/utils/analyze.js +16 -0
  200. package/dist/utils/analyze.js.map +1 -1
  201. package/dist/utils/analyze.test.js +14 -1
  202. package/dist/utils/analyze.test.js.map +1 -1
  203. package/dist/utils/installed.d.ts +25 -1
  204. package/dist/utils/installed.js +33 -0
  205. package/dist/utils/installed.js.map +1 -1
  206. package/dist/utils/installed.test.js +55 -1
  207. package/dist/utils/installed.test.js.map +1 -1
  208. package/dist/utils/resolve-paths.d.ts +4 -4
  209. package/dist/utils/resolve-paths.js +5 -7
  210. package/dist/utils/resolve-paths.js.map +1 -1
  211. package/dist/utils/resolve-paths.test.js +9 -5
  212. package/dist/utils/resolve-paths.test.js.map +1 -1
  213. package/dist/utils/tailwind.d.ts +45 -0
  214. package/dist/utils/tailwind.js +90 -0
  215. package/dist/utils/tailwind.js.map +1 -0
  216. package/dist/utils/tailwind.test.d.ts +1 -0
  217. package/dist/utils/tailwind.test.js +134 -0
  218. package/dist/utils/tailwind.test.js.map +1 -0
  219. package/package.json +9 -1
  220. package/dist/ui/components/area-chart/area-chart-render.test.ts +0 -63
  221. package/dist/ui/components/bar-chart/bar-chart-render.test.ts +0 -58
  222. package/dist/ui/components/button/button-render.test.ts +0 -132
  223. package/dist/ui/components/button-group/button-group-render.test.ts +0 -146
  224. package/dist/ui/components/candlestick-chart/candlestick-chart-render.test.ts +0 -59
  225. package/dist/ui/components/chart/chart-render.test.ts +0 -730
  226. package/dist/ui/components/line-chart/line-chart-render.test.ts +0 -83
  227. package/dist/ui/components/scatter-chart/scatter-chart-render.test.ts +0 -64
@@ -0,0 +1,90 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import { join } from 'node:path';
4
+ /** The config files Tailwind 3 reads. Tailwind 4 needs none. */
5
+ export const TAILWIND_CONFIG_FILES = [
6
+ 'tailwind.config.ts',
7
+ 'tailwind.config.js',
8
+ 'tailwind.config.mjs',
9
+ 'tailwind.config.cjs',
10
+ ];
11
+ /** The Tailwind config in the project's folder, if it has one. */
12
+ export const tailwindConfigFile = (cwd) => TAILWIND_CONFIG_FILES.find((file) => existsSync(join(cwd, file)));
13
+ /**
14
+ * The version of `tailwindcss` that `require` finds, if any. Both majors'
15
+ * package.json resolve: Tailwind 3 has no `exports` map, and Tailwind 4's
16
+ * lists `./package.json`.
17
+ */
18
+ const tailwindVersion = (require) => {
19
+ try {
20
+ return require('tailwindcss/package.json').version;
21
+ }
22
+ catch {
23
+ return undefined;
24
+ }
25
+ };
26
+ /**
27
+ * The version of Tailwind CSS installed for the project, resolved from its
28
+ * folder as the build resolves it. Reading the project's package.json missed
29
+ * two setups that build fine: a workspace whose root installs `tailwindcss`
30
+ * for every package, and a Tailwind 4 app that lists only
31
+ * `@tailwindcss/postcss`, which depends on `tailwindcss` itself.
32
+ *
33
+ * npm hoists that dependency next to the plugin, where the app finds it.
34
+ * pnpm's strict layout keeps it out of the app's reach, so this looks from
35
+ * the plugin's own folder too. The build still finds it under pnpm: pnpm's
36
+ * bin scripts, `ng` among them, put the packages pnpm hoists on `NODE_PATH`,
37
+ * and Tailwind resolves the stylesheet's `@import 'tailwindcss'` against it.
38
+ * Undefined when neither finds it.
39
+ */
40
+ export const installedTailwindVersion = (cwd) => {
41
+ const require = createRequire(join(cwd, 'package.json'));
42
+ const version = tailwindVersion(require);
43
+ if (version !== undefined)
44
+ return version;
45
+ try {
46
+ // The plugin's `exports` map doesn't list its package.json, so this
47
+ // starts from its entry point.
48
+ return tailwindVersion(createRequire(require.resolve('@tailwindcss/postcss')));
49
+ }
50
+ catch {
51
+ return undefined;
52
+ }
53
+ };
54
+ /** A version's major, `4` for `4.3.3`. */
55
+ export const majorVersion = (version) => Number.parseInt(version, 10);
56
+ /**
57
+ * Whether the project is set up for Tailwind 4, where the stylesheet imports
58
+ * the theme's `tailwind.css` rather than `tailwind.config.ts` listing its
59
+ * plugin: it has no Tailwind config, and Tailwind 4 installed, as
60
+ * `ng add @blotch/angular-lynx` and `ng new --style=tailwind` set it up. A
61
+ * config means a Tailwind 3 setup, even with Tailwind 4 installed, as
62
+ * `doctor` warns.
63
+ */
64
+ export const usesTailwind4 = (cwd) => {
65
+ if (tailwindConfigFile(cwd))
66
+ return false;
67
+ const version = installedTailwindVersion(cwd);
68
+ return version !== undefined && majorVersion(version) >= 4;
69
+ };
70
+ /**
71
+ * The theme files a project takes: the colors, and what maps them to
72
+ * Tailwind's utilities, `tailwind.css` for Tailwind 4 (imported in the
73
+ * stylesheet) or `tailwind-plugin.ts` for Tailwind 3 (a config plugin).
74
+ *
75
+ * With `tests`, the plugin comes with its test, for the same reason as a
76
+ * component's (see getComponentFiles): it covers the plugin in the app's
77
+ * coverage report. `tailwind.css` has no code to cover. The theme folder's
78
+ * other tests stay behind: they check the theme in packages/ui with Node,
79
+ * against the components and Tailwind's own builds, which an app's `ng test`
80
+ * can't do.
81
+ */
82
+ export const themeFilesFor = (cwd, { tests = false } = {}) => usesTailwind4(cwd)
83
+ ? ['default.css', 'dark.css', 'tailwind.css']
84
+ : [
85
+ 'default.css',
86
+ 'dark.css',
87
+ 'tailwind-plugin.ts',
88
+ ...(tests ? ['tailwind-plugin.test.ts'] : []),
89
+ ];
90
+ //# sourceMappingURL=tailwind.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tailwind.js","sourceRoot":"","sources":["../../src/utils/tailwind.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,gEAAgE;AAChE,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,oBAAoB;IACpB,oBAAoB;IACpB,qBAAqB;IACrB,qBAAqB;CACtB,CAAC;AAEF,kEAAkE;AAClE,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,GAAW,EAAsB,EAAE,CACpE,qBAAqB,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;AAEpE;;;;GAIG;AACH,MAAM,eAAe,GAAG,CAAC,OAAuB,EAAsB,EAAE;IACtE,IAAI,CAAC;QACH,OAAQ,OAAO,CAAC,0BAA0B,CAAyB,CAAC,OAAO,CAAC;IAC9E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,GAAW,EAAsB,EAAE;IAC1E,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC;IACzD,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IACzC,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,OAAO,CAAC;IAC1C,IAAI,CAAC;QACH,oEAAoE;QACpE,+BAA+B;QAC/B,OAAO,eAAe,CACpB,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC,CACvD,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC,CAAC;AAEF,0CAA0C;AAC1C,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,OAAe,EAAU,EAAE,CACtD,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AAE/B;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,GAAW,EAAW,EAAE;IACpD,IAAI,kBAAkB,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,MAAM,OAAO,GAAG,wBAAwB,CAAC,GAAG,CAAC,CAAC;IAC9C,OAAO,OAAO,KAAK,SAAS,IAAI,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAC7D,CAAC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,GAAW,EACX,EAAE,KAAK,GAAG,KAAK,KAA0B,EAAE,EACjC,EAAE,CACZ,aAAa,CAAC,GAAG,CAAC;IAChB,CAAC,CAAC,CAAC,aAAa,EAAE,UAAU,EAAE,cAAc,CAAC;IAC7C,CAAC,CAAC;QACE,aAAa;QACb,UAAU;QACV,oBAAoB;QACpB,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9C,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,134 @@
1
+ import { describe, expect, it, afterEach } from 'vitest';
2
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { createFixture, installPackage } from '../test-utils';
5
+ import { getUiSourceDir } from './resolve-paths';
6
+ import { installedTailwindVersion, majorVersion, tailwindConfigFile, themeFilesFor, usesTailwind4, } from './tailwind';
7
+ let fixtures = [];
8
+ afterEach(() => {
9
+ for (const fixture of fixtures)
10
+ fixture.cleanup();
11
+ fixtures = [];
12
+ });
13
+ /**
14
+ * A project with these packages installed, and these other files. Its
15
+ * package.json lists none of them: what's installed is what counts.
16
+ */
17
+ const project = (installed = {}, files = {}) => {
18
+ const fixture = createFixture({});
19
+ fixtures.push(fixture);
20
+ writeFileSync(join(fixture.dir, 'package.json'), '{}');
21
+ for (const [name, version] of Object.entries(installed)) {
22
+ installPackage(fixture.dir, name, version);
23
+ }
24
+ for (const [file, content] of Object.entries(files)) {
25
+ writeFileSync(join(fixture.dir, file), content);
26
+ }
27
+ return fixture.dir;
28
+ };
29
+ /**
30
+ * A project that lists only `@tailwindcss/postcss`, with Tailwind in the
31
+ * plugin's own node_modules, out of the project's reach, as pnpm installs it.
32
+ */
33
+ const postcssOnly = () => {
34
+ const dir = project();
35
+ installPackage(installPackage(dir, '@tailwindcss/postcss', '4.3.3'), 'tailwindcss', '4.3.3');
36
+ return dir;
37
+ };
38
+ describe('installedTailwindVersion', () => {
39
+ it("reads the version of the project's own Tailwind, 3 or 4", () => {
40
+ expect(installedTailwindVersion(project({ tailwindcss: '4.3.3' }))).toBe('4.3.3');
41
+ expect(installedTailwindVersion(project({ tailwindcss: '3.4.19' }))).toBe('3.4.19');
42
+ });
43
+ it('finds the Tailwind a workspace root installs', () => {
44
+ // The app's package.json lists no Tailwind: the root's is every package's.
45
+ const root = project({ tailwindcss: '4.3.3' });
46
+ const app = join(root, 'apps', 'app');
47
+ mkdirSync(app, { recursive: true });
48
+ writeFileSync(join(app, 'package.json'), '{}');
49
+ expect(installedTailwindVersion(app)).toBe('4.3.3');
50
+ });
51
+ it('finds the Tailwind @tailwindcss/postcss depends on, hoisted next to it', () => {
52
+ // npm's layout: the plugin's dependency sits in the app's node_modules.
53
+ expect(installedTailwindVersion(project({ '@tailwindcss/postcss': '4.3.3', tailwindcss: '4.3.3' }))).toBe('4.3.3');
54
+ });
55
+ it("finds it from the plugin's own folder when the project can't", () => {
56
+ expect(installedTailwindVersion(postcssOnly())).toBe('4.3.3');
57
+ });
58
+ it("prefers the project's own Tailwind to the plugin's", () => {
59
+ // What the stylesheet's `@import 'tailwindcss'` finds first.
60
+ const dir = project({ tailwindcss: '4.2.0' });
61
+ installPackage(installPackage(dir, '@tailwindcss/postcss', '4.3.3'), 'tailwindcss', '4.3.3');
62
+ expect(installedTailwindVersion(dir)).toBe('4.2.0');
63
+ });
64
+ it('is undefined without Tailwind, even when the plugin is there', () => {
65
+ expect(installedTailwindVersion(project())).toBeUndefined();
66
+ expect(installedTailwindVersion(project({ '@tailwindcss/postcss': '4.3.3' }))).toBeUndefined();
67
+ });
68
+ });
69
+ describe('majorVersion', () => {
70
+ it("reads a version's major", () => {
71
+ expect(majorVersion('4.3.3')).toBe(4);
72
+ expect(majorVersion('3.4.19')).toBe(3);
73
+ expect(majorVersion('4.0.0-beta.1')).toBe(4);
74
+ });
75
+ });
76
+ describe('tailwindConfigFile', () => {
77
+ it('names the Tailwind config in the project, if any', () => {
78
+ expect(tailwindConfigFile(project({}, { 'tailwind.config.js': '' }))).toBe('tailwind.config.js');
79
+ expect(tailwindConfigFile(project())).toBeUndefined();
80
+ });
81
+ });
82
+ describe('usesTailwind4', () => {
83
+ it('is true for a project with Tailwind 4 installed and no Tailwind config', () => {
84
+ expect(usesTailwind4(project({ tailwindcss: '4.3.3' }))).toBe(true);
85
+ expect(usesTailwind4(postcssOnly())).toBe(true);
86
+ });
87
+ it('is false for Tailwind 3, a Tailwind config, or no Tailwind', () => {
88
+ expect(usesTailwind4(project({ tailwindcss: '3.4.19' }))).toBe(false);
89
+ // A config is a Tailwind 3 setup, whatever's installed.
90
+ expect(usesTailwind4(project({ tailwindcss: '4.3.3' }, { 'tailwind.config.js': '' }))).toBe(false);
91
+ expect(usesTailwind4(project())).toBe(false);
92
+ });
93
+ it('reads the installed version, not the one package.json asks for', () => {
94
+ const dir = project({ tailwindcss: '3.4.19' });
95
+ writeFileSync(join(dir, 'package.json'), JSON.stringify({ devDependencies: { tailwindcss: '^4.3.3' } }));
96
+ expect(usesTailwind4(dir)).toBe(false);
97
+ });
98
+ });
99
+ describe('themeFilesFor', () => {
100
+ it('takes tailwind.css on Tailwind 4, and the config plugin on Tailwind 3', () => {
101
+ expect(themeFilesFor(project({ tailwindcss: '4.3.3' }))).toEqual([
102
+ 'default.css',
103
+ 'dark.css',
104
+ 'tailwind.css',
105
+ ]);
106
+ expect(themeFilesFor(project({ tailwindcss: '3.4.19' }))).toEqual([
107
+ 'default.css',
108
+ 'dark.css',
109
+ 'tailwind-plugin.ts',
110
+ ]);
111
+ });
112
+ it("adds the Tailwind plugin's test with the components' tests", () => {
113
+ // It covers the plugin in the app's coverage report. Tailwind 4's
114
+ // tailwind.css has no code to cover.
115
+ expect(themeFilesFor(project({ tailwindcss: '3.4.19' }), { tests: true })).toEqual([
116
+ 'default.css',
117
+ 'dark.css',
118
+ 'tailwind-plugin.ts',
119
+ 'tailwind-plugin.test.ts',
120
+ ]);
121
+ expect(themeFilesFor(project({ tailwindcss: '4.3.3' }), { tests: true })).toEqual(['default.css', 'dark.css', 'tailwind.css']);
122
+ });
123
+ it('lists only files the theme folder has', () => {
124
+ for (const version of ['3.4.19', '4.3.3']) {
125
+ const files = themeFilesFor(project({ tailwindcss: version }), {
126
+ tests: true,
127
+ });
128
+ for (const file of files) {
129
+ expect(existsSync(join(getUiSourceDir(), 'theme', file)), file).toBe(true);
130
+ }
131
+ }
132
+ });
133
+ });
134
+ //# sourceMappingURL=tailwind.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tailwind.test.js","sourceRoot":"","sources":["../../src/utils/tailwind.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blotch/dolan",
3
- "version": "22.2.3",
3
+ "version": "22.2.5",
4
4
  "description": "CLI for adding @blotch/ui components to your project",
5
5
  "homepage": "https://github.com/Blotch-Smart-Frames/AngularLynx#readme",
6
6
  "bugs": {
@@ -31,6 +31,14 @@
31
31
  "./testing/set-input-signal": {
32
32
  "types": "./dist/testing/set-input-signal.d.ts",
33
33
  "default": "./dist/testing/set-input-signal.js"
34
+ },
35
+ "./testing/fire-lynx-event": {
36
+ "types": "./dist/testing/fire-lynx-event.d.ts",
37
+ "default": "./dist/testing/fire-lynx-event.js"
38
+ },
39
+ "./testing/lynx-main-thread": {
40
+ "types": "./dist/testing/lynx-main-thread.d.ts",
41
+ "default": "./dist/testing/lynx-main-thread.js"
34
42
  }
35
43
  },
36
44
  "scripts": {
@@ -1,63 +0,0 @@
1
- import { render } from '@blotch/angular-lynx-testing-library';
2
- import { createChartRuntime } from '@tanstack/charts/runtime';
3
- import type {
4
- ChartDefinition,
5
- ChartPoint,
6
- SceneNode,
7
- } from '@tanstack/charts/types';
8
- import { describe, expect, it, vi } from 'vitest';
9
- import { setInputSignal } from '../../../test-utils/set-input-signal';
10
- import { UiAreaChart } from './area-chart';
11
-
12
- const data = [
13
- { x: 0, y: 4 },
14
- { x: 1, y: -2 },
15
- { x: 2, y: 6 },
16
- ];
17
-
18
- /**
19
- * Every drawn shape in a definition's scene, labels excluded.
20
- */
21
- const shapes = (definition: ChartDefinition<any, any, any>) => {
22
- const scene = createChartRuntime().render(definition, {
23
- width: 300,
24
- height: 200,
25
- });
26
- const found: SceneNode[] = [];
27
- const walk = (nodes: readonly SceneNode[]) => {
28
- for (const node of nodes) {
29
- if (node.kind === 'group') {
30
- if (!node.focus) walk(node.children);
31
- } else if (node.kind !== 'label') found.push(node);
32
- }
33
- };
34
- walk(scene.nodes);
35
- return { scene, found };
36
- };
37
-
38
- describe('UiAreaChart', () => {
39
- type Internals = {
40
- definition: () => ChartDefinition<any, any, any>;
41
- onSelect(point: ChartPoint | null): void;
42
- };
43
-
44
- it('builds its definition from its inputs', async () => {
45
- const { componentRef } = await render(UiAreaChart);
46
- const chart = componentRef.instance as UiAreaChart;
47
- setInputSignal(chart.data, data);
48
- setInputSignal(chart.baseline, -20);
49
- const { scene } = shapes((chart as unknown as Internals).definition());
50
- expect(scene.scales['y']!.domain[0]).toBeLessThanOrEqual(-20);
51
- });
52
-
53
- it('emits the tapped datum', async () => {
54
- const { componentRef } = await render(UiAreaChart);
55
- const chart = componentRef.instance as UiAreaChart;
56
- const tapped = vi.fn();
57
- chart.pointTap.subscribe(tapped);
58
- const internals = chart as unknown as Internals;
59
- internals.onSelect({ datum: data[0] } as ChartPoint);
60
- internals.onSelect(null);
61
- expect(tapped.mock.calls).toEqual([[data[0]]]);
62
- });
63
- });
@@ -1,58 +0,0 @@
1
- import { render } from '@blotch/angular-lynx-testing-library';
2
- import { createChartRuntime } from '@tanstack/charts/runtime';
3
- import type {
4
- ChartDefinition,
5
- ChartPoint,
6
- SceneNode,
7
- SceneRect,
8
- } from '@tanstack/charts/types';
9
- import { describe, expect, it, vi } from 'vitest';
10
- import { setInputSignal } from '../../../test-utils/set-input-signal';
11
- import { UiBarChart } from './bar-chart';
12
-
13
- /**
14
- * Every bar in a definition's scene.
15
- */
16
- const bars = (definition: ChartDefinition<any, any, any>) => {
17
- const scene = createChartRuntime().render(definition, {
18
- width: 300,
19
- height: 200,
20
- });
21
- const found: SceneRect[] = [];
22
- const walk = (nodes: readonly SceneNode[]) => {
23
- for (const node of nodes) {
24
- if (node.kind === 'group') {
25
- if (!node.focus) walk(node.children);
26
- } else if (node.kind === 'rect') found.push(node);
27
- }
28
- };
29
- walk(scene.nodes);
30
- return { scene, found };
31
- };
32
-
33
- describe('UiBarChart', () => {
34
- type Internals = {
35
- definition: () => ChartDefinition<any, any, any>;
36
- onSelect(point: ChartPoint | null): void;
37
- };
38
-
39
- it('builds its definition from its inputs', async () => {
40
- const { componentRef } = await render(UiBarChart);
41
- const chart = componentRef.instance as UiBarChart;
42
- setInputSignal(chart.data, [{ x: 'a', y: 1 }]);
43
- setInputSignal(chart.color, 'red');
44
- const { found } = bars((chart as unknown as Internals).definition());
45
- expect(found[0]!.style?.fill).toBe('red');
46
- });
47
-
48
- it('emits the tapped datum', async () => {
49
- const { componentRef } = await render(UiBarChart);
50
- const chart = componentRef.instance as UiBarChart;
51
- const tapped = vi.fn();
52
- chart.barTap.subscribe(tapped);
53
- const internals = chart as unknown as Internals;
54
- internals.onSelect({ datum: { x: 'a', y: 1 } } as ChartPoint);
55
- internals.onSelect(null);
56
- expect(tapped.mock.calls).toEqual([[{ x: 'a', y: 1 }]]);
57
- });
58
- });
@@ -1,132 +0,0 @@
1
- import { Component } from '@angular/core';
2
- import { render, waitForUpdate } from '@blotch/angular-lynx-testing-library';
3
- import { LYNX_ELEMENTS } from '@blotch/angular-lynx';
4
- import { describe, expect, it, vi } from 'vitest';
5
- import { setInputSignal } from '../../../test-utils/set-input-signal';
6
- import { UiButton } from './button';
7
-
8
- @Component({
9
- standalone: true,
10
- imports: [UiButton, LYNX_ELEMENTS],
11
- template: `<ui-button (pressed)="onPressed()"><text>Go</text></ui-button>`,
12
- })
13
- class ButtonHost {
14
- count = 0;
15
- onPressed = () => {
16
- this.count++;
17
- };
18
- }
19
-
20
- describe('UiButton (render)', () => {
21
- it('renders and mounts without throwing', async () => {
22
- const { container } = await render(ButtonHost);
23
- expect(container).toBeTruthy();
24
- });
25
-
26
- it('emits `pressed` from onTap() when not disabled/loading', async () => {
27
- const { componentRef } = await render(UiButton);
28
- const inst = componentRef.instance as unknown as {
29
- onTap: () => void;
30
- pressed: { subscribe: (cb: () => void) => void };
31
- };
32
- const emitted = vi.fn();
33
- inst.pressed.subscribe(emitted);
34
- inst.onTap();
35
- expect(emitted).toHaveBeenCalledTimes(1);
36
- });
37
-
38
- it('onTap() is a no-op when disabled', async () => {
39
- const { componentRef } = await render(UiButton);
40
- const inst = componentRef.instance as unknown as UiButton & {
41
- onTap: () => void;
42
- };
43
- setInputSignal(inst.disabled, true);
44
- const emitted = vi.fn();
45
- inst.pressed.subscribe(emitted);
46
- inst.onTap();
47
- expect(emitted).not.toHaveBeenCalled();
48
- });
49
-
50
- it('onTap() is a no-op when loading', async () => {
51
- const { componentRef } = await render(UiButton);
52
- const inst = componentRef.instance as unknown as UiButton & {
53
- onTap: () => void;
54
- };
55
- setInputSignal(inst.loading, true);
56
- const emitted = vi.fn();
57
- inst.pressed.subscribe(emitted);
58
- inst.onTap();
59
- expect(emitted).not.toHaveBeenCalled();
60
- });
61
-
62
- it('shows the spinner while loading', async () => {
63
- const { componentRef, container } = await render(UiButton);
64
- setInputSignal((componentRef.instance as UiButton).loading, true);
65
- await waitForUpdate();
66
- // Spinner renders as a nested view with an svg — searching for that
67
- // suffices without asserting on the internal Lynx tree.
68
- expect(container.querySelector('svg')).toBeTruthy();
69
- });
70
-
71
- it('applies disabled dim opacity when disabled or loading', async () => {
72
- const { componentRef, container } = await render(UiButton);
73
- setInputSignal((componentRef.instance as UiButton).disabled, true);
74
- await waitForUpdate();
75
- const outer = container.querySelector('view');
76
- expect(outer?.getAttribute('class')).toContain('opacity-50');
77
- });
78
-
79
- it('picks the right spinner size for each button size', async () => {
80
- // spinnerSize returns 'xs' for size='sm' and 'sm' otherwise. Cover both.
81
- const { componentRef } = await render(UiButton);
82
- const inst = componentRef.instance as unknown as UiButton & {
83
- spinnerSize: () => 'xs' | 'sm';
84
- };
85
- setInputSignal(inst.size, 'sm');
86
- await waitForUpdate();
87
- expect(inst.spinnerSize()).toBe('xs');
88
-
89
- setInputSignal(inst.size, 'default');
90
- await waitForUpdate();
91
- expect(inst.spinnerSize()).toBe('sm');
92
- });
93
-
94
- it("colors the spinner like each variant's label", async () => {
95
- // The template binding doesn't reach the spinner's input under JIT (see
96
- // setInputSignal), so check the value the button passes it.
97
- const { componentRef } = await render(UiButton);
98
- const inst = componentRef.instance as unknown as UiButton & {
99
- spinnerColor: () => string;
100
- };
101
- for (const [variant, color] of [
102
- ['default', 'var(--primary-foreground)'],
103
- ['destructive', 'var(--destructive-foreground)'],
104
- ['outline', 'var(--foreground)'],
105
- ['secondary', 'var(--secondary-foreground)'],
106
- ['ghost', 'var(--foreground)'],
107
- ] as const) {
108
- setInputSignal(inst.variant, variant);
109
- expect(inst.spinnerColor()).toBe(color);
110
- }
111
- });
112
-
113
- it('press handlers run cleanly (guarded by disabled/loading state)', async () => {
114
- // onPressStart/End are no-ops when the button is disabled/loading; the
115
- // active-press path relies on the ref being wired up on-device — assert
116
- // only that the handlers run without throwing here.
117
- const { componentRef } = await render(UiButton);
118
- const inst = componentRef.instance as unknown as {
119
- onPressStart: () => void;
120
- onPressEnd: () => void;
121
- onPressCancel: () => void;
122
- };
123
- expect(() => inst.onPressStart()).not.toThrow();
124
- expect(() => inst.onPressEnd()).not.toThrow();
125
- expect(() => inst.onPressCancel()).not.toThrow();
126
-
127
- // Disabled path (early return in onPressStart / onPressEnd)
128
- setInputSignal((componentRef.instance as UiButton).disabled, true);
129
- expect(() => inst.onPressStart()).not.toThrow();
130
- expect(() => inst.onPressEnd()).not.toThrow();
131
- });
132
- });
@@ -1,146 +0,0 @@
1
- import { Component } from '@angular/core';
2
- import { render, waitForUpdate } from '@blotch/angular-lynx-testing-library';
3
- import { LYNX_ELEMENTS } from '@blotch/angular-lynx';
4
- import { describe, expect, it, vi } from 'vitest';
5
- import { setInputSignal } from '../../../test-utils/set-input-signal';
6
- import { UiButtonGroup, UiButtonGroupItem } from './button-group';
7
-
8
- @Component({
9
- standalone: true,
10
- imports: [UiButtonGroup, UiButtonGroupItem, LYNX_ELEMENTS],
11
- template: `
12
- <ui-button-group [orientation]="orientation">
13
- <ui-button-group-item (pressed)="onPressed()"
14
- ><text>A</text></ui-button-group-item
15
- >
16
- <ui-button-group-item [disabled]="disabled" (pressed)="onPressed()"
17
- ><text>B</text></ui-button-group-item
18
- >
19
- </ui-button-group>
20
- `,
21
- })
22
- class ButtonGroupHost {
23
- orientation: 'horizontal' | 'vertical' = 'horizontal';
24
- disabled = false;
25
- onPressed = () => {};
26
- }
27
-
28
- describe('UiButtonGroup (render)', () => {
29
- it('renders horizontally by default', async () => {
30
- const { container } = await render(ButtonGroupHost);
31
- expect(container).toBeTruthy();
32
- const flexRow = container.querySelector('.flex-row');
33
- expect(flexRow).toBeTruthy();
34
- });
35
-
36
- it('switches to vertical layout when orientation is flipped', async () => {
37
- const { componentRef, container } = await render(UiButtonGroup);
38
- setInputSignal(
39
- (componentRef.instance as UiButtonGroup).orientation,
40
- 'vertical',
41
- );
42
- await waitForUpdate();
43
- expect(container.querySelector('.flex-col')).toBeTruthy();
44
- });
45
-
46
- it('applies a user class from the alias input', async () => {
47
- const { componentRef, container } = await render(UiButtonGroup);
48
- setInputSignal(
49
- (componentRef.instance as UiButtonGroup).userClass,
50
- 'w-full',
51
- );
52
- await waitForUpdate();
53
- const outer = container.querySelector('view');
54
- expect(outer?.getAttribute('class')).toContain('w-full');
55
- });
56
- });
57
-
58
- describe('UiButtonGroupItem', () => {
59
- const renderItem = async (
60
- variant: 'default' | 'outline' = 'default',
61
- size: 'default' | 'sm' | 'lg' = 'default',
62
- disabledGroup = false,
63
- ) => {
64
- const parentResult = await render(UiButtonGroup);
65
- const parent = parentResult.componentRef.instance as UiButtonGroup;
66
- setInputSignal(parent.variant, variant);
67
- setInputSignal(parent.size, size);
68
- setInputSignal(parent.disabled, disabledGroup);
69
- await waitForUpdate();
70
-
71
- const itemResult = await render(UiButtonGroupItem, {
72
- providers: [{ provide: UiButtonGroup, useValue: parent }],
73
- });
74
- await waitForUpdate();
75
- return { parent, itemResult };
76
- };
77
-
78
- it('onTap emits `pressed` when neither the group nor the item is disabled', async () => {
79
- const { itemResult } = await renderItem();
80
- const inst = itemResult.componentRef
81
- .instance as unknown as UiButtonGroupItem & {
82
- onTap: () => void;
83
- };
84
- const emitted = vi.fn();
85
- inst.pressed.subscribe(emitted);
86
- inst.onTap();
87
- expect(emitted).toHaveBeenCalledTimes(1);
88
- });
89
-
90
- it('onTap is a no-op when the group is disabled', async () => {
91
- const { itemResult } = await renderItem('default', 'default', true);
92
- const inst = itemResult.componentRef
93
- .instance as unknown as UiButtonGroupItem & {
94
- onTap: () => void;
95
- };
96
- const emitted = vi.fn();
97
- inst.pressed.subscribe(emitted);
98
- inst.onTap();
99
- expect(emitted).not.toHaveBeenCalled();
100
- });
101
-
102
- it('onTap is a no-op when the item itself is disabled', async () => {
103
- const { itemResult } = await renderItem();
104
- const inst = itemResult.componentRef
105
- .instance as unknown as UiButtonGroupItem & {
106
- onTap: () => void;
107
- };
108
- setInputSignal(inst.disabled, true);
109
- await waitForUpdate();
110
- const emitted = vi.fn();
111
- inst.pressed.subscribe(emitted);
112
- inst.onTap();
113
- expect(emitted).not.toHaveBeenCalled();
114
- });
115
-
116
- it('press handlers run cleanly on both enabled and disabled paths', async () => {
117
- const { itemResult } = await renderItem();
118
- const inst = itemResult.componentRef.instance as unknown as {
119
- onPressStart: () => void;
120
- onPressEnd: () => void;
121
- onPressCancel: () => void;
122
- disabled: unknown;
123
- };
124
- expect(() => inst.onPressStart()).not.toThrow();
125
- expect(() => inst.onPressEnd()).not.toThrow();
126
- expect(() => inst.onPressCancel()).not.toThrow();
127
- setInputSignal((inst as unknown as UiButtonGroupItem).disabled, true);
128
- await waitForUpdate();
129
- expect(() => inst.onPressStart()).not.toThrow();
130
- expect(() => inst.onPressEnd()).not.toThrow();
131
- });
132
-
133
- it('renders label with `outline` group variant classes', async () => {
134
- // Covers the `outline` branch of both buttonGroupItemVariants and
135
- // buttonGroupTextVariants.
136
- const { itemResult } = await renderItem('outline', 'lg');
137
- const view = itemResult.container.querySelector('view');
138
- expect(view?.getAttribute('class')).toContain('bg-background');
139
- });
140
-
141
- it('renders with size sm variant classes', async () => {
142
- const { itemResult } = await renderItem('default', 'sm');
143
- const view = itemResult.container.querySelector('view');
144
- expect(view?.getAttribute('class')).toContain('h-9');
145
- });
146
- });