@marimo-team/islands 0.23.9-dev3 → 0.23.9-dev5

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 (242) hide show
  1. package/dist/{ConnectedDataExplorerComponent-2lBNiUv6.js → ConnectedDataExplorerComponent-OzrfMM5L.js} +20 -20
  2. package/dist/ErrorBoundary-rULOrC_p.js +175 -0
  3. package/dist/{ImageComparisonComponent-CNHIsPDj.js → ImageComparisonComponent-CHrI72em.js} +1 -1
  4. package/dist/{Plot-4wn-lMVn.js → Plot-CAYS29h9.js} +1 -1
  5. package/dist/{_baseUniq-CxZRxRRo.js → _baseUniq-B_2Hw7zG.js} +3 -3
  6. package/dist/{any-language-editor-VWs_7v27.js → any-language-editor-DfdpyDv_.js} +23 -23
  7. package/dist/architecture-7HQA4BMR-Kyc44TmC.js +6 -0
  8. package/dist/{architectureDiagram-VXUJARFQ-CXVJxFhH.js → architectureDiagram-VXUJARFQ-CT2SuxNw.js} +15 -15
  9. package/dist/{arrays-CldYf7p7.js → arrays-sEtDRoG4.js} +1 -1
  10. package/dist/{blockDiagram-VD42YOAC-DGDaxR8I.js → blockDiagram-VD42YOAC-Dy7hlFla.js} +7 -7
  11. package/dist/{button-Dj4BTre0.js → button-C5K9fIPF.js} +2 -2
  12. package/dist/{c4Diagram-YG6GDRKO-C2hc6ne8.js → c4Diagram-YG6GDRKO-BXlAmZ8Z.js} +4 -4
  13. package/dist/{capabilities-C9rrYCzf.js → capabilities-BceAxrAW.js} +2 -2
  14. package/dist/{channel-BBoIVUrJ.js → channel-D_PHgcig.js} +1 -1
  15. package/dist/{chat-ui-D3XBept8.js → chat-ui-BDI3FMI8.js} +29 -29
  16. package/dist/{check-BcUIXnUT.js → check-DTbrK0zt.js} +1 -1
  17. package/dist/{chunk-4F5CHEZ2-BZq7Kom7.js → chunk-4F5CHEZ2-D9nGEHV8.js} +1 -1
  18. package/dist/{chunk-5FQGJX7Z-BOg95xG5.js → chunk-5FQGJX7Z-BNjes6Yx.js} +5 -5
  19. package/dist/{chunk-ABZYJK2D-D0cLy8Bb.js → chunk-ABZYJK2D-Dz0-H2B5.js} +1 -1
  20. package/dist/{chunk-ATLVNIR6-BXsEjlHF.js → chunk-ATLVNIR6-o0Z5MZLd.js} +1 -1
  21. package/dist/{chunk-B2363JML-D9-XOau1.js → chunk-B2363JML-KEJpLGGP.js} +1 -1
  22. package/dist/{chunk-B4BG7PRW-Q1usn6T3.js → chunk-B4BG7PRW-BL98U9B4.js} +4 -4
  23. package/dist/{chunk-DI55MBZ5-D1qLYNrb.js → chunk-DI55MBZ5-Dwkn0LWm.js} +4 -4
  24. package/dist/{chunk-EXTU4WIE-BKNXdLmD.js → chunk-EXTU4WIE-9sNjmQrB.js} +1 -1
  25. package/dist/{chunk-FRFDVMJY-BSBUAX7r.js → chunk-FRFDVMJY-DzQqMWrl.js} +1 -1
  26. package/dist/{chunk-JA3XYJ7Z-D6c6cOBG.js → chunk-JA3XYJ7Z-C32Y7Epf.js} +2 -2
  27. package/dist/{chunk-JZLCHNYA-BvsPHJmL.js → chunk-JZLCHNYA-C6ftyVMN.js} +4 -4
  28. package/dist/{chunk-N4CR4FBY-8ycT-O9a.js → chunk-N4CR4FBY-DUhGZhZs.js} +5 -5
  29. package/dist/{chunk-PL6DKKU2-B0MTXvyc.js → chunk-PL6DKKU2-D7km-08O.js} +1 -1
  30. package/dist/{chunk-QN33PNHL-Bb-eUBW3.js → chunk-QN33PNHL-0K6SDYn3.js} +1 -1
  31. package/dist/{chunk-QXUST7PY-DV8yRwBd.js → chunk-QXUST7PY-DMhsRpYK.js} +5 -5
  32. package/dist/{chunk-S3R3BYOJ-mQeCz5CE.js → chunk-S3R3BYOJ-oAe3dEbO.js} +3 -3
  33. package/dist/{chunk-SJTYNZTY-CEG4F0pB.js → chunk-SJTYNZTY-BkJrPRFC.js} +1 -1
  34. package/dist/{chunk-TCCFYFTB-d3HOqL2I.js → chunk-TCCFYFTB-D58KeXnC.js} +6 -6
  35. package/dist/{chunk-TQ3KTPDO-DiCtqVSi.js → chunk-TQ3KTPDO-D_yA_wAb.js} +1 -1
  36. package/dist/{chunk-TZMSLE5B-BqW10dHe.js → chunk-TZMSLE5B-yBKS_DQU.js} +1 -1
  37. package/dist/{chunk-UMXZTB3W-97iS1iEl.js → chunk-UMXZTB3W-D7uwvNjd.js} +1 -1
  38. package/dist/{classDiagram-2ON5EDUG--Yh__LHb.js → classDiagram-2ON5EDUG-QjoAcuFE.js} +10 -10
  39. package/dist/{classDiagram-v2-WZHVMYZB-BC7X7Xtc.js → classDiagram-v2-WZHVMYZB-bUCv4gu2.js} +10 -10
  40. package/dist/{clone-BuIIsfA8.js → clone-Q4Fqwn6q.js} +1 -1
  41. package/dist/{code-block-37QAKDTI-BsGy1AOJ.js → code-block-37QAKDTI-m92Yc8pv.js} +2 -2
  42. package/dist/{code-visibility-BKxrBMod.js → code-visibility-DY2PjKMU.js} +7200 -7488
  43. package/dist/{constants-D0gkYoE2.js → constants-T20xxyNf.js} +2 -2
  44. package/dist/{copy-DLf4aN7I.js → copy-BuQpJEzp.js} +2 -2
  45. package/dist/{dagre-6UL2VRFP-DRBWoQUw.js → dagre-6UL2VRFP-J0JKgwOt.js} +11 -11
  46. package/dist/{dagre-VYEPqXIV.js → dagre-By_QsQgc.js} +11 -11
  47. package/dist/{data-grid-overlay-editor-efe5ZagF.js → data-grid-overlay-editor-mfEJ5475.js} +2 -2
  48. package/dist/{diagram-PSM6KHXK-H66ATWP2.js → diagram-PSM6KHXK-DYgJuNk9.js} +18 -18
  49. package/dist/{diagram-QEK2KX5R-DItl5Wns.js → diagram-QEK2KX5R-CKdBR2sb.js} +14 -14
  50. package/dist/{diagram-S2PKOQOG-CtuW_ZuL.js → diagram-S2PKOQOG-Dpi7mo5W.js} +14 -14
  51. package/dist/dist-0Fif7jnk.js +5 -0
  52. package/dist/{dist-Dh3wkoyH.js → dist-4j4c7bjm.js} +2 -2
  53. package/dist/{dist-CDFZi-QD.js → dist-B3P2fFpz.js} +1 -1
  54. package/dist/{dist-BNyrZfqT.js → dist-B3pZ0Ab6.js} +2 -2
  55. package/dist/dist-B5h_9sHB.js +6 -0
  56. package/dist/dist-B9M6R5ye.js +5 -0
  57. package/dist/dist-BCt3tnck.js +8 -0
  58. package/dist/{dist-BrBucRXs.js → dist-BTfv03uy.js} +2 -2
  59. package/dist/dist-BUIJwMwn.js +8 -0
  60. package/dist/{dist-CYEylvZA.js → dist-BbbIBDiQ.js} +1 -1
  61. package/dist/{dist-KnujRhFL.js → dist-BcuoonNH.js} +4 -4
  62. package/dist/{dist-DJ6zJQZ4.js → dist-Bde4a2kU.js} +2 -2
  63. package/dist/{dist-t_qL7eB8.js → dist-Bfwsv11D.js} +2 -2
  64. package/dist/{dist-CNtV21T_.js → dist-BhM8gdSO.js} +4 -4
  65. package/dist/{dist-nuW5EDYT.js → dist-BotSqB48.js} +2 -2
  66. package/dist/dist-BpquMd3k.js +5 -0
  67. package/dist/dist-BzJsqYfz.js +5 -0
  68. package/dist/{dist-D029TiHd.js → dist-Bz_sYWbr.js} +2 -2
  69. package/dist/{dist-D3ZI9nhS.js → dist-C1BYNeCR.js} +4 -4
  70. package/dist/{dist-Bc5pmZIw.js → dist-C5VC_yzu.js} +1 -1
  71. package/dist/dist-CA5ELXAf.js +6 -0
  72. package/dist/dist-CLBRs6Uv.js +5 -0
  73. package/dist/{dist-Dhk6FMb0.js → dist-CLJWPTX2.js} +3 -3
  74. package/dist/{dist-C34oIrQ9.js → dist-CLUtPrdy.js} +1 -1
  75. package/dist/dist-CStVCMbq.js +5 -0
  76. package/dist/{dist-B8RaFTRF.js → dist-CUCNs1ja.js} +2 -2
  77. package/dist/dist-CZRIEY3Y.js +8 -0
  78. package/dist/{dist-UcOPnRMa.js → dist-CcXxepx6.js} +3 -3
  79. package/dist/dist-CuUHbFD0.js +5 -0
  80. package/dist/{dist-B8BjrFUE.js → dist-Cy1WxgBD.js} +5 -5
  81. package/dist/{dist-WdPUFc56.js → dist-D4CewLk6.js} +1 -1
  82. package/dist/{dist-DMZNjfX4.js → dist-DRfcqpxJ.js} +2 -2
  83. package/dist/dist-DV7Iabxb.js +8 -0
  84. package/dist/{dist-usPCDYx8.js → dist-D_bzzWBm.js} +1 -1
  85. package/dist/{dist-BvCfQQQE.js → dist-DgnE8F-r.js} +1 -1
  86. package/dist/{dist-JEhxD_cn.js → dist-DhHh0jLg.js} +1 -1
  87. package/dist/{dist-DGAfI2rB.js → dist-DqAWR3CS.js} +2 -2
  88. package/dist/{dist--sWVZwjW.js → dist-Du8WkPuU.js} +1 -1
  89. package/dist/dist-DuEeHMvL.js +5 -0
  90. package/dist/{dist-BTyJtnNg.js → dist-DxvORzUR.js} +1 -1
  91. package/dist/{dist-B507mf_I.js → dist-RqXTaiir.js} +2 -2
  92. package/dist/{dist-Yrfc6L0I.js → dist-fQ0ViXGs.js} +3 -3
  93. package/dist/{dist-B4LJpMEg.js → dist-h2c8sZvT.js} +1 -1
  94. package/dist/{dist-C2ej4eOH.js → dist-luvabDEB.js} +2 -2
  95. package/dist/{dist-B52GXZbd.js → dist-p2qyWijU.js} +2 -2
  96. package/dist/{erDiagram-Q2GNP2WA--19X2kU5.js → erDiagram-Q2GNP2WA-BU-m41EQ.js} +10 -10
  97. package/dist/{error-banner-CVkfBUT3.js → error-banner-5bz0L9hS.js} +3 -3
  98. package/dist/{esm-CWp0KQeK.js → esm-BfhQmZjp.js} +4 -4
  99. package/dist/{esm-DjNnlmpf.js → esm-Duie8iU-.js} +23 -23
  100. package/dist/{extends-vAi97cpa.js → extends-BgdxCfYu.js} +6 -6
  101. package/dist/{flatten-CzBvFdvC.js → flatten-Bbw7g6-K.js} +1 -1
  102. package/dist/{flowDiagram-NV44I4VS-DQmWlo7f.js → flowDiagram-NV44I4VS-CRoXKjGq.js} +10 -10
  103. package/dist/{formats-Dsy9kkZu.js → formats-DQ5qjo_Q.js} +4 -4
  104. package/dist/{ganttDiagram-JELNMOA3-BOGXJ8Lk.js → ganttDiagram-JELNMOA3-7mq5f9cO.js} +7 -7
  105. package/dist/{gitGraph-G5XIXVHT-DGlbae5m.js → gitGraph-G5XIXVHT-DiniR35k.js} +3 -3
  106. package/dist/{gitGraphDiagram-V2S2FVAM-DjzxfF0P.js → gitGraphDiagram-V2S2FVAM-Dfuokq6w.js} +13 -13
  107. package/dist/{glide-data-editor-DucgdjRo.js → glide-data-editor-DqRY9naW.js} +557 -557
  108. package/dist/{graphlib-CVPKjKCS.js → graphlib-Ns7y5crs.js} +5 -5
  109. package/dist/{hasIn-COs6vImh.js → hasIn-Deg7jl_j.js} +3 -3
  110. package/dist/{html-to-image-CpggM7u1.js → html-to-image-CiSinpSR.js} +109 -109
  111. package/dist/{info-VBDWY6EO-D2lvLLw5.js → info-VBDWY6EO-DVZvGhkQ.js} +3 -3
  112. package/dist/{infoDiagram-HS3SLOUP-ChNufFsP.js → infoDiagram-HS3SLOUP-CEnzWruK.js} +13 -13
  113. package/dist/{input-D4kjoQUB.js → input-CZD2z6X2.js} +70 -67
  114. package/dist/{isEmpty-Dd8mx_WL.js → isEmpty-CJJMn-QP.js} +1 -1
  115. package/dist/{isSymbol-BvIfMnn6.js → isSymbol-CoUCgMCM.js} +1 -1
  116. package/dist/{journeyDiagram-XKPGCS4Q-BO_O4Ij1.js → journeyDiagram-XKPGCS4Q-8XYSU1GI.js} +3 -3
  117. package/dist/{kanban-definition-3W4ZIXB7-CPpiiiWk.js → kanban-definition-3W4ZIXB7--9pT9z1R.js} +7 -7
  118. package/dist/{label-BLqV33b1.js → label-LWtdw5i8.js} +3 -3
  119. package/dist/{linear-2NnK4cxi.js → linear-B5-AFRiR.js} +2 -2
  120. package/dist/{loader-Dr8Qem8p.js → loader-BWLPpjKK.js} +2 -2
  121. package/dist/main.js +1145 -989
  122. package/dist/{memoize-C9ltv0Cw.js → memoize-BOtf2yFf.js} +1 -1
  123. package/dist/{merge-CHn7Yx0N.js → merge-Be1CqGnU.js} +1 -1
  124. package/dist/mermaid-4DMBBIKO-DIdL224_.js +6 -0
  125. package/dist/{mermaid-DO-Daq7u.js → mermaid-IU93XzmY.js} +44 -44
  126. package/dist/{mermaid-parser.core-DreccfmS.js → mermaid-parser.core-C3XRsazI.js} +8 -8
  127. package/dist/{min-BNz2lZfk.js → min-Dtgc8txR.js} +4 -4
  128. package/dist/{mindmap-definition-VGOIOE7T-CC1_Vl0f.js → mindmap-definition-VGOIOE7T-B-4mnfFG.js} +9 -9
  129. package/dist/{now-Sgq5m3D-.js → now-Ch98bJO_.js} +2 -2
  130. package/dist/{number-overlay-editor-CpKi64Fy.js → number-overlay-editor-D-a0qCT8.js} +1 -1
  131. package/dist/{once-rJImu7SE.js → once-DPuqGUeo.js} +1 -1
  132. package/dist/{packet-DYOGHKS2-CmWtF3uO.js → packet-DYOGHKS2-34raHOiB.js} +3 -3
  133. package/dist/{pick-CRAXxDYn.js → pick-D1Qo8s2C.js} +4 -4
  134. package/dist/{pie-VRWISCQL-B6u8vus8.js → pie-VRWISCQL-BaLlzZa3.js} +3 -3
  135. package/dist/{pieDiagram-ADFJNKIX-Di34MOFQ.js → pieDiagram-ADFJNKIX-Cr3cNpZY.js} +15 -15
  136. package/dist/{precisionRound-CnHPY_5v.js → precisionRound-Tqb4mg-H.js} +1 -1
  137. package/dist/{process-output-X8TR20AK.js → process-output-5qJjMRKh.js} +4 -4
  138. package/dist/{quadrantDiagram-AYHSOK5B-B9kVk1ny.js → quadrantDiagram-AYHSOK5B-BuNL8Q93.js} +4 -4
  139. package/dist/{radar-ZZBFDIW7-XAmXSa8s.js → radar-ZZBFDIW7-Ci7bfoZa.js} +3 -3
  140. package/dist/{react-vega-Dh6-UKKe.js → react-vega-B0sAlDTL.js} +9 -9
  141. package/dist/react-vega-B6ncY2Tp.js +9 -0
  142. package/dist/{requirementDiagram-UZGBJVZJ-BxGfGYEx.js → requirementDiagram-UZGBJVZJ-BG2lLUN1.js} +9 -9
  143. package/dist/{reveal-component-BMyi2UMr.js → reveal-component-Bq1iwOTq.js} +28 -28
  144. package/dist/{sankeyDiagram-TZEHDZUN-D09PBJ-n.js → sankeyDiagram-TZEHDZUN-DMal8sps.js} +3 -3
  145. package/dist/{sequenceDiagram-WL72ISMW-t_Dpemj0.js → sequenceDiagram-WL72ISMW-DT6Tk-Eo.js} +4 -4
  146. package/dist/{spec-hVaaZsY5.js → spec-a6DaqW__.js} +4 -4
  147. package/dist/{stateDiagram-FKZM4ZOC-B18gTP_j.js → stateDiagram-FKZM4ZOC-CB_lodq3.js} +12 -12
  148. package/dist/{stateDiagram-v2-4FDKWEC3-B6e_t14A.js → stateDiagram-v2-4FDKWEC3-E0RGjKsm.js} +10 -10
  149. package/dist/stex-KfRnSHzF.js +4 -0
  150. package/dist/{strings-BiIhGaI8.js → strings-Bu3vlb6W.js} +7 -7
  151. package/dist/style.css +1 -1
  152. package/dist/{swiper-component-DlD2GU2g.js → swiper-component-B2t5sN1q.js} +3 -3
  153. package/dist/{time-C1SGcFMH.js → time-CsmIF9YZ.js} +3 -3
  154. package/dist/{timeline-definition-IT6M3QCI-DJnh1ks5.js → timeline-definition-IT6M3QCI-NfSKRvH0.js} +2 -2
  155. package/dist/{toDate-CIpC_34u.js → toDate-ZVVIBmdk.js} +5 -5
  156. package/dist/{tooltip-DRaMBu06.js → tooltip-C5FYOpQc.js} +4 -4
  157. package/dist/{treemap-GDKQZRPO-Du95DV6u.js → treemap-GDKQZRPO-Cl6OQh8D.js} +3 -3
  158. package/dist/{types-Dzuoc3LN.js → types-CVvp1fKr.js} +2 -9
  159. package/dist/{useAsyncData-C56Khv_R.js → useAsyncData-C008zUPi.js} +2 -2
  160. package/dist/{useDateFormatter-B_9k85Ex.js → useDateFormatter-BA4FCquG.js} +2 -2
  161. package/dist/{useDeepCompareMemoize-Dt98v2ua.js → useDeepCompareMemoize-BrA3_n61.js} +1 -1
  162. package/dist/{useIframeCapabilities-BkYHTrss.js → useIframeCapabilities-C4JTXTIh.js} +1 -1
  163. package/dist/{useLifecycle-BF6-z62y.js → useLifecycle-BNaoJ5a4.js} +4 -4
  164. package/dist/{useTheme-DykuNHR2.js → useTheme-7O0YWlE5.js} +3 -3
  165. package/dist/{vega-component-cSdqoAxe.js → vega-component-DJNmOdUj.js} +18 -18
  166. package/dist/{vega-loader.browser-3_z8GoFC.js → vega-loader.browser-CZ-J8Py3.js} +3 -3
  167. package/dist/{xychartDiagram-PRI3JC2R-Dk2d_bX0.js → xychartDiagram-PRI3JC2R-BvwftqMA.js} +9 -9
  168. package/dist/{zod-BWkcDORu.js → zod-CoBiJ5v4.js} +3 -3
  169. package/package.json +1 -1
  170. package/src/components/data-table/TableBottomBar.tsx +30 -6
  171. package/src/components/data-table/__tests__/TableBottomBar.test.tsx +73 -0
  172. package/src/components/data-table/__tests__/column-header.test.tsx +110 -277
  173. package/src/components/data-table/__tests__/data-table.test.tsx +52 -1
  174. package/src/components/data-table/__tests__/date-filter-inputs.test.tsx +33 -0
  175. package/src/components/data-table/__tests__/filter-pill-editor.test.tsx +75 -38
  176. package/src/components/data-table/__tests__/filter-pills.test.tsx +287 -0
  177. package/src/components/data-table/__tests__/filter-test-utils.ts +47 -0
  178. package/src/components/data-table/__tests__/filters.test.ts +5 -5
  179. package/src/components/data-table/__tests__/header-items.test.tsx +47 -1
  180. package/src/components/data-table/__tests__/useColumnVisibility.test.ts +42 -0
  181. package/src/components/data-table/add-filter-button.tsx +85 -0
  182. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +7 -1
  183. package/src/components/data-table/column-header.tsx +94 -691
  184. package/src/components/data-table/columns.tsx +3 -4
  185. package/src/components/data-table/context-menu.tsx +26 -12
  186. package/src/components/data-table/data-table.tsx +118 -56
  187. package/src/components/data-table/date-filter-inputs.tsx +13 -10
  188. package/src/components/data-table/filter-by-values-picker.tsx +13 -19
  189. package/src/components/data-table/filter-editor-context.tsx +34 -0
  190. package/src/components/data-table/filter-pill-editor.tsx +152 -175
  191. package/src/components/data-table/filter-pills.tsx +190 -153
  192. package/src/components/data-table/filters/builders.ts +102 -0
  193. package/src/components/data-table/filters/defaults.ts +31 -0
  194. package/src/components/data-table/filters/format.ts +131 -0
  195. package/src/components/data-table/filters/guards.ts +51 -0
  196. package/src/components/data-table/filters/index.ts +7 -0
  197. package/src/components/data-table/filters/operators.ts +76 -0
  198. package/src/components/data-table/filters/serialize.ts +186 -0
  199. package/src/components/data-table/filters/types.ts +33 -0
  200. package/src/components/data-table/header-items.tsx +25 -85
  201. package/src/components/data-table/hooks/use-column-visibility.ts +42 -0
  202. package/src/components/data-table/pagination.tsx +16 -3
  203. package/src/components/data-table/value-chips.tsx +52 -0
  204. package/src/components/ui/number-field.tsx +13 -1
  205. package/src/plugins/impl/DataTablePlugin.tsx +4 -0
  206. package/src/utils/dates.ts +39 -0
  207. package/dist/ErrorBoundary-D3wrPNma.js +0 -167
  208. package/dist/architecture-7HQA4BMR-CS9jOrqM.js +0 -6
  209. package/dist/dist-21ButRCu.js +0 -8
  210. package/dist/dist-B--tLnAP.js +0 -5
  211. package/dist/dist-BoHGySTM.js +0 -5
  212. package/dist/dist-ByAz19Qc.js +0 -5
  213. package/dist/dist-C1Ap5CYU.js +0 -5
  214. package/dist/dist-C93EysN4.js +0 -5
  215. package/dist/dist-CY-lVor6.js +0 -8
  216. package/dist/dist-CYDuv4bR.js +0 -8
  217. package/dist/dist-Cfo5EE2t.js +0 -6
  218. package/dist/dist-CjivSDvN.js +0 -5
  219. package/dist/dist-Cqwx-MH7.js +0 -5
  220. package/dist/dist-DbpcoFAV.js +0 -6
  221. package/dist/dist-FUNenbiQ.js +0 -5
  222. package/dist/dist-zhSud5X3.js +0 -8
  223. package/dist/mermaid-4DMBBIKO-B7VQMwJx.js +0 -6
  224. package/dist/react-vega-Cavbrg4l.js +0 -9
  225. package/dist/stex-ChDHQs3R.js +0 -4
  226. package/src/components/data-table/__tests__/column-header.test.ts +0 -65
  227. package/src/components/data-table/filters.ts +0 -386
  228. /package/dist/{_baseFor-BGiY-cm1.js → _baseFor-4jw-lnCC.js} +0 -0
  229. /package/dist/{clsx-CyyyQ8Ue.js → clsx-CIWA5tNO.js} +0 -0
  230. /package/dist/{defaultLocale-DoeErsX2.js → defaultLocale-BoHTsDG6.js} +0 -0
  231. /package/dist/{defaultLocale-BpsHxBd7.js → defaultLocale-u-3osm0P.js} +0 -0
  232. /package/dist/{dist-CCADb07R.js → dist-DNdhYsgW.js} +0 -0
  233. /package/dist/{emotion-is-prop-valid.esm-DtW2o230.js → emotion-is-prop-valid.esm-DzSb5hsH.js} +0 -0
  234. /package/dist/{invariant-UcGKQEhF.js → invariant-wRzNXIsJ.js} +0 -0
  235. /package/dist/{jsx-runtime-COBk7ree.js → jsx-runtime-DebpN0FN.js} +0 -0
  236. /package/dist/{main-CThhXnXU.js → main-Tj_-QTyF.js} +0 -0
  237. /package/dist/{micromark-factory-space-CwHmg6iz.js → micromark-factory-space-DF2w36zS.js} +0 -0
  238. /package/dist/{ordinal-B43ZeR68.js → ordinal-ArJavP1Q.js} +0 -0
  239. /package/dist/{purify.es-DT70lfR0.js → purify.es-H92eMd9-.js} +0 -0
  240. /package/dist/{range-BOiA8qqU.js → range-C-rmrM1O.js} +0 -0
  241. /package/dist/{react-dom-BWRJ_g_k.js → react-dom-BTJzcVJ9.js} +0 -0
  242. /package/dist/{stex-DrxP7bb3.js → stex-BIsgBmK4.js} +0 -0
