#
tokens: 45173/50000 2/1629 files (page 108/186)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 108 of 186. Use http://codebase.md/xmlui-org/xmlui/%7Bnode.props.src?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .changeset
│   └── config.json
├── .eslintrc.cjs
├── .github
│   ├── build-checklist.png
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── workflows
│       ├── deploy-blog-optimized.yml
│       ├── deploy-blog-swa.yml
│       ├── deploy-blog.yml
│       ├── deploy-docs-optimized.yml
│       ├── deploy-docs-swa.yml
│       ├── deploy-docs.yml
│       ├── prepare-versions.yml
│       ├── release-packages.yml
│       ├── run-all-tests.yml
│       └── run-smoke-tests.yml
├── .gitignore
├── .prettierrc.js
├── .vscode
│   ├── launch.json
│   └── settings.json
├── blog
│   ├── .gitignore
│   ├── .gitkeep
│   ├── CHANGELOG.md
│   ├── extensions.ts
│   ├── index.html
│   ├── index.ts
│   ├── package.json
│   ├── public
│   │   ├── blog
│   │   │   ├── images
│   │   │   │   ├── an-advanced-codefence.gif
│   │   │   │   ├── an-advanced-codefence.mp4
│   │   │   │   ├── blog-page-component.png
│   │   │   │   ├── blog-scrabble.png
│   │   │   │   ├── codefence-runner.png
│   │   │   │   ├── integrated-blog-search.png
│   │   │   │   ├── lorem-ipsum.png
│   │   │   │   ├── playground-checkbox-source.png
│   │   │   │   ├── playground.png
│   │   │   │   ├── use-xmlui-mcp-to-find-a-howto.png
│   │   │   │   └── xmlui-demo-gallery.png
│   │   │   ├── introducing-xmlui.md
│   │   │   ├── lorem-ipsum.md
│   │   │   ├── newest-post.md
│   │   │   ├── older-post.md
│   │   │   ├── xmlui-playground.md
│   │   │   └── xmlui-powered-blog.md
│   │   ├── mockServiceWorker.js
│   │   ├── resources
│   │   │   ├── favicon.ico
│   │   │   ├── files
│   │   │   │   └── for-download
│   │   │   │       └── xmlui
│   │   │   │           └── xmlui-standalone.umd.js
│   │   │   ├── github.svg
│   │   │   ├── llms.txt
│   │   │   ├── logo-dark.svg
│   │   │   ├── logo.svg
│   │   │   ├── pg-popout.svg
│   │   │   ├── rss.svg
│   │   │   └── xmlui-logo.svg
│   │   ├── serve.json
│   │   ├── staticwebapp.config.json
│   │   └── web.config
│   ├── scripts
│   │   ├── download-latest-xmlui.js
│   │   ├── generate-rss.js
│   │   ├── get-releases.js
│   │   └── utils.js
│   ├── src
│   │   ├── components
│   │   │   ├── BlogOverview.xmlui
│   │   │   ├── BlogPage.xmlui
│   │   │   └── PageNotFound.xmlui
│   │   ├── config.ts
│   │   ├── Main.xmlui
│   │   └── themes
│   │       └── blog-theme.ts
│   └── tsconfig.json
├── CONTRIBUTING.md
├── docs
│   ├── .gitignore
│   ├── CHANGELOG.md
│   ├── ComponentRefLinks.txt
│   ├── content
│   │   ├── _meta.json
│   │   ├── components
│   │   │   ├── _meta.json
│   │   │   ├── _overview.md
│   │   │   ├── APICall.md
│   │   │   ├── App.md
│   │   │   ├── AppHeader.md
│   │   │   ├── AppState.md
│   │   │   ├── AutoComplete.md
│   │   │   ├── Avatar.md
│   │   │   ├── Backdrop.md
│   │   │   ├── Badge.md
│   │   │   ├── BarChart.md
│   │   │   ├── Bookmark.md
│   │   │   ├── Breakout.md
│   │   │   ├── Button.md
│   │   │   ├── Card.md
│   │   │   ├── Carousel.md
│   │   │   ├── ChangeListener.md
│   │   │   ├── Checkbox.md
│   │   │   ├── CHStack.md
│   │   │   ├── ColorPicker.md
│   │   │   ├── Column.md
│   │   │   ├── ContentSeparator.md
│   │   │   ├── CVStack.md
│   │   │   ├── DataSource.md
│   │   │   ├── DateInput.md
│   │   │   ├── DatePicker.md
│   │   │   ├── DonutChart.md
│   │   │   ├── DropdownMenu.md
│   │   │   ├── EmojiSelector.md
│   │   │   ├── ExpandableItem.md
│   │   │   ├── FileInput.md
│   │   │   ├── FileUploadDropZone.md
│   │   │   ├── FlowLayout.md
│   │   │   ├── Footer.md
│   │   │   ├── Form.md
│   │   │   ├── FormItem.md
│   │   │   ├── FormSection.md
│   │   │   ├── Fragment.md
│   │   │   ├── H1.md
│   │   │   ├── H2.md
│   │   │   ├── H3.md
│   │   │   ├── H4.md
│   │   │   ├── H5.md
│   │   │   ├── H6.md
│   │   │   ├── Heading.md
│   │   │   ├── HSplitter.md
│   │   │   ├── HStack.md
│   │   │   ├── Icon.md
│   │   │   ├── IFrame.md
│   │   │   ├── Image.md
│   │   │   ├── Items.md
│   │   │   ├── LabelList.md
│   │   │   ├── Legend.md
│   │   │   ├── LineChart.md
│   │   │   ├── Link.md
│   │   │   ├── List.md
│   │   │   ├── Logo.md
│   │   │   ├── Markdown.md
│   │   │   ├── MenuItem.md
│   │   │   ├── MenuSeparator.md
│   │   │   ├── ModalDialog.md
│   │   │   ├── NavGroup.md
│   │   │   ├── NavLink.md
│   │   │   ├── NavPanel.md
│   │   │   ├── NoResult.md
│   │   │   ├── NumberBox.md
│   │   │   ├── Option.md
│   │   │   ├── Page.md
│   │   │   ├── PageMetaTitle.md
│   │   │   ├── Pages.md
│   │   │   ├── Pagination.md
│   │   │   ├── PasswordInput.md
│   │   │   ├── PieChart.md
│   │   │   ├── ProgressBar.md
│   │   │   ├── Queue.md
│   │   │   ├── RadioGroup.md
│   │   │   ├── RealTimeAdapter.md
│   │   │   ├── Redirect.md
│   │   │   ├── Select.md
│   │   │   ├── Slider.md
│   │   │   ├── Slot.md
│   │   │   ├── SpaceFiller.md
│   │   │   ├── Spinner.md
│   │   │   ├── Splitter.md
│   │   │   ├── Stack.md
│   │   │   ├── StickyBox.md
│   │   │   ├── SubMenuItem.md
│   │   │   ├── Switch.md
│   │   │   ├── TabItem.md
│   │   │   ├── Table.md
│   │   │   ├── TableOfContents.md
│   │   │   ├── Tabs.md
│   │   │   ├── Text.md
│   │   │   ├── TextArea.md
│   │   │   ├── TextBox.md
│   │   │   ├── Theme.md
│   │   │   ├── TimeInput.md
│   │   │   ├── Timer.md
│   │   │   ├── ToneChangerButton.md
│   │   │   ├── ToneSwitch.md
│   │   │   ├── Tooltip.md
│   │   │   ├── Tree.md
│   │   │   ├── VSplitter.md
│   │   │   ├── VStack.md
│   │   │   ├── xmlui-animations
│   │   │   │   ├── _meta.json
│   │   │   │   ├── _overview.md
│   │   │   │   ├── Animation.md
│   │   │   │   ├── FadeAnimation.md
│   │   │   │   ├── FadeInAnimation.md
│   │   │   │   ├── FadeOutAnimation.md
│   │   │   │   ├── ScaleAnimation.md
│   │   │   │   └── SlideInAnimation.md
│   │   │   ├── xmlui-pdf
│   │   │   │   ├── _meta.json
│   │   │   │   ├── _overview.md
│   │   │   │   └── Pdf.md
│   │   │   ├── xmlui-spreadsheet
│   │   │   │   ├── _meta.json
│   │   │   │   ├── _overview.md
│   │   │   │   └── Spreadsheet.md
│   │   │   └── xmlui-website-blocks
│   │   │       ├── _meta.json
│   │   │       ├── _overview.md
│   │   │       ├── Carousel.md
│   │   │       ├── HelloMd.md
│   │   │       ├── HeroSection.md
│   │   │       └── ScrollToTop.md
│   │   └── extensions
│   │       ├── _meta.json
│   │       ├── xmlui-animations
│   │       │   ├── _meta.json
│   │       │   ├── _overview.md
│   │       │   ├── Animation.md
│   │       │   ├── FadeAnimation.md
│   │       │   ├── FadeInAnimation.md
│   │       │   ├── FadeOutAnimation.md
│   │       │   ├── ScaleAnimation.md
│   │       │   └── SlideInAnimation.md
│   │       └── xmlui-website-blocks
│   │           ├── _meta.json
│   │           ├── _overview.md
│   │           ├── Carousel.md
│   │           ├── FancyButton.md
│   │           ├── HeroSection.md
│   │           └── ScrollToTop.md
│   ├── extensions.ts
│   ├── index.html
│   ├── index.ts
│   ├── package.json
│   ├── public
│   │   ├── feed.rss
│   │   ├── mockServiceWorker.js
│   │   ├── pages
│   │   │   ├── _meta.json
│   │   │   ├── app-structure.md
│   │   │   ├── build-editor-component.md
│   │   │   ├── build-hello-world-component.md
│   │   │   ├── components-intro.md
│   │   │   ├── context-variables.md
│   │   │   ├── forms.md
│   │   │   ├── globals.md
│   │   │   ├── glossary.md
│   │   │   ├── helper-tags.md
│   │   │   ├── hosted-deployment.md
│   │   │   ├── howto
│   │   │   │   ├── assign-a-complex-json-literal-to-a-component-variable.md
│   │   │   │   ├── chain-a-refetch.md
│   │   │   │   ├── control-cache-invalidation.md
│   │   │   │   ├── debounce-user-input-for-api-calls.md
│   │   │   │   ├── debounce-with-changelistener.md
│   │   │   │   ├── debug-a-component.md
│   │   │   │   ├── delay-a-datasource-until-another-datasource-is-ready.md
│   │   │   │   ├── delegate-a-method.md
│   │   │   │   ├── do-custom-form-validation.md
│   │   │   │   ├── expose-a-method-from-a-component.md
│   │   │   │   ├── filter-and-transform-data-from-an-api.md
│   │   │   │   ├── group-items-in-list-by-a-property.md
│   │   │   │   ├── handle-background-operations.md
│   │   │   │   ├── hide-an-element-until-its-datasource-is-ready.md
│   │   │   │   ├── make-a-set-of-equal-width-cards.md
│   │   │   │   ├── make-a-table-responsive.md
│   │   │   │   ├── make-navpanel-width-responsive.md
│   │   │   │   ├── modify-a-value-reported-in-a-column.md
│   │   │   │   ├── paginate-a-list.md
│   │   │   │   ├── pass-data-to-a-modal-dialog.md
│   │   │   │   ├── react-to-button-click-not-keystrokes.md
│   │   │   │   ├── set-the-initial-value-of-a-select-from-fetched-data.md
│   │   │   │   ├── share-a-modaldialog-across-components.md
│   │   │   │   ├── sync-selections-between-table-and-list-views.md
│   │   │   │   ├── update-ui-optimistically.md
│   │   │   │   ├── use-built-in-form-validation.md
│   │   │   │   └── use-the-same-modaldialog-to-add-or-edit.md
│   │   │   ├── howto.md
│   │   │   ├── intro.md
│   │   │   ├── layout.md
│   │   │   ├── markup.md
│   │   │   ├── mcp.md
│   │   │   ├── modal-dialogs.md
│   │   │   ├── news-and-reviews.md
│   │   │   ├── reactive-intro.md
│   │   │   ├── refactoring.md
│   │   │   ├── routing-and-links.md
│   │   │   ├── samples
│   │   │   │   ├── color-palette.xmlui
│   │   │   │   ├── color-values.xmlui
│   │   │   │   ├── shadow-sizes.xmlui
│   │   │   │   ├── spacing-sizes.xmlui
│   │   │   │   ├── swatch.xmlui
│   │   │   │   ├── theme-gallery-brief.xmlui
│   │   │   │   └── theme-gallery.xmlui
│   │   │   ├── scoping.md
│   │   │   ├── scripting.md
│   │   │   ├── styles-and-themes
│   │   │   │   ├── common-units.md
│   │   │   │   ├── layout-props.md
│   │   │   │   ├── theme-variable-defaults.md
│   │   │   │   ├── theme-variables.md
│   │   │   │   └── themes.md
│   │   │   ├── template-properties.md
│   │   │   ├── test.md
│   │   │   ├── tutorial-01.md
│   │   │   ├── tutorial-02.md
│   │   │   ├── tutorial-03.md
│   │   │   ├── tutorial-04.md
│   │   │   ├── tutorial-05.md
│   │   │   ├── tutorial-06.md
│   │   │   ├── tutorial-07.md
│   │   │   ├── tutorial-08.md
│   │   │   ├── tutorial-09.md
│   │   │   ├── tutorial-10.md
│   │   │   ├── tutorial-11.md
│   │   │   ├── tutorial-12.md
│   │   │   ├── universal-properties.md
│   │   │   ├── user-defined-components.md
│   │   │   ├── vscode.md
│   │   │   ├── working-with-markdown.md
│   │   │   ├── working-with-text.md
│   │   │   ├── xmlui-animations
│   │   │   │   ├── _meta.json
│   │   │   │   ├── _overview.md
│   │   │   │   ├── Animation.md
│   │   │   │   ├── FadeAnimation.md
│   │   │   │   ├── FadeInAnimation.md
│   │   │   │   ├── FadeOutAnimation.md
│   │   │   │   ├── ScaleAnimation.md
│   │   │   │   └── SlideInAnimation.md
│   │   │   ├── xmlui-charts
│   │   │   │   ├── _meta.json
│   │   │   │   ├── _overview.md
│   │   │   │   ├── BarChart.md
│   │   │   │   ├── DonutChart.md
│   │   │   │   ├── LabelList.md
│   │   │   │   ├── Legend.md
│   │   │   │   ├── LineChart.md
│   │   │   │   └── PieChart.md
│   │   │   ├── xmlui-pdf
│   │   │   │   ├── _meta.json
│   │   │   │   ├── _overview.md
│   │   │   │   └── Pdf.md
│   │   │   └── xmlui-spreadsheet
│   │   │       ├── _meta.json
│   │   │       ├── _overview.md
│   │   │       └── Spreadsheet.md
│   │   ├── resources
│   │   │   ├── devdocs
│   │   │   │   ├── debug-proxy-object-2.png
│   │   │   │   ├── debug-proxy-object.png
│   │   │   │   ├── table_editor_01.png
│   │   │   │   ├── table_editor_02.png
│   │   │   │   ├── table_editor_03.png
│   │   │   │   ├── table_editor_04.png
│   │   │   │   ├── table_editor_05.png
│   │   │   │   ├── table_editor_06.png
│   │   │   │   ├── table_editor_07.png
│   │   │   │   ├── table_editor_08.png
│   │   │   │   ├── table_editor_09.png
│   │   │   │   ├── table_editor_10.png
│   │   │   │   ├── table_editor_11.png
│   │   │   │   ├── table-editor-01.png
│   │   │   │   ├── table-editor-02.png
│   │   │   │   ├── table-editor-03.png
│   │   │   │   ├── table-editor-04.png
│   │   │   │   ├── table-editor-06.png
│   │   │   │   ├── table-editor-07.png
│   │   │   │   ├── table-editor-08.png
│   │   │   │   ├── table-editor-09.png
│   │   │   │   └── xmlui-rendering-of-tiptap-markdown.png
│   │   │   ├── favicon.ico
│   │   │   ├── files
│   │   │   │   ├── clients.json
│   │   │   │   ├── daily-revenue.json
│   │   │   │   ├── dashboard-stats.json
│   │   │   │   ├── demo.xmlui
│   │   │   │   ├── demo.xmlui.xs
│   │   │   │   ├── downloads
│   │   │   │   │   └── downloads.json
│   │   │   │   ├── for-download
│   │   │   │   │   ├── index-with-api.html
│   │   │   │   │   ├── index.html
│   │   │   │   │   ├── mockApi.js
│   │   │   │   │   ├── start-darwin.sh
│   │   │   │   │   ├── start-linux.sh
│   │   │   │   │   ├── start.bat
│   │   │   │   │   └── xmlui
│   │   │   │   │       └── xmlui-standalone.umd.js
│   │   │   │   ├── getting-started
│   │   │   │   │   ├── cl-tutorial-final.zip
│   │   │   │   │   ├── cl-tutorial.zip
│   │   │   │   │   ├── cl-tutorial2.zip
│   │   │   │   │   ├── cl-tutorial3.zip
│   │   │   │   │   ├── cl-tutorial4.zip
│   │   │   │   │   ├── cl-tutorial5.zip
│   │   │   │   │   ├── cl-tutorial6.zip
│   │   │   │   │   ├── getting-started.zip
│   │   │   │   │   ├── hello-xmlui.zip
│   │   │   │   │   ├── xmlui-empty.zip
│   │   │   │   │   └── xmlui-starter.zip
│   │   │   │   ├── howto
│   │   │   │   │   └── component-icons
│   │   │   │   │       └── up-arrow.svg
│   │   │   │   ├── invoices.json
│   │   │   │   ├── monthly-status.json
│   │   │   │   ├── news-and-reviews.json
│   │   │   │   ├── products.json
│   │   │   │   ├── releases.json
│   │   │   │   ├── tutorials
│   │   │   │   │   ├── datasource
│   │   │   │   │   │   └── api.ts
│   │   │   │   │   └── p2do
│   │   │   │   │       ├── api.ts
│   │   │   │   │       └── todo-logo.svg
│   │   │   │   └── xmlui.json
│   │   │   ├── github.svg
│   │   │   ├── images
│   │   │   │   ├── apiaction-tutorial
│   │   │   │   │   ├── add-success.png
│   │   │   │   │   ├── apiaction-param.png
│   │   │   │   │   ├── change-completed.png
│   │   │   │   │   ├── change-in-progress.png
│   │   │   │   │   ├── confirm-delete.png
│   │   │   │   │   ├── data-error.png
│   │   │   │   │   ├── data-progress.png
│   │   │   │   │   ├── data-success.png
│   │   │   │   │   ├── display-1.png
│   │   │   │   │   ├── item-deleted.png
│   │   │   │   │   ├── item-updated.png
│   │   │   │   │   ├── missing-api-key.png
│   │   │   │   │   ├── new-item-added.png
│   │   │   │   │   └── test-message.png
│   │   │   │   ├── chat-api
│   │   │   │   │   └── domain-model.svg
│   │   │   │   ├── components
│   │   │   │   │   ├── image
│   │   │   │   │   │   └── breakfast.jpg
│   │   │   │   │   ├── markdown
│   │   │   │   │   │   └── colors.png
│   │   │   │   │   └── modal
│   │   │   │   │       ├── deep_link_dialog_1.jpg
│   │   │   │   │       └── deep_link_dialog_2.jpg
│   │   │   │   ├── create-apps
│   │   │   │   │   ├── collapsed-vertical.png
│   │   │   │   │   ├── using-forms-warning-dialog.png
│   │   │   │   │   └── using-forms.png
│   │   │   │   ├── datasource-tutorial
│   │   │   │   │   ├── data-with-header.png
│   │   │   │   │   ├── filtered-data.png
│   │   │   │   │   ├── filtered-items.png
│   │   │   │   │   ├── initial-page-items.png
│   │   │   │   │   ├── list-items.png
│   │   │   │   │   ├── next-page-items.png
│   │   │   │   │   ├── no-data.png
│   │   │   │   │   ├── pagination-1.jpg
│   │   │   │   │   ├── pagination-1.png
│   │   │   │   │   ├── polling-1.png
│   │   │   │   │   ├── refetch-data.png
│   │   │   │   │   ├── slow-loading.png
│   │   │   │   │   ├── test-message.png
│   │   │   │   │   ├── Thumbs.db
│   │   │   │   │   ├── unconventional-data.png
│   │   │   │   │   └── unfiltered-items.png
│   │   │   │   ├── flower.jpg
│   │   │   │   ├── get-started
│   │   │   │   │   ├── add-new-contact.png
│   │   │   │   │   ├── app-modified.png
│   │   │   │   │   ├── app-start.png
│   │   │   │   │   ├── app-with-boxes.png
│   │   │   │   │   ├── app-with-toast.png
│   │   │   │   │   ├── boilerplate-structure.png
│   │   │   │   │   ├── cl-initial.png
│   │   │   │   │   ├── cl-start.png
│   │   │   │   │   ├── contact-counts.png
│   │   │   │   │   ├── contact-dialog-title.png
│   │   │   │   │   ├── contact-dialog.png
│   │   │   │   │   ├── contact-menus.png
│   │   │   │   │   ├── contact-predicates.png
│   │   │   │   │   ├── context-menu.png
│   │   │   │   │   ├── dashboard-numbers.png
│   │   │   │   │   ├── default-contact-list.png
│   │   │   │   │   ├── delete-contact.png
│   │   │   │   │   ├── delete-task.png
│   │   │   │   │   ├── detailed-template.png
│   │   │   │   │   ├── edit-contact-details.png
│   │   │   │   │   ├── edited-contact-saved.png
│   │   │   │   │   ├── empty-sections.png
│   │   │   │   │   ├── filter-completed.png
│   │   │   │   │   ├── fullwidth-desktop.png
│   │   │   │   │   ├── fullwidth-mobile.png
│   │   │   │   │   ├── initial-table.png
│   │   │   │   │   ├── items-and-badges.png
│   │   │   │   │   ├── loading-message.png
│   │   │   │   │   ├── new-contact-button.png
│   │   │   │   │   ├── new-contact-saved.png
│   │   │   │   │   ├── no-empty-sections.png
│   │   │   │   │   ├── personal-todo-initial.png
│   │   │   │   │   ├── piechart.png
│   │   │   │   │   ├── review-today.png
│   │   │   │   │   ├── rudimentary-dashboard.png
│   │   │   │   │   ├── section-collapsed.png
│   │   │   │   │   ├── sectioned-items.png
│   │   │   │   │   ├── sections-ordered.png
│   │   │   │   │   ├── spacex-list-with-links.png
│   │   │   │   │   ├── spacex-list.png
│   │   │   │   │   ├── start-personal-todo-1.png
│   │   │   │   │   ├── submit-new-contact.png
│   │   │   │   │   ├── submit-new-task.png
│   │   │   │   │   ├── syntax-highlighting.png
│   │   │   │   │   ├── table-with-badge.png
│   │   │   │   │   ├── template-with-card.png
│   │   │   │   │   ├── test-emulated-api.png
│   │   │   │   │   ├── Thumbs.db
│   │   │   │   │   ├── todo-logo.png
│   │   │   │   │   └── xmlui-tools.png
│   │   │   │   ├── HelloApp.png
│   │   │   │   ├── HelloApp2.png
│   │   │   │   ├── logos
│   │   │   │   │   ├── xmlui1.svg
│   │   │   │   │   ├── xmlui2.svg
│   │   │   │   │   ├── xmlui3.svg
│   │   │   │   │   ├── xmlui4.svg
│   │   │   │   │   ├── xmlui5.svg
│   │   │   │   │   ├── xmlui6.svg
│   │   │   │   │   └── xmlui7.svg
│   │   │   │   ├── pdf
│   │   │   │   │   └── dummy-pdf.jpg
│   │   │   │   ├── rendering-engine
│   │   │   │   │   ├── AppEngine-flow.svg
│   │   │   │   │   ├── Component.svg
│   │   │   │   │   ├── CompoundComponent.svg
│   │   │   │   │   ├── RootComponent.svg
│   │   │   │   │   └── tree-with-containers.svg
│   │   │   │   ├── reviewers-guide
│   │   │   │   │   ├── AppEngine-flow.svg
│   │   │   │   │   └── incbutton-in-action.png
│   │   │   │   ├── tools
│   │   │   │   │   └── boilerplate-structure.png
│   │   │   │   ├── try.svg
│   │   │   │   ├── tutorial
│   │   │   │   │   ├── app-chat-history.png
│   │   │   │   │   ├── app-content-placeholder.png
│   │   │   │   │   ├── app-header-and-content.png
│   │   │   │   │   ├── app-links-channel-selected.png
│   │   │   │   │   ├── app-links-click.png
│   │   │   │   │   ├── app-navigation.png
│   │   │   │   │   ├── finished-ex01.png
│   │   │   │   │   ├── finished-ex02.png
│   │   │   │   │   ├── hello.png
│   │   │   │   │   ├── splash-screen-advanced.png
│   │   │   │   │   ├── splash-screen-after-click.png
│   │   │   │   │   ├── splash-screen-centered.png
│   │   │   │   │   ├── splash-screen-events.png
│   │   │   │   │   ├── splash-screen-expression.png
│   │   │   │   │   ├── splash-screen-reuse-after.png
│   │   │   │   │   ├── splash-screen-reuse-before.png
│   │   │   │   │   └── splash-screen.png
│   │   │   │   └── tutorial-01.png
│   │   │   ├── llms.txt
│   │   │   ├── logo-dark.svg
│   │   │   ├── logo.svg
│   │   │   ├── pg-popout.svg
│   │   │   └── xmlui-logo.svg
│   │   ├── serve.json
│   │   └── web.config
│   ├── scripts
│   │   ├── download-latest-xmlui.js
│   │   ├── generate-rss.js
│   │   ├── get-releases.js
│   │   └── utils.js
│   ├── src
│   │   ├── components
│   │   │   ├── BlogOverview.xmlui
│   │   │   ├── BlogPage.xmlui
│   │   │   ├── Boxes.xmlui
│   │   │   ├── Breadcrumb.xmlui
│   │   │   ├── ChangeLog.xmlui
│   │   │   ├── ColorPalette.xmlui
│   │   │   ├── DocumentLinks.xmlui
│   │   │   ├── DocumentPage.xmlui
│   │   │   ├── DocumentPageNoTOC.xmlui
│   │   │   ├── Icons.xmlui
│   │   │   ├── IncButton.xmlui
│   │   │   ├── IncButton2.xmlui
│   │   │   ├── NameValue.xmlui
│   │   │   ├── PageNotFound.xmlui
│   │   │   ├── PaletteItem.xmlui
│   │   │   ├── Palettes.xmlui
│   │   │   ├── SectionHeader.xmlui
│   │   │   ├── TBD.xmlui
│   │   │   ├── Test.xmlui
│   │   │   ├── ThemesIntro.xmlui
│   │   │   ├── ThousandThemes.xmlui
│   │   │   ├── TubeStops.xmlui
│   │   │   ├── TubeStops.xmlui.xs
│   │   │   └── TwoColumnCode.xmlui
│   │   ├── config.ts
│   │   ├── Main.xmlui
│   │   └── themes
│   │       ├── docs-theme.ts
│   │       ├── earthtone.ts
│   │       ├── xmlui-gray-on-default.ts
│   │       ├── xmlui-green-on-default.ts
│   │       └── xmlui-orange-on-default.ts
│   └── tsconfig.json
├── LICENSE
├── package-lock.json
├── package.json
├── packages
│   ├── tsconfig.json
│   ├── xmlui-animations
│   │   ├── .gitignore
│   │   ├── CHANGELOG.md
│   │   ├── demo
│   │   │   └── Main.xmlui
│   │   ├── index.html
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   └── src
│   │       ├── Animation.tsx
│   │       ├── AnimationNative.tsx
│   │       ├── FadeAnimation.tsx
│   │       ├── FadeInAnimation.tsx
│   │       ├── FadeOutAnimation.tsx
│   │       ├── index.tsx
│   │       ├── ScaleAnimation.tsx
│   │       └── SlideInAnimation.tsx
│   ├── xmlui-devtools
│   │   ├── .gitignore
│   │   ├── CHANGELOG.md
│   │   ├── demo
│   │   │   └── Main.xmlui
│   │   ├── index.html
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   ├── src
│   │   │   ├── devtools
│   │   │   │   ├── DevTools.tsx
│   │   │   │   ├── DevToolsNative.module.scss
│   │   │   │   ├── DevToolsNative.tsx
│   │   │   │   ├── ModalDialog.module.scss
│   │   │   │   ├── ModalDialog.tsx
│   │   │   │   ├── ModalVisibilityContext.tsx
│   │   │   │   ├── Tooltip.module.scss
│   │   │   │   ├── Tooltip.tsx
│   │   │   │   └── utils.ts
│   │   │   ├── editor
│   │   │   │   └── Editor.tsx
│   │   │   └── index.tsx
│   │   └── vite.config-overrides.ts
│   ├── xmlui-hello-world
│   │   ├── .gitignore
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   └── src
│   │       ├── HelloWorld.module.scss
│   │       ├── HelloWorld.tsx
│   │       ├── HelloWorldNative.tsx
│   │       └── index.tsx
│   ├── xmlui-os-frames
│   │   ├── .gitignore
│   │   ├── demo
│   │   │   └── Main.xmlui
│   │   ├── index.html
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   └── src
│   │       ├── index.tsx
│   │       ├── IPhoneFrame.module.scss
│   │       ├── IPhoneFrame.tsx
│   │       ├── MacOSAppFrame.module.scss
│   │       ├── MacOSAppFrame.tsx
│   │       ├── WindowsAppFrame.module.scss
│   │       └── WindowsAppFrame.tsx
│   ├── xmlui-pdf
│   │   ├── .gitignore
│   │   ├── CHANGELOG.md
│   │   ├── demo
│   │   │   ├── components
│   │   │   │   └── Pdf.xmlui
│   │   │   └── Main.xmlui
│   │   ├── index.html
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   └── src
│   │       ├── index.tsx
│   │       ├── LazyPdfNative.tsx
│   │       ├── Pdf.module.scss
│   │       └── Pdf.tsx
│   ├── xmlui-playground
│   │   ├── .gitignore
│   │   ├── CHANGELOG.md
│   │   ├── demo
│   │   │   └── Main.xmlui
│   │   ├── index.html
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   └── src
│   │       ├── hooks
│   │       │   ├── usePlayground.ts
│   │       │   └── useToast.ts
│   │       ├── index.tsx
│   │       ├── playground
│   │       │   ├── Box.module.scss
│   │       │   ├── Box.tsx
│   │       │   ├── CodeSelector.module.scss
│   │       │   ├── CodeSelector.tsx
│   │       │   ├── ConfirmationDialog.module.scss
│   │       │   ├── ConfirmationDialog.tsx
│   │       │   ├── Editor.tsx
│   │       │   ├── Header.module.scss
│   │       │   ├── Header.tsx
│   │       │   ├── Playground.tsx
│   │       │   ├── PlaygroundContent.module.scss
│   │       │   ├── PlaygroundContent.tsx
│   │       │   ├── PlaygroundNative.module.scss
│   │       │   ├── PlaygroundNative.tsx
│   │       │   ├── Preview.tsx
│   │       │   ├── StandalonePlayground.tsx
│   │       │   ├── StandalonePlaygroundNative.module.scss
│   │       │   ├── StandalonePlaygroundNative.tsx
│   │       │   ├── ThemeSwitcher.module.scss
│   │       │   ├── ThemeSwitcher.tsx
│   │       │   └── utils.ts
│   │       ├── providers
│   │       │   ├── Toast.module.scss
│   │       │   └── ToastProvider.tsx
│   │       ├── state
│   │       │   └── store.ts
│   │       ├── themes
│   │       │   └── theme.ts
│   │       └── utils
│   │           └── helpers.ts
│   ├── xmlui-search
│   │   ├── .gitignore
│   │   ├── CHANGELOG.md
│   │   ├── demo
│   │   │   └── Main.xmlui
│   │   ├── index.html
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   └── src
│   │       ├── index.tsx
│   │       ├── Search.module.scss
│   │       └── Search.tsx
│   ├── xmlui-spreadsheet
│   │   ├── .gitignore
│   │   ├── demo
│   │   │   └── Main.xmlui
│   │   ├── index.html
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   └── src
│   │       ├── index.tsx
│   │       ├── Spreadsheet.tsx
│   │       └── SpreadsheetNative.tsx
│   └── xmlui-website-blocks
│       ├── .gitignore
│       ├── CHANGELOG.md
│       ├── demo
│       │   ├── components
│       │   │   ├── HeroBackgroundBreakoutPage.xmlui
│       │   │   ├── HeroBackgroundsPage.xmlui
│       │   │   ├── HeroContentsPage.xmlui
│       │   │   ├── HeroTextAlignPage.xmlui
│       │   │   ├── HeroTextPage.xmlui
│       │   │   └── HeroTonesPage.xmlui
│       │   ├── Main.xmlui
│       │   └── themes
│       │       └── default.ts
│       ├── index.html
│       ├── index.ts
│       ├── meta
│       │   └── componentsMetadata.ts
│       ├── package.json
│       ├── public
│       │   └── resources
│       │       ├── building.jpg
│       │       └── xmlui-logo.svg
│       └── src
│           ├── Carousel
│           │   ├── Carousel.module.scss
│           │   ├── Carousel.tsx
│           │   ├── CarouselContext.tsx
│           │   └── CarouselNative.tsx
│           ├── FancyButton
│           │   ├── FancyButton.module.scss
│           │   ├── FancyButton.tsx
│           │   └── FancyButton.xmlui
│           ├── Hello
│           │   ├── Hello.tsx
│           │   ├── Hello.xmlui
│           │   └── Hello.xmlui.xs
│           ├── HeroSection
│           │   ├── HeroSection.module.scss
│           │   ├── HeroSection.spec.ts
│           │   ├── HeroSection.tsx
│           │   └── HeroSectionNative.tsx
│           ├── index.tsx
│           ├── ScrollToTop
│           │   ├── ScrollToTop.module.scss
│           │   ├── ScrollToTop.tsx
│           │   └── ScrollToTopNative.tsx
│           └── vite-env.d.ts
├── playwright.config.ts
├── README.md
├── tools
│   ├── codefence
│   │   └── xmlui-code-fence-docs.md
│   ├── create-app
│   │   ├── .gitignore
│   │   ├── CHANGELOG.md
│   │   ├── create-app.ts
│   │   ├── helpers
│   │   │   ├── copy.ts
│   │   │   ├── get-pkg-manager.ts
│   │   │   ├── git.ts
│   │   │   ├── install.ts
│   │   │   ├── is-folder-empty.ts
│   │   │   ├── is-writeable.ts
│   │   │   ├── make-dir.ts
│   │   │   └── validate-pkg.ts
│   │   ├── index.ts
│   │   ├── package.json
│   │   ├── templates
│   │   │   ├── default
│   │   │   │   └── ts
│   │   │   │       ├── gitignore
│   │   │   │       ├── index.html
│   │   │   │       ├── index.ts
│   │   │   │       ├── public
│   │   │   │       │   ├── mockServiceWorker.js
│   │   │   │       │   ├── resources
│   │   │   │       │   │   ├── favicon.ico
│   │   │   │       │   │   └── xmlui-logo.svg
│   │   │   │       │   └── serve.json
│   │   │   │       └── src
│   │   │   │           ├── components
│   │   │   │           │   ├── ApiAware.xmlui
│   │   │   │           │   ├── Home.xmlui
│   │   │   │           │   ├── IncButton.xmlui
│   │   │   │           │   └── PagePanel.xmlui
│   │   │   │           ├── config.ts
│   │   │   │           └── Main.xmlui
│   │   │   ├── index.ts
│   │   │   └── types.ts
│   │   └── tsconfig.json
│   ├── create-xmlui-hello-world
│   │   ├── index.js
│   │   └── package.json
│   └── vscode
│       ├── .gitignore
│       ├── .vscode
│       │   ├── launch.json
│       │   └── tasks.json
│       ├── .vscodeignore
│       ├── build.sh
│       ├── CHANGELOG.md
│       ├── esbuild.js
│       ├── eslint.config.mjs
│       ├── formatter-docs.md
│       ├── generate-test-sample.sh
│       ├── LICENSE.md
│       ├── package-lock.json
│       ├── package.json
│       ├── README.md
│       ├── resources
│       │   ├── xmlui-logo.png
│       │   └── xmlui-markup-syntax-highlighting.png
│       ├── src
│       │   ├── extension.ts
│       │   └── server.ts
│       ├── syntaxes
│       │   └── xmlui.tmLanguage.json
│       ├── test-samples
│       │   └── sample.xmlui
│       ├── tsconfig.json
│       └── tsconfig.tsbuildinfo
├── turbo.json
└── xmlui
    ├── .gitignore
    ├── bin
    │   ├── bootstrap.cjs
    │   ├── bootstrap.js
    │   ├── build-lib.ts
    │   ├── build.ts
    │   ├── index.ts
    │   ├── preview.ts
    │   ├── start.ts
    │   ├── vite-xmlui-plugin.ts
    │   └── viteConfig.ts
    ├── CHANGELOG.md
    ├── conventions
    │   ├── component-qa-checklist.md
    │   ├── copilot-conventions.md
    │   ├── create-xmlui-components.md
    │   ├── mermaid.md
    │   ├── testing-conventions.md
    │   └── xmlui-in-a-nutshell.md
    ├── dev-docs
    │   ├── accessibility.md
    │   ├── build-system.md
    │   ├── build-xmlui.md
    │   ├── component-behaviors.md
    │   ├── component-metadata.md
    │   ├── components-with-options.md
    │   ├── containers.md
    │   ├── data-operations.md
    │   ├── glossary.md
    │   ├── index.md
    │   ├── next
    │   │   ├── component-dev-guide.md
    │   │   ├── configuration-management-enhancement-summary.md
    │   │   ├── documentation-scripts-refactoring-complete-summary.md
    │   │   ├── documentation-scripts-refactoring-plan.md
    │   │   ├── duplicate-pattern-extraction-summary.md
    │   │   ├── error-handling-standardization-summary.md
    │   │   ├── generating-component-reference.md
    │   │   ├── index.md
    │   │   ├── logging-consistency-implementation-summary.md
    │   │   ├── project-build.md
    │   │   ├── project-structure.md
    │   │   ├── theme-context.md
    │   │   ├── tiptap-design-considerations.md
    │   │   ├── working-with-code.md
    │   │   ├── xmlui-runtime-architecture
    │   │   └── xmlui-wcag-accessibility-report.md
    │   ├── react-fundamentals.md
    │   ├── release-method.md
    │   ├── standalone-app.md
    │   ├── theme-variables-refactoring.md
    │   ├── ud-components.md
    │   └── xmlui-repo.md
    ├── package.json
    ├── scripts
    │   ├── coverage-only.js
    │   ├── e2e-test-summary.js
    │   ├── extract-component-metadata.js
    │   ├── generate-docs
    │   │   ├── build-downloads-map.mjs
    │   │   ├── build-pages-map.mjs
    │   │   ├── components-config.json
    │   │   ├── configuration-management.mjs
    │   │   ├── constants.mjs
    │   │   ├── create-theme-files.mjs
    │   │   ├── DocsGenerator.mjs
    │   │   ├── error-handling.mjs
    │   │   ├── extensions-config.json
    │   │   ├── folders.mjs
    │   │   ├── generate-summary-files.mjs
    │   │   ├── get-docs.mjs
    │   │   ├── input-handler.mjs
    │   │   ├── logger.mjs
    │   │   ├── logging-standards.mjs
    │   │   ├── MetadataProcessor.mjs
    │   │   ├── pattern-utilities.mjs
    │   │   └── utils.mjs
    │   ├── generate-metadata-markdown.js
    │   ├── get-langserver-metadata.js
    │   ├── inline-links.mjs
    │   └── README-e2e-summary.md
    ├── src
    │   ├── abstractions
    │   │   ├── _conventions.md
    │   │   ├── ActionDefs.ts
    │   │   ├── AppContextDefs.ts
    │   │   ├── ComponentDefs.ts
    │   │   ├── ContainerDefs.ts
    │   │   ├── ExtensionDefs.ts
    │   │   ├── FunctionDefs.ts
    │   │   ├── RendererDefs.ts
    │   │   ├── scripting
    │   │   │   ├── BlockScope.ts
    │   │   │   ├── Compilation.ts
    │   │   │   ├── LogicalThread.ts
    │   │   │   ├── LoopScope.ts
    │   │   │   ├── modules.ts
    │   │   │   ├── ScriptParserError.ts
    │   │   │   ├── Token.ts
    │   │   │   ├── TryScope.ts
    │   │   │   └── TryScopeExp.ts
    │   │   └── ThemingDefs.ts
    │   ├── components
    │   │   ├── _conventions.md
    │   │   ├── abstractions.ts
    │   │   ├── Accordion
    │   │   │   ├── Accordion.md
    │   │   │   ├── Accordion.module.scss
    │   │   │   ├── Accordion.spec.ts
    │   │   │   ├── Accordion.tsx
    │   │   │   ├── AccordionContext.tsx
    │   │   │   ├── AccordionItem.tsx
    │   │   │   ├── AccordionItemNative.tsx
    │   │   │   └── AccordionNative.tsx
    │   │   ├── Animation
    │   │   │   └── AnimationNative.tsx
    │   │   ├── APICall
    │   │   │   ├── APICall.md
    │   │   │   ├── APICall.spec.ts
    │   │   │   ├── APICall.tsx
    │   │   │   └── APICallNative.tsx
    │   │   ├── App
    │   │   │   ├── App.md
    │   │   │   ├── App.module.scss
    │   │   │   ├── App.spec.ts
    │   │   │   ├── App.tsx
    │   │   │   ├── AppLayoutContext.ts
    │   │   │   ├── AppNative.tsx
    │   │   │   ├── AppStateContext.ts
    │   │   │   ├── doc-resources
    │   │   │   │   ├── condensed-sticky.xmlui
    │   │   │   │   ├── condensed.xmlui
    │   │   │   │   ├── horizontal-sticky.xmlui
    │   │   │   │   ├── horizontal.xmlui
    │   │   │   │   ├── vertical-full-header.xmlui
    │   │   │   │   ├── vertical-sticky.xmlui
    │   │   │   │   └── vertical.xmlui
    │   │   │   ├── IndexerContext.ts
    │   │   │   ├── LinkInfoContext.ts
    │   │   │   ├── SearchContext.tsx
    │   │   │   ├── Sheet.module.scss
    │   │   │   └── Sheet.tsx
    │   │   ├── AppHeader
    │   │   │   ├── AppHeader.md
    │   │   │   ├── AppHeader.module.scss
    │   │   │   ├── AppHeader.spec.ts
    │   │   │   ├── AppHeader.tsx
    │   │   │   └── AppHeaderNative.tsx
    │   │   ├── AppState
    │   │   │   ├── AppState.md
    │   │   │   ├── AppState.spec.ts
    │   │   │   ├── AppState.tsx
    │   │   │   └── AppStateNative.tsx
    │   │   ├── AutoComplete
    │   │   │   ├── AutoComplete.md
    │   │   │   ├── AutoComplete.module.scss
    │   │   │   ├── AutoComplete.spec.ts
    │   │   │   ├── AutoComplete.tsx
    │   │   │   ├── AutoCompleteContext.tsx
    │   │   │   └── AutoCompleteNative.tsx
    │   │   ├── Avatar
    │   │   │   ├── Avatar.md
    │   │   │   ├── Avatar.module.scss
    │   │   │   ├── Avatar.spec.ts
    │   │   │   ├── Avatar.tsx
    │   │   │   └── AvatarNative.tsx
    │   │   ├── Backdrop
    │   │   │   ├── Backdrop.md
    │   │   │   ├── Backdrop.module.scss
    │   │   │   ├── Backdrop.spec.ts
    │   │   │   ├── Backdrop.tsx
    │   │   │   └── BackdropNative.tsx
    │   │   ├── Badge
    │   │   │   ├── Badge.md
    │   │   │   ├── Badge.module.scss
    │   │   │   ├── Badge.spec.ts
    │   │   │   ├── Badge.tsx
    │   │   │   └── BadgeNative.tsx
    │   │   ├── Bookmark
    │   │   │   ├── Bookmark.md
    │   │   │   ├── Bookmark.module.scss
    │   │   │   ├── Bookmark.spec.ts
    │   │   │   ├── Bookmark.tsx
    │   │   │   └── BookmarkNative.tsx
    │   │   ├── Breakout
    │   │   │   ├── Breakout.module.scss
    │   │   │   ├── Breakout.spec.ts
    │   │   │   ├── Breakout.tsx
    │   │   │   └── BreakoutNative.tsx
    │   │   ├── Button
    │   │   │   ├── Button-style.spec.ts
    │   │   │   ├── Button.md
    │   │   │   ├── Button.module.scss
    │   │   │   ├── Button.spec.ts
    │   │   │   ├── Button.tsx
    │   │   │   └── ButtonNative.tsx
    │   │   ├── Card
    │   │   │   ├── Card.md
    │   │   │   ├── Card.module.scss
    │   │   │   ├── Card.spec.ts
    │   │   │   ├── Card.tsx
    │   │   │   └── CardNative.tsx
    │   │   ├── Carousel
    │   │   │   ├── Carousel.md
    │   │   │   ├── Carousel.module.scss
    │   │   │   ├── Carousel.spec.ts
    │   │   │   ├── Carousel.tsx
    │   │   │   ├── CarouselContext.tsx
    │   │   │   ├── CarouselItem.tsx
    │   │   │   ├── CarouselItemNative.tsx
    │   │   │   └── CarouselNative.tsx
    │   │   ├── ChangeListener
    │   │   │   ├── ChangeListener.md
    │   │   │   ├── ChangeListener.spec.ts
    │   │   │   ├── ChangeListener.tsx
    │   │   │   └── ChangeListenerNative.tsx
    │   │   ├── chart-color-schemes.ts
    │   │   ├── Charts
    │   │   │   ├── AreaChart
    │   │   │   │   ├── AreaChart.md
    │   │   │   │   ├── AreaChart.spec.ts
    │   │   │   │   ├── AreaChart.tsx
    │   │   │   │   └── AreaChartNative.tsx
    │   │   │   ├── BarChart
    │   │   │   │   ├── BarChart.md
    │   │   │   │   ├── BarChart.module.scss
    │   │   │   │   ├── BarChart.spec.ts
    │   │   │   │   ├── BarChart.tsx
    │   │   │   │   └── BarChartNative.tsx
    │   │   │   ├── DonutChart
    │   │   │   │   ├── DonutChart.spec.ts
    │   │   │   │   └── DonutChart.tsx
    │   │   │   ├── LabelList
    │   │   │   │   ├── LabelList.module.scss
    │   │   │   │   ├── LabelList.spec.ts
    │   │   │   │   ├── LabelList.tsx
    │   │   │   │   └── LabelListNative.tsx
    │   │   │   ├── Legend
    │   │   │   │   ├── Legend.spec.ts
    │   │   │   │   ├── Legend.tsx
    │   │   │   │   └── LegendNative.tsx
    │   │   │   ├── LineChart
    │   │   │   │   ├── LineChart.md
    │   │   │   │   ├── LineChart.module.scss
    │   │   │   │   ├── LineChart.spec.ts
    │   │   │   │   ├── LineChart.tsx
    │   │   │   │   └── LineChartNative.tsx
    │   │   │   ├── PieChart
    │   │   │   │   ├── PieChart.md
    │   │   │   │   ├── PieChart.spec.ts
    │   │   │   │   ├── PieChart.tsx
    │   │   │   │   ├── PieChartNative.module.scss
    │   │   │   │   └── PieChartNative.tsx
    │   │   │   ├── RadarChart
    │   │   │   │   ├── RadarChart.md
    │   │   │   │   ├── RadarChart.spec.ts
    │   │   │   │   ├── RadarChart.tsx
    │   │   │   │   └── RadarChartNative.tsx
    │   │   │   ├── Tooltip
    │   │   │   │   ├── TooltipContent.module.scss
    │   │   │   │   ├── TooltipContent.spec.ts
    │   │   │   │   └── TooltipContent.tsx
    │   │   │   └── utils
    │   │   │       ├── abstractions.ts
    │   │   │       └── ChartProvider.tsx
    │   │   ├── Checkbox
    │   │   │   ├── Checkbox.md
    │   │   │   ├── Checkbox.spec.ts
    │   │   │   └── Checkbox.tsx
    │   │   ├── CodeBlock
    │   │   │   ├── CodeBlock.module.scss
    │   │   │   ├── CodeBlock.spec.ts
    │   │   │   ├── CodeBlock.tsx
    │   │   │   ├── CodeBlockNative.tsx
    │   │   │   └── highlight-code.ts
    │   │   ├── collectedComponentMetadata.ts
    │   │   ├── ColorPicker
    │   │   │   ├── ColorPicker.md
    │   │   │   ├── ColorPicker.module.scss
    │   │   │   ├── ColorPicker.spec.ts
    │   │   │   ├── ColorPicker.tsx
    │   │   │   └── ColorPickerNative.tsx
    │   │   ├── Column
    │   │   │   ├── Column.md
    │   │   │   ├── Column.tsx
    │   │   │   ├── ColumnNative.tsx
    │   │   │   ├── doc-resources
    │   │   │   │   └── list-component-data.js
    │   │   │   └── TableContext.tsx
    │   │   ├── component-utils.ts
    │   │   ├── ComponentProvider.tsx
    │   │   ├── ComponentRegistryContext.tsx
    │   │   ├── container-helpers.tsx
    │   │   ├── ContentSeparator
    │   │   │   ├── ContentSeparator.md
    │   │   │   ├── ContentSeparator.module.scss
    │   │   │   ├── ContentSeparator.spec.ts
    │   │   │   ├── ContentSeparator.tsx
    │   │   │   └── ContentSeparatorNative.tsx
    │   │   ├── DataSource
    │   │   │   ├── DataSource.md
    │   │   │   └── DataSource.tsx
    │   │   ├── DateInput
    │   │   │   ├── DateInput.md
    │   │   │   ├── DateInput.module.scss
    │   │   │   ├── DateInput.spec.ts
    │   │   │   ├── DateInput.tsx
    │   │   │   └── DateInputNative.tsx
    │   │   ├── DatePicker
    │   │   │   ├── DatePicker.md
    │   │   │   ├── DatePicker.module.scss
    │   │   │   ├── DatePicker.spec.ts
    │   │   │   ├── DatePicker.tsx
    │   │   │   └── DatePickerNative.tsx
    │   │   ├── DropdownMenu
    │   │   │   ├── DropdownMenu.md
    │   │   │   ├── DropdownMenu.module.scss
    │   │   │   ├── DropdownMenu.spec.ts
    │   │   │   ├── DropdownMenu.tsx
    │   │   │   ├── DropdownMenuNative.tsx
    │   │   │   ├── MenuItem.md
    │   │   │   └── SubMenuItem.md
    │   │   ├── EmojiSelector
    │   │   │   ├── EmojiSelector.md
    │   │   │   ├── EmojiSelector.spec.ts
    │   │   │   ├── EmojiSelector.tsx
    │   │   │   └── EmojiSelectorNative.tsx
    │   │   ├── ExpandableItem
    │   │   │   ├── ExpandableItem.module.scss
    │   │   │   ├── ExpandableItem.spec.ts
    │   │   │   ├── ExpandableItem.tsx
    │   │   │   └── ExpandableItemNative.tsx
    │   │   ├── FileInput
    │   │   │   ├── FileInput.md
    │   │   │   ├── FileInput.module.scss
    │   │   │   ├── FileInput.spec.ts
    │   │   │   ├── FileInput.tsx
    │   │   │   └── FileInputNative.tsx
    │   │   ├── FileUploadDropZone
    │   │   │   ├── FileUploadDropZone.md
    │   │   │   ├── FileUploadDropZone.module.scss
    │   │   │   ├── FileUploadDropZone.spec.ts
    │   │   │   ├── FileUploadDropZone.tsx
    │   │   │   └── FileUploadDropZoneNative.tsx
    │   │   ├── FlowLayout
    │   │   │   ├── FlowLayout.md
    │   │   │   ├── FlowLayout.module.scss
    │   │   │   ├── FlowLayout.spec.ts
    │   │   │   ├── FlowLayout.spec.ts-snapshots
    │   │   │   │   └── Edge-cases-boxShadow-is-not-clipped-1-non-smoke-darwin.png
    │   │   │   ├── FlowLayout.tsx
    │   │   │   └── FlowLayoutNative.tsx
    │   │   ├── Footer
    │   │   │   ├── Footer.md
    │   │   │   ├── Footer.module.scss
    │   │   │   ├── Footer.spec.ts
    │   │   │   ├── Footer.tsx
    │   │   │   └── FooterNative.tsx
    │   │   ├── Form
    │   │   │   ├── Form.md
    │   │   │   ├── Form.module.scss
    │   │   │   ├── Form.spec.ts
    │   │   │   ├── Form.tsx
    │   │   │   ├── formActions.ts
    │   │   │   ├── FormContext.ts
    │   │   │   └── FormNative.tsx
    │   │   ├── FormItem
    │   │   │   ├── FormItem.md
    │   │   │   ├── FormItem.module.scss
    │   │   │   ├── FormItem.spec.ts
    │   │   │   ├── FormItem.tsx
    │   │   │   ├── FormItemNative.tsx
    │   │   │   ├── HelperText.module.scss
    │   │   │   ├── HelperText.tsx
    │   │   │   ├── ItemWithLabel.tsx
    │   │   │   └── Validations.ts
    │   │   ├── FormSection
    │   │   │   ├── FormSection.md
    │   │   │   ├── FormSection.ts
    │   │   │   └── FormSection.xmlui
    │   │   ├── Fragment
    │   │   │   ├── Fragment.spec.ts
    │   │   │   └── Fragment.tsx
    │   │   ├── Heading
    │   │   │   ├── abstractions.ts
    │   │   │   ├── H1.md
    │   │   │   ├── H1.spec.ts
    │   │   │   ├── H2.md
    │   │   │   ├── H2.spec.ts
    │   │   │   ├── H3.md
    │   │   │   ├── H3.spec.ts
    │   │   │   ├── H4.md
    │   │   │   ├── H4.spec.ts
    │   │   │   ├── H5.md
    │   │   │   ├── H5.spec.ts
    │   │   │   ├── H6.md
    │   │   │   ├── H6.spec.ts
    │   │   │   ├── Heading.md
    │   │   │   ├── Heading.module.scss
    │   │   │   ├── Heading.spec.ts
    │   │   │   ├── Heading.tsx
    │   │   │   └── HeadingNative.tsx
    │   │   ├── HoverCard
    │   │   │   ├── HoverCard.tsx
    │   │   │   └── HovercardNative.tsx
    │   │   ├── HtmlTags
    │   │   │   ├── HtmlTags.module.scss
    │   │   │   ├── HtmlTags.spec.ts
    │   │   │   └── HtmlTags.tsx
    │   │   ├── Icon
    │   │   │   ├── AdmonitionDanger.tsx
    │   │   │   ├── AdmonitionInfo.tsx
    │   │   │   ├── AdmonitionNote.tsx
    │   │   │   ├── AdmonitionTip.tsx
    │   │   │   ├── AdmonitionWarning.tsx
    │   │   │   ├── ApiIcon.tsx
    │   │   │   ├── ArrowDropDown.module.scss
    │   │   │   ├── ArrowDropDown.tsx
    │   │   │   ├── ArrowDropUp.module.scss
    │   │   │   ├── ArrowDropUp.tsx
    │   │   │   ├── ArrowLeft.module.scss
    │   │   │   ├── ArrowLeft.tsx
    │   │   │   ├── ArrowRight.module.scss
    │   │   │   ├── ArrowRight.tsx
    │   │   │   ├── Attach.tsx
    │   │   │   ├── Binding.module.scss
    │   │   │   ├── Binding.tsx
    │   │   │   ├── BoardIcon.tsx
    │   │   │   ├── BoxIcon.tsx
    │   │   │   ├── CheckIcon.tsx
    │   │   │   ├── ChevronDownIcon.tsx
    │   │   │   ├── ChevronLeft.tsx
    │   │   │   ├── ChevronRight.tsx
    │   │   │   ├── ChevronUpIcon.tsx
    │   │   │   ├── CodeFileIcon.tsx
    │   │   │   ├── CodeSandbox.tsx
    │   │   │   ├── CompactListIcon.tsx
    │   │   │   ├── ContentCopyIcon.tsx
    │   │   │   ├── DarkToLightIcon.tsx
    │   │   │   ├── DatabaseIcon.module.scss
    │   │   │   ├── DatabaseIcon.tsx
    │   │   │   ├── DocFileIcon.tsx
    │   │   │   ├── DocIcon.tsx
    │   │   │   ├── DotMenuHorizontalIcon.tsx
    │   │   │   ├── DotMenuIcon.tsx
    │   │   │   ├── EmailIcon.tsx
    │   │   │   ├── EmptyFolderIcon.tsx
    │   │   │   ├── ErrorIcon.tsx
    │   │   │   ├── ExpressionIcon.tsx
    │   │   │   ├── FillPlusCricleIcon.tsx
    │   │   │   ├── FilterIcon.tsx
    │   │   │   ├── FolderIcon.tsx
    │   │   │   ├── GlobeIcon.tsx
    │   │   │   ├── HomeIcon.tsx
    │   │   │   ├── HyperLinkIcon.tsx
    │   │   │   ├── Icon.md
    │   │   │   ├── Icon.module.scss
    │   │   │   ├── Icon.spec.ts
    │   │   │   ├── Icon.tsx
    │   │   │   ├── IconNative.tsx
    │   │   │   ├── ImageFileIcon.tsx
    │   │   │   ├── Inspect.tsx
    │   │   │   ├── LightToDark.tsx
    │   │   │   ├── LinkIcon.tsx
    │   │   │   ├── ListIcon.tsx
    │   │   │   ├── LooseListIcon.tsx
    │   │   │   ├── MoonIcon.tsx
    │   │   │   ├── MoreOptionsIcon.tsx
    │   │   │   ├── NoSortIcon.tsx
    │   │   │   ├── PDFIcon.tsx
    │   │   │   ├── PenIcon.tsx
    │   │   │   ├── PhoneIcon.tsx
    │   │   │   ├── PhotoIcon.tsx
    │   │   │   ├── PlusIcon.tsx
    │   │   │   ├── SearchIcon.tsx
    │   │   │   ├── ShareIcon.tsx
    │   │   │   ├── SortAscendingIcon.tsx
    │   │   │   ├── SortDescendingIcon.tsx
    │   │   │   ├── StarsIcon.tsx
    │   │   │   ├── SunIcon.tsx
    │   │   │   ├── svg
    │   │   │   │   ├── admonition_danger.svg
    │   │   │   │   ├── admonition_info.svg
    │   │   │   │   ├── admonition_note.svg
    │   │   │   │   ├── admonition_tip.svg
    │   │   │   │   ├── admonition_warning.svg
    │   │   │   │   ├── api.svg
    │   │   │   │   ├── arrow-dropdown.svg
    │   │   │   │   ├── arrow-left.svg
    │   │   │   │   ├── arrow-right.svg
    │   │   │   │   ├── arrow-up.svg
    │   │   │   │   ├── attach.svg
    │   │   │   │   ├── binding.svg
    │   │   │   │   ├── box.svg
    │   │   │   │   ├── bulb.svg
    │   │   │   │   ├── code-file.svg
    │   │   │   │   ├── code-sandbox.svg
    │   │   │   │   ├── dark_to_light.svg
    │   │   │   │   ├── database.svg
    │   │   │   │   ├── doc.svg
    │   │   │   │   ├── empty-folder.svg
    │   │   │   │   ├── expression.svg
    │   │   │   │   ├── eye-closed.svg
    │   │   │   │   ├── eye-dark.svg
    │   │   │   │   ├── eye.svg
    │   │   │   │   ├── file-text.svg
    │   │   │   │   ├── filter.svg
    │   │   │   │   ├── folder.svg
    │   │   │   │   ├── img.svg
    │   │   │   │   ├── inspect.svg
    │   │   │   │   ├── light_to_dark.svg
    │   │   │   │   ├── moon.svg
    │   │   │   │   ├── pdf.svg
    │   │   │   │   ├── photo.svg
    │   │   │   │   ├── share.svg
    │   │   │   │   ├── stars.svg
    │   │   │   │   ├── sun.svg
    │   │   │   │   ├── trending-down.svg
    │   │   │   │   ├── trending-level.svg
    │   │   │   │   ├── trending-up.svg
    │   │   │   │   ├── txt.svg
    │   │   │   │   ├── unknown-file.svg
    │   │   │   │   ├── unlink.svg
    │   │   │   │   └── xls.svg
    │   │   │   ├── TableDeleteColumnIcon.tsx
    │   │   │   ├── TableDeleteRowIcon.tsx
    │   │   │   ├── TableInsertColumnIcon.tsx
    │   │   │   ├── TableInsertRowIcon.tsx
    │   │   │   ├── TrashIcon.tsx
    │   │   │   ├── TrendingDownIcon.tsx
    │   │   │   ├── TrendingLevelIcon.tsx
    │   │   │   ├── TrendingUpIcon.tsx
    │   │   │   ├── TxtIcon.tsx
    │   │   │   ├── UnknownFileIcon.tsx
    │   │   │   ├── UnlinkIcon.tsx
    │   │   │   ├── UserIcon.tsx
    │   │   │   ├── WarningIcon.tsx
    │   │   │   └── XlsIcon.tsx
    │   │   ├── IconProvider.tsx
    │   │   ├── IconRegistryContext.tsx
    │   │   ├── IFrame
    │   │   │   ├── IFrame.md
    │   │   │   ├── IFrame.module.scss
    │   │   │   ├── IFrame.spec.ts
    │   │   │   ├── IFrame.tsx
    │   │   │   └── IFrameNative.tsx
    │   │   ├── Image
    │   │   │   ├── Image.md
    │   │   │   ├── Image.module.scss
    │   │   │   ├── Image.spec.ts
    │   │   │   ├── Image.tsx
    │   │   │   └── ImageNative.tsx
    │   │   ├── Input
    │   │   │   ├── index.ts
    │   │   │   ├── InputAdornment.module.scss
    │   │   │   ├── InputAdornment.tsx
    │   │   │   ├── InputDivider.module.scss
    │   │   │   ├── InputDivider.tsx
    │   │   │   ├── InputLabel.module.scss
    │   │   │   ├── InputLabel.tsx
    │   │   │   ├── PartialInput.module.scss
    │   │   │   └── PartialInput.tsx
    │   │   ├── InspectButton
    │   │   │   ├── InspectButton.module.scss
    │   │   │   └── InspectButton.tsx
    │   │   ├── Items
    │   │   │   ├── Items.md
    │   │   │   ├── Items.spec.ts
    │   │   │   ├── Items.tsx
    │   │   │   └── ItemsNative.tsx
    │   │   ├── Link
    │   │   │   ├── Link.md
    │   │   │   ├── Link.module.scss
    │   │   │   ├── Link.spec.ts
    │   │   │   ├── Link.tsx
    │   │   │   └── LinkNative.tsx
    │   │   ├── List
    │   │   │   ├── doc-resources
    │   │   │   │   └── list-component-data.js
    │   │   │   ├── List.md
    │   │   │   ├── List.module.scss
    │   │   │   ├── List.spec.ts
    │   │   │   ├── List.tsx
    │   │   │   └── ListNative.tsx
    │   │   ├── Logo
    │   │   │   ├── doc-resources
    │   │   │   │   └── xmlui-logo.svg
    │   │   │   ├── Logo.md
    │   │   │   ├── Logo.tsx
    │   │   │   └── LogoNative.tsx
    │   │   ├── Markdown
    │   │   │   ├── CodeText.module.scss
    │   │   │   ├── CodeText.tsx
    │   │   │   ├── Markdown.md
    │   │   │   ├── Markdown.module.scss
    │   │   │   ├── Markdown.spec.ts
    │   │   │   ├── Markdown.tsx
    │   │   │   ├── MarkdownNative.tsx
    │   │   │   ├── parse-binding-expr.ts
    │   │   │   └── utils.ts
    │   │   ├── metadata-helpers.ts
    │   │   ├── ModalDialog
    │   │   │   ├── ConfirmationModalContextProvider.tsx
    │   │   │   ├── Dialog.module.scss
    │   │   │   ├── Dialog.tsx
    │   │   │   ├── ModalDialog.md
    │   │   │   ├── ModalDialog.module.scss
    │   │   │   ├── ModalDialog.spec.ts
    │   │   │   ├── ModalDialog.tsx
    │   │   │   ├── ModalDialogNative.tsx
    │   │   │   └── ModalVisibilityContext.tsx
    │   │   ├── NavGroup
    │   │   │   ├── NavGroup.md
    │   │   │   ├── NavGroup.module.scss
    │   │   │   ├── NavGroup.spec.ts
    │   │   │   ├── NavGroup.tsx
    │   │   │   ├── NavGroupContext.ts
    │   │   │   └── NavGroupNative.tsx
    │   │   ├── NavLink
    │   │   │   ├── NavLink.md
    │   │   │   ├── NavLink.module.scss
    │   │   │   ├── NavLink.spec.ts
    │   │   │   ├── NavLink.tsx
    │   │   │   └── NavLinkNative.tsx
    │   │   ├── NavPanel
    │   │   │   ├── NavPanel.md
    │   │   │   ├── NavPanel.module.scss
    │   │   │   ├── NavPanel.spec.ts
    │   │   │   ├── NavPanel.tsx
    │   │   │   └── NavPanelNative.tsx
    │   │   ├── NestedApp
    │   │   │   ├── AppWithCodeView.module.scss
    │   │   │   ├── AppWithCodeView.tsx
    │   │   │   ├── AppWithCodeViewNative.tsx
    │   │   │   ├── defaultProps.tsx
    │   │   │   ├── logo.svg
    │   │   │   ├── NestedApp.module.scss
    │   │   │   ├── NestedApp.tsx
    │   │   │   ├── NestedAppNative.tsx
    │   │   │   ├── Tooltip.module.scss
    │   │   │   ├── Tooltip.tsx
    │   │   │   └── utils.ts
    │   │   ├── NoResult
    │   │   │   ├── NoResult.md
    │   │   │   ├── NoResult.module.scss
    │   │   │   ├── NoResult.spec.ts
    │   │   │   ├── NoResult.tsx
    │   │   │   └── NoResultNative.tsx
    │   │   ├── NumberBox
    │   │   │   ├── numberbox-abstractions.ts
    │   │   │   ├── NumberBox.md
    │   │   │   ├── NumberBox.module.scss
    │   │   │   ├── NumberBox.spec.ts
    │   │   │   ├── NumberBox.tsx
    │   │   │   └── NumberBoxNative.tsx
    │   │   ├── Option
    │   │   │   ├── Option.md
    │   │   │   ├── Option.spec.ts
    │   │   │   ├── Option.tsx
    │   │   │   ├── OptionNative.tsx
    │   │   │   └── OptionTypeProvider.tsx
    │   │   ├── PageMetaTitle
    │   │   │   ├── PageMetaTilteNative.tsx
    │   │   │   ├── PageMetaTitle.md
    │   │   │   ├── PageMetaTitle.spec.ts
    │   │   │   └── PageMetaTitle.tsx
    │   │   ├── Pages
    │   │   │   ├── Page.md
    │   │   │   ├── Pages.md
    │   │   │   ├── Pages.module.scss
    │   │   │   ├── Pages.tsx
    │   │   │   └── PagesNative.tsx
    │   │   ├── Pagination
    │   │   │   ├── Pagination.md
    │   │   │   ├── Pagination.module.scss
    │   │   │   ├── Pagination.spec.ts
    │   │   │   ├── Pagination.tsx
    │   │   │   └── PaginationNative.tsx
    │   │   ├── PositionedContainer
    │   │   │   ├── PositionedContainer.module.scss
    │   │   │   ├── PositionedContainer.tsx
    │   │   │   └── PositionedContainerNative.tsx
    │   │   ├── ProfileMenu
    │   │   │   ├── ProfileMenu.module.scss
    │   │   │   └── ProfileMenu.tsx
    │   │   ├── ProgressBar
    │   │   │   ├── ProgressBar.md
    │   │   │   ├── ProgressBar.module.scss
    │   │   │   ├── ProgressBar.spec.ts
    │   │   │   ├── ProgressBar.tsx
    │   │   │   └── ProgressBarNative.tsx
    │   │   ├── Queue
    │   │   │   ├── Queue.md
    │   │   │   ├── Queue.spec.ts
    │   │   │   ├── Queue.tsx
    │   │   │   ├── queueActions.ts
    │   │   │   └── QueueNative.tsx
    │   │   ├── RadioGroup
    │   │   │   ├── RadioGroup.md
    │   │   │   ├── RadioGroup.module.scss
    │   │   │   ├── RadioGroup.spec.ts
    │   │   │   ├── RadioGroup.tsx
    │   │   │   ├── RadioGroupNative.tsx
    │   │   │   ├── RadioItem.tsx
    │   │   │   └── RadioItemNative.tsx
    │   │   ├── RealTimeAdapter
    │   │   │   ├── RealTimeAdapter.tsx
    │   │   │   └── RealTimeAdapterNative.tsx
    │   │   ├── Redirect
    │   │   │   ├── Redirect.md
    │   │   │   ├── Redirect.spec.ts
    │   │   │   └── Redirect.tsx
    │   │   ├── ResponsiveBar
    │   │   │   ├── README.md
    │   │   │   ├── ResponsiveBar.md
    │   │   │   ├── ResponsiveBar.module.scss
    │   │   │   ├── ResponsiveBar.spec.ts
    │   │   │   ├── ResponsiveBar.tsx
    │   │   │   └── ResponsiveBarNative.tsx
    │   │   ├── Select
    │   │   │   ├── HiddenOption.tsx
    │   │   │   ├── OptionContext.ts
    │   │   │   ├── Select.md
    │   │   │   ├── Select.module.scss
    │   │   │   ├── Select.spec.ts
    │   │   │   ├── Select.tsx
    │   │   │   ├── SelectContext.tsx
    │   │   │   └── SelectNative.tsx
    │   │   ├── SelectionStore
    │   │   │   ├── SelectionStore.md
    │   │   │   ├── SelectionStore.tsx
    │   │   │   └── SelectionStoreNative.tsx
    │   │   ├── Slider
    │   │   │   ├── Slider.md
    │   │   │   ├── Slider.module.scss
    │   │   │   ├── Slider.spec.ts
    │   │   │   ├── Slider.tsx
    │   │   │   └── SliderNative.tsx
    │   │   ├── Slot
    │   │   │   ├── Slot.md
    │   │   │   ├── Slot.spec.ts
    │   │   │   └── Slot.ts
    │   │   ├── SlotItem.tsx
    │   │   ├── SpaceFiller
    │   │   │   ├── SpaceFiller.md
    │   │   │   ├── SpaceFiller.module.scss
    │   │   │   ├── SpaceFiller.spec.ts
    │   │   │   ├── SpaceFiller.tsx
    │   │   │   └── SpaceFillerNative.tsx
    │   │   ├── Spinner
    │   │   │   ├── Spinner.md
    │   │   │   ├── Spinner.module.scss
    │   │   │   ├── Spinner.spec.ts
    │   │   │   ├── Spinner.tsx
    │   │   │   └── SpinnerNative.tsx
    │   │   ├── Splitter
    │   │   │   ├── HSplitter.md
    │   │   │   ├── HSplitter.spec.ts
    │   │   │   ├── Splitter.md
    │   │   │   ├── Splitter.module.scss
    │   │   │   ├── Splitter.spec.ts
    │   │   │   ├── Splitter.tsx
    │   │   │   ├── SplitterNative.tsx
    │   │   │   ├── utils.ts
    │   │   │   ├── VSplitter.md
    │   │   │   └── VSplitter.spec.ts
    │   │   ├── Stack
    │   │   │   ├── CHStack.md
    │   │   │   ├── CHStack.spec.ts
    │   │   │   ├── CVStack.md
    │   │   │   ├── CVStack.spec.ts
    │   │   │   ├── HStack.md
    │   │   │   ├── HStack.spec.ts
    │   │   │   ├── Stack.md
    │   │   │   ├── Stack.module.scss
    │   │   │   ├── Stack.spec.ts
    │   │   │   ├── Stack.tsx
    │   │   │   ├── StackNative.tsx
    │   │   │   ├── VStack.md
    │   │   │   └── VStack.spec.ts
    │   │   ├── StickyBox
    │   │   │   ├── StickyBox.md
    │   │   │   ├── StickyBox.module.scss
    │   │   │   ├── StickyBox.tsx
    │   │   │   └── StickyBoxNative.tsx
    │   │   ├── Switch
    │   │   │   ├── Switch.md
    │   │   │   ├── Switch.spec.ts
    │   │   │   └── Switch.tsx
    │   │   ├── Table
    │   │   │   ├── doc-resources
    │   │   │   │   └── list-component-data.js
    │   │   │   ├── react-table-config.d.ts
    │   │   │   ├── Table.md
    │   │   │   ├── Table.module.scss
    │   │   │   ├── Table.spec.ts
    │   │   │   ├── Table.tsx
    │   │   │   ├── TableNative.tsx
    │   │   │   └── useRowSelection.tsx
    │   │   ├── TableOfContents
    │   │   │   ├── TableOfContents.module.scss
    │   │   │   ├── TableOfContents.spec.ts
    │   │   │   ├── TableOfContents.tsx
    │   │   │   └── TableOfContentsNative.tsx
    │   │   ├── Tabs
    │   │   │   ├── TabContext.tsx
    │   │   │   ├── TabItem.md
    │   │   │   ├── TabItem.tsx
    │   │   │   ├── TabItemNative.tsx
    │   │   │   ├── Tabs.md
    │   │   │   ├── Tabs.module.scss
    │   │   │   ├── Tabs.spec.ts
    │   │   │   ├── Tabs.tsx
    │   │   │   └── TabsNative.tsx
    │   │   ├── Text
    │   │   │   ├── Text.md
    │   │   │   ├── Text.module.scss
    │   │   │   ├── Text.spec.ts
    │   │   │   ├── Text.tsx
    │   │   │   └── TextNative.tsx
    │   │   ├── TextArea
    │   │   │   ├── TextArea.md
    │   │   │   ├── TextArea.module.scss
    │   │   │   ├── TextArea.spec.ts
    │   │   │   ├── TextArea.tsx
    │   │   │   ├── TextAreaNative.tsx
    │   │   │   ├── TextAreaResizable.tsx
    │   │   │   └── useComposedRef.ts
    │   │   ├── TextBox
    │   │   │   ├── TextBox.md
    │   │   │   ├── TextBox.module.scss
    │   │   │   ├── TextBox.spec.ts
    │   │   │   ├── TextBox.tsx
    │   │   │   └── TextBoxNative.tsx
    │   │   ├── Theme
    │   │   │   ├── NotificationToast.tsx
    │   │   │   ├── Theme.md
    │   │   │   ├── Theme.module.scss
    │   │   │   ├── Theme.spec.ts
    │   │   │   ├── Theme.tsx
    │   │   │   └── ThemeNative.tsx
    │   │   ├── TimeInput
    │   │   │   ├── TimeInput.md
    │   │   │   ├── TimeInput.module.scss
    │   │   │   ├── TimeInput.spec.ts
    │   │   │   ├── TimeInput.tsx
    │   │   │   ├── TimeInputNative.tsx
    │   │   │   └── utils.ts
    │   │   ├── Timer
    │   │   │   ├── Timer.md
    │   │   │   ├── Timer.spec.ts
    │   │   │   ├── Timer.tsx
    │   │   │   └── TimerNative.tsx
    │   │   ├── Toggle
    │   │   │   ├── Toggle.module.scss
    │   │   │   └── Toggle.tsx
    │   │   ├── ToneChangerButton
    │   │   │   ├── ToneChangerButton.md
    │   │   │   ├── ToneChangerButton.spec.ts
    │   │   │   └── ToneChangerButton.tsx
    │   │   ├── ToneSwitch
    │   │   │   ├── ToneSwitch.md
    │   │   │   ├── ToneSwitch.module.scss
    │   │   │   ├── ToneSwitch.spec.ts
    │   │   │   ├── ToneSwitch.tsx
    │   │   │   └── ToneSwitchNative.tsx
    │   │   ├── Tooltip
    │   │   │   ├── Tooltip.md
    │   │   │   ├── Tooltip.module.scss
    │   │   │   ├── Tooltip.spec.ts
    │   │   │   ├── Tooltip.tsx
    │   │   │   └── TooltipNative.tsx
    │   │   ├── Tree
    │   │   │   ├── testData.ts
    │   │   │   ├── Tree-dynamic.spec.ts
    │   │   │   ├── Tree-icons.spec.ts
    │   │   │   ├── Tree.md
    │   │   │   ├── Tree.spec.ts
    │   │   │   ├── TreeComponent.module.scss
    │   │   │   ├── TreeComponent.tsx
    │   │   │   └── TreeNative.tsx
    │   │   ├── TreeDisplay
    │   │   │   ├── TreeDisplay.md
    │   │   │   ├── TreeDisplay.module.scss
    │   │   │   ├── TreeDisplay.tsx
    │   │   │   └── TreeDisplayNative.tsx
    │   │   ├── ValidationSummary
    │   │   │   ├── ValidationSummary.module.scss
    │   │   │   └── ValidationSummary.tsx
    │   │   └── VisuallyHidden.tsx
    │   ├── components-core
    │   │   ├── abstractions
    │   │   │   ├── ComponentRenderer.ts
    │   │   │   ├── LoaderRenderer.ts
    │   │   │   ├── standalone.ts
    │   │   │   └── treeAbstractions.ts
    │   │   ├── action
    │   │   │   ├── actions.ts
    │   │   │   ├── APICall.tsx
    │   │   │   ├── FileDownloadAction.tsx
    │   │   │   ├── FileUploadAction.tsx
    │   │   │   ├── NavigateAction.tsx
    │   │   │   └── TimedAction.tsx
    │   │   ├── ApiBoundComponent.tsx
    │   │   ├── appContext
    │   │   │   ├── date-functions.ts
    │   │   │   ├── math-function.ts
    │   │   │   └── misc-utils.ts
    │   │   ├── AppContext.tsx
    │   │   ├── behaviors
    │   │   │   ├── Behavior.tsx
    │   │   │   └── CoreBehaviors.tsx
    │   │   ├── component-hooks.ts
    │   │   ├── ComponentDecorator.tsx
    │   │   ├── ComponentViewer.tsx
    │   │   ├── CompoundComponent.tsx
    │   │   ├── constants.ts
    │   │   ├── DebugViewProvider.tsx
    │   │   ├── descriptorHelper.ts
    │   │   ├── devtools
    │   │   │   ├── InspectorDialog.module.scss
    │   │   │   ├── InspectorDialog.tsx
    │   │   │   └── InspectorDialogVisibilityContext.tsx
    │   │   ├── EngineError.ts
    │   │   ├── event-handlers.ts
    │   │   ├── InspectorButton.module.scss
    │   │   ├── InspectorContext.tsx
    │   │   ├── interception
    │   │   │   ├── abstractions.ts
    │   │   │   ├── ApiInterceptor.ts
    │   │   │   ├── ApiInterceptorProvider.tsx
    │   │   │   ├── apiInterceptorWorker.ts
    │   │   │   ├── Backend.ts
    │   │   │   ├── Errors.ts
    │   │   │   ├── IndexedDb.ts
    │   │   │   ├── initMock.ts
    │   │   │   ├── InMemoryDb.ts
    │   │   │   ├── ReadonlyCollection.ts
    │   │   │   └── useApiInterceptorContext.tsx
    │   │   ├── loader
    │   │   │   ├── ApiLoader.tsx
    │   │   │   ├── DataLoader.tsx
    │   │   │   ├── ExternalDataLoader.tsx
    │   │   │   ├── Loader.tsx
    │   │   │   ├── MockLoaderRenderer.tsx
    │   │   │   └── PageableLoader.tsx
    │   │   ├── LoaderComponent.tsx
    │   │   ├── markup-check.ts
    │   │   ├── parts.ts
    │   │   ├── renderers.ts
    │   │   ├── rendering
    │   │   │   ├── AppContent.tsx
    │   │   │   ├── AppRoot.tsx
    │   │   │   ├── AppWrapper.tsx
    │   │   │   ├── buildProxy.ts
    │   │   │   ├── collectFnVarDeps.ts
    │   │   │   ├── ComponentAdapter.tsx
    │   │   │   ├── ComponentWrapper.tsx
    │   │   │   ├── Container.tsx
    │   │   │   ├── containers.ts
    │   │   │   ├── ContainerWrapper.tsx
    │   │   │   ├── ErrorBoundary.module.scss
    │   │   │   ├── ErrorBoundary.tsx
    │   │   │   ├── InvalidComponent.module.scss
    │   │   │   ├── InvalidComponent.tsx
    │   │   │   ├── nodeUtils.ts
    │   │   │   ├── reducer.ts
    │   │   │   ├── renderChild.tsx
    │   │   │   ├── StandaloneComponent.tsx
    │   │   │   ├── StateContainer.tsx
    │   │   │   ├── UnknownComponent.module.scss
    │   │   │   ├── UnknownComponent.tsx
    │   │   │   └── valueExtractor.ts
    │   │   ├── reportEngineError.ts
    │   │   ├── RestApiProxy.ts
    │   │   ├── script-runner
    │   │   │   ├── asyncProxy.ts
    │   │   │   ├── AttributeValueParser.ts
    │   │   │   ├── bannedFunctions.ts
    │   │   │   ├── BindingTreeEvaluationContext.ts
    │   │   │   ├── eval-tree-async.ts
    │   │   │   ├── eval-tree-common.ts
    │   │   │   ├── eval-tree-sync.ts
    │   │   │   ├── ParameterParser.ts
    │   │   │   ├── process-statement-async.ts
    │   │   │   ├── process-statement-common.ts
    │   │   │   ├── process-statement-sync.ts
    │   │   │   ├── ScriptingSourceTree.ts
    │   │   │   ├── simplify-expression.ts
    │   │   │   ├── statement-queue.ts
    │   │   │   └── visitors.ts
    │   │   ├── StandaloneApp.tsx
    │   │   ├── StandaloneExtensionManager.ts
    │   │   ├── TableOfContentsContext.tsx
    │   │   ├── theming
    │   │   │   ├── _themes.scss
    │   │   │   ├── component-layout-resolver.ts
    │   │   │   ├── extendThemeUtils.ts
    │   │   │   ├── hvar.ts
    │   │   │   ├── layout-resolver.ts
    │   │   │   ├── parse-layout-props.ts
    │   │   │   ├── StyleContext.tsx
    │   │   │   ├── StyleRegistry.ts
    │   │   │   ├── ThemeContext.tsx
    │   │   │   ├── ThemeProvider.tsx
    │   │   │   ├── themes
    │   │   │   │   ├── base-utils.ts
    │   │   │   │   ├── palette.ts
    │   │   │   │   ├── root.ts
    │   │   │   │   ├── solid.ts
    │   │   │   │   ├── theme-colors.ts
    │   │   │   │   └── xmlui.ts
    │   │   │   ├── themeVars.module.scss
    │   │   │   ├── themeVars.ts
    │   │   │   ├── transformThemeVars.ts
    │   │   │   └── utils.ts
    │   │   ├── utils
    │   │   │   ├── actionUtils.ts
    │   │   │   ├── audio-utils.ts
    │   │   │   ├── base64-utils.ts
    │   │   │   ├── compound-utils.ts
    │   │   │   ├── css-utils.ts
    │   │   │   ├── DataLoaderQueryKeyGenerator.ts
    │   │   │   ├── date-utils.ts
    │   │   │   ├── extractParam.ts
    │   │   │   ├── hooks.tsx
    │   │   │   ├── LruCache.ts
    │   │   │   ├── mergeProps.ts
    │   │   │   ├── misc.ts
    │   │   │   ├── request-params.ts
    │   │   │   ├── statementUtils.ts
    │   │   │   └── treeUtils.ts
    │   │   └── xmlui-parser.ts
    │   ├── index-standalone.ts
    │   ├── index.scss
    │   ├── index.ts
    │   ├── language-server
    │   │   ├── server-common.ts
    │   │   ├── server-web-worker.ts
    │   │   ├── server.ts
    │   │   ├── services
    │   │   │   ├── common
    │   │   │   │   ├── docs-generation.ts
    │   │   │   │   ├── lsp-utils.ts
    │   │   │   │   ├── metadata-utils.ts
    │   │   │   │   └── syntax-node-utilities.ts
    │   │   │   ├── completion.ts
    │   │   │   ├── diagnostic.ts
    │   │   │   ├── format.ts
    │   │   │   └── hover.ts
    │   │   └── xmlui-metadata-generated.js
    │   ├── logging
    │   │   ├── LoggerContext.tsx
    │   │   ├── LoggerInitializer.tsx
    │   │   ├── LoggerService.ts
    │   │   └── xmlui.ts
    │   ├── logo.svg
    │   ├── parsers
    │   │   ├── common
    │   │   │   ├── GenericToken.ts
    │   │   │   ├── InputStream.ts
    │   │   │   └── utils.ts
    │   │   ├── scripting
    │   │   │   ├── code-behind-collect.ts
    │   │   │   ├── Lexer.ts
    │   │   │   ├── modules.ts
    │   │   │   ├── Parser.ts
    │   │   │   ├── ParserError.ts
    │   │   │   ├── ScriptingNodeTypes.ts
    │   │   │   ├── TokenTrait.ts
    │   │   │   ├── TokenType.ts
    │   │   │   └── tree-visitor.ts
    │   │   ├── style-parser
    │   │   │   ├── errors.ts
    │   │   │   ├── source-tree.ts
    │   │   │   ├── StyleInputStream.ts
    │   │   │   ├── StyleLexer.ts
    │   │   │   ├── StyleParser.ts
    │   │   │   └── tokens.ts
    │   │   └── xmlui-parser
    │   │       ├── CharacterCodes.ts
    │   │       ├── diagnostics.ts
    │   │       ├── fileExtensions.ts
    │   │       ├── index.ts
    │   │       ├── lint.ts
    │   │       ├── parser.ts
    │   │       ├── ParserError.ts
    │   │       ├── scanner.ts
    │   │       ├── syntax-kind.ts
    │   │       ├── syntax-node.ts
    │   │       ├── transform.ts
    │   │       ├── utils.ts
    │   │       ├── xmlui-serializer.ts
    │   │       └── xmlui-tree.ts
    │   ├── react-app-env.d.ts
    │   ├── syntax
    │   │   ├── monaco
    │   │   │   ├── grammar.monacoLanguage.ts
    │   │   │   ├── index.ts
    │   │   │   ├── xmlui-dark.ts
    │   │   │   ├── xmlui-light.ts
    │   │   │   └── xmluiscript.monacoLanguage.ts
    │   │   └── textMate
    │   │       ├── index.ts
    │   │       ├── xmlui-dark.json
    │   │       ├── xmlui-light.json
    │   │       ├── xmlui.json
    │   │       └── xmlui.tmLanguage.json
    │   ├── testing
    │   │   ├── assertions.ts
    │   │   ├── component-test-helpers.ts
    │   │   ├── ComponentDrivers.ts
    │   │   ├── drivers
    │   │   │   ├── DateInputDriver.ts
    │   │   │   ├── index.ts
    │   │   │   ├── ModalDialogDriver.ts
    │   │   │   ├── NumberBoxDriver.ts
    │   │   │   ├── TextBoxDriver.ts
    │   │   │   ├── TimeInputDriver.ts
    │   │   │   ├── TimerDriver.ts
    │   │   │   └── TreeDriver.ts
    │   │   ├── fixtures.ts
    │   │   ├── index.ts
    │   │   ├── infrastructure
    │   │   │   ├── index.html
    │   │   │   ├── main.tsx
    │   │   │   ├── public
    │   │   │   │   ├── mockServiceWorker.js
    │   │   │   │   ├── resources
    │   │   │   │   │   ├── bell.svg
    │   │   │   │   │   ├── box.svg
    │   │   │   │   │   ├── doc.svg
    │   │   │   │   │   ├── eye.svg
    │   │   │   │   │   ├── flower-640x480.jpg
    │   │   │   │   │   ├── sun.svg
    │   │   │   │   │   ├── test-image-100x100.jpg
    │   │   │   │   │   └── txt.svg
    │   │   │   │   └── serve.json
    │   │   │   └── TestBed.tsx
    │   │   └── themed-app-test-helpers.ts
    │   └── vite-env.d.ts
    ├── tests
    │   ├── components
    │   │   ├── CodeBlock
    │   │   │   └── hightlight-code.test.ts
    │   │   ├── playground-pattern.test.ts
    │   │   └── Tree
    │   │       └── Tree-states.test.ts
    │   ├── components-core
    │   │   ├── abstractions
    │   │   │   └── treeAbstractions.test.ts
    │   │   ├── container
    │   │   │   └── buildProxy.test.ts
    │   │   ├── interception
    │   │   │   ├── orderBy.test.ts
    │   │   │   ├── ReadOnlyCollection.test.ts
    │   │   │   └── request-param-converter.test.ts
    │   │   ├── scripts-runner
    │   │   │   ├── AttributeValueParser.test.ts
    │   │   │   ├── eval-tree-arrow-async.test.ts
    │   │   │   ├── eval-tree-arrow.test.ts
    │   │   │   ├── eval-tree-func-decl-async.test.ts
    │   │   │   ├── eval-tree-func-decl.test.ts
    │   │   │   ├── eval-tree-pre-post.test.ts
    │   │   │   ├── eval-tree-regression.test.ts
    │   │   │   ├── eval-tree.test.ts
    │   │   │   ├── function-proxy.test.ts
    │   │   │   ├── parser-regression.test.ts
    │   │   │   ├── process-event.test.ts
    │   │   │   ├── process-function.test.ts
    │   │   │   ├── process-implicit-context.test.ts
    │   │   │   ├── process-statement-asgn.test.ts
    │   │   │   ├── process-statement-destruct.test.ts
    │   │   │   ├── process-statement-regs.test.ts
    │   │   │   ├── process-statement-sync.test.ts
    │   │   │   ├── process-statement.test.ts
    │   │   │   ├── process-switch-sync.test.ts
    │   │   │   ├── process-switch.test.ts
    │   │   │   ├── process-try-sync.test.ts
    │   │   │   ├── process-try.test.ts
    │   │   │   └── test-helpers.ts
    │   │   ├── test-metadata-handler.ts
    │   │   ├── theming
    │   │   │   ├── border-segments.test.ts
    │   │   │   ├── component-layout.resolver.test.ts
    │   │   │   ├── layout-property-parser.test.ts
    │   │   │   ├── layout-resolver.test.ts
    │   │   │   ├── layout-resolver2.test.ts
    │   │   │   ├── layout-vp-override.test.ts
    │   │   │   └── padding-segments.test.ts
    │   │   └── utils
    │   │       ├── date-utils.test.ts
    │   │       ├── format-human-elapsed-time.test.ts
    │   │       └── LruCache.test.ts
    │   ├── language-server
    │   │   ├── completion.test.ts
    │   │   ├── format.test.ts
    │   │   ├── hover.test.ts
    │   │   └── mockData.ts
    │   └── parsers
    │       ├── common
    │       │   └── input-stream.test.ts
    │       ├── markdown
    │       │   └── parse-binding-expression.test.ts
    │       ├── parameter-parser.test.ts
    │       ├── paremeter-parser.test.ts
    │       ├── scripting
    │       │   ├── eval-tree-arrow.test.ts
    │       │   ├── eval-tree-pre-post.test.ts
    │       │   ├── eval-tree.test.ts
    │       │   ├── function-proxy.test.ts
    │       │   ├── lexer-literals.test.ts
    │       │   ├── lexer-misc.test.ts
    │       │   ├── module-parse.test.ts
    │       │   ├── parser-arrow.test.ts
    │       │   ├── parser-assignments.test.ts
    │       │   ├── parser-binary.test.ts
    │       │   ├── parser-destructuring.test.ts
    │       │   ├── parser-errors.test.ts
    │       │   ├── parser-expressions.test.ts
    │       │   ├── parser-function.test.ts
    │       │   ├── parser-literals.test.ts
    │       │   ├── parser-primary.test.ts
    │       │   ├── parser-regex.test.ts
    │       │   ├── parser-statements.test.ts
    │       │   ├── parser-unary.test.ts
    │       │   ├── process-event.test.ts
    │       │   ├── process-implicit-context.test.ts
    │       │   ├── process-statement-asgn.test.ts
    │       │   ├── process-statement-destruct.test.ts
    │       │   ├── process-statement-regs.test.ts
    │       │   ├── process-statement-sync.test.ts
    │       │   ├── process-statement.test.ts
    │       │   ├── process-switch-sync.test.ts
    │       │   ├── process-switch.test.ts
    │       │   ├── process-try-sync.test.ts
    │       │   ├── process-try.test.ts
    │       │   ├── simplify-expression.test.ts
    │       │   ├── statement-hooks.test.ts
    │       │   └── test-helpers.ts
    │       ├── style-parser
    │       │   ├── generateHvarChain.test.ts
    │       │   ├── parseHVar.test.ts
    │       │   ├── parser.test.ts
    │       │   └── tokens.test.ts
    │       └── xmlui
    │           ├── lint.test.ts
    │           ├── parser.test.ts
    │           ├── scanner.test.ts
    │           ├── transform.attr.test.ts
    │           ├── transform.circular.test.ts
    │           ├── transform.element.test.ts
    │           ├── transform.errors.test.ts
    │           ├── transform.escape.test.ts
    │           ├── transform.regression.test.ts
    │           ├── transform.script.test.ts
    │           ├── transform.test.ts
    │           └── xmlui.ts
    ├── tests-e2e
    │   ├── api-bound-component-regression.spec.ts
    │   ├── api-call-as-extracted-component.spec.ts
    │   ├── assign-to-object-or-array-regression.spec.ts
    │   ├── binding-regression.spec.ts
    │   ├── children-as-template-context-vars.spec.ts
    │   ├── compound-component.spec.ts
    │   ├── context-vars-regression.spec.ts
    │   ├── data-bindings.spec.ts
    │   ├── datasource-and-api-usage-in-var.spec.ts
    │   ├── datasource-direct-binding.spec.ts
    │   ├── datasource-onLoaded-regression.spec.ts
    │   ├── modify-array-item-regression.spec.ts
    │   ├── namespaces.spec.ts
    │   ├── push-to-array-regression.spec.ts
    │   ├── screen-breakpoints.spec.ts
    │   ├── scripting.spec.ts
    │   ├── state-scope-in-pages.spec.ts
    │   └── state-var-scopes.spec.ts
    ├── tsconfig.json
    ├── tsdown.config.ts
    ├── vite.config.ts
    └── vitest.config.ts
