microui-wc 0.1.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.
- package/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +33 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +28 -0
- package/.github/workflows/ci.yml +42 -0
- package/.github/workflows/deploy-pages.yml +112 -0
- package/AGENTS.md +2366 -0
- package/CHANGELOG.md +47 -0
- package/CODE_OF_CONDUCT.md +59 -0
- package/CONTRIBUTING.md +156 -0
- package/LICENSE +190 -0
- package/README.md +254 -0
- package/SECURITY.md +58 -0
- package/app/.generated/routes/alerts.js +8 -0
- package/app/.generated/routes/avatars.js +8 -0
- package/app/.generated/routes/badges.js +8 -0
- package/app/.generated/routes/buttons.js +10 -0
- package/app/.generated/routes/cards.js +10 -0
- package/app/.generated/routes/checkboxes.js +9 -0
- package/app/.generated/routes/chips.js +8 -0
- package/app/.generated/routes/dropdowns.js +9 -0
- package/app/.generated/routes/home.js +7 -0
- package/app/.generated/routes/icons.js +9 -0
- package/app/.generated/routes/inputs.js +10 -0
- package/app/.generated/routes/installation.js +7 -0
- package/app/.generated/routes/layout.js +9 -0
- package/app/.generated/routes/modals.js +9 -0
- package/app/.generated/routes/navbar.js +7 -0
- package/app/.generated/routes/progress.js +9 -0
- package/app/.generated/routes/radios.js +9 -0
- package/app/.generated/routes/switches.js +9 -0
- package/app/.generated/routes/tabs.js +8 -0
- package/app/.generated/routes/toasts.js +9 -0
- package/app/index.html +67 -0
- package/app/pages/alerts.html +23 -0
- package/app/pages/avatars.html +22 -0
- package/app/pages/badges.html +22 -0
- package/app/pages/buttons.html +71 -0
- package/app/pages/cards.html +54 -0
- package/app/pages/checkboxes.html +39 -0
- package/app/pages/chips.html +23 -0
- package/app/pages/dropdowns.html +41 -0
- package/app/pages/home.html +59 -0
- package/app/pages/icons.html +29 -0
- package/app/pages/inputs.html +66 -0
- package/app/pages/installation.html +34 -0
- package/app/pages/layout.html +30 -0
- package/app/pages/modals.html +21 -0
- package/app/pages/navbar.html +22 -0
- package/app/pages/progress.html +35 -0
- package/app/pages/radios.html +40 -0
- package/app/pages/switches.html +39 -0
- package/app/pages/tabs.html +30 -0
- package/app/pages/toasts.html +22 -0
- package/app-dist/index.html +67 -0
- package/app-dist/pages/alerts.html +23 -0
- package/app-dist/pages/avatars.html +22 -0
- package/app-dist/pages/badges.html +22 -0
- package/app-dist/pages/buttons.html +71 -0
- package/app-dist/pages/cards.html +54 -0
- package/app-dist/pages/checkboxes.html +39 -0
- package/app-dist/pages/chips.html +23 -0
- package/app-dist/pages/dropdowns.html +41 -0
- package/app-dist/pages/home.html +59 -0
- package/app-dist/pages/icons.html +29 -0
- package/app-dist/pages/inputs.html +66 -0
- package/app-dist/pages/installation.html +34 -0
- package/app-dist/pages/layout.html +30 -0
- package/app-dist/pages/modals.html +21 -0
- package/app-dist/pages/navbar.html +22 -0
- package/app-dist/pages/progress.html +35 -0
- package/app-dist/pages/radios.html +40 -0
- package/app-dist/pages/switches.html +39 -0
- package/app-dist/pages/tabs.html +30 -0
- package/app-dist/pages/toasts.html +22 -0
- package/app-dist/pages.json +217 -0
- package/app-dist/routes/alerts.js +5 -0
- package/app-dist/routes/avatars.js +1 -0
- package/app-dist/routes/badges.js +1 -0
- package/app-dist/routes/buttons.js +1 -0
- package/app-dist/routes/cards.js +1 -0
- package/app-dist/routes/checkboxes.js +9 -0
- package/app-dist/routes/chips.js +4 -0
- package/app-dist/routes/chunk-019e5e2f.js +5 -0
- package/app-dist/routes/chunk-0m4j19yd.js +2 -0
- package/app-dist/routes/chunk-0tmmp5q0.js +1 -0
- package/app-dist/routes/chunk-10xn709r.js +1 -0
- package/app-dist/routes/chunk-15m2qcda.js +2 -0
- package/app-dist/routes/chunk-1bh8g23n.js +1 -0
- package/app-dist/routes/chunk-1vg0v937.js +1 -0
- package/app-dist/routes/chunk-1zvcgy3j.js +1 -0
- package/app-dist/routes/chunk-2afb0861.js +1 -0
- package/app-dist/routes/chunk-2c6ttpzt.js +5 -0
- package/app-dist/routes/chunk-3dy30fhs.js +1 -0
- package/app-dist/routes/chunk-426dnces.js +13 -0
- package/app-dist/routes/chunk-44kgxery.js +1 -0
- package/app-dist/routes/chunk-47fdnejd.js +33 -0
- package/app-dist/routes/chunk-49a6t2vq.js +1 -0
- package/app-dist/routes/chunk-4fe1rm5b.js +1 -0
- package/app-dist/routes/chunk-4ggmvkta.js +33 -0
- package/app-dist/routes/chunk-4vkz81q7.js +33 -0
- package/app-dist/routes/chunk-4w4tmj8f.js +31 -0
- package/app-dist/routes/chunk-532s62kr.js +31 -0
- package/app-dist/routes/chunk-5hm3bssy.js +33 -0
- package/app-dist/routes/chunk-5vrh24hc.js +1 -0
- package/app-dist/routes/chunk-61pcg25a.js +1 -0
- package/app-dist/routes/chunk-6nfhygvf.js +1 -0
- package/app-dist/routes/chunk-700e7je6.js +33 -0
- package/app-dist/routes/chunk-7fsn17kg.js +1 -0
- package/app-dist/routes/chunk-7k789b32.js +1 -0
- package/app-dist/routes/chunk-7r46q0ys.js +36 -0
- package/app-dist/routes/chunk-86fmc1fr.js +5 -0
- package/app-dist/routes/chunk-8qth37vw.js +1 -0
- package/app-dist/routes/chunk-924wv8n0.js +1 -0
- package/app-dist/routes/chunk-9mbhgxk9.js +1 -0
- package/app-dist/routes/chunk-a216hyd9.js +1 -0
- package/app-dist/routes/chunk-akzxykh9.js +33 -0
- package/app-dist/routes/chunk-b3dcvy8c.js +1 -0
- package/app-dist/routes/chunk-b74zahz5.js +31 -0
- package/app-dist/routes/chunk-bftj53p2.js +5 -0
- package/app-dist/routes/chunk-c01hnz3e.js +1 -0
- package/app-dist/routes/chunk-d8pvv5km.js +1 -0
- package/app-dist/routes/chunk-dev0aezr.js +2 -0
- package/app-dist/routes/chunk-dh6vnv0e.js +1 -0
- package/app-dist/routes/chunk-dn2cbpva.js +36 -0
- package/app-dist/routes/chunk-dvn0my90.js +1 -0
- package/app-dist/routes/chunk-dvq8mnve.js +36 -0
- package/app-dist/routes/chunk-e8c2gc4d.js +5 -0
- package/app-dist/routes/chunk-ejf9ak2x.js +1 -0
- package/app-dist/routes/chunk-f083m55s.js +1 -0
- package/app-dist/routes/chunk-fnrj28s1.js +31 -0
- package/app-dist/routes/chunk-fvg3yjdp.js +31 -0
- package/app-dist/routes/chunk-g7k381n1.js +1 -0
- package/app-dist/routes/chunk-h01kq2ae.js +13 -0
- package/app-dist/routes/chunk-h4dk761v.js +5 -0
- package/app-dist/routes/chunk-hmx91z2x.js +5 -0
- package/app-dist/routes/chunk-hxbg4m42.js +36 -0
- package/app-dist/routes/chunk-jbjnfp2b.js +2 -0
- package/app-dist/routes/chunk-jxtz5vv6.js +36 -0
- package/app-dist/routes/chunk-jxzcs0ey.js +36 -0
- package/app-dist/routes/chunk-kt7wwhcx.js +1 -0
- package/app-dist/routes/chunk-kzptszyc.js +33 -0
- package/app-dist/routes/chunk-mhgca4w4.js +2 -0
- package/app-dist/routes/chunk-mhswxa20.js +1 -0
- package/app-dist/routes/chunk-n8zfeex6.js +1 -0
- package/app-dist/routes/chunk-pee47b2r.js +1 -0
- package/app-dist/routes/chunk-pesmw829.js +1 -0
- package/app-dist/routes/chunk-pgc4c6f3.js +36 -0
- package/app-dist/routes/chunk-q8egegm1.js +1 -0
- package/app-dist/routes/chunk-q9mn2qyq.js +36 -0
- package/app-dist/routes/chunk-qh0rtaf3.js +5 -0
- package/app-dist/routes/chunk-qqhmk6ye.js +2 -0
- package/app-dist/routes/chunk-qrxygmf7.js +33 -0
- package/app-dist/routes/chunk-r46yzksx.js +36 -0
- package/app-dist/routes/chunk-rgpbw2w0.js +5 -0
- package/app-dist/routes/chunk-rnpzv3d8.js +2 -0
- package/app-dist/routes/chunk-s5v8cv05.js +2 -0
- package/app-dist/routes/chunk-sbwn5bpc.js +1 -0
- package/app-dist/routes/chunk-sqbg8jbt.js +33 -0
- package/app-dist/routes/chunk-sv8dqnf7.js +1 -0
- package/app-dist/routes/chunk-t67sw3za.js +1 -0
- package/app-dist/routes/chunk-tjdpqwdf.js +31 -0
- package/app-dist/routes/chunk-tq2mfghg.js +1 -0
- package/app-dist/routes/chunk-ttn10vt6.js +1 -0
- package/app-dist/routes/chunk-v2hzpjxr.js +1 -0
- package/app-dist/routes/chunk-wfjjkw9y.js +1 -0
- package/app-dist/routes/chunk-wt8cxzmf.js +31 -0
- package/app-dist/routes/chunk-x45d372k.js +5 -0
- package/app-dist/routes/chunk-y3wsazkt.js +1 -0
- package/app-dist/routes/chunk-y7pmgc7t.js +33 -0
- package/app-dist/routes/chunk-zefdt2q3.js +31 -0
- package/app-dist/routes/dropdowns.js +6 -0
- package/app-dist/routes/home.js +1 -0
- package/app-dist/routes/icons.js +1 -0
- package/app-dist/routes/inputs.js +12 -0
- package/app-dist/routes/installation.js +1 -0
- package/app-dist/routes/layout.js +1 -0
- package/app-dist/routes/modals.js +7 -0
- package/app-dist/routes/navbar.js +1 -0
- package/app-dist/routes/progress.js +1 -0
- package/app-dist/routes/radios.js +6 -0
- package/app-dist/routes/switches.js +6 -0
- package/app-dist/routes/tabs.js +1 -0
- package/app-dist/routes/toasts.js +16 -0
- package/assets/fonts/material-symbols-mini.woff2 +0 -0
- package/assets/fonts/material-symbols.woff2 +0 -0
- package/assets/fonts/roboto-400.woff2 +0 -0
- package/assets/fonts/roboto-500.woff2 +0 -0
- package/assets/fonts/roboto-700.woff2 +0 -0
- package/assets/logo-banner-400.jpg +0 -0
- package/assets/logo-banner-400.webp +0 -0
- package/assets/logo-banner-800.webp +0 -0
- package/assets/logo-banner.jpg +0 -0
- package/assets/logo-icon-64.jpg +0 -0
- package/assets/logo-icon-64.webp +0 -0
- package/assets/logo-icon.jpg +0 -0
- package/assets/logo-square.jpg +0 -0
- package/bun.lock +312 -0
- package/bunfig.toml +4 -0
- package/custom-elements.json +1916 -0
- package/demo/api/sample-data.json +38 -0
- package/demo/content/alerts.html +115 -0
- package/demo/content/avatars.html +70 -0
- package/demo/content/badges.html +65 -0
- package/demo/content/buttons.html +188 -0
- package/demo/content/callouts.html +91 -0
- package/demo/content/cards.html +121 -0
- package/demo/content/checkboxes.html +178 -0
- package/demo/content/chips.html +67 -0
- package/demo/content/codeblocks.html +101 -0
- package/demo/content/confirms.html +115 -0
- package/demo/content/datatables.html +149 -0
- package/demo/content/dividers.html +119 -0
- package/demo/content/dropdowns.html +89 -0
- package/demo/content/enterprise.html +252 -0
- package/demo/content/home.html +149 -0
- package/demo/content/icons.html +89 -0
- package/demo/content/inputs.html +135 -0
- package/demo/content/installation.html +16 -0
- package/demo/content/layout.html +136 -0
- package/demo/content/modals.html +141 -0
- package/demo/content/navbar.html +70 -0
- package/demo/content/progress.html +119 -0
- package/demo/content/radios.html +88 -0
- package/demo/content/skeletons.html +109 -0
- package/demo/content/spinners.html +96 -0
- package/demo/content/switches.html +84 -0
- package/demo/content/tables.html +124 -0
- package/demo/content/tabs.html +85 -0
- package/demo/content/toasts.html +116 -0
- package/demo/content/tooltips.html +107 -0
- package/demo/content/virtual-lists.html +233 -0
- package/demo/favicon.ico +0 -0
- package/demo/favicon.png +0 -0
- package/demo/full.html +52 -0
- package/demo/iife.html +46 -0
- package/demo/manifest.json +34 -0
- package/demo/pages/datatable-demo.html +237 -0
- package/demo/pages/prompt-ui-demo.html +218 -0
- package/demo/pages/responsive-demo.html +122 -0
- package/demo/pages/schema-form-demo.html +270 -0
- package/demo/robots.txt +6 -0
- package/demo/shell.html +712 -0
- package/demo/sw.js +387 -0
- package/dist/AGENTS.md +2366 -0
- package/dist/README.md +254 -0
- package/dist/chunks/advanced.js +174 -0
- package/dist/chunks/chunk-1nhr1wrq.js +14 -0
- package/dist/chunks/chunk-hssyjbr0.js +2 -0
- package/dist/chunks/chunk-k8etzx0z.js +2 -0
- package/dist/chunks/chunk-rr1et8fg.js +2 -0
- package/dist/chunks/chunk-sjcx4fd5.js +6 -0
- package/dist/chunks/chunk-v1c777xh.js +5 -0
- package/dist/chunks/chunk-w5k5vwjd.js +13 -0
- package/dist/chunks/core.js +10 -0
- package/dist/chunks/display.js +17 -0
- package/dist/chunks/feedback.js +15 -0
- package/dist/chunks/forms.js +48 -0
- package/dist/chunks/layout.js +9 -0
- package/dist/components/chunk-4tezav8r.js +2 -0
- package/dist/components/chunk-fqyb2pms.js +2 -0
- package/dist/components/chunk-h7cdbhxw.js +13 -0
- package/dist/components/chunk-mzd8jwrs.js +2 -0
- package/dist/components/chunk-qwmxyn8e.js +2 -0
- package/dist/components/chunk-redtk47a.js +14 -0
- package/dist/components/mu-alert.js +5 -0
- package/dist/components/mu-api-table.js +33 -0
- package/dist/components/mu-avatar.js +1 -0
- package/dist/components/mu-badge.js +1 -0
- package/dist/components/mu-bottom-nav.js +1 -0
- package/dist/components/mu-button.js +1 -0
- package/dist/components/mu-callout.js +1 -0
- package/dist/components/mu-card.js +1 -0
- package/dist/components/mu-checkbox.js +9 -0
- package/dist/components/mu-chip.js +4 -0
- package/dist/components/mu-code.js +48 -0
- package/dist/components/mu-confirm.js +10 -0
- package/dist/components/mu-container.js +1 -0
- package/dist/components/mu-datatable.js +96 -0
- package/dist/components/mu-divider.js +1 -0
- package/dist/components/mu-doc-page.js +26 -0
- package/dist/components/mu-drawer-item.js +9 -0
- package/dist/components/mu-drawer.js +1 -0
- package/dist/components/mu-dropdown.js +6 -0
- package/dist/components/mu-error-boundary.js +10 -0
- package/dist/components/mu-example.js +38 -0
- package/dist/components/mu-fetch.js +1 -0
- package/dist/components/mu-form.js +1 -0
- package/dist/components/mu-grid.js +1 -0
- package/dist/components/mu-icon.js +5 -0
- package/dist/components/mu-input.js +12 -0
- package/dist/components/mu-layout.js +1 -0
- package/dist/components/mu-lazy.js +1 -0
- package/dist/components/mu-modal.js +7 -0
- package/dist/components/mu-navbar.js +1 -0
- package/dist/components/mu-page.js +1 -0
- package/dist/components/mu-progress.js +1 -0
- package/dist/components/mu-prompt-ui.js +20 -0
- package/dist/components/mu-radio.js +6 -0
- package/dist/components/mu-repeat.js +1 -0
- package/dist/components/mu-router.js +6 -0
- package/dist/components/mu-schema-form.js +76 -0
- package/dist/components/mu-sidebar.js +1 -0
- package/dist/components/mu-skeleton.js +13 -0
- package/dist/components/mu-spinner.js +1 -0
- package/dist/components/mu-stack.js +1 -0
- package/dist/components/mu-switch.js +6 -0
- package/dist/components/mu-table.js +1 -0
- package/dist/components/mu-tabs.js +1 -0
- package/dist/components/mu-textarea.js +11 -0
- package/dist/components/mu-theme-toggle.js +5 -0
- package/dist/components/mu-toast.js +4 -0
- package/dist/components/mu-tooltip.js +10 -0
- package/dist/components/mu-virtual-list.js +33 -0
- package/dist/components.css +1 -0
- package/dist/microui.css +1 -0
- package/dist/microui.d.ts +234 -0
- package/dist/microui.esm.js +549 -0
- package/dist/microui.esm.js.map +79 -0
- package/dist/microui.min.js +549 -0
- package/dist/microui.min.js.map +79 -0
- package/dist/routes/alerts.js +1 -0
- package/dist/routes/avatars.js +1 -0
- package/dist/routes/badges.js +1 -0
- package/dist/routes/buttons.js +1 -0
- package/dist/routes/callouts.js +1 -0
- package/dist/routes/cards.js +1 -0
- package/dist/routes/checkboxes.js +9 -0
- package/dist/routes/chips.js +4 -0
- package/dist/routes/chunk-19wgcncm.js +2 -0
- package/dist/routes/chunk-1khyr3v1.js +33 -0
- package/dist/routes/chunk-4rhxe97g.js +1 -0
- package/dist/routes/chunk-5qah04bh.js +2 -0
- package/dist/routes/chunk-7gfxy70n.js +5 -0
- package/dist/routes/chunk-e86zbeta.js +1 -0
- package/dist/routes/chunk-fagt36h6.js +2 -0
- package/dist/routes/chunk-fed7zr7m.js +1 -0
- package/dist/routes/chunk-hwj7pfwp.js +1 -0
- package/dist/routes/chunk-mhvcs2f8.js +5 -0
- package/dist/routes/chunk-nv3bddmj.js +13 -0
- package/dist/routes/chunk-q3f2aeqe.js +7 -0
- package/dist/routes/chunk-qxxa8trk.js +1 -0
- package/dist/routes/chunk-rw15y9zh.js +1 -0
- package/dist/routes/chunk-sfb7x11v.js +5 -0
- package/dist/routes/chunk-swyhghrm.js +48 -0
- package/dist/routes/chunk-sxddjs2d.js +2 -0
- package/dist/routes/chunk-vby0zg5w.js +17 -0
- package/dist/routes/chunk-w6zqjqqs.js +9 -0
- package/dist/routes/chunk-z960rexd.js +38 -0
- package/dist/routes/codeblocks.js +1 -0
- package/dist/routes/confirms.js +10 -0
- package/dist/routes/datatables.js +96 -0
- package/dist/routes/dividers.js +1 -0
- package/dist/routes/dropdowns.js +6 -0
- package/dist/routes/enterprise.js +15 -0
- package/dist/routes/home.js +1 -0
- package/dist/routes/icons.js +1 -0
- package/dist/routes/inputs.js +22 -0
- package/dist/routes/installation.js +1 -0
- package/dist/routes/layout.js +1 -0
- package/dist/routes/modals.js +1 -0
- package/dist/routes/navbar.js +1 -0
- package/dist/routes/page-components.json +316 -0
- package/dist/routes/progress.js +1 -0
- package/dist/routes/radios.js +6 -0
- package/dist/routes/route-deps.json +156 -0
- package/dist/routes/shell-critical.js +1 -0
- package/dist/routes/shell-deferred.js +1 -0
- package/dist/routes/shell.js +20 -0
- package/dist/routes/skeletons.js +13 -0
- package/dist/routes/spinners.js +1 -0
- package/dist/routes/src/chunks/core.js +36 -0
- package/dist/routes/switches.js +6 -0
- package/dist/routes/tables.js +1 -0
- package/dist/routes/tabs.js +1 -0
- package/dist/routes/toasts.js +1 -0
- package/dist/routes/tooltips.js +10 -0
- package/dist/routes/virtual-lists.js +33 -0
- package/dist/styles/common.css +1 -0
- package/dist/styles/components/animations.css +1 -0
- package/dist/styles/components/avatar.css +1 -0
- package/dist/styles/components/badge.css +1 -0
- package/dist/styles/components/bottom-nav.css +1 -0
- package/dist/styles/components/button.css +1 -0
- package/dist/styles/components/card.css +1 -0
- package/dist/styles/components/checkbox.css +1 -0
- package/dist/styles/components/chip.css +1 -0
- package/dist/styles/components/datatable.css +1 -0
- package/dist/styles/components/divider.css +1 -0
- package/dist/styles/components/drawer-item.css +1 -0
- package/dist/styles/components/drawer.css +1 -0
- package/dist/styles/components/grid.css +1 -0
- package/dist/styles/components/icon.css +1 -0
- package/dist/styles/components/input.css +1 -0
- package/dist/styles/components/layout.css +1 -0
- package/dist/styles/components/navbar.css +1 -0
- package/dist/styles/components/overlays.css +1 -0
- package/dist/styles/components/progress.css +1 -0
- package/dist/styles/components/prompt-ui.css +1 -0
- package/dist/styles/components/radio.css +1 -0
- package/dist/styles/components/schema-form.css +1 -0
- package/dist/styles/components/switch.css +1 -0
- package/dist/styles/components/tabs.css +1 -0
- package/dist/styles/components/tooltip.css +1 -0
- package/dist/styles/components/virtual-list.css +1 -0
- package/dist/tokens.css +1 -0
- package/docs/api-reference.md +175 -0
- package/docs/component-schema.md +231 -0
- package/docs/components.md +269 -0
- package/docs/design-system.md +183 -0
- package/docs/getting-started.md +198 -0
- package/docs/message-protocol.md +262 -0
- package/docs/utility-classes.md +205 -0
- package/lighthouse-audit.mjs +113 -0
- package/package.json +45 -0
- package/scripts/analyze-components.js +105 -0
- package/scripts/build-app.js +193 -0
- package/scripts/build-framework.js +444 -0
- package/scripts/build-utils.js +101 -0
- package/scripts/test-isolated.js +151 -0
- package/server.js +256 -0
- package/src/chunks/advanced.js +27 -0
- package/src/chunks/core.js +61 -0
- package/src/chunks/display.js +25 -0
- package/src/chunks/feedback.js +15 -0
- package/src/chunks/forms.js +25 -0
- package/src/chunks/layout.js +27 -0
- package/src/components/mu-alert.js +96 -0
- package/src/components/mu-api-table.js +167 -0
- package/src/components/mu-avatar.js +94 -0
- package/src/components/mu-badge.js +32 -0
- package/src/components/mu-bottom-nav.js +115 -0
- package/src/components/mu-button.js +61 -0
- package/src/components/mu-callout.js +71 -0
- package/src/components/mu-card.js +36 -0
- package/src/components/mu-checkbox.js +186 -0
- package/src/components/mu-chip.js +125 -0
- package/src/components/mu-code.js +534 -0
- package/src/components/mu-confirm.js +268 -0
- package/src/components/mu-container.js +53 -0
- package/src/components/mu-datatable.js +517 -0
- package/src/components/mu-divider.js +40 -0
- package/src/components/mu-doc-page.js +100 -0
- package/src/components/mu-drawer-item.js +158 -0
- package/src/components/mu-drawer.js +305 -0
- package/src/components/mu-dropdown.js +239 -0
- package/src/components/mu-error-boundary.js +191 -0
- package/src/components/mu-example.js +335 -0
- package/src/components/mu-fetch.js +256 -0
- package/src/components/mu-form.js +133 -0
- package/src/components/mu-grid.js +63 -0
- package/src/components/mu-icon.js +211 -0
- package/src/components/mu-input.js +142 -0
- package/src/components/mu-layout.js +129 -0
- package/src/components/mu-lazy.js +94 -0
- package/src/components/mu-modal.js +160 -0
- package/src/components/mu-navbar.js +71 -0
- package/src/components/mu-page.js +77 -0
- package/src/components/mu-progress.js +54 -0
- package/src/components/mu-prompt-ui.js +382 -0
- package/src/components/mu-radio.js +200 -0
- package/src/components/mu-repeat.js +135 -0
- package/src/components/mu-router.js +169 -0
- package/src/components/mu-schema-form.js +441 -0
- package/src/components/mu-sidebar.js +81 -0
- package/src/components/mu-skeleton.js +69 -0
- package/src/components/mu-spinner.js +30 -0
- package/src/components/mu-stack.js +59 -0
- package/src/components/mu-switch.js +150 -0
- package/src/components/mu-table.js +80 -0
- package/src/components/mu-tabs.js +112 -0
- package/src/components/mu-textarea.js +96 -0
- package/src/components/mu-theme-toggle.js +52 -0
- package/src/components/mu-toast.js +151 -0
- package/src/components/mu-tooltip.js +182 -0
- package/src/components/mu-virtual-list.js +184 -0
- package/src/core/MuElement.js +562 -0
- package/src/core/agent-api.js +771 -0
- package/src/core/breakpoints.js +195 -0
- package/src/core/bus.js +378 -0
- package/src/core/component-schema.js +287 -0
- package/src/core/feature-registry.js +241 -0
- package/src/core/form-state.js +252 -0
- package/src/core/http.js +104 -0
- package/src/core/keyboard.js +105 -0
- package/src/core/layers.js +71 -0
- package/src/core/render.js +201 -0
- package/src/core/ripple.js +158 -0
- package/src/core/router.js +100 -0
- package/src/core/scheduler.js +109 -0
- package/src/core/signals.js +164 -0
- package/src/core/store.js +268 -0
- package/src/core/theme.js +68 -0
- package/src/core/transitions.js +72 -0
- package/src/core/utils.js +30 -0
- package/src/index.d.ts +234 -0
- package/src/index.js +308 -0
- package/src/styles/animations.css +252 -0
- package/src/styles/common.css +82 -0
- package/src/styles/components/animations.css +129 -0
- package/src/styles/components/avatar.css +83 -0
- package/src/styles/components/badge.css +80 -0
- package/src/styles/components/bottom-nav.css +37 -0
- package/src/styles/components/button.css +348 -0
- package/src/styles/components/card.css +138 -0
- package/src/styles/components/checkbox.css +201 -0
- package/src/styles/components/chip.css +93 -0
- package/src/styles/components/datatable.css +180 -0
- package/src/styles/components/divider.css +49 -0
- package/src/styles/components/drawer-item.css +123 -0
- package/src/styles/components/drawer.css +273 -0
- package/src/styles/components/grid.css +189 -0
- package/src/styles/components/icon.css +40 -0
- package/src/styles/components/input.css +203 -0
- package/src/styles/components/layout.css +121 -0
- package/src/styles/components/navbar.css +91 -0
- package/src/styles/components/overlays.css +329 -0
- package/src/styles/components/progress.css +79 -0
- package/src/styles/components/prompt-ui.css +286 -0
- package/src/styles/components/radio.css +17 -0
- package/src/styles/components/schema-form.css +85 -0
- package/src/styles/components/switch.css +69 -0
- package/src/styles/components/tabs.css +145 -0
- package/src/styles/components/tooltip.css +93 -0
- package/src/styles/components/virtual-list.css +36 -0
- package/src/styles/components.css +3677 -0
- package/src/styles/routes/home.css +97 -0
- package/src/styles/tokens.css +675 -0
- package/tests/agents/agent-integration.test.js +76 -0
- package/tests/benchmark.html +296 -0
- package/tests/build/scan-components.test.js +173 -0
- package/tests/components/all-components.test.js +245 -0
- package/tests/components/all-missing-components.test.js +574 -0
- package/tests/components/mu-alert.test.js +113 -0
- package/tests/components/mu-avatar.test.js +148 -0
- package/tests/components/mu-badge.test.js +92 -0
- package/tests/components/mu-button.test.js +112 -0
- package/tests/components/mu-card.test.js +89 -0
- package/tests/components/mu-checkbox.test.js +158 -0
- package/tests/components/mu-chip.test.js +118 -0
- package/tests/components/mu-container.test.js +120 -0
- package/tests/components/mu-divider.test.js +98 -0
- package/tests/components/mu-drawer-item.test.js +199 -0
- package/tests/components/mu-drawer.test.js +96 -0
- package/tests/components/mu-dropdown.test.js +125 -0
- package/tests/components/mu-form.test.js +138 -0
- package/tests/components/mu-grid.test.js +135 -0
- package/tests/components/mu-icon.test.js +110 -0
- package/tests/components/mu-input.test.js +131 -0
- package/tests/components/mu-lazy.test.js +103 -0
- package/tests/components/mu-modal.test.js +275 -0
- package/tests/components/mu-navbar.test.js +101 -0
- package/tests/components/mu-progress.test.js +115 -0
- package/tests/components/mu-radio.test.js +114 -0
- package/tests/components/mu-repeat.test.js +106 -0
- package/tests/components/mu-sidebar.test.js +126 -0
- package/tests/components/mu-skeleton.test.js +162 -0
- package/tests/components/mu-stack.test.js +143 -0
- package/tests/components/mu-switch.test.js +292 -0
- package/tests/components/mu-table.test.js +124 -0
- package/tests/components/mu-tabs.test.js +104 -0
- package/tests/components/mu-textarea.test.js +115 -0
- package/tests/components/mu-toast.test.js +321 -0
- package/tests/components/mu-tooltip.test.js +133 -0
- package/tests/components/mu-virtual-list.test.js +109 -0
- package/tests/core/MuElement.test.js +120 -0
- package/tests/core/agent-api.test.js +125 -0
- package/tests/core/all-core-modules.test.js +442 -0
- package/tests/core/bus.test.js +364 -0
- package/tests/core/component-schema.test.js +160 -0
- package/tests/core/feature-registry.test.js +198 -0
- package/tests/core/form-state.test.js +167 -0
- package/tests/core/http.test.js +119 -0
- package/tests/core/keyboard.test.js +319 -0
- package/tests/core/layers.test.js +129 -0
- package/tests/core/namespaced-stores.test.js +114 -0
- package/tests/core/render.test.js +121 -0
- package/tests/core/ripple.test.js +131 -0
- package/tests/core/router.test.js +89 -0
- package/tests/core/scheduler.test.js +121 -0
- package/tests/core/signals.test.js +128 -0
- package/tests/core/store.test.js +171 -0
- package/tests/core/transitions.test.js +82 -0
- package/tests/e2e/accessibility-harness.html +58 -0
- package/tests/e2e/accessibility.test.js +401 -0
- package/tests/e2e/agent-features.test.js +372 -0
- package/tests/e2e/card-spacing.test.js +287 -0
- package/tests/e2e/components.test.js +439 -0
- package/tests/e2e/demo-routes.test.js +478 -0
- package/tests/e2e/layout-css-fallback.test.js +334 -0
- package/tests/e2e/mu-alert.e2e.test.js +111 -0
- package/tests/e2e/mu-checkbox.test.js +489 -0
- package/tests/e2e/mu-chip.test.js +347 -0
- package/tests/e2e/mu-form.test.js +499 -0
- package/tests/e2e/mu-icon.test.js +114 -0
- package/tests/e2e/mu-radio.test.js +113 -0
- package/tests/e2e/mu-skeleton.test.js +140 -0
- package/tests/e2e/mu-switch.test.js +415 -0
- package/tests/e2e/mu-tabs.test.js +494 -0
- package/tests/e2e/mu-textarea.test.js +242 -0
- package/tests/e2e/mu-virtual-list.test.js +427 -0
- package/tests/e2e/perf-memory.test.js +161 -0
- package/tests/e2e/puppeteer-helper.js +137 -0
- package/tests/e2e/puppeteer.test.js +226 -0
- package/tests/e2e/pwa.test.js +261 -0
- package/tests/e2e/test-harness.html +319 -0
- package/tests/manual/test-components.html +120 -0
- package/tests/memory-test.html +309 -0
- package/tests/setup-dom.js +93 -0
- package/tests/visual-test.html +301 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Comprehensive E2E Tests for mu-textarea Component
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, test, expect, beforeAll, afterEach, afterAll } from 'bun:test';
|
|
6
|
+
import { launchBrowser, puppeteer } from './puppeteer-helper.js';
|
|
7
|
+
import { dirname, join } from 'path';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = dirname(__filename);
|
|
12
|
+
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
13
|
+
|
|
14
|
+
describe('mu-textarea E2E Tests', () => {
|
|
15
|
+
let browser, page, server;
|
|
16
|
+
|
|
17
|
+
beforeAll(async () => {
|
|
18
|
+
const projectRoot = join(__dirname, '../..');
|
|
19
|
+
server = Bun.serve({
|
|
20
|
+
port: 0,
|
|
21
|
+
async fetch(req) {
|
|
22
|
+
const url = new URL(req.url);
|
|
23
|
+
let filePath = url.pathname === '/' ? join(projectRoot, 'demo/shell.html') : join(projectRoot, url.pathname);
|
|
24
|
+
try {
|
|
25
|
+
const file = Bun.file(filePath);
|
|
26
|
+
if (!await file.exists()) return new Response('Not Found', { status: 404 });
|
|
27
|
+
const ext = filePath.split('.').pop();
|
|
28
|
+
const types = { 'html': 'text/html', 'js': 'text/javascript', 'css': 'text/css', 'json': 'application/json', 'svg': 'image/svg+xml' };
|
|
29
|
+
return new Response(await file.arrayBuffer(), { headers: { 'Content-Type': types[ext] || 'application/octet-stream' } });
|
|
30
|
+
} catch (e) { return new Response('Error', { status: 500 }); }
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
console.log(`[mu-textarea test] Server at http://localhost:${server.port}`);
|
|
34
|
+
browser = await puppeteer.launch({
|
|
35
|
+
headless: true,
|
|
36
|
+
executablePath: '/tmp/puppeteer/chrome-headless-shell/mac_arm-144.0.7559.96/chrome-headless-shell-mac-arm64/chrome-headless-shell',
|
|
37
|
+
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
|
38
|
+
userDataDir: `./.tmp/puppeteer-mu_textarea-${Date.now()}`
|
|
39
|
+
});
|
|
40
|
+
page = await browser.newPage();
|
|
41
|
+
}, 60000);
|
|
42
|
+
|
|
43
|
+
afterEach(async () => { await page.goto('about:blank'); });
|
|
44
|
+
afterAll(async () => { if (browser) await browser.close(); if (server) server.stop(); });
|
|
45
|
+
|
|
46
|
+
test('should be registered as custom element', async () => {
|
|
47
|
+
await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
|
|
48
|
+
await delay(500);
|
|
49
|
+
const isRegistered = await page.evaluate(() => customElements.get('mu-textarea') !== undefined);
|
|
50
|
+
expect(isRegistered).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('should render with base class', async () => {
|
|
54
|
+
await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
|
|
55
|
+
await delay(500);
|
|
56
|
+
const hasClass = await page.evaluate(() => document.querySelector('mu-textarea')?.classList.contains('mu-textarea'));
|
|
57
|
+
expect(hasClass).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('should render native textarea element', async () => {
|
|
61
|
+
await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
|
|
62
|
+
await delay(500);
|
|
63
|
+
const hasTextarea = await page.evaluate(() => document.querySelector('mu-textarea textarea') !== null);
|
|
64
|
+
expect(hasTextarea).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('should have placeholder attribute forwarded', async () => {
|
|
68
|
+
await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
|
|
69
|
+
await delay(500);
|
|
70
|
+
const placeholder = await page.evaluate(() => {
|
|
71
|
+
const ta = document.querySelector('mu-textarea[placeholder] textarea');
|
|
72
|
+
return ta?.placeholder;
|
|
73
|
+
});
|
|
74
|
+
expect(placeholder).toBeTruthy();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('should support value getter/setter', async () => {
|
|
78
|
+
await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
|
|
79
|
+
await delay(500);
|
|
80
|
+
const result = await page.evaluate(() => {
|
|
81
|
+
const ta = document.querySelector('mu-textarea');
|
|
82
|
+
const initial = ta.value;
|
|
83
|
+
ta.value = 'Test content';
|
|
84
|
+
const afterSet = ta.value;
|
|
85
|
+
return { initial, afterSet };
|
|
86
|
+
});
|
|
87
|
+
expect(result.afterSet).toBe('Test content');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('should emit mu-input event on input', async () => {
|
|
91
|
+
await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
|
|
92
|
+
await delay(500);
|
|
93
|
+
const result = await page.evaluate(() => {
|
|
94
|
+
return new Promise(resolve => {
|
|
95
|
+
const ta = document.querySelector('mu-textarea');
|
|
96
|
+
let fired = false;
|
|
97
|
+
ta.addEventListener('mu-input', () => { fired = true; });
|
|
98
|
+
const native = ta.querySelector('textarea');
|
|
99
|
+
native.value = 'test';
|
|
100
|
+
native.dispatchEvent(new Event('input', { bubbles: true }));
|
|
101
|
+
setTimeout(() => resolve({ fired }), 100);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
expect(result.fired).toBe(true);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('should emit mu-change event on change', async () => {
|
|
108
|
+
await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
|
|
109
|
+
await delay(500);
|
|
110
|
+
const result = await page.evaluate(() => {
|
|
111
|
+
return new Promise(resolve => {
|
|
112
|
+
const ta = document.querySelector('mu-textarea');
|
|
113
|
+
let fired = false;
|
|
114
|
+
ta.addEventListener('mu-change', () => { fired = true; });
|
|
115
|
+
const native = ta.querySelector('textarea');
|
|
116
|
+
native.value = 'test';
|
|
117
|
+
native.dispatchEvent(new Event('change', { bubbles: true }));
|
|
118
|
+
setTimeout(() => resolve({ fired }), 100);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
expect(result.fired).toBe(true);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('should have display block', async () => {
|
|
125
|
+
await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
|
|
126
|
+
await delay(500);
|
|
127
|
+
const display = await page.evaluate(() => getComputedStyle(document.querySelector('mu-textarea')).display);
|
|
128
|
+
expect(display).toBe('block');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('should have focus method', async () => {
|
|
132
|
+
await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
|
|
133
|
+
await delay(500);
|
|
134
|
+
const hasFocus = await page.evaluate(() => typeof document.querySelector('mu-textarea').focus === 'function');
|
|
135
|
+
expect(hasFocus).toBe(true);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// ========================================
|
|
139
|
+
// ROWS ATTRIBUTE (was skipped in unit tests)
|
|
140
|
+
// Note: mu-textarea applies rows at render time only
|
|
141
|
+
// ========================================
|
|
142
|
+
|
|
143
|
+
test('should support rows attribute at render time', async () => {
|
|
144
|
+
await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
|
|
145
|
+
await delay(500);
|
|
146
|
+
const result = await page.evaluate(() => {
|
|
147
|
+
// Create a new mu-textarea with rows attribute
|
|
148
|
+
const container = document.createElement('div');
|
|
149
|
+
container.innerHTML = '<mu-textarea rows="8" placeholder="Test rows"></mu-textarea>';
|
|
150
|
+
document.body.appendChild(container);
|
|
151
|
+
|
|
152
|
+
// Wait for render
|
|
153
|
+
return new Promise(resolve => {
|
|
154
|
+
setTimeout(() => {
|
|
155
|
+
const ta = container.querySelector('mu-textarea');
|
|
156
|
+
const native = ta?.querySelector('textarea');
|
|
157
|
+
resolve({
|
|
158
|
+
hasAttr: ta?.getAttribute('rows') === '8',
|
|
159
|
+
nativeRows: native?.rows || native?.getAttribute('rows'),
|
|
160
|
+
rowsApplied: native?.rows === 8 || native?.getAttribute('rows') === '8'
|
|
161
|
+
});
|
|
162
|
+
}, 200);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
expect(result.hasAttr).toBe(true);
|
|
166
|
+
expect(result.rowsApplied).toBe(true);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test('should reflect rows attribute to native textarea', async () => {
|
|
170
|
+
await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
|
|
171
|
+
await delay(500);
|
|
172
|
+
const result = await page.evaluate(() => {
|
|
173
|
+
const ta = document.querySelector('mu-textarea');
|
|
174
|
+
const native = ta.querySelector('textarea');
|
|
175
|
+
return {
|
|
176
|
+
hasTextarea: native !== null,
|
|
177
|
+
defaultRows: native?.rows || null
|
|
178
|
+
};
|
|
179
|
+
});
|
|
180
|
+
expect(result.hasTextarea).toBe(true);
|
|
181
|
+
// Default rows should be a number > 0
|
|
182
|
+
expect(result.defaultRows).toBeGreaterThan(0);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// ========================================
|
|
186
|
+
// READONLY ATTRIBUTE (was skipped in unit tests)
|
|
187
|
+
// Note: mu-textarea applies readonly at render time only
|
|
188
|
+
// ========================================
|
|
189
|
+
|
|
190
|
+
test('should support readonly attribute at render time', async () => {
|
|
191
|
+
await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
|
|
192
|
+
await delay(500);
|
|
193
|
+
const result = await page.evaluate(() => {
|
|
194
|
+
// Create a new mu-textarea with readonly attribute
|
|
195
|
+
const container = document.createElement('div');
|
|
196
|
+
container.innerHTML = '<mu-textarea readonly placeholder="Readonly test"></mu-textarea>';
|
|
197
|
+
document.body.appendChild(container);
|
|
198
|
+
|
|
199
|
+
// Wait for render
|
|
200
|
+
return new Promise(resolve => {
|
|
201
|
+
setTimeout(() => {
|
|
202
|
+
const ta = container.querySelector('mu-textarea');
|
|
203
|
+
const native = ta?.querySelector('textarea');
|
|
204
|
+
resolve({
|
|
205
|
+
hasAttr: ta?.hasAttribute('readonly'),
|
|
206
|
+
isReadOnly: native?.readOnly || native?.hasAttribute('readonly')
|
|
207
|
+
});
|
|
208
|
+
}, 200);
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
expect(result.hasAttr).toBe(true);
|
|
212
|
+
expect(result.isReadOnly).toBe(true);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test('readonly textarea should have readonly native element', async () => {
|
|
216
|
+
await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
|
|
217
|
+
await delay(500);
|
|
218
|
+
const result = await page.evaluate(() => {
|
|
219
|
+
// Find existing textarea with readonly or create one
|
|
220
|
+
let ta = document.querySelector('mu-textarea[readonly]');
|
|
221
|
+
if (!ta) {
|
|
222
|
+
const container = document.createElement('div');
|
|
223
|
+
container.innerHTML = '<mu-textarea readonly placeholder="Test readonly"></mu-textarea>';
|
|
224
|
+
document.body.appendChild(container);
|
|
225
|
+
ta = container.querySelector('mu-textarea');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return new Promise(resolve => {
|
|
229
|
+
setTimeout(() => {
|
|
230
|
+
const native = ta?.querySelector('textarea');
|
|
231
|
+
resolve({
|
|
232
|
+
hasNative: native !== null,
|
|
233
|
+
isReadOnly: native?.readOnly || native?.hasAttribute('readonly'),
|
|
234
|
+
hasAttr: ta?.hasAttribute('readonly')
|
|
235
|
+
});
|
|
236
|
+
}, 200);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
expect(result.hasNative).toBe(true);
|
|
240
|
+
expect(result.hasAttr).toBe(true);
|
|
241
|
+
});
|
|
242
|
+
});
|
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Comprehensive E2E Tests for mu-virtual-list Component
|
|
3
|
+
*
|
|
4
|
+
* Tests:
|
|
5
|
+
* - Component registration and basic rendering
|
|
6
|
+
* - Virtual scrolling with large datasets
|
|
7
|
+
* - Item rendering and visibility
|
|
8
|
+
* - scrollToIndex method
|
|
9
|
+
* - Performance with large lists
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { describe, test, expect, beforeAll, afterEach, afterAll } from 'bun:test';
|
|
13
|
+
import { launchBrowser, puppeteer } from './puppeteer-helper.js';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
15
|
+
import { dirname, join } from 'path';
|
|
16
|
+
|
|
17
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
18
|
+
const __dirname = dirname(__filename);
|
|
19
|
+
|
|
20
|
+
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
21
|
+
|
|
22
|
+
describe('mu-virtual-list E2E Tests', () => {
|
|
23
|
+
let browser;
|
|
24
|
+
let page;
|
|
25
|
+
let server;
|
|
26
|
+
|
|
27
|
+
beforeAll(async () => {
|
|
28
|
+
const projectRoot = join(__dirname, '../..');
|
|
29
|
+
|
|
30
|
+
server = Bun.serve({
|
|
31
|
+
port: 0,
|
|
32
|
+
async fetch(req) {
|
|
33
|
+
const url = new URL(req.url);
|
|
34
|
+
let filePath = join(projectRoot, url.pathname);
|
|
35
|
+
|
|
36
|
+
if (url.pathname === '/') {
|
|
37
|
+
filePath = join(projectRoot, 'demo/shell.html');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Serve the virtual-list test harness
|
|
41
|
+
if (url.pathname === '/test-virtual-list.html') {
|
|
42
|
+
return new Response(`
|
|
43
|
+
<!DOCTYPE html>
|
|
44
|
+
<html>
|
|
45
|
+
<head>
|
|
46
|
+
<title>Virtual List Test</title>
|
|
47
|
+
<script type="module" src="/dist/microui.esm.js"></script>
|
|
48
|
+
<style>
|
|
49
|
+
#test-list {
|
|
50
|
+
height: 400px;
|
|
51
|
+
border: 1px solid #ccc;
|
|
52
|
+
}
|
|
53
|
+
</style>
|
|
54
|
+
</head>
|
|
55
|
+
<body>
|
|
56
|
+
<mu-virtual-list id="test-list" item-height="50" buffer="5"></mu-virtual-list>
|
|
57
|
+
<script type="module">
|
|
58
|
+
const list = document.getElementById('test-list');
|
|
59
|
+
// Create 1000 items
|
|
60
|
+
const items = Array.from({ length: 1000 }, (_, i) => ({
|
|
61
|
+
id: i,
|
|
62
|
+
name: 'Item ' + i
|
|
63
|
+
}));
|
|
64
|
+
list.items = items;
|
|
65
|
+
list.renderItem = (item) => '<div class="item">Item ' + item.id + ': ' + item.name + '</div>';
|
|
66
|
+
</script>
|
|
67
|
+
</body>
|
|
68
|
+
</html>
|
|
69
|
+
`, { headers: { 'Content-Type': 'text/html' } });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const file = Bun.file(filePath);
|
|
74
|
+
if (!await file.exists()) {
|
|
75
|
+
return new Response('Not Found', { status: 404 });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const content = await file.arrayBuffer();
|
|
79
|
+
const ext = filePath.split('.').pop();
|
|
80
|
+
const mimeTypes = {
|
|
81
|
+
'html': 'text/html',
|
|
82
|
+
'js': 'text/javascript',
|
|
83
|
+
'css': 'text/css',
|
|
84
|
+
'json': 'application/json',
|
|
85
|
+
'svg': 'image/svg+xml'
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return new Response(content, {
|
|
89
|
+
headers: { 'Content-Type': mimeTypes[ext] || 'application/octet-stream' }
|
|
90
|
+
});
|
|
91
|
+
} catch (e) {
|
|
92
|
+
return new Response('Error: ' + e.message, { status: 500 });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
console.log(`[mu-virtual-list test] Server started at http://localhost:${server.port}`);
|
|
98
|
+
|
|
99
|
+
browser = await puppeteer.launch({
|
|
100
|
+
headless: true,
|
|
101
|
+
executablePath: '/tmp/puppeteer/chrome-headless-shell/mac_arm-144.0.7559.96/chrome-headless-shell-mac-arm64/chrome-headless-shell',
|
|
102
|
+
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
|
103
|
+
userDataDir: `./.tmp/puppeteer-mu_virtual_list-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
104
|
+
});
|
|
105
|
+
page = await browser.newPage();
|
|
106
|
+
}, 60000);
|
|
107
|
+
|
|
108
|
+
afterEach(async () => {
|
|
109
|
+
await page.goto('about:blank');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
afterAll(async () => {
|
|
113
|
+
if (browser) await browser.close();
|
|
114
|
+
if (server) server.stop();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// ========================================
|
|
118
|
+
// COMPONENT REGISTRATION
|
|
119
|
+
// ========================================
|
|
120
|
+
|
|
121
|
+
test('should be registered as custom element', async () => {
|
|
122
|
+
await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
|
|
123
|
+
waitUntil: 'networkidle0'
|
|
124
|
+
});
|
|
125
|
+
await delay(500);
|
|
126
|
+
|
|
127
|
+
const isRegistered = await page.evaluate(() =>
|
|
128
|
+
customElements.get('mu-virtual-list') !== undefined
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
expect(isRegistered).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('should render with base class', async () => {
|
|
135
|
+
await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
|
|
136
|
+
waitUntil: 'networkidle0'
|
|
137
|
+
});
|
|
138
|
+
await delay(500);
|
|
139
|
+
|
|
140
|
+
const hasBaseClass = await page.evaluate(() => {
|
|
141
|
+
const el = document.querySelector('mu-virtual-list');
|
|
142
|
+
return el?.classList.contains('mu-virtual-list');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
expect(hasBaseClass).toBe(true);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// ========================================
|
|
149
|
+
// VIEWPORT AND CONTAINER STRUCTURE
|
|
150
|
+
// ========================================
|
|
151
|
+
|
|
152
|
+
test('should render viewport container', async () => {
|
|
153
|
+
await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
|
|
154
|
+
waitUntil: 'networkidle0'
|
|
155
|
+
});
|
|
156
|
+
await delay(500);
|
|
157
|
+
|
|
158
|
+
const result = await page.evaluate(() => {
|
|
159
|
+
const el = document.querySelector('mu-virtual-list');
|
|
160
|
+
const viewport = el?.querySelector('.mu-virtual-list__viewport');
|
|
161
|
+
return {
|
|
162
|
+
hasViewport: viewport !== null,
|
|
163
|
+
overflowY: viewport ? getComputedStyle(viewport).overflowY : null
|
|
164
|
+
};
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
expect(result.hasViewport).toBe(true);
|
|
168
|
+
expect(result.overflowY).toBe('auto');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test('should render content container with correct total height', async () => {
|
|
172
|
+
await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
|
|
173
|
+
waitUntil: 'networkidle0'
|
|
174
|
+
});
|
|
175
|
+
await delay(500);
|
|
176
|
+
|
|
177
|
+
const result = await page.evaluate(() => {
|
|
178
|
+
const el = document.querySelector('mu-virtual-list');
|
|
179
|
+
const content = el?.querySelector('.mu-virtual-list__content');
|
|
180
|
+
const height = content ? parseInt(content.style.height) : 0;
|
|
181
|
+
// 1000 items * 50px = 50000px
|
|
182
|
+
return { height, expected: 1000 * 50 };
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
expect(result.height).toBe(result.expected);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// ========================================
|
|
189
|
+
// VIRTUAL RENDERING
|
|
190
|
+
// ========================================
|
|
191
|
+
|
|
192
|
+
test('should only render visible items (virtual scrolling)', async () => {
|
|
193
|
+
await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
|
|
194
|
+
waitUntil: 'networkidle0'
|
|
195
|
+
});
|
|
196
|
+
await delay(500);
|
|
197
|
+
|
|
198
|
+
const result = await page.evaluate(() => {
|
|
199
|
+
const el = document.querySelector('mu-virtual-list');
|
|
200
|
+
const items = el?.querySelectorAll('.mu-virtual-list__item');
|
|
201
|
+
// With 400px height, 50px items, and buffer of 5, should have ~18-20 items
|
|
202
|
+
return {
|
|
203
|
+
renderedCount: items?.length || 0,
|
|
204
|
+
totalItems: 1000
|
|
205
|
+
};
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Should NOT render all 1000 items
|
|
209
|
+
expect(result.renderedCount).toBeLessThan(50);
|
|
210
|
+
expect(result.renderedCount).toBeGreaterThan(0);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('should render items with correct positioning', async () => {
|
|
214
|
+
await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
|
|
215
|
+
waitUntil: 'networkidle0'
|
|
216
|
+
});
|
|
217
|
+
await delay(500);
|
|
218
|
+
|
|
219
|
+
const result = await page.evaluate(() => {
|
|
220
|
+
const el = document.querySelector('mu-virtual-list');
|
|
221
|
+
const items = el?.querySelectorAll('.mu-virtual-list__item');
|
|
222
|
+
if (!items || items.length === 0) return { error: 'No items' };
|
|
223
|
+
|
|
224
|
+
const firstItem = items[0];
|
|
225
|
+
const top = parseInt(firstItem.style.top);
|
|
226
|
+
const height = parseInt(firstItem.style.height);
|
|
227
|
+
const position = getComputedStyle(firstItem).position;
|
|
228
|
+
|
|
229
|
+
return { top, height, position };
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
expect(result.position).toBe('absolute');
|
|
233
|
+
expect(result.height).toBe(50);
|
|
234
|
+
expect(result.top).toBe(0); // First visible item starts at 0
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// ========================================
|
|
238
|
+
// SCROLL BEHAVIOR
|
|
239
|
+
// ========================================
|
|
240
|
+
|
|
241
|
+
test('should update visible items on scroll', async () => {
|
|
242
|
+
await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
|
|
243
|
+
waitUntil: 'networkidle0'
|
|
244
|
+
});
|
|
245
|
+
await delay(500);
|
|
246
|
+
|
|
247
|
+
const result = await page.evaluate(async () => {
|
|
248
|
+
const el = document.querySelector('mu-virtual-list');
|
|
249
|
+
const viewport = el.querySelector('.mu-virtual-list__viewport');
|
|
250
|
+
|
|
251
|
+
// Get first visible item before scroll
|
|
252
|
+
const beforeItems = el.querySelectorAll('.mu-virtual-list__item');
|
|
253
|
+
const beforeIndex = parseInt(beforeItems[0]?.getAttribute('data-index') || '0');
|
|
254
|
+
|
|
255
|
+
// Scroll down 500px (10 items)
|
|
256
|
+
viewport.scrollTop = 500;
|
|
257
|
+
|
|
258
|
+
// Wait for throttled scroll handler (16ms) + render time
|
|
259
|
+
await new Promise(r => setTimeout(r, 150));
|
|
260
|
+
|
|
261
|
+
const afterItems = el.querySelectorAll('.mu-virtual-list__item');
|
|
262
|
+
const afterIndex = parseInt(afterItems[0]?.getAttribute('data-index') || '0');
|
|
263
|
+
|
|
264
|
+
return { beforeIndex, afterIndex, scrolled: afterIndex > beforeIndex };
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
expect(result.scrolled).toBe(true);
|
|
268
|
+
expect(result.afterIndex).toBeGreaterThan(result.beforeIndex);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// ========================================
|
|
272
|
+
// scrollToIndex METHOD
|
|
273
|
+
// ========================================
|
|
274
|
+
|
|
275
|
+
test('should have scrollToIndex method', async () => {
|
|
276
|
+
await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
|
|
277
|
+
waitUntil: 'networkidle0'
|
|
278
|
+
});
|
|
279
|
+
await delay(500);
|
|
280
|
+
|
|
281
|
+
const hasMethod = await page.evaluate(() => {
|
|
282
|
+
const el = document.querySelector('mu-virtual-list');
|
|
283
|
+
return typeof el?.scrollToIndex === 'function';
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
expect(hasMethod).toBe(true);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
test('scrollToIndex should scroll to correct position', async () => {
|
|
290
|
+
await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
|
|
291
|
+
waitUntil: 'networkidle0'
|
|
292
|
+
});
|
|
293
|
+
await delay(500);
|
|
294
|
+
|
|
295
|
+
const result = await page.evaluate(async () => {
|
|
296
|
+
const el = document.querySelector('mu-virtual-list');
|
|
297
|
+
const viewport = el.querySelector('.mu-virtual-list__viewport');
|
|
298
|
+
|
|
299
|
+
el.scrollToIndex(100);
|
|
300
|
+
|
|
301
|
+
// Wait for scroll
|
|
302
|
+
await new Promise(r => setTimeout(r, 100));
|
|
303
|
+
|
|
304
|
+
const scrollTop = viewport.scrollTop;
|
|
305
|
+
const expectedScrollTop = 100 * 50; // index * itemHeight
|
|
306
|
+
|
|
307
|
+
return { scrollTop, expectedScrollTop };
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
expect(result.scrollTop).toBe(result.expectedScrollTop);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// ========================================
|
|
314
|
+
// ITEMS PROPERTY
|
|
315
|
+
// ========================================
|
|
316
|
+
|
|
317
|
+
test('should have items getter and setter', async () => {
|
|
318
|
+
await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
|
|
319
|
+
waitUntil: 'networkidle0'
|
|
320
|
+
});
|
|
321
|
+
await delay(500);
|
|
322
|
+
|
|
323
|
+
const result = await page.evaluate(() => {
|
|
324
|
+
const el = document.querySelector('mu-virtual-list');
|
|
325
|
+
const items = el.items;
|
|
326
|
+
return {
|
|
327
|
+
isArray: Array.isArray(items),
|
|
328
|
+
length: items?.length
|
|
329
|
+
};
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
expect(result.isArray).toBe(true);
|
|
333
|
+
expect(result.length).toBe(1000);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
test('should update when items are changed', async () => {
|
|
337
|
+
await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
|
|
338
|
+
waitUntil: 'networkidle0'
|
|
339
|
+
});
|
|
340
|
+
await delay(500);
|
|
341
|
+
|
|
342
|
+
const result = await page.evaluate(async () => {
|
|
343
|
+
const el = document.querySelector('mu-virtual-list');
|
|
344
|
+
const content = el.querySelector('.mu-virtual-list__content');
|
|
345
|
+
|
|
346
|
+
const heightBefore = parseInt(content.style.height);
|
|
347
|
+
|
|
348
|
+
// Set new items array
|
|
349
|
+
el.items = Array.from({ length: 500 }, (_, i) => ({ id: i, name: 'New Item ' + i }));
|
|
350
|
+
|
|
351
|
+
// Wait for render
|
|
352
|
+
await new Promise(r => setTimeout(r, 100));
|
|
353
|
+
|
|
354
|
+
const heightAfter = parseInt(content.style.height);
|
|
355
|
+
|
|
356
|
+
return { heightBefore, heightAfter, changed: heightBefore !== heightAfter };
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
expect(result.changed).toBe(true);
|
|
360
|
+
expect(result.heightAfter).toBe(500 * 50); // 500 items * 50px
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// ========================================
|
|
364
|
+
// RENDER ITEM CALLBACK
|
|
365
|
+
// ========================================
|
|
366
|
+
|
|
367
|
+
test('should use custom renderItem callback', async () => {
|
|
368
|
+
await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
|
|
369
|
+
waitUntil: 'networkidle0'
|
|
370
|
+
});
|
|
371
|
+
await delay(500);
|
|
372
|
+
|
|
373
|
+
const result = await page.evaluate(() => {
|
|
374
|
+
const el = document.querySelector('mu-virtual-list');
|
|
375
|
+
const item = el.querySelector('.mu-virtual-list__item');
|
|
376
|
+
return {
|
|
377
|
+
hasCustomContent: item !== null,
|
|
378
|
+
text: item?.textContent?.trim()
|
|
379
|
+
};
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
expect(result.hasCustomContent).toBe(true);
|
|
383
|
+
// The item renders the content from renderItem callback
|
|
384
|
+
expect(result.text).toBeTruthy();
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
// ========================================
|
|
388
|
+
// DATA ATTRIBUTES
|
|
389
|
+
// ========================================
|
|
390
|
+
|
|
391
|
+
test('should have data-index attribute on items', async () => {
|
|
392
|
+
await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
|
|
393
|
+
waitUntil: 'networkidle0'
|
|
394
|
+
});
|
|
395
|
+
await delay(500);
|
|
396
|
+
|
|
397
|
+
const result = await page.evaluate(() => {
|
|
398
|
+
const el = document.querySelector('mu-virtual-list');
|
|
399
|
+
const items = el.querySelectorAll('.mu-virtual-list__item');
|
|
400
|
+
const indices = Array.from(items).map(i => parseInt(i.getAttribute('data-index')));
|
|
401
|
+
return {
|
|
402
|
+
hasIndices: indices.length > 0,
|
|
403
|
+
firstIndex: indices[0],
|
|
404
|
+
allHaveIndex: indices.every(i => !isNaN(i))
|
|
405
|
+
};
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
expect(result.hasIndices).toBe(true);
|
|
409
|
+
expect(result.firstIndex).toBe(0);
|
|
410
|
+
expect(result.allHaveIndex).toBe(true);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
test('should have data-mu-state=visible on rendered items', async () => {
|
|
414
|
+
await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
|
|
415
|
+
waitUntil: 'networkidle0'
|
|
416
|
+
});
|
|
417
|
+
await delay(500);
|
|
418
|
+
|
|
419
|
+
const result = await page.evaluate(() => {
|
|
420
|
+
const el = document.querySelector('mu-virtual-list');
|
|
421
|
+
const items = el.querySelectorAll('.mu-virtual-list__item[data-mu-state="visible"]');
|
|
422
|
+
return { count: items.length };
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
expect(result.count).toBeGreaterThan(0);
|
|
426
|
+
});
|
|
427
|
+
});
|