@@ -1,10 +1,17 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
- import type { Column, Table } from "@tanstack/react-table";
3
2
  import { fireEvent, render, screen } from "@testing-library/react";
4
3
  import { beforeAll, describe, expect, it, vi } from "vitest";
5
4
  import { TooltipProvider } from "@/components/ui/tooltip";
6
- import { FilterPillEditor } from "../filter-pill-editor";
7
- import { Filter } from "../filters";
5
+ import {
6
+ buildEditorSnapshot,
7
+ buildEmptyFilterValue,
8
+ FilterPillEditor,
9
+ } from "../filter-pill-editor";
10
+ import { defaultFilterValueFor, Filter } from "../filters";
11
+ import {
12
+ buildFilterTestTable,
13
+ type FilterColumnSpec,
14
+ } from "./filter-test-utils";
8
15
 
9
16
  const renderWithProviders = (ui: React.ReactElement) =>
10
17
  render(<TooltipProvider>{ui}</TooltipProvider>);
@@ -24,37 +31,15 @@ beforeAll(() => {
24
31
  }
25
32
  });
26
33
 
27
- function makeColumn(
28
- id: string,
29
- filterType:
30
- | "text"
31
- | "number"
32
- | "boolean"
33
- | "select"
34
- | "date"
35
- | "datetime"
36
- | "time",
37
- ): Column<unknown, unknown> {
38
- return {
39
- id,
40
- columnDef: { meta: { filterType, dataType: "string" } },
41
- } as unknown as Column<unknown, unknown>;
42
- }
34
+ const DEFAULT_COLUMNS: FilterColumnSpec[] = [
35
+ { id: "name", filterType: "text" },
36
+ { id: "age", filterType: "number" },
37
+ { id: "when", filterType: "date" },
38
+ { id: "at", filterType: "datetime" },
39
+ { id: "clock", filterType: "time" },
40
+ ];
43
41
 