```

# Files

--------------------------------------------------------------------------------
/xmlui/conventions/testing-conventions.md:
--------------------------------------------------------------------------------

```markdown
  1 | # XMLUI Component Testing Conventions
  2 | 
  3 | This document outlines the testing conventions and standards for XMLUI components using Playwright for end-to-end testing.
  4 | 
  5 | ## Test Categories
  6 | 
  7 | XMLUI components must be tested across four categories:
  8 | 
  9 | 1. **Basic Functionality** - Core behavior but also test cases for each property, event, exposed method of the component. Each such item can be it's own category, if there are sufficiently numerous test cases for it (like >= 5, but that's not a hard requirement). It might make sense to combine multiple properties into the group, like `icon` and `iconPosition` under the `icon prop` group. For the props, also handle edge cases like null, undefined, unexpected input types like objects where text is expected for props, invalid values (like -5 pixels for a width prop), specially long unicode characters like 👨‍👩‍👧‍👦, or chinese characters. For interactive componenets, test interactivity with keyboard and mouse. For Layout Components, test arrangement, spacing
 10 | 
 11 | ```typescript
 12 | test("renders with basic props", async ({ initTestBed, page }) => {
 13 |   await initTestBed(`<ComponentName size="sm" variant="primary"/>`);
 14 |   await expect(page.getByTestId("component")).toBeVisible();
 15 | });
 16 | ```
 17 | 
 18 | 2. **Accessibility**
 19 | 
 20 | Has ARIA attributes, keyboard navigation and conforms to Accessibility standards.
 21 | These tests are partially to conform to WCAG and to reach a better score on benchmarks for them.
 22 | 
 23 | ```typescript
 24 | test("has correct accessibility attributes", async ({ initTestBed, page }) => {
 25 |   await initTestBed(`<ComponentName label="Test Label"/>`);
 26 |   const component = page.getByLabel("Test Label");
 27 |   await expect(component).toHaveRole("button");
 28 | });
 29 | ```
 30 | 
 31 | 3. **Theme Variables** - CSS custom properties and fallback behavior. Use exact browser values: `"rgb(255, 0, 0)"` not `"red"`. **Only include this category for components that support theme variables** (check component metadata for theme variable documentation).
 32 | 
 33 | ```typescript
 34 | test("applies theme variables", async ({ initTestBed, page }) => {
 35 |   await initTestBed(`<ComponentName/>`, {
 36 |     testThemeVars: { "backgroundColor-ComponentName": "rgb(255, 0, 0)" },
 37 |   });
 38 |   await expect(page.getByTestId("component")).toHaveCSS("background-color", "rgb(255, 0, 0)");
 39 | });
 40 | ```
 41 | 
 42 | 4. **Other Edge Cases** - These are the test cases that fall into the edge cases category, but not within one particular property. So a button with an object variant should still live in the basic functionality category, within the variant prop group. Here could be a test for a datepicker stating that it covers the text below it (the text is not visible), when the user clicks on it, because the detepicker menu appears. This doesn't belong to a property, but is still an edge case.
 43 | 
 44 | ```typescript
 45 | test("handles no props gracefully", async ({ initTestBed, page }) => {
 46 |   await initTestBed(`<ComponentName/>`);
 47 |   await expect(page.getByTestId("component")).toBeVisible();
 48 | });
 49 | ```
 50 | 
 51 | ## File Organization
 52 | 
 53 | - **Location**: Test files MUST be in the same directory as the component's implementation.
 54 | - **Naming**: Use `ComponentName.spec.ts` format
 55 | - **Import**: `import { test, expect } from "../../testing/fixtures";`
 56 | 
 57 | ## Context Information
 58 | 
 59 | Xmlui is a declarative, reactive, component based web framework.
 60 | 
 61 | Checkbox.spec.ts is an excellent testing file with good examples, and it's a great resource on how to write tests.
 62 | 
 63 | ### Documentation location
 64 | 
 65 | There's documentation for the components, which we call metadata. They lives in the component's file, like `Button.tsx` next to the testing file `Button.spec.ts`, but not in the native implementation file `ButtonNative.tsx`. Generally, you should not read or rely on the native implementation details.
 66 | There's also documentation in the `.md` files, next to the component's file.
 67 | 
 68 | #### Component Metadata (Required Reading)
 69 | 
 70 | **ALWAYS read the component's `.tsx` file before creating tests.** The component files contain essential metadata that documents:
 71 | 
 72 | - **Properties**: All available props with their types and descriptions
 73 | - **Events**: Available event handlers and their parameters
 74 | - **Theme Variables**: Default values and names for CSS custom properties used in theme testing
 75 | 
 76 | Use the documented theme variable names when creating theme tests instead of guessing:
 77 | 
 78 | ```typescript
 79 | // ✅ CORRECT - Use documented theme variable names from metadata
 80 | test("applies theme variables", async ({ initTestBed, page }) => {
 81 |   await initTestBed(`<Button/>`, {
 82 |     testThemeVars: { "backgroundColor-Button": "rgb(255, 0, 0)" }, // From metadata
 83 |   });
 84 |   await expect(page.getByTestId("component")).toHaveCSS("background-color", "rgb(255, 0, 0)");
 85 | });
 86 | ```
 87 | 
 88 | #### Documentation Files (Highly Recommended)
 89 | 
 90 | **ALWAYS check for and read the component's `.md` documentation file** in the same directory. This documentation:
 91 | 
 92 | - **Merges with metadata** to provide complete component information
 93 | - **Contains comprehensive examples** showing real usage patterns
 94 | - **Documents advanced features** and edge cases
 95 | - **Provides context** for proper testing scenarios
 96 | 
 97 | Example documentation locations:
 98 | 
 99 | - `Button.tsx` - Contains metadata