44
- function mockTable(): Table<unknown> {
45
- const columns = [
46
- makeColumn("name", "text"),
47
- makeColumn("age", "number"),
48
- makeColumn("when", "date"),
49
- makeColumn("at", "datetime"),
50
- makeColumn("clock", "time"),
51
- ];
52
- return {
53
- getAllColumns: () => columns,
54
- getColumn: (id: string) => columns.find((c) => c.id === id),
55
- setColumnFilters: vi.fn(),
56
- } as unknown as Table<unknown>;
57
- }
42
+ const mockTable = () => buildFilterTestTable(DEFAULT_COLUMNS).table;
58
43
 
59
44
  async function calculateTopK() {
60
45
  return {
@@ -111,7 +96,8 @@ describe("FilterPillEditor — snapshot rehydration", () => {
111
96
  onClose={vi.fn()}
112
97
  />,
113
98
  );
114
- expect(await screen.findByText("[a, b]")).toBeInTheDocument();
99
+ expect(await screen.findByText("a")).toBeInTheDocument();
100
+ expect(screen.getByText("b")).toBeInTheDocument();
115
101
  });
116
102
 
117
103
  it("rehydrates a text contains snapshot with seeded text", () => {
@@ -232,7 +218,7 @@ describe("FilterPillEditor — date/datetime/time", () => {
232
218
 
233
219
  describe("FilterPillEditor — apply", () => {
234
220
  it("commits a number > filter via setColumnFilters", () => {
235
- const table = mockTable();
221
+ const { table, setColumnFilters } = buildFilterTestTable(DEFAULT_COLUMNS);
236
222
  const onClose = vi.fn();
237
223
  renderWithProviders(
238
224
  <FilterPillEditor
@@ -245,11 +231,10 @@ describe("FilterPillEditor — apply", () => {
245
231
  />,
246
232
  );
247
233
  fireEvent.click(screen.getByLabelText("Apply filter"));
248
- expect(table.setColumnFilters).toHaveBeenCalledTimes(1);
234
+ expect(setColumnFilters).toHaveBeenCalledTimes(1);
249
235
  expect(onClose).toHaveBeenCalledTimes(1);
250
236
 
251
- const updater = (table.setColumnFilters as ReturnType<typeof vi.fn>).mock
252
- .calls[0][0];
237
+ const updater = setColumnFilters.mock.calls[0][0];
253
238
  const next = updater([]);
254
239
  expect(next).toEqual([
255
240
  {
@@ -259,3 +244,55 @@ describe("FilterPillEditor — apply", () => {
259
244
  ]);
260
245
  });
261
246
  });
247
+
248
+ describe("defaultFilterValueFor", () => {
249
+ it.each([
250
+ ["number", "between", { type: "number", operator: "between" }],
251
+ ["number", ">", { type: "number", operator: ">" }],
252
+ ["text", "contains", { type: "text", operator: "contains" }],
253
+ ["text", "in", { type: "text", operator: "in", values: [] }],
254
+ ["text", "not_in", { type: "text", operator: "not_in", values: [] }],
255
+ ["boolean", "is_true", { type: "boolean", operator: "is_true" }],
256
+ ["number", "in", { type: "number", operator: "in", values: [] }],
257
+ ["number", "not_in", { type: "number", operator: "not_in", values: [] }],
258
+ ["date", "between", { type: "date", operator: "between" }],
259
+ ["datetime", "between", { type: "datetime", operator: "between" }],
260
+ ["time", "between", { type: "time", operator: "between" }],
261
+ ] as const)("seeds %s + %s", (type, operator, expected) => {
262
+ expect(defaultFilterValueFor(type, operator)).toEqual(expected);
263
+ });
264
+ });
265
+
266
+ describe("buildEmptyFilterValue", () => {
267
+ it.each([
268
+ ["name", { type: "text", operator: "contains" }],
269
+ ["age", { type: "number", operator: "between" }],
270
+ ["when", { type: "date", operator: "==" }],
271
+ ["at", { type: "datetime", operator: "==" }],
272
+ ["clock", { type: "time", operator: "between" }],
273
+ ] as const)(
274
+ "picks the dtype-default operator for %s",
275
+ (columnId, expected) => {
276
+ const column = mockTable().getColumn(columnId)!;
277
+ expect(buildEmptyFilterValue(column)).toEqual(expected);
278
+ },
279
+ );
280
+ });
281
+
282
+ describe("buildEditorSnapshot", () => {
283
+ it("uses dtype-default operator when none provided", () => {
284
+ const column = mockTable().getColumn("name")!;
285
+ expect(buildEditorSnapshot(column)).toEqual({
286
+ columnId: "name",
287
+ value: { type: "text", operator: "contains" },
288
+ });
289
+ });
290
+
291
+ it("honors an explicit operator override", () => {
292
+ const column = mockTable().getColumn("name")!;
293
+ expect(buildEditorSnapshot(column, { operator: "in" })).toEqual({
294
+ columnId: "name",
295
+ value: { type: "text", operator: "in", values: [] },
296
+ });
297
+ });
298
+ });
@@ -0,0 +1,287 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import type { ColumnFiltersState } from "@tanstack/react-table";
3
+ import { fireEvent, render, screen } from "@testing-library/react";
4
+ import { beforeAll, describe, expect, it, vi } from "vitest";
5
+ import { TooltipProvider } from "@/components/ui/tooltip";
6
+ import { AddFilterButton } from "../add-filter-button";
7
+ import { FilterPills } from "../filter-pills";
8
+ import { buildEditorSnapshot } from "../filter-pill-editor";
9
+ import { Filter, type Snapshot } from "../filters";
10
+ import {
11
+ buildFilterTestTable,
12
+ type FilterColumnSpec,
13
+ } from "./filter-test-utils";
14
+
15
+ const renderWithProviders = (ui: React.ReactElement) =>
16
+ render(<TooltipProvider>{ui}</TooltipProvider>);
17
+
18
+ beforeAll(() => {
19
+ global.HTMLElement.prototype.scrollIntoView = () => {
20
+ // jsdom does not implement scrollIntoView; cmdk calls it on selection.
21
+ };
22
+ if (!global.HTMLElement.prototype.hasPointerCapture) {
23
+ global.HTMLElement.prototype.hasPointerCapture = () => false;
24
+ }
25
+ if (!global.HTMLElement.prototype.scrollTo) {
26
+ global.HTMLElement.prototype.scrollTo = () => {
27
+ // noop for jsdom
28
+ };
29
+ }
30
+ });
31
+
32
+ const DEFAULT_COLUMNS: FilterColumnSpec[] = [
33
+ { id: "name", filterType: "text" },
34
+ { id: "age", filterType: "number" },
35
+ { id: "when", filterType: "date" },
36
+ ];
37
+
38
+ const mockTable = (specs: FilterColumnSpec[] = DEFAULT_COLUMNS) =>
39
+ buildFilterTestTable(specs).table;
40
+
41
+ describe("FilterPills — strip gating", () => {
42
+ it("renders nothing when there are no filters and no pending add-snapshot", () => {
43
+ const { container } = renderWithProviders(
44
+ <FilterPills
45
+ filters={[]}
46
+ table={mockTable()}
47
+ addFilterSnapshot={null}
48
+ onAddFilterSnapshotChange={vi.fn()}
49
+ />,
50
+ );
51
+ expect(container.firstChild).toBeNull();
52
+ });
53
+
54
+ it("renders the strip and + button when at least one filter exists", () => {
55
+ const filters: ColumnFiltersState = [
56
+ {
57
+ id: "age",
58
+ value: Filter.number({ operator: ">", value: 18 }),
59
+ },
60
+ ];
61
+ renderWithProviders(
62
+ <FilterPills
63
+ filters={filters}
64
+ table={mockTable()}
65
+ addFilterSnapshot={null}
66
+ onAddFilterSnapshotChange={vi.fn()}
67
+ />,
68
+ );
69
+ expect(screen.getByLabelText("Add filter")).toBeInTheDocument();
70
+ expect(screen.getByLabelText("Edit filter on age")).toBeInTheDocument();
71
+ });
72
+
73
+ it("mounts the strip with no filters when a pending add-snapshot is set", () => {
74
+ const table = mockTable();
75
+ const snapshot: Snapshot = buildEditorSnapshot(table.getAllColumns()[0]);
76
+ renderWithProviders(
77
+ <FilterPills
78
+ filters={[]}
79
+ table={table}
80
+ addFilterSnapshot={snapshot}
81
+ onAddFilterSnapshotChange={vi.fn()}
82
+ />,
83
+ );
84
+ expect(screen.getByLabelText("Add filter")).toBeInTheDocument();
85
+ });
86
+ });
87
+
88
+ describe("AddFilterButton", () => {
89
+ it("does not render when there are no editable columns", () => {
90
+ const table = mockTable([{ id: "opaque" }]);
91
+ const { container } = renderWithProviders(
92
+ <AddFilterButton
93
+ table={table}
94
+ snapshot={null}
95
+ onSnapshotChange={vi.fn()}
96
+ />,
97
+ );
98
+ expect(container.firstChild).toBeNull();
99
+ });
100
+
101
+ it("seeds the editor with the first editable column's dtype default on click", () => {
102
+ const table = mockTable();
103
+ const onSnapshotChange = vi.fn();
104
+ renderWithProviders(
105
+ <AddFilterButton
106
+ table={table}
107
+ snapshot={null}
108
+ onSnapshotChange={onSnapshotChange}
109
+ />,
110
+ );
111
+ fireEvent.click(screen.getByLabelText("Add filter"));
112
+ expect(onSnapshotChange).toHaveBeenCalledTimes(1);
113
+ expect(onSnapshotChange).toHaveBeenCalledWith({
114
+ columnId: "name",
115
+ value: { type: "text", operator: "contains" },
116
+ });
117
+ });
118
+
119
+ it("clears the snapshot when the popover closes", () => {
120
+ const onSnapshotChange = vi.fn();
121
+ const snapshot: Snapshot = {
122
+ columnId: "age",
123
+ value: Filter.number({ operator: ">", value: 5 }),
124
+ };
125
+ renderWithProviders(
126
+ <AddFilterButton
127
+ table={mockTable()}
128
+ snapshot={snapshot}
129
+ onSnapshotChange={onSnapshotChange}
130
+ />,
131
+ );
132
+ // Pressing Escape closes the popover via radix.
133
+ fireEvent.keyDown(document.body, { key: "Escape" });
134
+ expect(onSnapshotChange).toHaveBeenCalledWith(null);
135
+ });
136
+
137
+ it("renders the editor when a snapshot is provided", () => {
138
+ renderWithProviders(
139
+ <AddFilterButton
140
+ table={mockTable()}
141
+ snapshot={{
142
+ columnId: "age",
143
+ value: Filter.number({ operator: ">", value: 7 }),
144
+ }}
145
+ onSnapshotChange={vi.fn()}
146
+ />,
147
+ );
148
+ expect(screen.getByLabelText("Apply filter")).toBeInTheDocument();
149
+ expect(screen.getByDisplayValue("7")).toBeInTheDocument();
150
+ });
151
+ });
152
+
153
+ describe("FilterPills — pill edit", () => {
154
+ it("opens the editor with the pill's snapshot when clicked", () => {
155
+ const filters: ColumnFiltersState = [
156
+ {
157
+ id: "age",
158
+ value: Filter.number({ operator: ">", value: 42 }),
159
+ },
160
+ ];
161
+ renderWithProviders(
162
+ <FilterPills
163
+ filters={filters}
164
+ table={mockTable()}
165
+ addFilterSnapshot={null}
166
+ onAddFilterSnapshotChange={vi.fn()}
167
+ />,
168
+ );
169
+ fireEvent.click(screen.getByLabelText("Edit filter on age"));
170
+ expect(screen.getByLabelText("Apply filter")).toBeInTheDocument();
171
+ expect(screen.getByDisplayValue("42")).toBeInTheDocument();
172
+ });
173
+
174
+ it("splices in place via editIndex when applying an edit", () => {
175
+ const { table, setColumnFilters } = buildFilterTestTable(DEFAULT_COLUMNS);
176
+ const filters: ColumnFiltersState = [
177
+ {
178
+ id: "name",
179
+ value: Filter.text({ operator: "contains", text: "foo" }),
180
+ },
181
+ {
182
+ id: "age",
183
+ value: Filter.number({ operator: ">", value: 1 }),
184
+ },
185
+ ];
186
+ renderWithProviders(
187
+ <FilterPills
188
+ filters={filters}
189
+ table={table}
190
+ addFilterSnapshot={null}
191
+ onAddFilterSnapshotChange={vi.fn()}
192
+ />,
193
+ );
194
+ fireEvent.click(screen.getByLabelText("Edit filter on age"));
195
+ fireEvent.click(screen.getByLabelText("Apply filter"));
196
+
197
+ const updater = setColumnFilters.mock.calls[0][0];
198
+ const next = updater(filters);
199
+ expect(next).toHaveLength(2);
200
+ expect(next[0].id).toBe("name");
201
+ expect(next[1]).toEqual({
202
+ id: "age",
203
+ value: { type: "number", operator: ">", value: 1 },
204
+ });
205
+ });
206
+ });
207
+
208
+ describe("FilterPills — column-header trigger integration", () => {
209
+ it("opens the editor under the + button with a column-header-supplied snapshot", () => {
210
+ // Simulates what DataTable does when column-header calls
211
+ // requestAddFilter({ columnId: "name" }): it computes a snapshot via
212
+ // buildEditorSnapshot and passes it down as addFilterSnapshot.
213
+ const table = mockTable();
214
+ const snapshot = buildEditorSnapshot(table.getColumn("name")!);
215
+ renderWithProviders(
216
+ <FilterPills
217
+ filters={[]}
218
+ table={table}
219
+ addFilterSnapshot={snapshot}
220
+ onAddFilterSnapshotChange={vi.fn()}
221
+ />,
222
+ );
223
+ expect(screen.getByLabelText("Apply filter")).toBeInTheDocument();
224
+ // Default text operator is "contains" — the text input renders.
225
+ expect(screen.getByPlaceholderText("Text…")).toBeInTheDocument();
226
+ });
227
+
228
+ it("pre-selects 'in' operator when column-header 'Filter by values' is used", () => {
229
+ const table = mockTable();
230
+ const snapshot = buildEditorSnapshot(table.getColumn("name")!, {
231
+ operator: "in",
232
+ });
233
+ renderWithProviders(
234
+ <FilterPills
235
+ filters={[]}
236
+ table={table}
237
+ addFilterSnapshot={snapshot}
238
+ onAddFilterSnapshotChange={vi.fn()}
239
+ />,
240
+ );
241
+ expect(snapshot.value).toEqual({
242
+ type: "text",
243
+ operator: "in",
244
+ values: [],
245
+ });
246
+ expect(screen.getByLabelText("Apply filter")).toBeInTheDocument();
247
+ });
248
+ });
249
+
250
+ describe("FilterPills — overflow", () => {
251
+ it("does not render the 'See all' button when there is no overflow", () => {
252
+ const filters: ColumnFiltersState = [
253
+ { id: "age", value: Filter.number({ operator: ">", value: 5 }) },
254
+ ];
255
+ renderWithProviders(
256
+ <FilterPills
257
+ filters={filters}
258
+ table={mockTable()}
259
+ addFilterSnapshot={null}
260
+ onAddFilterSnapshotChange={vi.fn()}
261
+ />,
262
+ );
263
+ expect(screen.queryByLabelText("See all filters")).toBeNull();
264
+ });
265
+ });
266
+
267
+ describe("FilterPill — value truncation", () => {
268
+ it("renders the value with truncation classes inside a tooltip trigger", () => {
269
+ const filters: ColumnFiltersState = [
270
+ {
271
+ id: "name",
272
+ value: Filter.text({ operator: "contains", text: "x".repeat(100) }),
273
+ },
274
+ ];
275
+ renderWithProviders(
276
+ <FilterPills
277
+ filters={filters}
278
+ table={mockTable()}
279
+ addFilterSnapshot={null}
280
+ onAddFilterSnapshotChange={vi.fn()}
281
+ />,
282
+ );
283
+ const valueSpan = screen.getByText(/^"x{100}"$/);
284
+ expect(valueSpan.className).toMatch(/overflow-hidden/);
285
+ expect(valueSpan.className).toMatch(/text-ellipsis/);
286
+ });
287
+ });
@@ -0,0 +1,47 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import {
4
+ type ColumnDef,
5
+ getCoreRowModel,
6
+ type Table,
7
+ useReactTable,
8
+ } from "@tanstack/react-table";
9
+ import { renderHook } from "@testing-library/react";
10
+ import { vi } from "vitest";
11
+ import type { FilterType } from "../filters";
12
+
13
+ export interface FilterColumnSpec {
14
+ id: string;
15
+ filterType?: FilterType;
16
+ dtype?: string;
17
+ }
18
+
19
+ export interface FilterTestHarness {
20
+ table: Table<unknown>;
21
+ setColumnFilters: ReturnType<typeof vi.fn>;
22
+ }
23
+
24
+ export function buildFilterTestTable(
25
+ specs: FilterColumnSpec[],
26
+ ): FilterTestHarness {
27
+ const setColumnFilters = vi.fn();
28
+ const columns: Array<ColumnDef<unknown>> = specs.map((spec) => ({
29
+ id: spec.id,
30
+ accessorFn: () => undefined,
31
+ header: spec.id,
32
+ meta: {
33
+ ...(spec.filterType !== undefined ? { filterType: spec.filterType } : {}),
34
+ ...(spec.dtype !== undefined ? { dtype: spec.dtype } : {}),
35
+ },
36
+ }));
37
+ const { result } = renderHook(() =>
38
+ useReactTable<unknown>({
39
+ data: [],
40
+ columns,
41
+ locale: "en-US",
42
+ getCoreRowModel: getCoreRowModel(),
43
+ onColumnFiltersChange: setColumnFilters,
44
+ }),
45
+ );
46
+ return { table: result.current, setColumnFilters };
47
+ }
@@ -172,7 +172,7 @@ describe("filterToFilterCondition", () => {
172
172
  it("handles boolean true filter", () => {
173
173
  const result = filterToFilterCondition(
174
174
  "active",
175
- Filter.boolean({ value: true }),
175
+ Filter.boolean({ operator: "is_true" }),
176
176
  );
177
177
  expect(result).toEqual([
178
178
  {
@@ -187,7 +187,7 @@ describe("filterToFilterCondition", () => {
187
187
  it("handles boolean false filter", () => {
188
188
  const result = filterToFilterCondition(
189
189
  "active",
190
- Filter.boolean({ value: false }),
190
+ Filter.boolean({ operator: "is_false" }),
191
191
  );
192
192
  expect(result).toEqual([
193
193
  {
@@ -199,16 +199,16 @@ describe("filterToFilterCondition", () => {
199
199
  ]);
200
200
  });
201
201
 
202
- it("handles select in filter", () => {
202
+ it("handles number in filter", () => {
203
203
  const result = filterToFilterCondition(
204
204
  "status",
205
- Filter.select({ options: ["a", "b"], operator: "in" }),
205
+ Filter.number({ operator: "in", values: [1, 2] }),
206
206
  );
207
207
  expect(result).toEqual([
208
208
  {
209
209
  column_id: "status",
210
210
  operator: "in",
211
- value: ["a", "b"],
211
+ value: [1, 2],
212
212
  type: "condition",
213
213
  negate: false,
214
214
  },
@@ -1,7 +1,14 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
- import type { SortingState } from "@tanstack/react-table";
3
+ import type { Column, SortingState } from "@tanstack/react-table";
4
+ import { fireEvent, render, screen } from "@testing-library/react";
4
5
  import { describe, expect, it, vi } from "vitest";
6
+ import {
7
+ DropdownMenu,
8
+ DropdownMenuContent,
9
+ DropdownMenuTrigger,
10
+ } from "@/components/ui/dropdown-menu";
11
+ import { HideColumn } from "../header-items";
5
12
 
6
13
  describe("multi-column sorting logic", () => {
7
14
  // Extract the core sorting logic to test in isolation
@@ -146,3 +153,42 @@ describe("multi-column sorting logic", () => {
146
153
  // After removal, dept should move from priority 3 to priority 2
147
154
  });
148
155
  });
156
+
157
+ describe("HideColumn", () => {
158
+ const makeColumn = ({
159
+ canHide = true,
160
+ toggleVisibility = vi.fn(),
161
+ }: {
162
+ canHide?: boolean;
163
+ toggleVisibility?: (value?: boolean) => void;
164
+ } = {}) =>
165
+ ({
166
+ getCanHide: () => canHide,
167
+ toggleVisibility,
168
+ }) as unknown as Column<unknown, unknown>;
169
+
170
+ const renderInMenu = (node: React.ReactNode) =>
171
+ render(
172
+ <DropdownMenu open={true}>
173
+ <DropdownMenuTrigger />
174
+ <DropdownMenuContent>{node}</DropdownMenuContent>
175
+ </DropdownMenu>,
176
+ );
177
+
178
+ it("renders 'Hide column' when canHide is true", () => {
179
+ renderInMenu(<HideColumn column={makeColumn()} />);
180
+ expect(screen.getByText("Hide column")).toBeInTheDocument();
181
+ });
182
+
183
+ it("returns null when getCanHide is false", () => {
184
+ renderInMenu(<HideColumn column={makeColumn({ canHide: false })} />);
185
+ expect(screen.queryByText("Hide column")).toBeNull();
186
+ });
187
+
188
+ it("calls toggleVisibility(false) on click", () => {
189
+ const toggleVisibility = vi.fn();
190
+ renderInMenu(<HideColumn column={makeColumn({ toggleVisibility })} />);
191
+ fireEvent.click(screen.getByText("Hide column"));
192
+ expect(toggleVisibility).toHaveBeenCalledWith(false);
193
+ });
194
+ });
@@ -0,0 +1,42 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { act, renderHook } from "@testing-library/react";
4
+ import { describe, expect, it } from "vitest";
5
+ import { useColumnVisibility } from "../hooks/use-column-visibility";
6
+
7
+ describe("useColumnVisibility", () => {
8
+ it("should initialize with correct default values", () => {
9
+ const { result } = renderHook(() => useColumnVisibility());
10
+ expect(result.current.columnVisibility).toEqual({});
11
+ });
12
+
13
+ it("should seed hidden columns as { name: false }", () => {
14
+ const { result } = renderHook(() => useColumnVisibility(["a", "b"]));
15
+ expect(result.current.columnVisibility).toEqual({ a: false, b: false });
16
+ });
17
+
18
+ it("should treat empty hidden list as a no-op", () => {
19
+ const { result } = renderHook(() => useColumnVisibility([]));
20
+ expect(result.current.columnVisibility).toEqual({});
21
+ });
22
+
23
+ it("should update visibility state via setter", () => {
24
+ const { result } = renderHook(() => useColumnVisibility(["a"]));
25
+
26
+ act(() => {
27
+ result.current.setColumnVisibility({ a: true, b: false });
28
+ });
29
+
30
+ expect(result.current.columnVisibility).toEqual({ a: true, b: false });
31
+ });
32
+
33
+ it("should handle functional updates", () => {
34
+ const { result } = renderHook(() => useColumnVisibility(["a"]));
35
+
36
+ act(() => {
37
+ result.current.setColumnVisibility((prev) => ({ ...prev, c: false }));
38
+ });
39
+
40
+ expect(result.current.columnVisibility).toEqual({ a: false, c: false });
41
+ });
42
+ });