100 | - `Button.md` - Contains examples and detailed usage
101 | - `Button.spec.ts` - Your test file
102 | 
103 | **Best Practice**: Read both the `.tsx` metadata AND the `.md` documentation before writing any tests to ensure comprehensive coverage of all documented features.
104 | 
105 | ### Commands
106 | 
107 | These commands should be ran inside the npm package named `xmlui`, which is inside the directory called `xmlui`.
108 | There might be a root level directory called `xmlui` (a monorepo) which contains the `xmlui` subdirectory. You want to run the commands from the `xmlui` subdirectory.
109 | 
110 | ```bash
111 | # Standard execution
112 | npx playwright test ComponentName.spec.ts
113 | 
114 | # Category-specific
115 | npx playwright test ComponentName.spec.ts --grep "accessibility"
116 | 
117 | # Development (recommended during creation) - prevents HTML report auto-opening
118 | npx playwright test ComponentName.spec.ts --reporter=line
119 | 
120 | # Single worker for debugging (prevents race conditions)
121 | npx playwright test ComponentName.spec.ts --workers=1
122 | 
123 | # Fast feedback during development (single worker + line reporter)
124 | npx playwright test ComponentName.spec.ts --workers=1 --reporter=line
125 | ```
126 | 
127 | ### Timeout Configuration
128 | 
129 | XMLUI uses optimized timeout settings for faster feedback during development:
130 | 
131 | - **Expect timeout**: 1000ms (1 second) - How long to wait for assertions like `expect.poll()`
132 | - **Test timeout**: 5 seconds - Maximum time for entire test execution
133 | - **Global timeout**: Configured in `playwright.config.ts`
134 | 
135 | These settings ensure tests fail quickly when conditions aren't met, providing rapid feedback during development. The shorter expect timeout helps identify issues faster than the default 5-second timeout.
136 | 
137 | **Important**: Never manually show the HTML report or wait for Ctrl+C during test development. Use `--reporter=line` to get immediate console feedback without browser interference.
138 | 
139 | ### Development Testing Commands
140 | 
141 | For comprehensive debugging and development, use these command combinations:
142 | 
143 | ```bash
144 | # Best practice during test development - single worker + line reporter
145 | npx playwright test ComponentName.spec.ts --workers=1 --reporter=line
146 | 
147 | # Debug specific failing tests only
148 | npx playwright test ComponentName.spec.ts --grep "test name pattern" --reporter=line
149 | 
150 | # Run specific test categories during development
151 | npx playwright test ComponentName.spec.ts --grep "Basic Functionality" --reporter=line
152 | ```
153 | 
154 | The line reporter provides detailed progress information and immediate feedback about test failures without opening browser windows or HTML reports, making it ideal for iterative test development.
155 | 
156 | ### Event Handler Naming
157 | 
158 | **ALWAYS use "on" prefix for event handlers:**
159 | 
160 | ```typescript
161 | // ✅ CORRECT
162 | onClick = "testState = 'clicked'";
163 | onWillOpen = "testState = 'opening'";
164 | 
165 | // ❌ INCORRECT
166 | click = "testState = 'clicked'";
167 | willOpen = "testState = 'opening'";
168 | ```
169 | 
170 | **Event vs Handler distinction:**
171 | 
172 | - **Event names** (no "on"): Used in `<event name="click">` tags
173 | - **Event handlers** (with "on"): Used as attributes `onClick="..."`
174 | 
175 | ### Event Handler Parameters
176 | 
177 | **ALWAYS use arrow function syntax:**
178 | 
179 | ```typescript
180 | // ✅ CORRECT
181 | onExpandedChange = "arg => testState = arg";
182 | onClick = "event => testState = event.type";
183 | 
184 | // ❌ INCORRECT - arguments object doesn't work
185 | onExpandedChange = "testState = arguments[0]";
186 | ```
187 | 
188 | most of the time (when the event handler does not need to access the event object),
189 | you can omit the arrow function and write the handler body directly.
190 | 
191 | ### XMLUI Script Limitations
192 | 
193 | XMLUI scripts have important JavaScript syntax limitations that must be followed:
194 | 
195 | **NO "new" operator support:**
196 | 
197 | ```typescript
198 | // ❌ INCORRECT - "new" operator not supported
199 | throw new Error("test error");
200 | const items = new Set([1, 2, 3]);
201 | const date = new Date();
202 | const regex = new RegExp("pattern");
203 | 
204 | // ✅ CORRECT - Use alternatives
205 | throw "test error"; // String-based error throwing
206 | // Manual uniqueness check instead of Set
207 | const uniqueCheck = {};
208 | let allUnique = true;
209 | for (let i = 0; i < items.length; i++) {
210 |   if (uniqueCheck[items[i]]) {
211 |     allUnique = false;
212 |     break;
213 |   }
214 |   uniqueCheck[items[i]] = true;
215 | }
216 | // Use string literals for dates
217 | const dateStr = "2025-08-07";
218 | // Use string patterns instead of regex literals
219 | const pattern = "test";
220 | ```
221 | 
222 | ### Non-Visual Component Testing
223 | 
224 | For non-visual components (like Queue, DataStore), use Button click handlers to access APIs:
225 | 
226 | ```typescript
227 | // ✅ CORRECT - Access APIs through Button onClick
228 | const { testStateDriver } = await initTestBed(`
229 |   <Fragment>
230 |     <Queue id="testQueue" />
231 |     <Button onClick="testState = testQueue.enqueueItem('test')" />
232 |   </Fragment>
233 | `);
234 | 
235 | // ❌ INCORRECT - Script tags don't provide API access
236 | const { testStateDriver } = await initTestBed(`
237 |   <Queue id="testQueue" />
238 |   <script>testState = testQueue.enqueueItem('test')</script>
239 | `);
240 | ```
241 | 
242 | **Important**: Non-visual components often require event handlers (like `onProcess`) to exhibit expected behavior. Without processing handlers, queue-like components may not retain items as expected.
243 | 
244 | ### Template Properties
245 | 
246 | **ALWAYS wrap template properties in `<property>` tags:**
247 | 
248 | ```typescript
249 | // ✅ CORRECT
250 | <ComponentName>
251 |   <property name="triggerTemplate">
252 |     <Button>Custom Trigger</Button>
253 |   </property>
254 | </ComponentName>
255 | 
256 | // ❌ INCORRECT
257 | <ComponentName>
258 |   <triggerTemplate>
259 |     <Button>Custom Trigger</Button>
260 |   </triggerTemplate>
261 | </ComponentName>
262 | ```
263 | 
264 | ## Mandatory Test Structure
265 | 
266 | Use `test.describe("category name")` to group test cases.
267 | Don't need a top level group that encompuses every test case.
268 | 
269 | ```typescript
270 | // =============================================================================
271 | // BASIC FUNCTIONALITY TESTS
272 | // =============================================================================
273 | 
274 | test.describe("Basic Functionality", () => {
275 |   test("component renders with basic props", async ({ initTestBed, createComponentDriver }) => {
276 |     // Test implementation
277 |   });
278 | });
279 | 
280 | // =============================================================================
281 | // ACCESSIBILITY TESTS
282 | // =============================================================================
283 | 
284 | test.describe("Accessibility", () => {
285 |   test("component has correct accessibility attributes", async ({ initTestBed, page }) => {
286 |     // Test implementation
287 |   });
288 | });
289 | 
290 | // =============================================================================
291 | // THEME VARIABLE TESTS (Only for components that support theme variables)
292 | // =============================================================================
293 | 
294 | test.describe("Theme Variables", () => {
295 |   test("component applies theme variables", async ({ initTestBed, createComponentDriver }) => {
296 |     // Test implementation
297 |   });
298 | });
299 | 
300 | // =============================================================================
301 | // OTHER EDGE CASE TESTS
302 | // =============================================================================
303 | 
304 | test.describe("Other Edge Cases", () => {
305 |   test("component handles null props gracefully", async ({ initTestBed }) => {
306 |     // Test implementation
307 |   });
308 | });
309 | ```
310 | 
311 | ## Testing Framework
312 | 
313 | ### Test Initialization
314 | 
315 | **Always use XMLUI test function from `fixtures.ts`:**
316 | 
317 | ```typescript
318 | import { test, expect } from "../../testing/fixtures";
319 | 
320 | test("component renders correctly", async ({ initTestBed }) => {
321 |   await initTestBed(`<ComponentName prop="value"/>`, {});
322 |   // This will create a webpage where the whole content of it is the top level xmlui component,
323 |   // specified in the initTestBed's first parameter as source code.
324 | 
325 |   // Test implementation
326 | });
327 | ```
328 | 
329 | ### initTestBed Usage
330 | 
331 | ```typescript
332 | // Basic usage
333 | await initTestBed(`<ComponentName prop="value"/>`, {});
334 | 
335 | // With theme variables
336 | await initTestBed(`<ComponentName/>`, {
337 |   testThemeVars: { "backgroundColor-ComponentName": "rgb(255, 0, 0)" },
338 | });
339 | 
340 | // With test state for events
341 | const { testStateDriver } = await initTestBed(`
342 |   <ComponentName onClick="testState = 'clicked'"/>
343 | `);
344 | ```
345 | 
346 | ## Testing Approaches
347 | 
348 | ### Obtaining a locator (element)
349 | 
350 | Locate elements in a way a user would locate them guided by their intention. For example `const checkboxLocator = await page.getByRole('checkbox')`, rather than relying on internal structure like `page.locator("input").nth(2)`. If and only if there are more elements of the same type and it does not make sense to use text (because the test is not about labels or content text, or because the layout would change due to the presence of the content), prefer to use `testId`-s in the source code and use those test ids to locate the elements. You should aviod using `page.locator()` in test cases.
351 | 
352 | If it is possible to test something at the time of locationg the element, prefer that. `page.getByRole` is the prime example of this, because it also tests for accessibility and intent (the user is not looking for a div with a certain class on it, they are looking for an image or a button). Avoid long selectors, that could be more readable by adding some assertions. For example, this is better:
353 | 
354 | ```ts
355 | const cb = page.getByRole("checkbox");
356 | await expect(cb).toBeDisabled();
357 | ```
358 | 
359 | and this is worse:
360 | 
361 | ```ts
362 | const cb = page.getByRole("checkbox", { disabled: true });
363 | await expect(cb).toBeVisible();
364 | ```
365 | 
366 | However, if there are multiple elements of the same type and an option could differentiate them and assertions would need to be written out after locating the element anyway, this approach is better than using test ids. For example, this is a good use of the `{disabled: true}` option:
367 | 
368 | ```ts
369 | await initTestBed(`
370 | <Fragment>
371 |   <Checkbox enabled="false"/>
372 |   <Checkbox />
373 | </Fragment>`);
374 | const cb = page.getByRole("checkbox", { disabled: true });
375 | // do something with the disabled checkbox
376 | ```
377 | 
378 | ### Event testing
379 | 
380 | Sometimes you need to test events. The easiest way is to do something like this:
381 | 
382 | Don't worry about how the testState is actually obtained. Just know that inside an event handler, you can write javascript and access the `testState` variable, which you can make assertions against later by polling that value. Just make it obvious that testState is actually changed, so use an obvious name like 'changed', or if you need to test the handler multiple times, use a counter like `onDidChange="testState = testState == null ? 1 : testState + 1"` Test state is initialized to be null.
383 | 
384 | ```typescript
385 | test("click event fires on click", async ({ initTestBed, page }) => {
386 |   const { testStateDriver } = await initTestBed(`<Button onClick="testState = 'clicked'"/>`);
387 | 
388 |   await page.getByRole("button").click();
389 |   await expect.poll(testStateDriver.testState).toEqual("clicked");
390 | });
391 | ```
392 | 
393 | **Testing API Returns vs Component State**: For non-visual components, focus on testing API return values rather than internal component state, as the latter may not behave as expected without proper event handlers:
394 | 
395 | ```typescript
396 | // ✅ CORRECT - Test API return values
397 | test("enqueueItem returns valid ID", async ({ initTestBed, createButtonDriver }) => {
398 |   const { testStateDriver } = await initTestBed(`
399 |     <Fragment>
400 |       <Queue id="testQueue" />
401 |       <Button onClick="testState = { id: testQueue.enqueueItem('test'), hasId: true }" />
402 |     </Fragment>
403 |   `);
404 | 
405 |   const buttonDriver = await createButtonDriver("button");
406 |   await buttonDriver.component.click();
407 | 
408 |   const result = await testStateDriver.testState();
409 |   expect(result.id).toBeTruthy();
410 |   expect(result.hasId).toBe(true);
411 | });
412 | 
413 | // ❌ POTENTIALLY INCORRECT - Component state may not persist without handlers
414 | test("queue length increases", async ({ initTestBed, createButtonDriver }) => {
415 |   // Without onProcess handler, queue might not retain items
416 |   const { testStateDriver } = await initTestBed(`
417 |     <Fragment>
418 |       <Queue id="testQueue" />
419 |       <Button onClick="testQueue.enqueueItem('test'); testState = testQueue.getQueueLength()" />
420 |     </Fragment>
421 |   `);
422 |   // This test might fail if Queue doesn't retain items without processing
423 | });
424 | ```
425 | 
426 | ### Writing actions and drivers
427 | 
428 | Not all tests need actions. Some are just instantiating the webpage with a given component and then making assertions on that.
429 | 
430 | Actions are the most varried part of writing component tests. If and only if the action is super simple and consists of one step, and the action's name gives clarity on what it is doing, should you use actions directly on the locators. For example, this is good, because it is clear what the actions are doing, as the `check` and `uncheck` actions are complete in themselves:
431 | 
432 | ```ts
433 | const cb = page.getByRole("checkbox");
434 | await cb.check();
435 | await expect(cb).toBeChecked();
436 | await cb.uncheck();
437 | await expect(cb).not.toBeChecked();
438 | ```
439 | 
440 | However, in any other case, you should use a component driver to encapsulate the logic of the action. For example:
441 | 
442 | ```ts
443 | test("search filters option labels", async ({ initTestBed, page, createSelectDriver }) => {
444 |   await initTestBed(`
445 |     <Select searchable="true">
446 |       <Option value="opt1" label="first"/>
447 |       <Option value="opt2" label="second"/>
448 |       <Option value="opt3" label="third"/>
449 |     </FormItem>
450 |   `);
451 |   const select = page.getByRole("combobox");
452 |   const driver = await createSelectDriver(select);
453 | 
454 |   await driver.searchFor("second");
455 |   await driver.chooseFirstOption();
456 |   await expect(select).toHaveValue("opt2");
457 | });
458 | ```
459 | 
460 | In this case, if the `searchFor` method were to be substituted with clicking on the locator and typing into the input field the label text, that would not convey the intent of the action, which is searching. Actions should have componenet-specific names. For example, a Dropdown can have a `toggleDropdownVisibility` method, which is pretty much just a click, but with a more meaningful name. You would not call it `click`.
461 | 
462 | Drivers are derived from a base ComponentDriver class. Not every component needs a driver, so don't just create one for each component. In fact, if you can avoid it, you should, as they are a layer of abstraction.
463 | 
464 | ### Drivers for internal component structure
465 | 
466 | In extremely rare cases, you need to access the DOM structure of the component. This is usually a sign that the component is not well designed, and lacking accessibility features. In these cases, if you are an AI express the best approach that has accessability baked in. For example, you might be asked to test a spinner's border. You cound use the internal structure of the top level component, but it's better to encourage the programmer to implement role="status" on the component, so you can use `page.getByRole("status")`.
467 | In some RARE cases, they are the right approach though.
468 | For example, if the component is purely visual and has no accessibility attributes, or we actually want to assert something about a particular DOM element (like an internal element's border has a certain width): you can use the driver to obtain the locator, but only for that.
469 | 
470 | Drivers should NOT:
471 | 
472 | - have expectations and assertions in them. Those belong to the test case. Instead, return the locator from a method (in case driver is used for obtaining internal structure) and assert against it in the test case.
473 | - reimplement the playwright API, such as having a `click` or `hover` method which just wraps the playwright API and executes it on one of it's internal element'.
474 | 
475 | ### Writing assertions
476 | 
477 | Use web-first assertions, like `await expect(checkboxLocator).not.toBeChecked()`. If there is a built-in assertion, prefer that over an assertion checking against a the dom attributes. This is an antipattern: `await expect(checkboxLocator).toHaveAttribute("type", "checkbox");` because it relies on the element being an input element.
478 | 
479 | ## Test Naming & Patterns
480 | 
481 | ### Naming Standards
482 | 
483 | - Avoid using the "component" word in names, it's redundant
484 | - Use concrete property, event, api, attribute, etc.
485 | 
486 | - ✅ `"renders with 'variant' property"`
487 | - ✅ `"has correct 'aria-clickable'"`
488 | - ✅ `"handles null and undefined 'variant' property"`
489 | - ❌ `"test component"` or `"basic test"`
490 | 
491 | ## Best Practices
492 | 
493 | ### Avoid Frontend Code in E2E Tests
494 | 
495 | It is crucial to avoid importing frontend code into E2E tests, especially if it transitively imports stylesheets. This can lead to unexpected issues with the test runner and slow down test execution.
496 | 
497 | For example, importing `defaultProps` from a component file like `ButtonNative.tsx` into a test file like `Button.spec.ts` is an anti-pattern. The component file likely imports SCSS or CSS files, which should not be part of the test environment.
498 | 
499 | Instead, if you need to share data between your component and your tests, define it in a separate file that can be imported safely by both.
500 | 
501 | ### Skipping tests
502 | 
503 | #### Skipping For coverage
504 | 
505 | Use `test.skip` for comprehensive coverage, when you know which tests you want to write, but you haven't gotten around to implementing them yet.
506 | 
507 | ```typescript
508 | test.skip("placeholder defaults to 'example.com'", async ({ initTestBed, page }) => {});
509 | ```
510 | 
511 | #### Skipping for any other reason
512 | 
513 | There are other reasons to skip tests, such as when a feature is not yet implemented or when a bug is present.
514 | After becoming certain the test is well written, skip the test with the appropriate skip reason.
515 | 
516 | ```typescript
517 | test.fixme(
518 |   `label displayed for type 'autocomplete'`,
519 |   SKIP_REASON.XMLUI_BUG(
520 |     "There are two labels in Autocomplete: one is ours, the other comes from the wrapped component -> this results in an error",
521 |   ),
522 |   async ({ initTestBed, page }) => {
523 |     await initTestBed(`<Autocomplete label="test" />`);
524 |     await expect(page.getByLabel("test"));
525 |     // ...rest of the test is not relevant
526 |   },
527 | );
528 | ```
529 | 
530 | ### Systematic Testing
531 | 
532 | In case there would be a lot of duplication for testing a property that has the exact same structure, use parameterized tests.
533 | 
534 | ```typescript
535 | // Data type testing
536 | [
537 |   { label: "null", value: "'{null}'", expected: "" },
538 |   { label: "string", value: "'test'", expected: "test" },
539 |   { label: "integer", value: "'{123}'", expected: "123" },
540 |   // more cases...
541 | ].forEach(({ label, value, expected }) => {
542 |   test(`handles ${label} correctly`, async ({ initTestBed, page }) => {
543 |     await initTestBed(`<ComponentName value=${value} testId="component"/>`);
544 |     await expect(page.getByTestId("component")).toHaveText(expected);
545 |   });
546 | });
547 | ```
548 | 
549 | ## Good test case patterns
550 | 
551 | ### Layout/Positioning Tests
552 | 
553 | Components that support layout properties (like `labelPosition`, `direction`, positioning, sizing) should include tests that verify visual arrangement using the `getBounds` utility function.
554 | 
555 | #### Best Practices for Layout Testing
556 | 
557 | - **Import getBounds**: Import from `"../../testing/component-test-helpers"`
558 | - **Use descriptive coordinates**: Destructure specific properties like `{ left, right, top, bottom }`
559 | - **Test both directions**: Include RTL tests when direction affects layout
560 | - **Verify invalid values**: Test graceful handling of invalid layout properties
561 | 
562 | #### Testing Element Positioning
563 | 
564 | Use `getBounds()` to get element coordinates and verify relative positioning:
565 | 
566 | ```typescript
567 | test("ComponentName appears at the correct side of ComponentName2", async ({
568 |   initTestBed,
569 |   page,
570 | }) => {
571 |   await initTestBed(`
572 |     <Fragment>
573 |       <ComponentName testId="comp1" />
574 |       <ComponentName2 testId="comp2" />
575 |     <Fragment>
576 |   `);
577 | 
578 |   const { left: comp1Left } = await getBounds(page.getByTestId("comp1"));
579 |   const { right: comp2Right } = await getBounds(page.getByTestId("comp2"));
580 | 
581 |   expect(comp1Left).toBeLessThan(comp2Right);
582 | });
583 | ```
584 | 
585 | #### Testing Directional Layout (RTL/LTR)
586 | 
587 | Test layout behavior in both directions when applicable:
588 | 
589 | ```typescript
590 | test("startText displays at beginning of input (rtl)", async ({ initTestBed, page }) => {
591 |   await initTestBed(`<TextBox testId="input" direction="rtl" startText="$" />`);
592 | 
593 |   const { left: compLeft, right: compRight } = await getBounds(page.getByTestId("input"));
594 |   const { left: textLeft, right: textRight } = await getBounds(page.getByText("$"));
595 | 
596 |   await expect(page.getByTestId("input")).toContainText("$");
597 |   expect(textRight - compLeft).toBeGreaterThanOrEqual(compRight - textLeft);
598 | });
599 | ```
600 | 
601 | #### Testing Size Properties
602 | 
603 | Verify width, height, and other sizing properties:
604 | 
605 | ```typescript
606 | test("labelWidth applies custom label width", async ({ initTestBed, page }) => {
607 |   const expected = 200;
608 |   await initTestBed(`<InputComponent label="test test" labelWidth="${expected}px" />`);
609 |   const { width } = await getBounds(page.getByText("test test"));
610 |   expect(width).toEqual(expected);
611 | });
612 | ```
613 | 
614 | #### Testing Complex Layout Arrangements
615 | 
616 | For components with multiple positioned elements, test their relative arrangement:
617 | 
618 | ```typescript
619 | test("all adornments appear in the right place", async ({ initTestBed, page }) => {
620 |   await initTestBed(`
621 |     <TextBox testId="input" startText="$" endText="USD" startIcon="search" endIcon="search" direction="ltr" />
622 |   `);
623 |   const { left: compLeft, right: compRight } = await getBounds(page.getByTestId("input"));
624 |   const { left: startTextLeft, right: startTextRight } = await getBounds(page.getByText("$"));
625 |   const { left: endTextLeft, right: endTextRight } = await getBounds(page.getByText("USD"));
626 |   const { left: startIconLeft, right: startIconRight } = await getBounds(
627 |     page.getByRole("img").first(),
628 |   );
629 |   const { left: endIconLeft, right: endIconRight } = await getBounds(page.getByRole("img").last());
630 | 
631 |   // Check order of adornments relative to their container component bounds
632 |   expect(startTextRight - compLeft).toBeLessThanOrEqual(compRight - startTextLeft);
633 |   expect(startIconRight - compLeft).toBeLessThanOrEqual(compRight - startIconLeft);
634 |   expect(endTextRight - compLeft).toBeGreaterThanOrEqual(compRight - endTextLeft);
635 |   expect(endIconRight - compLeft).toBeGreaterThanOrEqual(compRight - endIconLeft);
636 | });
637 | ```
638 | 
639 | ### Testing Input Component API
640 | 
641 | Test the following in a `test.describe("Api", () => {...})` block for input components (such as TextBox, Checkbox, Slider, etc.):
642 | 
643 | - value
644 | - setValue
645 | - focus
646 | 
647 | #### Example
648 | 
649 | ```typescript
650 | test("component setValue API updates state", async ({ initTestBed, page }) => {
651 |   await initTestBed(`
652 |     <Fragment>
653 |       <TextBox id="myTextBox" />
654 |       <Button testId="setBtn" onClick="myTextBox.setValue('api value')" />
655 |     </Fragment>
656 |   `);
657 |   await page.getByTestId("setBtn").click();
658 |   await expect(page.getByRole("textbox")).toHaveValue("api value");
659 | });
660 | ```
661 | 
662 | ## Bad test case patterns
663 | 
664 | fill in later
665 | 
```

--------------------------------------------------------------------------------
/docs/public/pages/howto.md:
--------------------------------------------------------------------------------

```markdown
   1 | # How To
   2 | 
   3 | These examples answer common questions of the form "How do I do SOMETHING with XMLUI?" The [XMLUI MCP server](https://github.com/xmlui-org/xmlui-mcp) provides two related tools. Agents can call `xmlui-list-howto` to list the entries here and `xmlui-search-howto` to search them.
   4 | 
   5 | ## Expose a method from a component
   6 | ```xmlui-pg
   7 | ---app display
   8 | <App height="300px" >
   9 |   <UsingInternalModal id="component"/>
  10 |   <Button label="Open the internal dialog" onClick="component.openDialog()" />
  11 | </App>
  12 | ---comp display
  13 | <Component name="UsingInternalModal">
  14 |   <ModalDialog id="dialog" title="Example Dialog">
  15 |     <Button label="Close Dialog" onClick="dialog.close()" />
  16 |   </ModalDialog>
  17 | 
  18 |   <H1>Using an Internal Modal Dialog</H1>
  19 | 
  20 |   <method name="openDialog">
  21 |     console.log('internal method called')
  22 |     dialog.open();
  23 |   </method>
  24 | </Component>
  25 | ```
  26 | 
  27 | ## React to button click not keystrokes
  28 | 
  29 | ```xmlui-pg noHeader
  30 | ---app
  31 | <App>
  32 |   <Test />
  33 | </App>
  34 | ---comp display
  35 |   <!-- Use two different variables -->
  36 | <Component name="Test" var.searchText="" var.triggerSearch="{false}">
  37 |   <TextBox
  38 |     id="searchInput"
  39 |     placeholder="Type something..."
  40 |     width="300px"
  41 |   />
  42 |   <Button
  43 |     label="Search"
  44 |     onClick="searchText = searchInput.value; triggerSearch = true"
  45 |   />
  46 |   <DataSource
  47 |     id="searchResults"
  48 |     url="https://httpbin.org/post"
  49 |     body="{JSON.stringify({query: searchText})}"
  50 |     method="POST"
  51 |     when="{triggerSearch}"
  52 |     onDidLoad="triggerSearch = false"
  53 |   />
  54 |   <Fragment when="{searchResults.loaded}">
  55 |     <Text>Search results for: {searchText}</Text>
  56 |     <Text>Response received: {searchResults.value.json ? 'Yes' : 'No'}</Text>
  57 |   </Fragment>
  58 | </Component>
  59 | ```
  60 | 
  61 | ## Modify a value reported in a Column
  62 | 
  63 | ```xmlui-pg noHeader
  64 | ---app
  65 | <App>
  66 |   <Test />
  67 | </App>
  68 | ---comp display
  69 | <Component name="Test">
  70 |   <DataSource
  71 |     id="invoices_with_badges"
  72 |     url="/resources/files/invoices.json"
  73 |     transformResult="{data => data.slice(0,5)}"
  74 |   />
  75 |   <Table data="{invoices_with_badges}">
  76 |     <Column bindTo="invoice_number" />         <!-- empty tag for bound column -->
  77 |     <Column bindTo="client" />
  78 |     <Column bindTo="issue_date" />
  79 |     <Column bindTo="due_date" />
  80 |     <Column bindTo="paid_date" />
  81 |     <Column>
  82 |       ${$item.total}             <!-- unbound column, prepend $ to the $item value -->
  83 |     </Column>
  84 |     <Column>
  85 |         <StatusBadge status="{$item.status}" />  <!-- embed component, pass value -->
  86 |     </Column>
  87 |   </Table>
  88 | </Component>
  89 | ---comp display
  90 | <Component
  91 |     name="StatusBadge"
  92 |     var.statusColors="{{
  93 |         draft: { background: '#f59e0b', label: 'white' },
  94 |         sent: { background: '#3b82f6', label: 'white' },
  95 |         paid: { background: '#10b981', label: 'white' }
  96 |     }}"
  97 | >
  98 |     <Badge
  99 |         value="{$props.status}"
 100 |         colorMap="{statusColors}"
 101 |         variant="pill"
 102 |     />
 103 | </Component>
 104 | ```
 105 | 
 106 | ## Filter and transform data from an API
 107 | 
 108 | ```xmlui-pg noHeader
 109 | ---app
 110 | <App>
 111 |   <Test />
 112 | </App>
 113 | ---api
 114 | {
 115 |   "apiUrl": "/api",
 116 |   "initialize": "$state.people = [
 117 |     { id: 1, name: 'Alice', active: true,  group: 'A' },
 118 |     { id: 2, name: 'Bob',   active: false, group: 'B' },
 119 |     { id: 3, name: 'Carol', active: true,  group: 'A' },
 120 |     { id: 4, name: 'Dave',  active: true,  group: 'B' }
 121 |   ]",
 122 |   "operations": {
 123 |     "get-people": {
 124 |       "url": "/people",
 125 |       "method": "get",
 126 |       "handler": "return { status: 'ok', data: { items: $state.people } }"
 127 |     }
 128 |   }
 129 | }
 130 | ---comp display
 131 | <Component name="Test">
 132 | 
 133 |   <!--
 134 |   {
 135 |     items:
 136 |       [
 137 |         { id: 1, name: 'Alice', active: true,  group: 'A' },
 138 |         { id: 2, name: 'Bob',   active: false, group: 'B' },
 139 |         { id: 3, name: 'Carol', active: true,  group: 'A' },
 140 |         { id: 4, name: 'Dave',  active: true,  group: 'B' }
 141 |       ]
 142 |   }
 143 |   -->
 144 | 
 145 |   <!-- Use resultSelector to select the items array -->
 146 |   <DataSource
 147 |     id="allPeople"
 148 |     url="/api/people"
 149 |     resultSelector="data.items"
 150 |   />
 151 | 
 152 |   <!-- Use resultSelector to filter the items array -->
 153 |   <DataSource
 154 |     id="activePeople"
 155 |     url="/api/people"
 156 |     resultSelector="data.items.filter(p => p.active)"
 157 |   />
 158 | 
 159 |   <!-- Use transformResult -->
 160 | 
 161 |   <!--
 162 |   window.transformPeople = function(data) {
 163 |     console.log(data);
 164 |     const items = data.data.items;
 165 |     const itemMap = {
 166 |       A: 'Austin',
 167 |       B: 'Boston'
 168 |     };
 169 |     return items.map(item => ({
 170 |       ...item,
 171 |       city: itemMap[item.group]
 172 |     }));
 173 |   };
 174 |   -->
 175 | 
 176 |   <DataSource
 177 |     id="transformedPeople"
 178 |     url="/api/people"
 179 |     transformResult="{window.transformPeople}"
 180 |   />
 181 | 
 182 |   <Text>All people:</Text>
 183 |   <List data="{allPeople}">
 184 |     <Text>{$item.name} ({$item.group})</Text>
 185 |   </List>
 186 | 
 187 |   <Text>Active people:</Text>
 188 |   <List data="{activePeople}">
 189 |     <Text>{$item.name} ({$item.group})</Text>
 190 |   </List>
 191 | 
 192 |   <Text>Transformed people:</Text>
 193 |   <List data="{transformedPeople}">
 194 |     <Text>{$item.name} ({$item.city})</Text>
 195 |   </List>
 196 | 
 197 | 
 198 | </Component>
 199 | ```
 200 | 
 201 | ## Group items in List by a property
 202 | 
 203 | ```xmlui-pg noHeader
 204 | ---app
 205 | <App>
 206 |   <Test />
 207 | </App>
 208 | ---api display
 209 | {
 210 |   "apiUrl": "/api",
 211 |   "initialize": "$state.people_groupby = [
 212 |     { id: 1, name: 'Alice', active: true,  group: 'A' },
 213 |     { id: 2, name: 'Bob',   active: false, group: 'B' },
 214 |     { id: 3, name: 'Carol', active: true,  group: 'A' },
 215 |     { id: 4, name: 'Dave',  active: true,  group: 'B' }
 216 |   ]",
 217 |   "operations": {
 218 |     "get-people-groupby": {
 219 |       "url": "/people_groupby",
 220 |       "method": "get",
 221 |       "handler": "return { status: 'ok', data: { items: $state.people_groupby } }"
 222 |     }
 223 |   }
 224 | }
 225 | ---comp display
 226 | <Component name="Test">
 227 | 
 228 |   <!--
 229 |   {
 230 |     items:
 231 |       [
 232 |         { id: 1, name: 'Alice', active: true,  group: 'A' },
 233 |         { id: 2, name: 'Bob',   active: false, group: 'B' },
 234 |         { id: 3, name: 'Carol', active: true,  group: 'A' },
 235 |         { id: 4, name: 'Dave',  active: true,  group: 'B' }
 236 |       ]
 237 |   }
 238 |   -->
 239 | 
 240 |   <DataSource
 241 |     id="allPeopleGroupBy"
 242 |     url="/api/people_groupby"
 243 |     resultSelector="data.items"
 244 |   />
 245 |   <List data="{allPeopleGroupBy}" groupBy="group">
 246 |     <property name="groupHeaderTemplate">
 247 |       <Text variant="subtitle">Group {$group.key}</Text>
 248 |     </property>
 249 |     <Text>{$item.name}</Text>
 250 |   </List>
 251 | </Component>
 252 | ```
 253 | 
 254 | ## Delay a DataSource until another DataSource is ready
 255 | 
 256 | ```xmlui-pg noHeader
 257 | ---app
 258 | <App>
 259 |   <Test />
 260 | </App>
 261 | ---api
 262 | {
 263 |   "apiUrl": "/api",
 264 |   "initialize": "$state.users_for_ds_dependency =
 265 |     [
 266 |       { id: 1, name: 'Alice', departmentId: 1 },
 267 |       { id: 2, name: 'Bob', departmentId: 2 }
 268 |       ];
 269 |     $state.departments_with_ds_dependency = [
 270 |       { id: 1, name: 'Engineering' },
 271 |       { id: 2, name: 'Marketing' }
 272 |     ]",
 273 |   "operations": {
 274 |     "get_users_for_ds_dependency": {
 275 |       "url": "/users_for_ds_dependency",
 276 |       "method": "get",
 277 |       "handler": "delay(1000); return $state.users_for_ds_dependency"
 278 |     },
 279 |     "get_departments_with_ds_dependency": {
 280 |       "url": "/departments_with_ds_dependency",
 281 |       "method": "get",
 282 |       "handler": "delay(1000); return $state.departments_with_ds_dependency"
 283 |     }
 284 |   }
 285 | }
 286 | ---comp display
 287 | <Component name="Test" var.selectedId="" var.nonce="{0}">
 288 | 
 289 |   <DataSource
 290 |     id="users_for_ds_dependency"
 291 |     url="/api/users_for_ds_dependency?nonce"
 292 |     inProgressNotificationMessage="Loading users..."
 293 |     when="{ nonce > 0 }"
 294 |     />
 295 | 
 296 |   <DataSource
 297 |     id="departments_with_ds_dependency"
 298 |     url="/api/departments_with_ds_dependency"
 299 |     when="{ users_for_ds_dependency.loaded }"
 300 |     inProgressNotificationMessage="Loading departments..."
 301 |   />
 302 | 
 303 |   <Select
 304 |     id="usersForDsDepencency"
 305 |     data="{users_for_ds_dependency}"
 306 |     when="{departments_with_ds_dependency.loaded}"
 307 |     onDidChange="(newVal) => selectedId = newVal"
 308 |   >
 309 |     <Items data="{users_for_ds_dependency}">
 310 |       <Option
 311 |         value="{$item.id}"
 312 |         label="{$item.name} ({departments_with_ds_dependency.value.find(d => d.id === $item.departmentId)?.name})"
 313 |      />
 314 |     </Items>
 315 |   </Select>
 316 | 
 317 |   <Button
 318 |     label="Run"
 319 |     onClick="{nonce++}"
 320 |   />
 321 | 
 322 | 
 323 | </Component>
 324 | ```
 325 | 
 326 | ## Hide an element until its DataSource is ready
 327 | 
 328 | ```xmlui-pg noHeader
 329 | ---app
 330 | <App>
 331 |   <Test />
 332 | </App>
 333 | ---api
 334 | {
 335 |   "apiUrl": "/api",
 336 |   "initialize": "$state.fruits = [
 337 |     { id: 1, name: 'Orange' },
 338 |     { id: 2, name: 'Apple' },
 339 |     { id: 3, name: 'Pear' },
 340 |   ]",
 341 |   "operations": {
 342 |     "get-fruits": {
 343 |       "url": "/fruits",
 344 |       "method": "get",
 345 |       "handler": "delay(3000); return $state.fruits;"
 346 |     }
 347 |   }
 348 | }
 349 | ---comp display
 350 | <Component name="Test" var.nonce="{0}">
 351 | 
 352 | <DataSource
 353 |   id="fruits"
 354 |   url="/api/fruits?{nonce}"
 355 |   inProgressNotificationMessage="Loading fruits"
 356 |   when="{nonce > 0}"
 357 |   />
 358 | 
 359 | <Button
 360 |   label="Run"
 361 |   onClick="{nonce++}"
 362 | />
 363 | 
 364 | <Fragment when="{fruits.loaded}">
 365 |   <Text>Fruits: {fruits.value.length} found</Text>
 366 | </Fragment>
 367 | 
 368 | </Component>
 369 | ```
 370 | 
 371 | ## Use built-in form validation
 372 | 
 373 | ```xmlui-pg noHeader
 374 | ---app
 375 | <App>
 376 |   <Test />
 377 | </App>
 378 | ---api
 379 | {}
 380 | ---comp display
 381 | <Component name="Test">
 382 | 
 383 | <Form
 384 |   data="{{ password: '' }}"
 385 |   onSubmit="(data) => console.log('Submitted:', data)"
 386 | >
 387 |   <FormItem
 388 |     label="Password"
 389 |     bindTo="password"
 390 |     type="password"
 391 |     minLength="8"
 392 |     lengthInvalidMessage="Password must be at least 8 characters"
 393 |   />
 394 | </Form>
 395 | 
 396 | </Component>
 397 | ```
 398 | 
 399 | ## Do custom form validation
 400 | 
 401 | ```xmlui-pg noHeader
 402 | ---app
 403 | <App>
 404 |   <Test />
 405 | </App>
 406 | ---api
 407 | {}
 408 | ---comp display
 409 | <Component name="Test" var.limit="{100}">
 410 | 
 411 | <Form
 412 |   data="{{ spending: 0 }}"
 413 |   onSubmit="(data) => console.log('Submitted:', data)"
 414 | >
 415 | 
 416 |   <FormItem
 417 |     label="Requested Amount (limit {limit})"
 418 |     bindTo="total"
 419 |     type="integer"
 420 |     onValidate="{ (value) => value > 0 && value <= limit }"
 421 |   />
 422 | </Form>
 423 | 
 424 | </Component>
 425 | ```
 426 | 
 427 | ## Assign a complex JSON literal to a component variable
 428 | 
 429 | ```xmlui-pg noHeader
 430 | ---app
 431 | <App>
 432 |   <Test />
 433 | </App>
 434 | ---api
 435 | {}
 436 | ---comp display
 437 | <Component name="Test"
 438 |   <!-- double curly braces inside double quote -->
 439 |   var.appConfig="{{
 440 |     name: 'Photo Gallery',
 441 |     version: '1.2.0',
 442 |     isPublic: true,
 443 |     photos: [
 444 |       { id: 1, title: 'Sunset Beach', likes: 42 },
 445 |       { id: 2, title: 'Mountain View', likes: 38 },
 446 |       { id: 3, title: 'City Lights', likes: 55 }
 447 |     ],
 448 |     authors: [
 449 |       { name: 'Alice Johnson', role: 'Photographer' },
 450 |       { name: 'Bob Smith', role: 'Editor' }
 451 |     ]
 452 |   }}">
 453 |   <!-- double curly braces inside double quote -->
 454 | 
 455 |   <Text>{appConfig.name} v{appConfig.version}</Text>
 456 | 
 457 |   <Text>Photos ({appConfig.photos.length})</Text>
 458 |   <Items data="{appConfig.photos}">
 459 |     <Text>{$item.title} - {$item.likes} likes</Text>
 460 |   </Items>
 461 | 
 462 |   <Text>Team</Text>
 463 |   <Items data="{appConfig.authors}">
 464 |     <Text>{$item.name} ({$item.role})</Text>
 465 |   </Items>
 466 | 
 467 | </Component>
 468 | ```
 469 | 
 470 | ## Make a set of equal-width cards
 471 | 
 472 | ```xmlui-pg noHeader
 473 | ---app
 474 | <App>
 475 |   <Test />
 476 | </App>
 477 | ---api
 478 | {
 479 |   "apiUrl": "/api",
 480 | "initialize": "$state.dashboard_stats = {
 481 |       \"draft_invoices\":6,
 482 |       \"outstanding\":3502.9,
 483 |       \"paid_invoices\":79,
 484 |       \"paid_this_year\":1745.18,
 485 |       \"sent_invoices\":43,
 486 |       \"total_clients\":30,
 487 |       \"total_invoices\":91
 488 |   }",
 489 |   "operations": {
 490 |     "get-dashboard-stats": {
 491 |       "url": "/dashboard_stats",
 492 |       "method": "get",
 493 |       "handler": "$state.dashboard_stats"
 494 |     }
 495 |   }
 496 | }
 497 | ---comp display
 498 | <Component name="Test" >
 499 | 
 500 | <DataSource id="dashboard_stats" url="/api/dashboard_stats" method="GET" />
 501 | 
 502 |   <FlowLayout>
 503 |     <InfoCard
 504 |       width="*"               <!-- use star sizing -->
 505 |       title="Outstanding"
 506 |       value="{ dashboard_stats.value.outstanding }"
 507 |     />
 508 |     <InfoCard
 509 |     width="*"
 510 |       title="Paid This Year"
 511 |       value="{ dashboard_stats.value.paid_this_year }"
 512 |     />
 513 |     <InfoCard
 514 |       width="*"
 515 |       title="Draft"
 516 |       value="{ dashboard_stats.value.draft_invoices }"
 517 | 
 518 |     />
 519 |     <InfoCard
 520 |       width="*"
 521 |       title="Sent"
 522 |       value="{ dashboard_stats.value.sent_invoices }"
 523 |     />
 524 |   </FlowLayout>
 525 | 
 526 | </Component>
 527 | ---comp display
 528 | <Component name="InfoCard">
 529 | 
 530 |     <Card width="{$props.width}" borderRadius="8px" boxShadow="$boxShadow-spread">
 531 | 
 532 |         <Text>{$props.title}</Text>
 533 | 
 534 |         <Text fontWeight="$fontWeight-extra-bold" fontSize="larger">
 535 |             { $props.value }
 536 |         </Text>
 537 |     </Card>
 538 | 
 539 | </Component>
 540 | ```
 541 | 
 542 | ## Set the initial value of a Select from fetched data
 543 | 
 544 | ```xmlui-pg noHeader
 545 | ---app
 546 | <App>
 547 |   <Test />
 548 | </App>
 549 | ---api
 550 | {
 551 |   "apiUrl": "/api",
 552 |   "initialize": "$state.users_initial_value = [
 553 |         {
 554 |           id: 1,
 555 |           username: 'Coder Gal',
 556 |         },
 557 |         {
 558 |           id: 2,
 559 |           username: 'Tech Ninja',
 560 |         },
 561 |         {
 562 |           id: 3,
 563 |           username: 'Design Diva',
 564 |         },
 565 |       ]",
 566 |   "operations": {
 567 |     "get_users_initial_value": {
 568 |       "url": "/users_initial_value",
 569 |       "method": "get",
 570 |       "handler": "$state.users_initial_value"
 571 |     }
 572 |   }
 573 | }
 574 | ---comp display
 575 | <Component name="Test" var.selectedValue="">
 576 | 
 577 | <DataSource
 578 |   id="myData"
 579 |   url="/api/users_initial_value"
 580 |   onLoaded="(data) => { selectedValue = data[0].id }"
 581 | />
 582 | 
 583 | <Select initialValue="{selectedValue}">
 584 |   <Items data="{myData}">
 585 |     <Option value="{$item.id}" label="{$item.username}" />
 586 |   </Items>
 587 | </Select>
 588 | 
 589 | </Component>
 590 | ```
 591 | 
 592 | ## Pass data to a Modal Dialog
 593 | 
 594 | ```xmlui-pg name="Click on a team member to edit details"
 595 | ---app
 596 | <App>
 597 |   <Test />
 598 | </App>
 599 | ---api
 600 | {
 601 |   "apiUrl": "/api",
 602 |   "initialize": "$state.team_members = [
 603 |     { id: 1, name: 'Sarah Chen', role: 'Product Manager', email: '[email protected]', avatar: 'https://i.pravatar.cc/100?u=sarah', department: 'Product', startDate: '2022-03-15' },
 604 |     { id: 2, name: 'Marcus Johnson', role: 'Senior Developer', email: '[email protected]', avatar: 'https://i.pravatar.cc/100?u=marcus', department: 'Engineering', startDate: '2021-08-20' },
 605 |     { id: 3, name: 'Elena Rodriguez', role: 'UX Designer', email: '[email protected]', avatar: 'https://i.pravatar.cc/100?u=elena', department: 'Design', startDate: '2023-01-10' }
 606 |   ]",
 607 |   "operations": {
 608 |     "get_team_members": {
 609 |       "url": "/team_members",
 610 |       "method": "get",
 611 |       "handler": "return $state.team_members"
 612 |     }
 613 |   }
 614 | }
 615 | ---comp display
 616 | <Component name="Test">
 617 | 
 618 |   <DataSource
 619 |     id="team_members"
 620 |     url="/api/team_members"
 621 |   />
 622 | 
 623 |   <ModalDialog id="memberDetailsDialog" title="Team Member Details">
 624 |     <Theme backgroundColor-overlay="$color-surface-900">
 625 |       <VStack gap="1rem" padding="1rem">
 626 |       <!-- Avatar and Basic Info -->
 627 |       <HStack gap="1rem" alignItems="center">
 628 |         <Avatar
 629 |           url="{$param.avatar}"
 630 |           size="lg"
 631 |           name="{$param.name}"
 632 |         />
 633 |         <VStack gap="0.25rem" alignItems="start">
 634 |           <Text variant="strong" fontSize="1.2rem">{$param.name}</Text>
 635 |           <Text variant="caption">{$param.role}</Text>
 636 |           <Text variant="caption" color="blue">{$param.email}</Text>
 637 |         </VStack>
 638 |       </HStack>
 639 | 
 640 |       <!-- Details Card -->
 641 |       <Card padding="1rem">
 642 |         <VStack gap="0.5rem">
 643 |           <HStack>
 644 |             <Text variant="strong">Department:</Text>
 645 |             <Text>{$param.department}</Text>
 646 |           </HStack>
 647 |           <HStack>
 648 |             <Text variant="strong">Start Date:</Text>
 649 |             <Text>{$param.startDate}</Text>
 650 |           </HStack>
 651 |           <HStack>
 652 |             <Text variant="strong">Employee ID:</Text>
 653 |             <Text>#{$param.id}</Text>
 654 |           </HStack>
 655 |         </VStack>
 656 |       </Card>
 657 | 
 658 |       <!-- Actions -->
 659 |       <HStack gap="0.5rem">
 660 |         <Button
 661 |           label="Send Email"
 662 |           size="sm"
 663 |           onClick="console.log('Email to:', $param.email)"
 664 |         />
 665 |         <Button
 666 |           label="View Calendar"
 667 |           size="sm"
 668 |           variant="secondary"
 669 |           onClick="console.log('Calendar for:', $param.name)"
 670 |         />
 671 |       </HStack>
 672 |     </VStack>
 673 |     </Theme>
 674 |   </ModalDialog>
 675 | 
 676 |   <Text variant="strong" marginBottom="1rem">Team Directory</Text>
 677 | 
 678 |   <VStack gap="0.5rem">
 679 |     <Items data="{team_members}">
 680 |       <Card
 681 |         padding="1rem"
 682 |         cursor="pointer"
 683 |         onClick="{
 684 |           memberDetailsDialog.open({
 685 |             id: $item.id,
 686 |             name: $item.name,
 687 |             role: $item.role,
 688 |             email: $item.email,
 689 |             avatar: $item.avatar,
 690 |             department: $item.department,
 691 |             startDate: $item.startDate
 692 |           })
 693 |         }"
 694 |       >
 695 |         <HStack gap="1rem" alignItems="center">
 696 |           <Avatar
 697 |             url="{$item.avatar}"
 698 |             size="sm"
 699 |             name="{$item.name}"
 700 |           />
 701 |           <VStack gap="0.25rem" alignItems="start">
 702 |             <Text variant="strong">{$item.name}</Text>
 703 |             <Text variant="caption">{$item.role} - {$item.department}</Text>
 704 |           </VStack>
 705 |         </HStack>
 706 |       </Card>
 707 |     </Items>
 708 |   </VStack>
 709 | 
 710 | </Component>
 711 | ```
 712 | 
 713 | ## Debug a component
 714 | 
 715 | ```xmlui-pg noHeader
 716 | ---app
 717 | <App>
 718 |   <Test />
 719 | </App>
 720 | ---api
 721 | {
 722 |   "apiUrl": "/api",
 723 |   "initialize": "$state.user_data = {
 724 |       id: 42,
 725 |       name: 'John Doe',
 726 |       preferences: { theme: 'dark', notifications: true },
 727 |       recentItems: ['item1', 'item2', 'item3']
 728 |     }",
 729 |   "operations": {
 730 |     "get_user_data": {
 731 |       "url": "/user_data",
 732 |       "method": "get",
 733 |       "handler": "console.log('API called:', $state.user_data); return $state.user_data"
 734 |     }
 735 |   }
 736 | }
 737 | ---comp display
 738 | <Component name="Test"
 739 |   var.localState="{{
 740 |     currentStep: 2,
 741 |     errors: ['Invalid email', 'Password too short'],
 742 |     formData: { email: '[email protected]', age: 25 }
 743 |   }}">
 744 | 
 745 |   <DataSource
 746 |     id="userData"
 747 |     url="/api/user_data"
 748 |   />
 749 | 
 750 |   <Text>User Debug Info</Text>
 751 | 
 752 |   <!-- Method 1: JSON.stringify with preserveLinebreaks -->
 753 |   <Text preserveLinebreaks="true">
 754 |     {JSON.stringify(userData.value, null, 2)}
 755 |   </Text>
 756 | 
 757 |   <!-- Method 2: Console.log in handler -->
 758 |   <Button
 759 |     label="Log to Console"
 760 |     onClick="console.log('Button clicked, userData:', userData.value)"
 761 |   />
 762 | 
 763 |   <!-- Method 3: Window function for component variables -->
 764 |   <Button
 765 |   label="Debug Local State"
 766 |   onClick="window.debugLog(localState, 'Local component state')"
 767 |   />
 768 | 
 769 |   <!-- Method 4: Unwrapping Proxy objects -->
 770 |   <Button
 771 |     label="Debug Unwrapped Data"
 772 |     onClick="console.log('Unwrapped userData:', JSON.parse(JSON.stringify(userData.value)))"
 773 |   />
 774 | 
 775 | 
 776 | </Component>
 777 | ```
 778 | 
 779 | ![](/resources/devdocs/debug-proxy-object.png)
 780 | 
 781 | ![](/resources/devdocs/debug-proxy-object-2.png)
 782 | 
 783 | ## Share a ModalDialog across components
 784 | 
 785 | ```xmlui-pg noHeader
 786 | ---app
 787 | <App>
 788 |   <Test />
 789 | </App>
 790 | ---api
 791 | {
 792 |   "apiUrl": "/api",
 793 |   "initialize": "$state.items = [
 794 |     { id: 1, title: 'Mountain View' },
 795 |     { id: 2, title: 'City Lights' },
 796 |     { id: 3, title: 'Ocean Sunset' }
 797 |   ]",
 798 |   "operations": {
 799 |     "get-items": {
 800 |       "url": "/items",
 801 |       "method": "get",
 802 |       "handler": "return $state.items"
 803 |     }
 804 |   }
 805 | }
 806 | ---comp display
 807 | <Component name="Test">
 808 | 
 809 |   <AppState id="settings" bucket="appSettings" initialValue="{{
 810 |     itemSize: 'medium',
 811 |     showDetails: true
 812 |   }}" />
 813 | 
 814 |   <!-- Settings modal defined at App level - accessible to all components -->
 815 |   <ModalDialog id="settingsDialog" title="Settings">
 816 |     <SettingsPanel />
 817 |   </ModalDialog>
 818 | 
 819 |   <DataSource id="items" url="/api/items" />
 820 | 
 821 |   <AppHeader title="Demo App">
 822 |     <property name="profileMenuTemplate">
 823 |       <Icon name="cog" onClick="settingsDialog.open()" />
 824 |     </property>
 825 |   </AppHeader>
 826 | 
 827 |   <VStack gap="1rem">
 828 |     <HStack gap="1rem">
 829 |       <Text>Items ({settings.value.itemSize} size)</Text>
 830 |       <Button
 831 |         label="Settings"
 832 |         size="sm"
 833 |         onClick="settingsDialog.open()"
 834 |       />
 835 |     </HStack>
 836 | 
 837 |     <List data="{items}">
 838 |       <Card>
 839 |         <VStack>
 840 |           <Text>{$item.title}</Text>
 841 |           <Fragment when="{settings.value.showDetails}">
 842 |             <Text variant="caption">ID: {$item.id}</Text>
 843 |           </Fragment>
 844 |         </VStack>
 845 |       </Card>
 846 |     </List>
 847 |   </VStack>
 848 | 
 849 | </Component>
 850 | ---comp display
 851 | <Component name="SettingsPanel">
 852 |   <AppState id="settings" bucket="appSettings" />
 853 | 
 854 |   <VStack gap="1rem">
 855 | 
 856 |     <Select
 857 |       label="Item Size"
 858 |       initialValue="{settings.value.itemSize}"
 859 |       onDidChange="(value) => settings.update({ itemSize: value })"
 860 |     >
 861 |       <Option value="small" label="Small" />
 862 |       <Option value="medium" label="Medium" />
 863 |       <Option value="large" label="Large" />
 864 |     </Select>
 865 | 
 866 |     <Switch
 867 |       label="Show details"
 868 |       initialValue="{settings.value.showDetails}"
 869 |       onDidChange="(value) => settings.update({ showDetails: value })"
 870 |     />
 871 | 
 872 |   </VStack>
 873 | </Component>
 874 | ```
 875 | 
 876 | ## Use the same ModalDialog to add or edit
 877 | 
 878 | See also the [refactoring](/refactoring) guide. Briefly: props flow down, events flow up.
 879 | 
 880 | ```xmlui-pg noHeader height="400px"
 881 | ---app
 882 | <App>
 883 |   <Test />
 884 | </App>
 885 | ---comp display
 886 | <Component name="Test" var.editingProductId="{null}" var.showAddModal="{false}">
 887 |   <DataSource id="products" url="/api/products" />
 888 | 
 889 |   <HStack alignItems="center">
 890 |     <Text variant="strong" fontSize="$fontSize-2xl">Product Inventory</Text>
 891 |     <SpaceFiller />
 892 |     <Button
 893 |       label="Add New Product"
 894 |       size="sm"
 895 |       onClick="showAddModal = true"
 896 |     />
 897 |   </HStack>
 898 | 
 899 |   <Table data="{products}">
 900 |     <Column bindTo="name" />
 901 |     <Column bindTo="price" width="120px"/>
 902 |     <Column header="Actions" width="240px">
 903 |       <HStack>
 904 |         <Button label="Edit" icon="pencil" size="sm" variant="outlined"
 905 |           onClick="editingProductId = $item.id"
 906 |         />
 907 |         <Button label="Delete" icon="trash" size="sm" variant="outlined"
 908 |           themeColor="attention">
 909 |           <event name="click">
 910 |             <APICall
 911 |               method="delete"
 912 |               url="/api/products/{$item.id}"
 913 |               confirmMessage="Are you sure you want to delete '{$item.name}'?" />
 914 |           </event>
 915 |         </Button>
 916 |       </HStack>
 917 |     </Column>
 918 |   </Table>
 919 | 
 920 |   <ProductModal
 921 |     when="{showAddModal}"
 922 |     mode="add"
 923 |     onClose="showAddModal = false"
 924 |     onSaved="products.refetch()"
 925 |   />
 926 | 
 927 |   <ProductModal
 928 |     when="{editingProductId}"
 929 |     mode="edit"
 930 |     productId="{editingProductId}"
 931 |     onClose="editingProductId = null"
 932 |     onSaved="products.refetch()"
 933 |   />
 934 | </Component>
 935 | ---comp display
 936 | <Component name="ProductModal">
 937 |   <DataSource
 938 |     id="productDetails"
 939 |     url="/api/products/{$props.productId}"
 940 |     when="{$props.mode === 'edit' && $props.productId}"
 941 |   />
 942 | 
 943 |   <ModalDialog
 944 |     title="{$props.mode === 'edit' ? 'Edit Product' : 'Add Product'}"
 945 |     when="{$props.mode === 'add' || productDetails.loaded}"
 946 |     onClose="emitEvent('close')"
 947 |   >
 948 |     <Form
 949 |       data="{$props.mode === 'edit' ? productDetails.value : {}}"
 950 |       submitUrl="{$props.mode === 'edit' ? '/api/products/' + $props.productId : '/api/products'}"
 951 |       submitMethod="{$props.mode === 'edit' ? 'put' : 'post'}"
 952 |       onSuccess="emitEvent('saved')"
 953 |     >
 954 |       <FormItem bindTo="name" label="Product Name" required="true" />
 955 |       <FormItem bindTo="price" label="Price" type="number" required="true" />
 956 |     </Form>
 957 |   </ModalDialog>
 958 | </Component>
 959 | ---api
 960 | {
 961 |   "apiUrl": "/api",
 962 |   "initialize": "$state.products = [
 963 |     { id: 1, name: 'Laptop Pro', price: 1299 },
 964 |     { id: 2, name: 'Wireless Mouse', price: 29 }
 965 |   ]",
 966 |   "operations": {
 967 |     "get-products": {
 968 |       "url": "/products",
 969 |       "method": "get",
 970 |       "handler": "$state.products"
 971 |     },
 972 |     "get-product": {
 973 |       "url": "/products/:id",
 974 |       "method": "get",
 975 |       "pathParamTypes": {
 976 |         "id": "integer"
 977 |       },
 978 |       "handler": "return $state.products.find(p => p.id === $pathParams.id)"
 979 |     },
 980 |     "insert-product": {
 981 |       "url": "/products",
 982 |       "method": "post",
 983 |       "handler": "
 984 |         const newId = $state.products.length > 0 ? Math.max(...$state.products.map(p => p.id)) + 1 : 1;
 985 |         $state.products.push({
 986 |           id: newId,
 987 |           name: $requestBody.name,
 988 |           price: Number($requestBody.price)
 989 |         });
 990 |       "
 991 |     },
 992 |     "update-product": {
 993 |       "url": "/products/:id",
 994 |       "method": "put",
 995 |       "pathParamTypes": {
 996 |         "id": "integer"
 997 |       },
 998 |       "handler": "
 999 |         const oldItem = $state.products.find(p => p.id === $pathParams.id);
1000 |         if (oldItem) {
1001 |           oldItem.name = $requestBody.name;
1002 |           oldItem.price = Number($requestBody.price);
1003 |         }
1004 |       "
1005 |     },
1006 |     "delete-product": {
1007 |       "url": "/products/:id",
1008 |       "method": "delete",
1009 |       "pathParamTypes": {
1010 |         "id": "integer"
1011 |       },
1012 |       "handler": "$state.products = $state.products.filter(p => p.id !== $pathParams.id)"
1013 |     }
1014 |   }
1015 | }
1016 | ```
1017 | 
1018 | ## Paginate a List
1019 | 
1020 | XMLUI provides a `Pagination` component that can be used to display visual controls for the pagination feature, no matter whether it is handled inside or outside of a layout component requiring that feature.
1021 | 
1022 | The [`Table`](./table) component provides out-of-the-box support for pagination,
1023 | so you can access pagination options via the following properties: `isPaginated`, `pageSize`, `pageSizeOptions`, `paginationControlsLocation`.
1024 | 
1025 | ```xmlui noHeader copy
1026 | <Table
1027 |   data="/api/endpoint"
1028 |   isPaginated
1029 |   pageSize="10"
1030 |   pageSizeOptions="{[5, 10, 20, 30]}"
1031 |   paginationControlsLocation="both"
1032 | >
1033 |     ...
1034 | </Table>
1035 | ```
1036 | 
1037 | Other components, such as the `List`, can be hooked up with pagination using a `DataSource` combined with the `Pagination` component. This pattern works as a more generic solution where either the component does not have pagination implemented in the component itself, or you wish to use custom pagination logic.
1038 | 
1039 | In this case the `DataSource` component does the heavy lifting by querying the page index, the previous and next page IDs. This can be done using variables and query parameters.
1040 | 
1041 | ```xmlui-pg
1042 | ---app display
1043 | <App
1044 |     var.pageSize="{5}"
1045 |     var.currentPage="{0}"
1046 |     var.before="{0}"
1047 |     var.after="{pageSize-1}"
1048 |   >
1049 |   <DataSource
1050 |     id="pagination_ds"
1051 |     url="/api/pagination_items/{before}/{after}"
1052 |     />
1053 |     <Text>
1054 |       Page {currentPage + 1}, showing items {before + 1}-{after + 1}
1055 |     </Text>
1056 |     <Pagination
1057 |       id="pagination"
1058 |       itemCount="20"
1059 |       pageSize="{pageSize}"
1060 |       pageIndex="{currentPage}"
1061 |       onPageDidChange="(page, size, total) => {
1062 |         currentPage = page;
1063 |         before = page * size;
1064 |         after = before + size - 1;
1065 |         pagination_ds.refetch();
1066 |       }"
1067 |       onPageSizeDidChange="(size) => {
1068 |         pageSize = size;
1069 |         before = currentPage * size;
1070 |         after = before + size - 1;
1071 |         pagination_ds.refetch();
1072 |       }"
1073 |     />
1074 |     <List data="{pagination_ds}" />
1075 | </App>
1076 | ---api
1077 | {
1078 |   "apiUrl": "/api",
1079 |   "initialize": "$state.pagination_items = [
1080 |     { id: 1, name: 'Laptop Pro', price: 1299 },
1081 |     { id: 2, name: 'Wireless Mouse', price: 29 },
1082 |     { id: 3, name: 'Mechanical Keyboard', price: 149 },
1083 |     { id: 4, name: '4K Monitor', price: 399 },
1084 |     { id: 5, name: 'USB-C Hub', price: 79 },
1085 |     { id: 6, name: 'Bluetooth Headphones', price: 199 },
1086 |     { id: 7, name: 'Webcam HD', price: 89 },
1087 |     { id: 8, name: 'Standing Desk', price: 299 },
1088 |     { id: 9, name: 'Ergonomic Chair', price: 249 },
1089 |     { id: 10, name: 'Desk Lamp', price: 45 },
1090 |     { id: 11, name: 'Cable Organizer', price: 15 },
1091 |     { id: 12, name: 'Mouse Pad', price: 12 },
1092 |     { id: 13, name: 'Laptop Stand', price: 35 },
1093 |     { id: 14, name: 'External SSD', price: 129 },
1094 |     { id: 15, name: 'Wireless Charger', price: 59 },
1095 |     { id: 16, name: 'Smart Speaker', price: 99 },
1096 |     { id: 17, name: 'Fitness Tracker', price: 199 },
1097 |     { id: 18, name: 'Tablet Pro', price: 799 },
1098 |     { id: 19, name: 'Gaming Mouse', price: 89 },
1099 |     { id: 20, name: 'Noise Cancelling Headphones', price: 349 }
1100 |   ]",
1101 |   "operations": {
1102 |     "get-pagination-items": {
1103 |       "url": "/pagination_items/:from/:to",
1104 |       "method": "get",
1105 |       "pathParamTypes": {
1106 |         "from": "integer",
1107 |         "to": "integer"
1108 |       },
1109 |       "handler": "$state.pagination_items.slice($pathParams.from, $pathParams.to + 1)"
1110 |     }
1111 |   }
1112 | }
1113 | ```
1114 | 
```
Page 108/186FirstPrevNextLast