#
tokens: 61888/50000 1/1764 files (page 217/224)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 217 of 224. Use http://codebase.md/xmlui-org/xmlui?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .changeset
│   ├── config.json
│   ├── hip-dogs-see.md
│   ├── open-dodos-fetch.md
│   ├── smooth-pears-kneel.md
│   ├── smooth-steaks-jog.md
│   ├── sour-queens-grab.md
│   ├── tasty-owls-win.md
│   └── wild-pots-glow.md
├── .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-fast.yml
│       ├── run-all-tests.yml
│       └── run-smoke-tests.yml
├── .gitignore
├── .npmrc
├── .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
│   │   │   ├── icons
│   │   │   │   ├── github.svg
│   │   │   │   └── rss.svg
│   │   │   ├── llms.txt
│   │   │   ├── logo-dark.svg
│   │   │   ├── logo.svg
│   │   │   ├── pg-popout.svg
│   │   │   ├── rss.svg
│   │   │   └── xmlui-logo.svg
│   │   ├── serve.json
│   │   └── staticwebapp.config.json
│   ├── scripts
│   │   ├── download-latest-xmlui.js
│   │   ├── generate-rss.js
│   │   ├── get-releases.js
│   │   └── utils.js
│   ├── src
│   │   ├── components
│   │   │   ├── BlogOverview.xmlui
│   │   │   ├── BlogPage.xmlui
│   │   │   ├── LinkButton.xmlui
│   │   │   ├── PageNotFound.xmlui
│   │   │   └── Separator.xmlui
│   │   ├── config.ts
│   │   └── Main.xmlui
│   └── 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
│   │   │   ├── ResponsiveBar.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
│   │           ├── HelloMd.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
│   │   │   │   ├── copy-billing-to-shipping.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
│   │   │   │   ├── implement-an-authentication-gate.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-modal-dialog-onclose.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
│   │   │   ├── icons
│   │   │   │   ├── github.svg
│   │   │   │   └── rss.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
│   │   └── staticwebapp.config.json
│   ├── 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
│   │   │   ├── LinkButton.xmlui
│   │   │   ├── NameValue.xmlui
│   │   │   ├── PageNotFound.xmlui
│   │   │   ├── PaletteItem.xmlui
│   │   │   ├── Palettes.xmlui
│   │   │   ├── SectionHeader.xmlui
│   │   │   ├── Separator.xmlui
│   │   │   ├── TBD.xmlui
│   │   │   ├── Test.xmlui
│   │   │   ├── ThemesIntro.xmlui
│   │   │   ├── ThousandThemes.xmlui
│   │   │   ├── TubeStops.xmlui
│   │   │   ├── TubeStops.xmlui.xs
│   │   │   └── TwoColumnCode.xmlui
│   │   ├── config.ts
│   │   ├── Main.xmlui
│   │   └── themes
│   │       ├── 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
    │   ├── app-next.md
    │   ├── build-system.md
    │   ├── build-xmlui.md
    │   ├── component-behaviors.md
    │   ├── component-metadata.md
    │   ├── components-with-options.md
    │   ├── containers.md
    │   ├── data-operations.md
    │   ├── glossary.md
    │   ├── images
    │   │   ├── condensed-layout-content-scroll-no-gutters-bottom.svg
    │   │   ├── condensed-layout-content-scroll-no-gutters-no-overflow.svg
    │   │   ├── condensed-layout-content-scroll-no-gutters-top.svg
    │   │   ├── condensed-layout-content-scroll-with-gutters-bottom.svg
    │   │   ├── condensed-layout-content-scroll-with-gutters-no-overflow.svg
    │   │   ├── condensed-layout-content-scroll-with-gutters-top.svg
    │   │   ├── condensed-layout-no-gutters-bottom.svg
    │   │   ├── condensed-layout-no-gutters-mid.svg
    │   │   ├── condensed-layout-no-gutters-top.svg
    │   │   ├── condensed-layout-no-overflow.svg
    │   │   ├── condensed-layout-with-gutters-bottom.svg
    │   │   ├── condensed-layout-with-gutters-no-overflow.svg
    │   │   ├── condensed-layout-with-gutters-top.svg
    │   │   ├── condensed-sticky-content-scroll-no-gutters-bottom.svg
    │   │   ├── condensed-sticky-content-scroll-no-gutters-no-overflow.svg
    │   │   ├── condensed-sticky-content-scroll-no-gutters-top.svg
    │   │   ├── condensed-sticky-content-scroll-with-gutters-bottom.svg
    │   │   ├── condensed-sticky-content-scroll-with-gutters-no-overflow.svg
    │   │   ├── condensed-sticky-content-scroll-with-gutters-top.svg
    │   │   ├── condensed-sticky-layout-no-gutters-bottom.svg
    │   │   ├── condensed-sticky-layout-no-gutters-top.svg
    │   │   ├── condensed-sticky-layout-no-overflow.svg
    │   │   ├── condensed-sticky-layout-with-gutters-bottom.svg
    │   │   ├── condensed-sticky-layout-with-gutters-no-overflow.svg
    │   │   ├── condensed-sticky-layout-with-gutters-top.svg
    │   │   ├── desktop-layout-no-overflow.svg
    │   │   ├── desktop-layout-overflow-bottom.svg
    │   │   ├── desktop-layout-overflow-top.svg
    │   │   ├── horizontal-layout-content-scroll-no-gutters-bottom.svg
    │   │   ├── horizontal-layout-content-scroll-no-gutters-top.svg
    │   │   ├── horizontal-layout-content-scroll-no-gutters.svg
    │   │   ├── horizontal-layout-content-scroll-with-gutters-bottom.svg
    │   │   ├── horizontal-layout-content-scroll-with-gutters-diagram.svg
    │   │   ├── horizontal-layout-content-scroll-with-gutters-top.svg
    │   │   ├── horizontal-layout-diagram.svg
    │   │   ├── horizontal-layout-no-gutters-overflow-scrollbar-bottom.svg
    │   │   ├── horizontal-layout-no-gutters-overflow-scrollbar-top.svg
    │   │   ├── horizontal-layout-overflow-scrollbar-bottom.svg
    │   │   ├── horizontal-layout-overflow-scrollbar-nogutter-bottom.svg
    │   │   ├── horizontal-layout-overflow-scrollbar-nogutter-mid.svg
    │   │   ├── horizontal-layout-overflow-scrollbar-top.svg
    │   │   ├── horizontal-layout-with-gutters-diagram.svg
    │   │   ├── horizontal-sticky-content-scroll-no-gutters-bottom.svg
    │   │   ├── horizontal-sticky-content-scroll-no-gutters-top.svg
    │   │   ├── horizontal-sticky-content-scroll-no-gutters.svg
    │   │   ├── horizontal-sticky-content-scroll-with-gutters-bottom.svg
    │   │   ├── horizontal-sticky-content-scroll-with-gutters-top.svg
    │   │   ├── horizontal-sticky-content-scroll-with-gutters.svg
    │   │   ├── horizontal-sticky-layout-overflow-bottom.svg
    │   │   ├── horizontal-sticky-layout-overflow-top.svg
    │   │   ├── horizontal-sticky-layout-with-gutters-bottom-scroll.svg
    │   │   ├── horizontal-sticky-layout-with-gutters-mid-scroll.svg
    │   │   ├── horizontal-sticky-layout-with-gutters-no-overflow.svg
    │   │   ├── horizontal-sticky-layout-with-gutters-overflow-bottom.svg
    │   │   ├── horizontal-sticky-layout-with-gutters-overflow-mid.svg
    │   │   ├── horizontal-sticky-layout-with-gutters-overflow-top.svg
    │   │   ├── horizontal-sticky-layout-with-gutters.svg
    │   │   ├── horizontal-sticky-layout.svg
    │   │   ├── vertical-full-header-content-scroll-no-gutters-overflow-bottom.svg
    │   │   ├── vertical-full-header-content-scroll-no-gutters-overflow-top.svg
    │   │   ├── vertical-full-header-content-scroll-no-gutters.svg
    │   │   ├── vertical-full-header-content-scroll-with-gutters-overflow-bottom.svg
    │   │   ├── vertical-full-header-content-scroll-with-gutters-overflow-top.svg
    │   │   ├── vertical-full-header-content-scroll-with-gutters.svg
    │   │   ├── vertical-full-header-layout-no-gutters-overflow-bottom.svg
    │   │   ├── vertical-full-header-layout-no-gutters-overflow-top.svg
    │   │   ├── vertical-full-header-layout-no-gutters.svg
    │   │   ├── vertical-full-header-layout-with-gutters-overflow-bottom.svg
    │   │   ├── vertical-full-header-layout-with-gutters-overflow-top.svg
    │   │   ├── vertical-full-header-layout-with-gutters.svg
    │   │   ├── vertical-layout-content-scroll-no-gutters-overflow-bottom.svg
    │   │   ├── vertical-layout-content-scroll-no-gutters-overflow-top.svg
    │   │   ├── vertical-layout-content-scroll-no-gutters.svg
    │   │   ├── vertical-layout-content-scroll-with-gutters-overflow-bottom.svg
    │   │   ├── vertical-layout-content-scroll-with-gutters-overflow-top.svg
    │   │   ├── vertical-layout-content-scroll-with-gutters.svg
    │   │   ├── vertical-layout-no-gutters-overflow-bottom.svg
    │   │   ├── vertical-layout-no-gutters-overflow-top.svg
    │   │   ├── vertical-layout-no-gutters.svg
    │   │   ├── vertical-layout-with-gutters-overflow-bottom.svg
    │   │   ├── vertical-layout-with-gutters-overflow-top.svg
    │   │   ├── vertical-layout-with-gutters.svg
    │   │   ├── vertical-sticky-content-scroll-no-gutters-overflow-bottom.svg
    │   │   ├── vertical-sticky-content-scroll-no-gutters-overflow-top.svg
    │   │   ├── vertical-sticky-content-scroll-no-gutters.svg
    │   │   ├── vertical-sticky-content-scroll-with-gutters-overflow-bottom.svg
    │   │   ├── vertical-sticky-content-scroll-with-gutters-overflow-top.svg
    │   │   ├── vertical-sticky-content-scroll-with-gutters.svg
    │   │   ├── vertical-sticky-layout-no-gutters-overflow-bottom.svg
    │   │   ├── vertical-sticky-layout-no-gutters-overflow-top.svg
    │   │   ├── vertical-sticky-layout-no-gutters.svg
    │   │   ├── vertical-sticky-layout-with-gutters-overflow-bottom.svg
    │   │   ├── vertical-sticky-layout-with-gutters-overflow-top.svg
    │   │   └── vertical-sticky-layout-with-gutters.svg
    │   ├── 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
    │   ├── svg-diagram-rules.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-layout.spec.ts
    │   │   │   ├── 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
    │   │   ├── App2
    │   │   │   ├── app-refactor.md
    │   │   │   ├── App2-layout-mobile.spec.ts
    │   │   │   ├── App2-layout.spec.ts
    │   │   │   ├── App2.md
    │   │   │   ├── App2.module.scss
    │   │   │   ├── App2.spec.ts
    │   │   │   ├── App2.tsx
    │   │   │   ├── App2Native.tsx
    │   │   │   ├── App2Navigation.ts
    │   │   │   ├── 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
    │   │   │   ├── SearchIndexCollector.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
    │   │   │   └── test-padding.xmlui
    │   │   ├── 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
    │   │   ├── Part
    │   │   │   └── Part.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
    │   │   │   ├── ResponsiveBarItem.tsx
    │   │   │   └── ResponsiveBarNative.tsx
    │   │   ├── Select
    │   │   │   ├── HiddenOption.tsx
    │   │   │   ├── MultiSelectOption.tsx
    │   │   │   ├── OptionContext.ts
    │   │   │   ├── Select.md
    │   │   │   ├── Select.module.scss
    │   │   │   ├── Select.spec.ts
    │   │   │   ├── Select.tsx
    │   │   │   ├── SelectContext.tsx
    │   │   │   ├── SelectNative.tsx
    │   │   │   ├── SelectOption.tsx
    │   │   │   └── SimpleSelect.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
    │   │   ├── Toast
    │   │   │   ├── Toast.spec.ts
    │   │   │   ├── Toast.tsx
    │   │   │   └── ToastNative.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
    │   └── 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-disabled.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
    │   ├── inline-styles-disabled.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/src/components/Form/Form.spec.ts:
--------------------------------------------------------------------------------

```typescript
   1 | import type { ApiInterceptorDefinition } from "../../components-core/interception/abstractions";
   2 | import { labelPositionValues } from "../abstractions";
   3 | import { expect, test } from "../../testing/fixtures";
   4 | 
   5 | // Test data constants
   6 | const errorDisplayInterceptor: ApiInterceptorDefinition = {
   7 |   initialize: `
   8 |     $state.items = {
   9 |       [10]: { name: "Smith", id: 10 }
  10 |     };
  11 |     $state.currentId = 10;
  12 |   `,
  13 |   operations: {
  14 |     "no-validation-error": {
  15 |       url: "/no-validation-error",
  16 |       method: "post",
  17 |       handler: `return true;`,
  18 |     },
  19 |     "general-validation-error": {
  20 |       url: "/general-validation-error",
  21 |       method: "post",
  22 |       handler: `
  23 |         throw Errors.HttpError(404,
  24 |           {
  25 |             message: "General error message from the backend",
  26 |             issues: [
  27 |               { message: "Error for the whole form", severity: "error" },
  28 |               { message: "Warning for the whole form", severity: "warning" },
  29 |             ]
  30 |           }
  31 |         );
  32 |       `,
  33 |     },
  34 |     "field-validation-error": {
  35 |       url: "/field-validation-error",
  36 |       method: "post",
  37 |       handler: `
  38 |         throw Errors.HttpError(404,
  39 |           {
  40 |             message: "Field error message from the backend",
  41 |             issues: [
  42 |               { field: "test", message: "Display warning", severity: "warning" },
  43 |             ]
  44 |           }
  45 |         );
  46 |       `,
  47 |     },
  48 |   },
  49 | };
  50 | 
  51 | // =============================================================================
  52 | // BASIC FUNCTIONALITY TESTS
  53 | // =============================================================================
  54 | 
  55 | test.describe("Basic Functionality", () => {
  56 |   test("component renders with default props", async ({ initTestBed, createFormDriver }) => {
  57 |     await initTestBed(`<Form testId="form"/>`);
  58 |     const driver = await createFormDriver("form");
  59 |     await expect(driver.component).toBeVisible();
  60 |   });
  61 | 
  62 |   test("component renders with form items", async ({ initTestBed, page }) => {
  63 |     await initTestBed(`
  64 |       <Form>
  65 |         <FormItem label="Name" bindTo="name" />
  66 |         <FormItem label="Email" bindTo="email" />
  67 |       </Form>
  68 |     `);
  69 | 
  70 |     await expect(page.getByText("Name")).toBeVisible();
  71 |     await expect(page.getByText("Email")).toBeVisible();
  72 |   });
  73 | 
  74 |   test("component renders save and cancel buttons by default", async ({ initTestBed, page }) => {
  75 |     await initTestBed(`<Form/>`);
  76 | 
  77 |     await expect(page.getByRole("button", { name: "Cancel" })).toBeVisible();
  78 |     await expect(page.getByRole("button", { name: "Save" })).toBeVisible();
  79 |   });
  80 | 
  81 |   test("component renders custom button labels", async ({ initTestBed, page }) => {
  82 |     await initTestBed(`
  83 |       <Form cancelLabel="Go Back" saveLabel="Submit"/>
  84 |     `);
  85 | 
  86 |     await expect(page.getByRole("button", { name: "Go Back" })).toBeVisible();
  87 |     await expect(page.getByRole("button", { name: "Submit" })).toBeVisible();
  88 |   });
  89 | 
  90 |   test("component swaps cancel and save button positions", async ({ initTestBed, page }) => {
  91 |     await initTestBed(`
  92 |       <Form swapCancelAndSave="true"/>
  93 |     `);
  94 | 
  95 |     const buttons = page.getByRole("button");
  96 |     await expect(buttons.first()).toHaveText("Save");
  97 |     await expect(buttons.last()).toHaveText("Cancel");
  98 |   });
  99 | 
 100 |   // =============================================================================
 101 |   // HIDE BUTTON ROW TESTS
 102 |   // =============================================================================
 103 | 
 104 |   test.describe("hideButtonRow property", () => {
 105 |     test("hides button row when set to true", async ({ initTestBed, page }) => {
 106 |       await initTestBed(`<Form hideButtonRow="true"/>`);
 107 | 
 108 |       await expect(page.getByRole("button", { name: "Cancel" })).not.toBeVisible();
 109 |       await expect(page.getByRole("button", { name: "Save" })).not.toBeVisible();
 110 |     });
 111 | 
 112 |     test("shows button row when set to false", async ({ initTestBed, page }) => {
 113 |       await initTestBed(`<Form hideButtonRow="false"/>`);
 114 | 
 115 |       await expect(page.getByRole("button", { name: "Cancel" })).toBeVisible();
 116 |       await expect(page.getByRole("button", { name: "Save" })).toBeVisible();
 117 |     });
 118 | 
 119 |     test("shows button row by default when property not set", async ({ initTestBed, page }) => {
 120 |       await initTestBed(`<Form/>`);
 121 | 
 122 |       await expect(page.getByRole("button", { name: "Cancel" })).toBeVisible();
 123 |       await expect(page.getByRole("button", { name: "Save" })).toBeVisible();
 124 |     });
 125 | 
 126 |     test("hides custom button row template when set to true", async ({ initTestBed, page }) => {
 127 |       await initTestBed(`
 128 |         <Form hideButtonRow="true">
 129 |           <property name="buttonRowTemplate">
 130 |             <Button label="Custom Save" type="submit" testId="customSave" />
 131 |             <Button label="Custom Cancel" type="button" testId="customCancel" />
 132 |           </property>
 133 |         </Form>
 134 |       `);
 135 | 
 136 |       await expect(page.getByTestId("customSave")).not.toBeVisible();
 137 |       await expect(page.getByTestId("customCancel")).not.toBeVisible();
 138 |     });
 139 | 
 140 |     test("overrides hideButtonRowUntilDirty when both are set", async ({
 141 |       initTestBed,
 142 |       page,
 143 |       createFormItemDriver,
 144 |       createTextBoxDriver,
 145 |     }) => {
 146 |       await initTestBed(`
 147 |         <Form hideButtonRow="true" hideButtonRowUntilDirty="true">
 148 |           <FormItem label="Name" bindTo="name" testId="nameField" />
 149 |         </Form>
 150 |       `);
 151 | 
 152 |       // Button row should be hidden even before making changes
 153 |       await expect(page.getByRole("button", { name: "Save" })).not.toBeVisible();
 154 |       await expect(page.getByRole("button", { name: "Cancel" })).not.toBeVisible();
 155 | 
 156 |       // Make the form dirty
 157 |       const driver = await createFormItemDriver("nameField");
 158 |       const input = await createTextBoxDriver(driver.input);
 159 |       await input.field.fill("John");
 160 | 
 161 |       // Button row should still be hidden even after making changes
 162 |       await expect(page.getByRole("button", { name: "Save" })).not.toBeVisible();
 163 |       await expect(page.getByRole("button", { name: "Cancel" })).not.toBeVisible();
 164 |     });
 165 | 
 166 |     test("handles null value gracefully", async ({ initTestBed, page }) => {
 167 |       await initTestBed(`<Form hideButtonRow="{null}"/>`);
 168 | 
 169 |       // Should show button row (default behavior)
 170 |       await expect(page.getByRole("button", { name: "Cancel" })).toBeVisible();
 171 |       await expect(page.getByRole("button", { name: "Save" })).toBeVisible();
 172 |     });
 173 | 
 174 |     test("handles undefined value gracefully", async ({ initTestBed, page }) => {
 175 |       await initTestBed(`<Form hideButtonRow="{undefined}"/>`);
 176 | 
 177 |       // Should show button row (default behavior)
 178 |       await expect(page.getByRole("button", { name: "Cancel" })).toBeVisible();
 179 |       await expect(page.getByRole("button", { name: "Save" })).toBeVisible();
 180 |     });
 181 | 
 182 |     test("handles string 'true' value", async ({ initTestBed, page }) => {
 183 |       await initTestBed(`<Form hideButtonRow="true"/>`);
 184 | 
 185 |       await expect(page.getByRole("button", { name: "Cancel" })).not.toBeVisible();
 186 |       await expect(page.getByRole("button", { name: "Save" })).not.toBeVisible();
 187 |     });
 188 | 
 189 |     test("handles string 'false' value", async ({ initTestBed, page }) => {
 190 |       await initTestBed(`<Form hideButtonRow="false"/>`);
 191 | 
 192 |       await expect(page.getByRole("button", { name: "Cancel" })).toBeVisible();
 193 |       await expect(page.getByRole("button", { name: "Save" })).toBeVisible();
 194 |     });
 195 | 
 196 |     test("form submission still works with hidden button row via external submit", async ({
 197 |       initTestBed,
 198 |       page,
 199 |       createFormItemDriver,
 200 |       createTextBoxDriver,
 201 |     }) => {
 202 |       const { testStateDriver } = await initTestBed(`
 203 |         <Fragment>
 204 |           <Form id="testForm" hideButtonRow="true" onSubmit="arg => testState = arg">
 205 |             <FormItem label="Name" bindTo="name" testId="nameField" />
 206 |             <Button type="submit" label="External Submit" testId="externalSubmit" />
 207 |           </Form>
 208 |         </Fragment>
 209 |       `);
 210 | 
 211 |       const driver = await createFormItemDriver("nameField");
 212 |       const input = await createTextBoxDriver(driver.input);
 213 |       await input.field.fill("John Doe");
 214 | 
 215 |       await page.getByTestId("externalSubmit").click();
 216 | 
 217 |       const submittedData = await testStateDriver.testState();
 218 |       expect(submittedData).toEqual({ name: "John Doe" });
 219 |     });
 220 |   });
 221 | 
 222 |   // =============================================================================
 223 |   // HIDE BUTTON ROW UNTIL DIRTY TESTS
 224 |   // =============================================================================
 225 | 
 226 |   test.describe("hideButtonRowUntilDirty property", () => {
 227 |     test("hides button row initially when form is not dirty", async ({ initTestBed, page }) => {
 228 |       await initTestBed(`
 229 |         <Form hideButtonRowUntilDirty="true">
 230 |           <FormItem label="Name" bindTo="name" testId="nameField" />
 231 |         </Form>
 232 |       `);
 233 | 
 234 |       await expect(page.getByRole("button", { name: "Cancel" })).not.toBeVisible();
 235 |       await expect(page.getByRole("button", { name: "Save" })).not.toBeVisible();
 236 |     });
 237 | 
 238 |     test("shows button row when form becomes dirty", async ({
 239 |       initTestBed,
 240 |       page,
 241 |       createFormItemDriver,
 242 |       createTextBoxDriver,
 243 |     }) => {
 244 |       await initTestBed(`
 245 |         <Form hideButtonRowUntilDirty="true">
 246 |           <FormItem label="Name" bindTo="name" testId="nameField" />
 247 |         </Form>
 248 |       `);
 249 | 
 250 |       // Initially hidden
 251 |       await expect(page.getByRole("button", { name: "Save" })).not.toBeVisible();
 252 | 
 253 |       // Make form dirty
 254 |       const driver = await createFormItemDriver("nameField");
 255 |       const input = await createTextBoxDriver(driver.input);
 256 |       await input.field.fill("John");
 257 | 
 258 |       // Now visible
 259 |       await expect(page.getByRole("button", { name: "Cancel" })).toBeVisible();
 260 |       await expect(page.getByRole("button", { name: "Save" })).toBeVisible();
 261 |     });
 262 | 
 263 |     test("keeps button row visible after form becomes dirty", async ({
 264 |       initTestBed,
 265 |       page,
 266 |       createFormItemDriver,
 267 |       createTextBoxDriver,
 268 |     }) => {
 269 |       await initTestBed(`
 270 |         <Form hideButtonRowUntilDirty="true">
 271 |           <FormItem label="Name" bindTo="name" testId="nameField" />
 272 |         </Form>
 273 |       `);
 274 | 
 275 |       const driver = await createFormItemDriver("nameField");
 276 |       const input = await createTextBoxDriver(driver.input);
 277 | 
 278 |       // Make form dirty
 279 |       await input.field.fill("John");
 280 |       await expect(page.getByRole("button", { name: "Save" })).toBeVisible();
 281 | 
 282 |       // Clear the input (form is still dirty)
 283 |       await input.field.clear();
 284 |       await expect(page.getByRole("button", { name: "Save" })).toBeVisible();
 285 |     });
 286 | 
 287 |     test("shows button row by default when property set to false", async ({
 288 |       initTestBed,
 289 |       page,
 290 |     }) => {
 291 |       await initTestBed(`
 292 |         <Form hideButtonRowUntilDirty="false">
 293 |           <FormItem label="Name" bindTo="name" testId="nameField" />
 294 |         </Form>
 295 |       `);
 296 | 
 297 |       await expect(page.getByRole("button", { name: "Cancel" })).toBeVisible();
 298 |       await expect(page.getByRole("button", { name: "Save" })).toBeVisible();
 299 |     });
 300 | 
 301 |     test("shows button row by default when property not set", async ({ initTestBed, page }) => {
 302 |       await initTestBed(`
 303 |         <Form>
 304 |           <FormItem label="Name" bindTo="name" testId="nameField" />
 305 |         </Form>
 306 |       `);
 307 | 
 308 |       await expect(page.getByRole("button", { name: "Cancel" })).toBeVisible();
 309 |       await expect(page.getByRole("button", { name: "Save" })).toBeVisible();
 310 |     });
 311 | 
 312 |     test("works with multiple form items", async ({
 313 |       initTestBed,
 314 |       page,
 315 |       createFormItemDriver,
 316 |       createTextBoxDriver,
 317 |     }) => {
 318 |       await initTestBed(`
 319 |         <Form hideButtonRowUntilDirty="true">
 320 |           <FormItem label="Name" bindTo="name" testId="nameField" />
 321 |           <FormItem label="Email" bindTo="email" testId="emailField" />
 322 |         </Form>
 323 |       `);
 324 | 
 325 |       // Initially hidden
 326 |       await expect(page.getByRole("button", { name: "Save" })).not.toBeVisible();
 327 | 
 328 |       // Modify second field
 329 |       const emailDriver = await createFormItemDriver("emailField");
 330 |       const emailInput = await createTextBoxDriver(emailDriver.input);
 331 |       await emailInput.field.fill("[email protected]");
 332 | 
 333 |       // Now visible
 334 |       await expect(page.getByRole("button", { name: "Save" })).toBeVisible();
 335 |     });
 336 | 
 337 |     test("hides custom button row template until dirty", async ({
 338 |       initTestBed,
 339 |       page,
 340 |       createFormItemDriver,
 341 |       createTextBoxDriver,
 342 |     }) => {
 343 |       await initTestBed(`
 344 |         <Form hideButtonRowUntilDirty="true">
 345 |           <FormItem label="Name" bindTo="name" testId="nameField" />
 346 |           <property name="buttonRowTemplate">
 347 |             <Button label="Custom Save" type="submit" testId="customSave" />
 348 |           </property>
 349 |         </Form>
 350 |       `);
 351 | 
 352 |       // Initially hidden
 353 |       await expect(page.getByTestId("customSave")).not.toBeVisible();
 354 | 
 355 |       // Make form dirty
 356 |       const driver = await createFormItemDriver("nameField");
 357 |       const input = await createTextBoxDriver(driver.input);
 358 |       await input.field.fill("John");
 359 | 
 360 |       // Now visible
 361 |       await expect(page.getByTestId("customSave")).toBeVisible();
 362 |     });
 363 | 
 364 |     test("handles null value gracefully", async ({ initTestBed, page }) => {
 365 |       await initTestBed(`
 366 |         <Form hideButtonRowUntilDirty="{null}">
 367 |           <FormItem label="Name" bindTo="name" testId="nameField" />
 368 |         </Form>
 369 |       `);
 370 | 
 371 |       // Should show button row (default behavior)
 372 |       await expect(page.getByRole("button", { name: "Cancel" })).toBeVisible();
 373 |       await expect(page.getByRole("button", { name: "Save" })).toBeVisible();
 374 |     });
 375 | 
 376 |     test("handles undefined value gracefully", async ({ initTestBed, page }) => {
 377 |       await initTestBed(`
 378 |         <Form hideButtonRowUntilDirty="{undefined}">
 379 |           <FormItem label="Name" bindTo="name" testId="nameField" />
 380 |         </Form>
 381 |       `);
 382 | 
 383 |       // Should show button row (default behavior)
 384 |       await expect(page.getByRole("button", { name: "Cancel" })).toBeVisible();
 385 |       await expect(page.getByRole("button", { name: "Save" })).toBeVisible();
 386 |     });
 387 | 
 388 |     test("works with form initialized with data", async ({
 389 |       initTestBed,
 390 |       page,
 391 |       createFormItemDriver,
 392 |       createTextBoxDriver,
 393 |     }) => {
 394 |       await initTestBed(`
 395 |         <Form hideButtonRowUntilDirty="true" data="{{ name: 'Initial' }}">
 396 |           <FormItem label="Name" bindTo="name" testId="nameField" />
 397 |         </Form>
 398 |       `);
 399 | 
 400 |       // Initially hidden (form has data but is not dirty)
 401 |       await expect(page.getByRole("button", { name: "Save" })).not.toBeVisible();
 402 | 
 403 |       // Make form dirty
 404 |       const driver = await createFormItemDriver("nameField");
 405 |       const input = await createTextBoxDriver(driver.input);
 406 |       await input.field.fill("Modified");
 407 | 
 408 |       // Now visible
 409 |       await expect(page.getByRole("button", { name: "Save" })).toBeVisible();
 410 |     });
 411 | 
 412 |     test("button row appears when checkbox is checked", async ({ initTestBed, page }) => {
 413 |       await initTestBed(`
 414 |         <Form hideButtonRowUntilDirty="true">
 415 |           <FormItem label="Accept Terms" bindTo="terms" type="checkbox" />
 416 |         </Form>
 417 |       `);
 418 | 
 419 |       // Initially hidden
 420 |       await expect(page.getByRole("button", { name: "Save" })).not.toBeVisible();
 421 | 
 422 |       // Check the checkbox
 423 |       const checkbox = page.getByRole("checkbox");
 424 |       await checkbox.check();
 425 | 
 426 |       // Now visible
 427 |       await expect(page.getByRole("button", { name: "Save" })).toBeVisible();
 428 |     });
 429 | 
 430 |     test("button row appears when slider value changes", async ({ initTestBed, page }) => {
 431 |       await initTestBed(`
 432 |         <Form hideButtonRowUntilDirty="true">
 433 |           <FormItem label="Volume" bindTo="volume" type="slider" testId="volumeField" />
 434 |         </Form>
 435 |       `);
 436 | 
 437 |       // Initially hidden
 438 |       await expect(page.getByRole("button", { name: "Save" })).not.toBeVisible();
 439 | 
 440 |       // Move the slider using keyboard
 441 |       const slider = page.getByRole("slider");
 442 |       await slider.press("ArrowRight");
 443 | 
 444 |       // Now visible
 445 |       await expect(page.getByRole("button", { name: "Save" })).toBeVisible();
 446 |     });
 447 |   });
 448 | 
 449 |   // =============================================================================
 450 |   // ENABLE SUBMIT PROPERTY TESTS
 451 |   // =============================================================================
 452 | 
 453 |   test.describe("enableSubmit property", () => {
 454 |     test("disables submit button when set to false", async ({ initTestBed, page }) => {
 455 |       await initTestBed(`<Form enableSubmit="false"/>`);
 456 | 
 457 |       const saveButton = page.getByRole("button", { name: "Save" });
 458 |       await expect(saveButton).toBeVisible();
 459 |       await expect(saveButton).toBeDisabled();
 460 |     });
 461 | 
 462 |     test("enables submit button when set to true", async ({ initTestBed, page }) => {
 463 |       await initTestBed(`<Form enableSubmit="true"/>`);
 464 | 
 465 |       const saveButton = page.getByRole("button", { name: "Save" });
 466 |       await expect(saveButton).toBeVisible();
 467 |       await expect(saveButton).toBeEnabled();
 468 |     });
 469 | 
 470 |     test("submit button is enabled by default when property not set", async ({
 471 |       initTestBed,
 472 |       page,
 473 |     }) => {
 474 |       await initTestBed(`<Form/>`);
 475 | 
 476 |       const saveButton = page.getByRole("button", { name: "Save" });
 477 |       await expect(saveButton).toBeEnabled();
 478 |     });
 479 | 
 480 |     test("prevents form submission when set to false", async ({ initTestBed, page }) => {
 481 |       const { testStateDriver } = await initTestBed(`
 482 |         <Form enableSubmit="false" onSubmit="arg => testState = arg">
 483 |           <FormItem label="Name" bindTo="name" testId="nameField" />
 484 |         </Form>
 485 |       `);
 486 | 
 487 |       const saveButton = page.getByRole("button", { name: "Save" });
 488 |       await expect(saveButton).toBeDisabled();
 489 | 
 490 |       // Verify form does not submit (button is disabled, so click won't work)
 491 |       await saveButton.click({ force: true }); // Force click on disabled button
 492 | 
 493 |       // testState should remain null since submit was prevented
 494 |       await expect.poll(testStateDriver.testState).toBeNull();
 495 |     });
 496 | 
 497 |     test("allows form submission when set to true", async ({
 498 |       initTestBed,
 499 |       page,
 500 |       createFormItemDriver,
 501 |       createTextBoxDriver,
 502 |     }) => {
 503 |       const { testStateDriver } = await initTestBed(`
 504 |         <Form enableSubmit="true" onSubmit="arg => testState = arg">
 505 |           <FormItem label="Name" bindTo="name" testId="nameField" />
 506 |         </Form>
 507 |       `);
 508 | 
 509 |       const driver = await createFormItemDriver("nameField");
 510 |       const input = await createTextBoxDriver(driver.input);
 511 |       await input.field.fill("John Doe");
 512 | 
 513 |       const saveButton = page.getByRole("button", { name: "Save" });
 514 |       await expect(saveButton).toBeEnabled();
 515 |       await saveButton.click();
 516 | 
 517 |       const submittedData = await testStateDriver.testState();
 518 |       expect(submittedData).toEqual({ name: "John Doe" });
 519 |     });
 520 | 
 521 |     test("handles null value gracefully (defaults to enabled)", async ({ initTestBed, page }) => {
 522 |       await initTestBed(`<Form enableSubmit="{null}"/>`);
 523 | 
 524 |       const saveButton = page.getByRole("button", { name: "Save" });
 525 |       await expect(saveButton).toBeEnabled();
 526 |     });
 527 | 
 528 |     test("handles string 'true' value", async ({ initTestBed, page }) => {
 529 |       await initTestBed(`<Form enableSubmit="true"/>`);
 530 | 
 531 |       const saveButton = page.getByRole("button", { name: "Save" });
 532 |       await expect(saveButton).toBeEnabled();
 533 |     });
 534 | 
 535 |     test("handles string 'false' value", async ({ initTestBed, page }) => {
 536 |       await initTestBed(`<Form enableSubmit="false"/>`);
 537 | 
 538 |       const saveButton = page.getByRole("button", { name: "Save" });
 539 |       await expect(saveButton).toBeDisabled();
 540 |     });
 541 | 
 542 |     test("does not affect cancel button", async ({ initTestBed, page }) => {
 543 |       await initTestBed(`<Form enableSubmit="false"/>`);
 544 | 
 545 |       const cancelButton = page.getByRole("button", { name: "Cancel" });
 546 |       await expect(cancelButton).toBeEnabled();
 547 |     });
 548 | 
 549 |     test("works with custom submit button label", async ({ initTestBed, page }) => {
 550 |       await initTestBed(`<Form enableSubmit="false" saveLabel="Submit Now"/>`);
 551 | 
 552 |       const submitButton = page.getByRole("button", { name: "Submit Now" });
 553 |       await expect(submitButton).toBeDisabled();
 554 |     });
 555 | 
 556 |     test("works together with form disabled state", async ({ initTestBed, page }) => {
 557 |       await initTestBed(`<Form enabled="false" enableSubmit="true"/>`);
 558 | 
 559 |       const saveButton = page.getByRole("button", { name: "Save" });
 560 |       // Form disabled takes precedence
 561 |       await expect(saveButton).toBeDisabled();
 562 |     });
 563 |   });
 564 | 
 565 |   // =============================================================================
 566 |   // DATA PROPERTY TESTS
 567 |   // =============================================================================
 568 | 
 569 |   test.describe("data property", () => {
 570 |     test("sets initial form data", async ({
 571 |       initTestBed,
 572 |       createFormItemDriver,
 573 |       createTextBoxDriver,
 574 |     }) => {
 575 |       await initTestBed(`
 576 |         <Form data="{{ name: 'John', age: 30 }}">
 577 |           <FormItem label="Name" bindTo="name" testId="nameField" />
 578 |           <FormItem label="Age" bindTo="age" type="integer" testId="ageField" />
 579 |         </Form>
 580 |       `);
 581 | 
 582 |       const nameDriver = await createFormItemDriver("nameField");
 583 |       const nameInput = await createTextBoxDriver(nameDriver.input);
 584 |       const ageDriver = await createFormItemDriver("ageField");
 585 |       const ageInput = await createTextBoxDriver(ageDriver.input);
 586 | 
 587 |       await expect(nameInput.field).toHaveValue("John");
 588 |       await expect(ageInput.field).toHaveValue("30");
 589 |     });
 590 | 
 591 |     test("handles null data gracefully", async ({ initTestBed, createFormDriver }) => {
 592 |       await initTestBed(`<Form data="{null}" testId="form"/>`);
 593 |       const driver = await createFormDriver("form");
 594 |       await expect(driver.component).toBeVisible();
 595 |     });
 596 | 
 597 |     test("handles undefined data gracefully", async ({ initTestBed, createFormDriver }) => {
 598 |       await initTestBed(`<Form data="{undefined}" testId="form"/>`);
 599 |       const driver = await createFormDriver("form");
 600 |       await expect(driver.component).toBeVisible();
 601 |     });
 602 | 
 603 |     test("handles empty object data", async ({ initTestBed, createFormDriver }) => {
 604 |       await initTestBed(`<Form data="{{}}" testId="form"/>`);
 605 |       const driver = await createFormDriver("form");
 606 |       await expect(driver.component).toBeVisible();
 607 |     });
 608 |   });
 609 | 
 610 |   // =============================================================================
 611 |   // ITEM LABEL POSITION TESTS
 612 |   // =============================================================================
 613 | 
 614 |   test.describe("itemLabelPosition property", () => {
 615 |     labelPositionValues.forEach((position) => {
 616 |       test(`sets item label position to ${position}`, async ({
 617 |         initTestBed,
 618 |         createFormItemDriver,
 619 |       }) => {
 620 |         await initTestBed(`
 621 |           <Form itemLabelPosition="${position}">
 622 |             <FormItem label="Test Label" bindTo="test" testId="testField" />
 623 |           </Form>
 624 |         `);
 625 | 
 626 |         const driver = await createFormItemDriver("testField");
 627 |         await expect(driver.label).toBeVisible();
 628 |       });
 629 |     });
 630 | 
 631 |     test("handles invalid itemLabelPosition gracefully", async ({
 632 |       initTestBed,
 633 |       createFormDriver,
 634 |     }) => {
 635 |       await initTestBed(`<Form itemLabelPosition="invalid" testId="form"/>`);
 636 |       const driver = await createFormDriver("form");
 637 |       await expect(driver.component).toBeVisible();
 638 |     });
 639 |   });
 640 | 
 641 |   // =============================================================================
 642 |   // ITEM LABEL WIDTH TESTS
 643 |   // =============================================================================
 644 | 
 645 |   test.describe("itemLabelWidth property", () => {
 646 |     test("sets custom label width", async ({ initTestBed, createFormItemDriver }) => {
 647 |       await initTestBed(`
 648 |         <Form itemLabelWidth="200px">
 649 |           <FormItem label="Test Label" bindTo="test" testId="testField" />
 650 |         </Form>
 651 |       `);
 652 | 
 653 |       const driver = await createFormItemDriver("testField");
 654 |       await expect(driver.label).toBeVisible();
 655 |     });
 656 | 
 657 |     test("handles numeric label width", async ({ initTestBed, createFormItemDriver }) => {
 658 |       await initTestBed(`
 659 |         <Form itemLabelWidth="150">
 660 |           <FormItem label="Test Label" bindTo="test" testId="testField" />
 661 |         </Form>
 662 |       `);
 663 | 
 664 |       const driver = await createFormItemDriver("testField");
 665 |       await expect(driver.label).toBeVisible();
 666 |     });
 667 | 
 668 |     test("handles invalid label width gracefully", async ({ initTestBed, createFormDriver }) => {
 669 |       await initTestBed(`<Form itemLabelWidth="invalid" testId="form"/>`);
 670 |       const driver = await createFormDriver("form");
 671 |       await expect(driver.component).toBeVisible();
 672 |     });
 673 | 
 674 |     test("handles theme variable", async ({ initTestBed, createFormItemDriver }) => {
 675 |       const spaceBase = 0.25; //rem
 676 |       const labelSize = 10;
 677 |       const widthInPx = labelSize * spaceBase * 16; //px
 678 |       await initTestBed(`
 679 |         <Theme space-base="${spaceBase}rem">
 680 |           <Form itemLabelWidth="$space-${labelSize}">
 681 |             <FormItem label="Test Label" bindTo="test" testId="testField" />
 682 |           </Form>
 683 |         </Theme>
 684 |       `);
 685 |       const driver = await createFormItemDriver("testField");
 686 |       await expect(driver.label).toHaveCSS("width", widthInPx + "px");
 687 |     });
 688 |   });
 689 | 
 690 |   // =============================================================================
 691 |   // ITEM LABEL BREAK TESTS
 692 |   // =============================================================================
 693 | 
 694 |   test.describe("itemLabelBreak property", () => {
 695 |     test("enables label line breaking", async ({ initTestBed, createFormItemDriver }) => {
 696 |       await initTestBed(`
 697 |         <Form itemLabelBreak="true">
 698 |           <FormItem label="Very Long Label That Should Break" bindTo="test" testId="testField" />
 699 |         </Form>
 700 |       `);
 701 | 
 702 |       const driver = await createFormItemDriver("testField");
 703 |       await expect(driver.label).toBeVisible();
 704 |     });
 705 | 
 706 |     test("disables label line breaking", async ({ initTestBed, createFormItemDriver }) => {
 707 |       await initTestBed(`
 708 |         <Form itemLabelBreak="false">
 709 |           <FormItem label="Very Long Label That Should Not Break" bindTo="test" testId="testField" />
 710 |         </Form>
 711 |       `);
 712 | 
 713 |       const driver = await createFormItemDriver("testField");
 714 |       await expect(driver.label).toBeVisible();
 715 |     });
 716 |   });
 717 | 
 718 |   // =============================================================================
 719 |   // ENABLED PROPERTY TESTS
 720 |   // =============================================================================
 721 | 
 722 |   test.describe("enabled property", () => {
 723 |     test("disables save button when enabled is false", async ({ initTestBed, page }) => {
 724 |       await initTestBed(`
 725 |         <Form enabled="false">
 726 |           <FormItem label="Test" bindTo="test" />
 727 |         </Form>
 728 |       `);
 729 | 
 730 |       const saveButton = page.getByRole("button", { name: "Save" });
 731 |       await expect(saveButton).toBeDisabled();
 732 |     });
 733 | 
 734 |     test("enables form when enabled is true", async ({ initTestBed, page }) => {
 735 |       await initTestBed(`
 736 |         <Form enabled="true">
 737 |           <FormItem label="Test" bindTo="test" />
 738 |         </Form>
 739 |       `);
 740 | 
 741 |       const saveButton = page.getByRole("button", { name: "Save" });
 742 |       const cancelButton = page.getByRole("button", { name: "Cancel" });
 743 | 
 744 |       await expect(saveButton).toBeEnabled();
 745 |       await expect(cancelButton).toBeEnabled();
 746 |     });
 747 |   });
 748 | 
 749 |   // =============================================================================
 750 |   // BUTTON ROW TEMPLATE TESTS
 751 |   // =============================================================================
 752 | 
 753 |   test.describe("buttonRowTemplate property", () => {
 754 |     test("supports custom button row template", async ({ initTestBed, page }) => {
 755 |       await initTestBed(`
 756 |         <Form>
 757 |           <FormItem label="Test" bindTo="test" />
 758 |           <property name="buttonRowTemplate">
 759 |             <Button label="Custom Save" type="submit" />
 760 |             <Button label="Custom Cancel" type="button" />
 761 |           </property>
 762 |         </Form>
 763 |       `);
 764 | 
 765 |       await expect(page.getByRole("button", { name: "Custom Save" })).toBeVisible();
 766 |       await expect(page.getByRole("button", { name: "Custom Cancel" })).toBeVisible();
 767 |     });
 768 |   });
 769 | 
 770 |   // =============================================================================
 771 |   // EVENT TESTS
 772 |   // =============================================================================
 773 | 
 774 |   test.describe("Events", () => {
 775 |     test("onSubmit event fires with form data", async ({ initTestBed, page }) => {
 776 |       const { testStateDriver } = await initTestBed(`
 777 |         <Form data="{{ name: 'John', email: '[email protected]' }}" onSubmit="data => testState = data">
 778 |           <FormItem label="Name" bindTo="name" />
 779 |           <FormItem label="Email" bindTo="email" />
 780 |         </Form>
 781 |       `);
 782 | 
 783 |       await page.getByRole("button", { name: "Save" }).click();
 784 | 
 785 |       await expect.poll(testStateDriver.testState).toEqual({
 786 |         name: "John",
 787 |         email: "[email protected]",
 788 |       });
 789 |     });
 790 | 
 791 |     test("onCancel event fires when cancel button clicked", async ({ initTestBed, page }) => {
 792 |       const { testStateDriver } = await initTestBed(`
 793 |         <Form onCancel="testState = 'cancelled'">
 794 |           <FormItem label="Test" bindTo="test" />
 795 |         </Form>
 796 |       `);
 797 | 
 798 |       await page.getByRole("button", { name: "Cancel" }).click();
 799 | 
 800 |       await expect.poll(testStateDriver.testState).toEqual("cancelled");
 801 |     });
 802 | 
 803 |     test("onSuccess event fires on successful submission", async ({
 804 |       initTestBed,
 805 |       page,
 806 |       createFormDriver,
 807 |     }) => {
 808 |       const { testStateDriver } = await initTestBed(
 809 |         `
 810 |         <Form
 811 |           testId="form"
 812 |           submitUrl="/test-success"
 813 |           onSuccess="testState = 'success'; console.log('Submitted successfully')"
 814 |           data="{{ name: 'Test' }}">
 815 |           <FormItem label="Name" bindTo="name" />
 816 |         </Form>
 817 |       `,
 818 |         {
 819 |           apiInterceptor: {
 820 |             operations: {
 821 |               testSuccess: {
 822 |                 url: "/test-success",
 823 |                 method: "put",
 824 |                 handler: `return { success: true };`,
 825 |               },
 826 |             },
 827 |           },
 828 |         },
 829 |       );
 830 | 
 831 |       const driver = await createFormDriver("form");
 832 |       await driver.submitForm();
 833 |       await expect.poll(testStateDriver.testState).toEqual("success");
 834 |     });
 835 | 
 836 |     test("onReset event fires when form is reset", async ({ initTestBed, page }) => {
 837 |       const { testStateDriver } = await initTestBed(`
 838 |         <Form
 839 |           id="testForm"
 840 |           onReset="testState = 'reset'"
 841 |           data="{{ name: 'Test' }}">
 842 |           <FormItem label="Name" bindTo="name" />
 843 |           <Button onClick="testForm.reset()" label="Reset Form" />
 844 |         </Form>
 845 |       `);
 846 | 
 847 |       await page.getByRole("button", { name: "Reset Form" }).click();
 848 | 
 849 |       await expect.poll(testStateDriver.testState).toEqual("reset");
 850 |     });
 851 | 
 852 |     test("onWillSubmit allows submission when returning null", async ({ initTestBed, page }) => {
 853 |       const { testStateDriver } = await initTestBed(`
 854 |         <Form 
 855 |           data="{{ name: 'John' }}"
 856 |           onWillSubmit="data => null"
 857 |           onSubmit="data => testState = data">
 858 |           <FormItem label="Name" bindTo="name" />
 859 |         </Form>
 860 |       `);
 861 | 
 862 |       await page.getByRole("button", { name: "Save" }).click();
 863 | 
 864 |       await expect.poll(testStateDriver.testState).toEqual({ name: "John" });
 865 |     });
 866 | 
 867 |     test("onWillSubmit allows submission when returning undefined", async ({
 868 |       initTestBed,
 869 |       page,
 870 |     }) => {
 871 |       const { testStateDriver } = await initTestBed(`
 872 |         <Form 
 873 |           data="{{ name: 'Jane' }}"
 874 |           onWillSubmit="data => undefined"
 875 |           onSubmit="data => testState = data">
 876 |           <FormItem label="Name" bindTo="name" />
 877 |         </Form>
 878 |       `);
 879 | 
 880 |       await page.getByRole("button", { name: "Save" }).click();
 881 | 
 882 |       await expect.poll(testStateDriver.testState).toEqual({ name: "Jane" });
 883 |     });
 884 | 
 885 |     test("onWillSubmit allows submission when returning empty string", async ({
 886 |       initTestBed,
 887 |       page,
 888 |     }) => {
 889 |       const { testStateDriver } = await initTestBed(`
 890 |         <Form 
 891 |           data="{{ name: 'Bob' }}"
 892 |           onWillSubmit='data => ""'
 893 |           onSubmit="data => testState = data">
 894 |           <FormItem label="Name" bindTo="name" />
 895 |         </Form>
 896 |       `);
 897 | 
 898 |       await page.getByRole("button", { name: "Save" }).click();
 899 | 
 900 |       await expect.poll(testStateDriver.testState).toEqual({ name: "Bob" });
 901 |     });
 902 | 
 903 |     test("onWillSubmit allows submission when returning true", async ({ initTestBed, page }) => {
 904 |       const { testStateDriver } = await initTestBed(`
 905 |         <Form 
 906 |           data="{{ name: 'Alice' }}"
 907 |           onWillSubmit="data => true"
 908 |           onSubmit="data => testState = data">
 909 |           <FormItem label="Name" bindTo="name" />
 910 |         </Form>
 911 |       `);
 912 | 
 913 |       await page.getByRole("button", { name: "Save" }).click();
 914 | 
 915 |       await expect.poll(testStateDriver.testState).toEqual({ name: "Alice" });
 916 |     });
 917 | 
 918 |     test("onWillSubmit allows submission when returning a number", async ({
 919 |       initTestBed,
 920 |       page,
 921 |     }) => {
 922 |       const { testStateDriver } = await initTestBed(`
 923 |         <Form 
 924 |           data="{{ name: 'Charlie' }}"
 925 |           onWillSubmit="data => 42"
 926 |           onSubmit="data => testState = data">
 927 |           <FormItem label="Name" bindTo="name" />
 928 |         </Form>
 929 |       `);
 930 | 
 931 |       await page.getByRole("button", { name: "Save" }).click();
 932 | 
 933 |       await expect.poll(testStateDriver.testState).toEqual({ name: "Charlie" });
 934 |     });
 935 | 
 936 |     test("onWillSubmit cancels submission when returning false", async ({ initTestBed, page }) => {
 937 |       const { testStateDriver } = await initTestBed(`
 938 |         <Form 
 939 |           data="{{ name: 'Test' }}"
 940 |           onWillSubmit="data => false"
 941 |           onSubmit="data => testState = 'submitted'">
 942 |           <FormItem label="Name" bindTo="name" />
 943 |         </Form>
 944 |       `);
 945 | 
 946 |       await page.getByRole("button", { name: "Save" }).click();
 947 | 
 948 |       // Wait a bit to ensure submission doesn't happen
 949 |       await page.waitForTimeout(200);
 950 |       await expect.poll(testStateDriver.testState).toEqual(null);
 951 |     });
 952 | 
 953 |     test("onWillSubmit submits modified data when returning a plain object", async ({
 954 |       initTestBed,
 955 |       page,
 956 |     }) => {
 957 |       const { testStateDriver } = await initTestBed(`
 958 |         <Form 
 959 |           data="{{ name: 'Original', email: '[email protected]' }}"
 960 |           onWillSubmit="data => ({ name: 'Modified', email: '[email protected]', extra: 'added' })"
 961 |           onSubmit="data => testState = data">
 962 |           <FormItem label="Name" bindTo="name" />
 963 |           <FormItem label="Email" bindTo="email" />
 964 |         </Form>
 965 |       `);
 966 | 
 967 |       await page.getByRole("button", { name: "Save" }).click();
 968 | 
 969 |       await expect.poll(testStateDriver.testState).toEqual({
 970 |         name: "Modified",
 971 |         email: "[email protected]",
 972 |         extra: "added",
 973 |       });
 974 |     });
 975 | 
 976 |     test("onWillSubmit can add fields to submission data", async ({ initTestBed, page }) => {
 977 |       const { testStateDriver } = await initTestBed(`
 978 |         <Form 
 979 |           data="{{ name: 'User' }}"
 980 |           onWillSubmit="data => ({ name: data.name, timestamp: 1234567890, source: 'web' })"
 981 |           onSubmit="data => testState = data">
 982 |           <FormItem label="Name" bindTo="name" />
 983 |         </Form>
 984 |       `);
 985 | 
 986 |       await page.getByRole("button", { name: "Save" }).click();
 987 | 
 988 |       await expect.poll(testStateDriver.testState).toEqual({
 989 |         name: "User",
 990 |         timestamp: 1234567890,
 991 |         source: "web",
 992 |       });
 993 |     });
 994 | 
 995 |     test("onWillSubmit can remove fields from submission data", async ({ initTestBed, page }) => {
 996 |       const { testStateDriver } = await initTestBed(`
 997 |         <Form 
 998 |           data="{{ name: 'User', password: 'secret', email: '[email protected]' }}"
 999 |           onWillSubmit="data => ({ name: data.name, email: data.email })"
1000 |           onSubmit="data => testState = data">
1001 |           <FormItem label="Name" bindTo="name" />
1002 |           <FormItem label="Password" bindTo="password" />
1003 |           <FormItem label="Email" bindTo="email" />
1004 |         </Form>
1005 |       `);
1006 | 
1007 |       await page.getByRole("button", { name: "Save" }).click();
1008 | 
1009 |       await expect.poll(testStateDriver.testState).toEqual({
1010 |         name: "User",
1011 |         email: "[email protected]",
1012 |       });
1013 |     });
1014 | 
1015 |     test("onWillSubmit does not submit array when returned", async ({ initTestBed, page }) => {
1016 |       const { testStateDriver } = await initTestBed(`
1017 |         <Form 
1018 |           data="{{ name: 'Test' }}"
1019 |           onWillSubmit="data => ['array', 'value']"
1020 |           onSubmit="data => testState = data">
1021 |           <FormItem label="Name" bindTo="name" />
1022 |         </Form>
1023 |       `);
1024 | 
1025 |       await page.getByRole("button", { name: "Save" }).click();
1026 | 
1027 |       // Array is treated as non-object, so original data should be submitted
1028 |       await expect.poll(testStateDriver.testState).toEqual({ name: "Test" });
1029 |     });
1030 | 
1031 |     test("onWillSubmit with complex object transformation", async ({ initTestBed, page }) => {
1032 |       const { testStateDriver } = await initTestBed(`
1033 |         <Form 
1034 |           data="{{ firstName: 'John', lastName: 'Doe', age: 30 }}"
1035 |           onWillSubmit="data => ({ fullName: data.firstName + ' ' + data.lastName, age: data.age })"
1036 |           onSubmit="data => testState = data">
1037 |           <FormItem label="First Name" bindTo="firstName" />
1038 |           <FormItem label="Last Name" bindTo="lastName" />
1039 |           <FormItem label="Age" bindTo="age" type="integer" />
1040 |         </Form>
1041 |       `);
1042 | 
1043 |       await page.getByRole("button", { name: "Save" }).click();
1044 | 
1045 |       await expect.poll(testStateDriver.testState).toEqual({
1046 |         fullName: "John Doe",
1047 |         age: 30,
1048 |       });
1049 |     });
1050 |   });
1051 | 
1052 |   // =============================================================================
1053 |   // API TESTS
1054 |   // =============================================================================
1055 | 
1056 |   test.describe("APIs", () => {
1057 |     test("update method updates form data", async ({
1058 |       initTestBed,
1059 |       page,
1060 |       createFormItemDriver,
1061 |       createTextBoxDriver,
1062 |     }) => {
1063 |       await initTestBed(`
1064 |         <Form
1065 |           id="testForm"
1066 |           data="{{ name: 'Original', age: 25 }}">
1067 |           <FormItem label="Name" bindTo="name" testId="nameField" />
1068 |           <FormItem label="Age" bindTo="age" type="integer" testId="ageField" />
1069 |           <Button onClick="testForm.update({ name: 'Updated', age: 30 })" label="Update" />
1070 |         </Form>
1071 |       `);
1072 | 
1073 |       const nameDriver = await createFormItemDriver("nameField");
1074 |       const nameInput = await createTextBoxDriver(nameDriver.input);
1075 |       const ageDriver = await createFormItemDriver("ageField");
1076 |       const ageInput = await createTextBoxDriver(ageDriver.input);
1077 | 
1078 |       await expect(nameInput.field).toHaveValue("Original");
1079 |       await expect(ageInput.field).toHaveValue("25");
1080 | 
1081 |       await page.getByRole("button", { name: "Update" }).click();
1082 | 
1083 |       await expect(nameInput.field).toHaveValue("Updated");
1084 |       await expect(ageInput.field).toHaveValue("30");
1085 |     });
1086 | 
1087 |     test("reset method resets form to initial state", async ({
1088 |       initTestBed,
1089 |       page,
1090 |       createFormItemDriver,
1091 |       createTextBoxDriver,
1092 |     }) => {
1093 |       await initTestBed(`
1094 |         <Form
1095 |           id="testForm"
1096 |           data="{{ name: 'Initial' }}">
1097 |           <FormItem label="Name" bindTo="name" testId="nameField" />
1098 |           <Button onClick="testForm.reset()" label="Reset" />
1099 |         </Form>
1100 |       `);
1101 | 
1102 |       const nameDriver = await createFormItemDriver("nameField");
1103 |       const nameInput = await createTextBoxDriver(nameDriver.input);
1104 | 
1105 |       // Change the input value
1106 |       await nameInput.field.fill("Changed");
1107 |       await expect(nameInput.field).toHaveValue("Changed");
1108 | 
1109 |       // Reset the form
1110 |       await page.getByRole("button", { name: "Reset" }).click();
1111 | 
1112 |       await expect(nameInput.field).toHaveValue("Initial");
1113 |     });
1114 | 
1115 |     test("validate method returns validation results without submitting", async ({
1116 |       initTestBed,
1117 |       page,
1118 |       createFormItemDriver,
1119 |       createTextBoxDriver,
1120 |     }) => {
1121 |       const { testStateDriver } = await initTestBed(`
1122 |         <Form id="testForm">
1123 |           <FormItem label="Name" bindTo="name" required="true" testId="nameField" />
1124 |           <FormItem label="Email" bindTo="email" testId="emailField" />
1125 |           <Button onClick="testState = testForm.validate()" label="Validate" testId="validateBtn" />
1126 |         </Form>
1127 |       `);
1128 | 
1129 |       // Click validate button without filling required field
1130 |       await page.getByTestId("validateBtn").click();
1131 | 
1132 |       // Wait for validation to complete
1133 |       await page.waitForTimeout(100);
1134 | 
1135 |       const result = await testStateDriver.testState();
1136 |       expect(result).toBeTruthy();
1137 |       expect(result.isValid).toBe(false);
1138 |       expect(result.errors).toBeDefined();
1139 |       expect(result.errors.length).toBeGreaterThan(0);
1140 |     });
1141 | 
1142 |     test("validate method returns isValid true when all validations pass", async ({
1143 |       initTestBed,
1144 |       page,
1145 |       createFormItemDriver,
1146 |       createTextBoxDriver,
1147 |     }) => {
1148 |       const { testStateDriver } = await initTestBed(`
1149 |         <Form id="testForm">
1150 |           <FormItem label="Name" bindTo="name" required="true" testId="nameField" />
1151 |           <Button onClick="testState = testForm.validate()" label="Validate" testId="validateBtn" />
1152 |         </Form>
1153 |       `);
1154 | 
1155 |       // Fill the required field
1156 |       const nameDriver = await createFormItemDriver("nameField");
1157 |       const nameInput = await createTextBoxDriver(nameDriver.input);
1158 |       await nameInput.field.fill("John Doe");
1159 | 
1160 |       // Click validate button
1161 |       await page.getByTestId("validateBtn").click();
1162 | 
1163 |       // Wait for validation to complete
1164 |       await page.waitForTimeout(100);
1165 | 
1166 |       const result = await testStateDriver.testState();
1167 |       expect(result).toBeTruthy();
1168 |       expect(result.isValid).toBe(true);
1169 |       expect(result.errors.length).toBe(0);
1170 |     });
1171 | 
1172 |     test("validate method returns cleaned form data", async ({
1173 |       initTestBed,
1174 |       page,
1175 |       createFormItemDriver,
1176 |       createTextBoxDriver,
1177 |     }) => {
1178 |       const { testStateDriver } = await initTestBed(`
1179 |         <Form id="testForm">
1180 |           <FormItem label="Name" bindTo="name" testId="nameField" />
1181 |           <FormItem label="Age" bindTo="age" type="integer" testId="ageField" />
1182 |           <Button onClick="testState = testForm.validate()" label="Validate" testId="validateBtn" />
1183 |         </Form>
1184 |       `);
1185 | 
1186 |       // Fill form fields
1187 |       const nameDriver = await createFormItemDriver("nameField");
1188 |       const nameInput = await createTextBoxDriver(nameDriver.input);
1189 |       await nameInput.field.fill("John Doe");
1190 | 
1191 |       const ageDriver = await createFormItemDriver("ageField");
1192 |       const ageInput = await createTextBoxDriver(ageDriver.input);
1193 |       await ageInput.field.fill("30");
1194 | 
1195 |       // Click validate button
1196 |       await page.getByTestId("validateBtn").click();
1197 | 
1198 |       // Wait for validation to complete
1199 |       await page.waitForTimeout(100);
1200 | 
1201 |       const result = await testStateDriver.testState();
1202 |       expect(result).toBeTruthy();
1203 |       expect(result.data).toEqual({ name: "John Doe", age: 30 });
1204 |     });
1205 | 
1206 |     test("validate method displays validation errors on form", async ({
1207 |       initTestBed,
1208 |       page,
1209 |       createFormItemDriver,
1210 |     }) => {
1211 |       await initTestBed(`
1212 |         <Form id="testForm">
1213 |           <FormItem label="Name" bindTo="name" required="true" testId="nameField" />
1214 |           <Button onClick="testForm.validate()" label="Validate" testId="validateBtn" />
1215 |         </Form>
1216 |       `);
1217 | 
1218 |       // Click validate without filling required field
1219 |       await page.getByTestId("validateBtn").click();
1220 | 
1221 |       // Validation error should be displayed
1222 |       const nameField = page.getByTestId("nameField");
1223 |       await expect(nameField).toContainText("This field is required");
1224 |     });
1225 | 
1226 |     test("validate method does not trigger form submission", async ({
1227 |       initTestBed,
1228 |       page,
1229 |       createFormItemDriver,
1230 |       createTextBoxDriver,
1231 |     }) => {
1232 |       const { testStateDriver } = await initTestBed(`
1233 |         <Form id="testForm" onSubmit="testState = 'submitted'">
1234 |           <FormItem label="Name" bindTo="name" testId="nameField" />
1235 |           <Button onClick="testForm.validate()" label="Validate" testId="validateBtn" />
1236 |         </Form>
1237 |       `);
1238 | 
1239 |       // Fill form
1240 |       const nameDriver = await createFormItemDriver("nameField");
1241 |       const nameInput = await createTextBoxDriver(nameDriver.input);
1242 |       await nameInput.field.fill("John");
1243 | 
1244 |       // Click validate button
1245 |       await page.getByTestId("validateBtn").click();
1246 | 
1247 |       // Wait a bit
1248 |       await page.waitForTimeout(200);
1249 | 
1250 |       // testState should remain null (not 'submitted')
1251 |       await expect.poll(testStateDriver.testState).toBeNull();
1252 |     });
1253 | 
1254 |     test("validate method returns complete validation results object", async ({
1255 |       initTestBed,
1256 |       page,
1257 |       createFormItemDriver,
1258 |       createTextBoxDriver,
1259 |     }) => {
1260 |       const { testStateDriver } = await initTestBed(`
1261 |         <Form id="testForm">
1262 |           <FormItem label="Name" bindTo="name" required="true" testId="nameField" />
1263 |           <FormItem label="Email" bindTo="email" testId="emailField" />
1264 |           <Button onClick="testState = testForm.validate()" label="Validate" testId="validateBtn" />
1265 |         </Form>
1266 |       `);
1267 | 
1268 |       // Fill only email (name is required)
1269 |       const emailDriver = await createFormItemDriver("emailField");
1270 |       const emailInput = await createTextBoxDriver(emailDriver.input);
1271 |       await emailInput.field.fill("[email protected]");
1272 | 
1273 |       // Click validate button
1274 |       await page.getByTestId("validateBtn").click();
1275 | 
1276 |       // Wait for validation to complete
1277 |       await page.waitForTimeout(100);
1278 | 
1279 |       const result = await testStateDriver.testState();
1280 |       expect(result).toBeTruthy();
1281 |       expect(result.isValid).toBeDefined();
1282 |       expect(result.data).toBeDefined();
1283 |       expect(result.errors).toBeDefined();
1284 |       expect(result.warnings).toBeDefined();
1285 |       expect(result.validationResults).toBeDefined();
1286 |     });
1287 |   });
1288 | 
1289 |   // =============================================================================
1290 |   // CONTEXT VARIABLE TESTS
1291 |   // =============================================================================
1292 | 
1293 |   test.describe("Context Variables", () => {
1294 |     test("$data context variable provides access to form data", async ({
1295 |       initTestBed,
1296 |       page,
1297 |       createFormItemDriver,
1298 |     }) => {
1299 |       // This test needs specific FormItem behavior that may vary
1300 |       await initTestBed(`
1301 |         <Form data="{{ isEnabled: true, name: 'Joe' }}">
1302 |           <FormItem testId="isEnabled" label="Enable name" bindTo="isEnabled" type="checkbox" />
1303 |           <FormItem testId="name" enabled="{$data.isEnabled}" label="Name" bindTo="name" />
1304 |         </Form>
1305 |       `);
1306 | 
1307 |       const enableSwitch = (await createFormItemDriver("isEnabled")).checkbox;
1308 |       const nameInput = (await createFormItemDriver("name")).textBox;
1309 | 
1310 |       await expect(enableSwitch).toBeVisible();
1311 |       await expect(nameInput).toBeEnabled();
1312 |       await enableSwitch.click();
1313 |       await expect(nameInput).toBeDisabled();
1314 |     });
1315 | 
1316 |     test("$data.update method updates form data", async ({
1317 |       initTestBed,
1318 |       page,
1319 |       createFormItemDriver,
1320 |     }) => {
1321 |       await initTestBed(`
1322 |         <Form data="{{ counter: 0 }}">
1323 |           <FormItem testId="counter" label="Counter" bindTo="counter" type="integer" />
1324 |           <Button onClick="$data.update({ counter: $data.counter + 1 })" label="Increment" />
1325 |         </Form>
1326 |       `);
1327 | 
1328 |       const counterDriver = await createFormItemDriver("counter");
1329 |       const counterInput = counterDriver.textBox;
1330 |       await expect(counterInput).toHaveValue("0");
1331 | 
1332 |       await page.getByRole("button", { name: "Increment" }).click({ force: true });
1333 | 
1334 |       await expect(counterInput).toHaveValue("1");
1335 |     });
1336 |   });
1337 | 
1338 |   // =============================================================================
1339 |   // SUBMIT URL AND METHOD TESTS
1340 |   // =============================================================================
1341 | 
1342 |   test.describe("Submit URL and Method", () => {
1343 |     test("submits to custom URL with POST method (new date)", async ({
1344 |       initTestBed,
1345 |       createFormDriver,
1346 |     }) => {
1347 |       await initTestBed(
1348 |         `<App><Form testId="form" submitUrl="/custom-endpoint" data="{null}">
1349 |             <FormItem label="Name" bindTo="name" />
1350 |         </Form></App>`,
1351 |         {
1352 |           apiInterceptor: {
1353 |             operations: {
1354 |               customEndpoint: {
1355 |                 url: "/custom-endpoint",
1356 |                 method: "post",
1357 |                 handler: `return { success: true };`,
1358 |               },
1359 |             },
1360 |           },
1361 |         },
1362 |       );
1363 | 
1364 |       const driver = await createFormDriver("form");
1365 |       await driver.submitForm();
1366 | 
1367 |       const response = await driver.getSubmitResponse("/custom-endpoint");
1368 |       expect(response.ok()).toEqual(true);
1369 |     });
1370 | 
1371 |     test("uses PUT method for existing data", async ({ initTestBed, createFormDriver }) => {
1372 |       await initTestBed(
1373 |         `<Form submitUrl="/entities/1" data="{{ id: 1, name: 'Existing' }}">
1374 |           <FormItem label="Name" bindTo="name" />
1375 |          </Form>`,
1376 |         {
1377 |           apiInterceptor: {
1378 |             operations: {
1379 |               updateEntity: {
1380 |                 url: "/entities/1",
1381 |                 method: "put",
1382 |                 handler: `return { success: true };`,
1383 |               },
1384 |             },
1385 |           },
1386 |         },
1387 |       );
1388 | 
1389 |       const driver = await createFormDriver();
1390 |       await driver.submitForm();
1391 | 
1392 |       const response = await driver.getSubmitResponse("/entities/1");
1393 |       expect(response.ok()).toEqual(true);
1394 |     });
1395 | 
1396 |     test("uses custom submit method", async ({ initTestBed, createFormDriver }) => {
1397 |       await initTestBed(
1398 |         `<Form submitUrl="/custom" submitMethod="put" data="{{ name: 'Test' }}">
1399 |           <FormItem label="Name" bindTo="name" />
1400 |          </Form>`,
1401 |         {
1402 |           apiInterceptor: {
1403 |             operations: {
1404 |               putCustom: {
1405 |                 url: "/custom",
1406 |                 method: "put",
1407 |                 handler: `return { success: true };`,
1408 |               },
1409 |             },
1410 |           },
1411 |         },
1412 |       );
1413 | 
1414 |       const driver = await createFormDriver();
1415 |       await driver.submitForm();
1416 | 
1417 |       const response = await driver.getSubmitResponse("/custom");
1418 |       expect(response.ok()).toEqual(true);
1419 |     });
1420 |   });
1421 | });
1422 | 
1423 | // =============================================================================
1424 | // ACCESSIBILITY TESTS
1425 | // =============================================================================
1426 | 
1427 | test.describe("Accessibility", () => {
1428 |   test("form has correct semantic role", async ({ initTestBed, page }) => {
1429 |     await initTestBed(`<Form/>`);
1430 |     await expect(page.locator("form")).toBeVisible();
1431 |   });
1432 | 
1433 |   test("form items are properly associated with labels", async ({
1434 |     initTestBed,
1435 |     createFormItemDriver,
1436 |   }) => {
1437 |     await initTestBed(`
1438 |       <Form>
1439 |         <FormItem label="Full Name" bindTo="name" testId="nameField" />
1440 |       </Form>
1441 |     `);
1442 | 
1443 |     const driver = await createFormItemDriver("nameField");
1444 |     await expect(driver.label).toBeVisible();
1445 |     await expect(driver.label).toHaveText("Full Name");
1446 |   });
1447 | 
1448 |   test("form submission is keyboard accessible", async ({ initTestBed, page }) => {
1449 |     const { testStateDriver } = await initTestBed(`
1450 |       <Form onSubmit="testState = 'submitted via keyboard'">
1451 |         <FormItem label="Name" bindTo="name" />
1452 |       </Form>
1453 |     `);
1454 | 
1455 |     const submitButton = page.getByRole("button", { name: "Save" });
1456 |     await submitButton.focus();
1457 |     await page.keyboard.press("Enter");
1458 | 
1459 |     await expect.poll(testStateDriver.testState).toEqual("submitted via keyboard");
1460 |   });
1461 | 
1462 |   test("form cancel is keyboard accessible", async ({ initTestBed, page }) => {
1463 |     const { testStateDriver } = await initTestBed(`
1464 |       <Form onCancel="testState = 'cancelled via keyboard'">
1465 |         <FormItem label="Name" bindTo="name" />
1466 |       </Form>
1467 |     `);
1468 | 
1469 |     const cancelButton = page.getByRole("button", { name: "Cancel" });
1470 |     await cancelButton.focus();
1471 |     await page.keyboard.press("Enter");
1472 | 
1473 |     await expect.poll(testStateDriver.testState).toEqual("cancelled via keyboard");
1474 |   });
1475 | 
1476 |   test("disabled form buttons are properly disabled", async ({ initTestBed, page }) => {
1477 |     await initTestBed(`
1478 |       <Form enabled="false">
1479 |         <FormItem label="Name" bindTo="name" />
1480 |       </Form>
1481 |     `);
1482 | 
1483 |     const saveButton = page.getByRole("button", { name: "Save" });
1484 |     await expect(saveButton).toBeDisabled();
1485 |   });
1486 | });
1487 | 
1488 | // =============================================================================
1489 | // THEME VARIABLE TESTS
1490 | // =============================================================================
1491 | 
1492 | test.describe("Theme Variables", () => {
1493 |   test("applies custom gap theme variable", async ({ initTestBed, createFormDriver }) => {
1494 |     await initTestBed(`<Form testId="form"/>`, {
1495 |       testThemeVars: {
1496 |         "gap-Form": "2rem",
1497 |       },
1498 |     });
1499 | 
1500 |     const driver = await createFormDriver("form");
1501 |     await expect(driver.component).toHaveCSS("gap", "32px");
1502 |   });
1503 | 
1504 |   test("applies custom button row gap theme variable", async ({
1505 |     initTestBed,
1506 |     createFormDriver,
1507 |   }) => {
1508 |     await initTestBed(`<Form testId="form"/>`, {
1509 |       testThemeVars: {
1510 |         "gap-buttonRow-Form": "1rem",
1511 |       },
1512 |     });
1513 | 
1514 |     const driver = await createFormDriver("form");
1515 |     await expect(driver.component).toBeVisible();
1516 |   });
1517 | 
1518 |   test("applies validation display theme variables", async ({ initTestBed, page }) => {
1519 |     // This test requires validation system to trigger error display
1520 |     await initTestBed(
1521 |       `
1522 |       <Form>
1523 |         <FormItem testId="email" label="Email" bindTo="email" type="email" required="true" />
1524 |       </Form>
1525 |     `,
1526 |       {
1527 |         testThemeVars: {
1528 |           "backgroundColor-ValidationDisplay-error": "rgb(255, 0, 0)",
1529 |           "textColor-ValidationDisplay-error": "rgb(255, 255, 255)",
1530 |         },
1531 |       },
1532 |     );
1533 | 
1534 |     // Trigger validation by submitting with empty required field
1535 |     await page.getByRole("button", { name: "Save" }).click();
1536 | 
1537 |     const emailComp = page.getByTestId("email");
1538 |     await expect(emailComp).toContainText("This field is required");
1539 |   });
1540 | });
1541 | 
1542 | // =============================================================================
1543 | // EDGE CASES TESTS
1544 | // =============================================================================
1545 | 
1546 | test.describe("Edge Cases", () => {
1547 |   test("handles form without any form items", async ({ initTestBed, createFormDriver }) => {
1548 |     await initTestBed(`<Form testId="form"/>`);
1549 |     const driver = await createFormDriver("form");
1550 |     await expect(driver.component).toBeVisible();
1551 |   });
1552 | 
1553 |   test("handles malformed data input gracefully", async ({ initTestBed, createFormDriver }) => {
1554 |     await initTestBed(`<Form data="{invalidJson}" testId="form"/>`);
1555 |     const driver = await createFormDriver("form");
1556 |     await expect(driver.component).toBeVisible();
1557 |   });
1558 | 
1559 |   test("Form does not render if data receives malformed input", async ({
1560 |     initTestBed,
1561 |     createFormDriver,
1562 |   }) => {
1563 |     await initTestBed(`<Form data="{}" />`);
1564 |     await expect((await createFormDriver()).component).not.toBeAttached();
1565 |   });
1566 | 
1567 |   test("handles deeply nested data structure", async ({
1568 |     initTestBed,
1569 |     createFormItemDriver,
1570 |     createTextBoxDriver,
1571 |   }) => {
1572 |     await initTestBed(`
1573 |       <Form data="{{ user: { profile: { name: 'John' } } }}">
1574 |         <FormItem label="Name" bindTo="user.profile.name" testId="nameField" />
1575 |       </Form>
1576 |     `);
1577 | 
1578 |     const driver = await createFormItemDriver("nameField");
1579 |     const input = await createTextBoxDriver(driver.input);
1580 |     await expect(input.field).toHaveValue("John");
1581 |   });
1582 | 
1583 |   test("handles form with validation errors", async ({ initTestBed, page }) => {
1584 |     await initTestBed(`
1585 |       <Form>
1586 |         <FormItem label="Email" bindTo="email" type="email" required="true" />
1587 |       </Form>
1588 |     `);
1589 | 
1590 |     // Try to submit form without filling required field
1591 |     await page.getByRole("button", { name: "Save" }).click();
1592 | 
1593 |     // Validation should prevent submission and show error
1594 |     const form = page.locator("form");
1595 |     await expect(form).toBeVisible();
1596 |   });
1597 | 
1598 |   test("handles rapid form submissions", async ({ initTestBed, page }) => {
1599 |     const { testStateDriver } = await initTestBed(`
1600 |       <Form onSubmit="testState = (testState || 0) + 1">
1601 |         <FormItem label="Name" bindTo="name" />
1602 |       </Form>
1603 |     `);
1604 | 
1605 |     const submitButton = page.getByRole("button", { name: "Save" });
1606 | 
1607 |     // Click submit button multiple times rapidly
1608 |     await submitButton.click();
1609 |     await submitButton.click();
1610 |     await submitButton.click();
1611 | 
1612 |     // Should only submit once or handle gracefully
1613 |     await expect.poll(testStateDriver.testState).toBeGreaterThanOrEqual(1);
1614 |   });
1615 | 
1616 |   test("handles null and undefined in nested data", async ({
1617 |     initTestBed,
1618 |     createFormItemDriver,
1619 |     createTextBoxDriver,
1620 |   }) => {
1621 |     await initTestBed(`
1622 |       <Form data="{{ user: null, settings: undefined, name: 'Test' }}">
1623 |         <FormItem label="Name" bindTo="name" testId="nameField" />
1624 |       </Form>
1625 |     `);
1626 | 
1627 |     const driver = await createFormItemDriver("nameField");
1628 |     const input = await createTextBoxDriver(driver.input);
1629 |     await expect(input.field).toHaveValue("Test");
1630 |   });
1631 | 
1632 |   test("handles form with empty string properties", async ({ initTestBed, page }) => {
1633 |     await initTestBed(`
1634 |       <Form
1635 |         cancelLabel=""
1636 |         saveLabel=""
1637 |         data="{{ name: '' }}">
1638 |         <FormItem label="Name" bindTo="name" />
1639 |       </Form>
1640 |     `);
1641 | 
1642 |     // Form should still be visible
1643 |     const form = page.locator("form");
1644 |     await expect(form).toBeVisible();
1645 |   });
1646 | 
1647 |   test("handles special characters in form data", async ({
1648 |     initTestBed,
1649 |     createFormItemDriver,
1650 |     createTextBoxDriver,
1651 |   }) => {
1652 |     await initTestBed(`
1653 |       <Form data="{{ name: 'José María', description: 'Test & symbols' }}">
1654 |         <FormItem label="Name" bindTo="name" testId="nameField" />
1655 |         <FormItem label="Description" bindTo="description" testId="descField" />
1656 |       </Form>
1657 |     `);
1658 | 
1659 |     const nameDriver = await createFormItemDriver("nameField");
1660 |     const nameInput = await createTextBoxDriver(nameDriver.input);
1661 |     const descDriver = await createFormItemDriver("descField");
1662 |     const descInput = await createTextBoxDriver(descDriver.input);
1663 | 
1664 |     await expect(nameInput.field).toHaveValue("José María");
1665 |     await expect(descInput.field).toHaveValue("Test & symbols");
1666 |   });
1667 | 
1668 |   test("user cannot submit with clientside errors present", async ({
1669 |     initTestBed,
1670 |     createFormDriver,
1671 |   }) => {
1672 |     const { testStateDriver } = await initTestBed(`
1673 |       <Form onSubmit="testState = true">
1674 |         <FormItem bindTo="name" required="true" />
1675 |       </Form>
1676 |     `);
1677 |     const driver = await createFormDriver();
1678 | 
1679 |     // The onSubmit event should have been triggered if not for the client error of an empty required field
1680 |     await driver.submitForm("click");
1681 |     await expect.poll(testStateDriver.testState).toEqual(null);
1682 |   });
1683 | 
1684 |   test("can submit with invisible required field", async ({
1685 |     initTestBed,
1686 |     createFormDriver,
1687 |     createFormItemDriver,
1688 |     createTextBoxDriver,
1689 |     page,
1690 |   }) => {
1691 |     const { testStateDriver } = await initTestBed(`
1692 |       <Form onSubmit="testState = true">
1693 |         <FormItem testId="select" bindTo="authenticationType"
1694 |           type="select" label="Authentication Type:" initialValue="{0}">
1695 |           <Option value="{0}" label="Password" />
1696 |           <Option value="{1}" label="Public Key" />
1697 |         </FormItem>
1698 |         <FormItem label="name1" testId="name1" bindTo="name1"
1699 |           required="true" when="{$data.authenticationType == 0}"/>
1700 |         <FormItem label="name2" testId="name2" bindTo="name2"
1701 |           required="true" when="{$data.authenticationType == 1}"/>
1702 |       </Form>
1703 |     `);
1704 |     const formDriver = await createFormDriver();
1705 |     const selectDriver = await createFormItemDriver("select");
1706 |     const textfieldElement = (await createFormItemDriver("name2")).input;
1707 |     const textfieldDriver = await createTextBoxDriver(textfieldElement);
1708 | 
1709 |     await selectDriver.component.click();
1710 |     await page.getByText("Public Key").click();
1711 |     await textfieldDriver.field.fill("John");
1712 |     await formDriver.submitForm();
1713 | 
1714 |     await expect.poll(testStateDriver.testState).toEqual(true);
1715 |   });
1716 | 
1717 |   test("conditional fields keep the state", async ({
1718 |     initTestBed,
1719 |     createFormItemDriver,
1720 |     createOptionDriver,
1721 |     createTextBoxDriver,
1722 |   }) => {
1723 |     await initTestBed(`
1724 |       <Form>
1725 |         <FormItem testId="select" bindTo="authenticationType"
1726 |           type="radioGroup" label="Authentication Type:" initialValue="{0}">
1727 |           <Option value="{0}" label="Password" testId="password"/>
1728 |           <Option value="{1}" label="Public Key" testId="publicKey" />
1729 |         </FormItem>
1730 |         <FormItem label="name1" testId="name1" bindTo="name1"
1731 |           required="true" when="{$data.authenticationType == 0}"/>
1732 |         <FormItem label="name2" testId="name2" bindTo="name2"
1733 |           required="true" when="{$data.authenticationType == 1}"/>
1734 |       </Form>
1735 |     `);
1736 |     const option1Driver = await createFormItemDriver("password");
1737 |     const option2Driver = await createOptionDriver("publicKey");
1738 |     const textfield1Element = (await createFormItemDriver("name1")).input;
1739 |     const textfield1Driver = await createTextBoxDriver(textfield1Element);
1740 | 
1741 |     // Fill in first field
1742 |     await textfield1Driver.field.fill("Test Value");
1743 |     await expect(textfield1Driver.field).toHaveValue("Test Value");
1744 | 
1745 |     // Switch to second option
1746 |     await option2Driver.component.click();
1747 | 
1748 |     // Switch back to first option
1749 |     await option1Driver.component.click();
1750 | 
1751 |     // Field should retain its value
1752 |     await expect(textfield1Driver.field).toHaveValue("Test Value");
1753 |   });
1754 | });
1755 | 
1756 | // =============================================================================
1757 | // ORIGINAL TEST SUITE (LEGACY TESTS)
1758 | // =============================================================================
1759 | 
1760 | test("mock service responds", async ({ initTestBed, createFormDriver }) => {
1761 |   await initTestBed(
1762 |     `
1763 |     <Form submitUrl="/test" />`,
1764 |     {
1765 |       apiInterceptor: {
1766 |         operations: {
1767 |           test: {
1768 |             url: "/test",
1769 |             method: "post",
1770 |             handler: `return true;`,
1771 |           },
1772 |         },
1773 |       },
1774 |     },
1775 |   );
1776 |   const driver = await createFormDriver();
1777 |   await driver.submitForm();
1778 | 
1779 |   const request = await driver.getSubmitResponse("/test");
1780 |   expect(request.ok()).toEqual(true);
1781 | });
1782 | 
1783 | // --- $data
1784 | 
1785 | test("$data is correctly bound to form data", async ({ initTestBed, createButtonDriver }) => {
1786 |   await initTestBed(`
1787 |         <Form data="{{ field: 'test' }}">
1788 |           <FormItem label="testField" bindTo="field">
1789 |             <Button testId="custom" label="{$data.field}" />
1790 |           </FormItem>
1791 |         </Form> `);
1792 |   const driver = await createButtonDriver("custom");
1793 |   await expect(driver.component).toHaveExplicitLabel("test");
1794 | });
1795 | 
1796 | test("$data is correctly undefined if data is not set in props", async ({
1797 |   initTestBed,
1798 |   createButtonDriver,
1799 | }) => {
1800 |   await initTestBed(`
1801 |         <Form>
1802 |           <FormItem label="testField" bindTo="field">
1803 |             <Button testId="custom" label="{$data.field}" />
1804 |           </FormItem>
1805 |         </Form> `);
1806 |   const driver = await createButtonDriver("custom");
1807 |   await expect(driver.component).toHaveExplicitLabel(undefined);
1808 | });
1809 | 
1810 | test("Form buttons and contained FormItems are enabled", async ({
1811 |   initTestBed,
1812 |   page,
1813 |   createFormDriver,
1814 | }) => {
1815 |   await initTestBed(`
1816 |       <Form testId="form">
1817 |         <FormItem label="Name" bindTo="name" />
1818 |         <FormItem label="Email" bindTo="email" />
1819 |       </Form>
1820 |     `);
1821 | 
1822 |   const driver = await createFormDriver("form");
1823 |   await expect(page.getByText("Name")).toBeVisible();
1824 |   await expect(page.getByText("Email")).toBeVisible();
1825 |   await expect(driver.cancelButton).toBeEnabled();
1826 |   await expect(driver.submitButton).toBeEnabled();
1827 | });
1828 | 
1829 | test("submit only triggers when enabled", async ({ initTestBed, createFormDriver }) => {
1830 |   const { testStateDriver } = await initTestBed(
1831 |     `
1832 |       <Form enabled="false" data="{{ name: 'John' }}" onSubmit="testState = true">
1833 |         <FormItem bindTo="name" />
1834 |       </Form>`,
1835 |   );
1836 |   const driver = await createFormDriver();
1837 |   await expect(driver.submitButton).toBeDisabled();
1838 | 
1839 |   await driver.submitForm("keypress");
1840 |   await expect.poll(testStateDriver.testState).toEqual(null);
1841 | });
1842 | 
1843 | test("submit with unbound fields", async ({ page, initTestBed, createFormDriver }) => {
1844 |   await initTestBed(`
1845 |       <Fragment var.output="none">
1846 |         <Form testId="form"
1847 |           data="{{ firstname: 'James', lastname: 'Clewell' }}"
1848 |           onSubmit="args => output = JSON.stringify(args)">
1849 |           <FormItem label="Firstname" bindTo="firstname" />
1850 |           <FormItem label="Middle name" initialValue="Robert" />
1851 |           <FormItem label="Lastname" />
1852 |         </Form>
1853 |         <Text testId="text">{output}</Text>
1854 |       </Fragment>
1855 |     `);
1856 |   const driver = await createFormDriver("form");
1857 |   await driver.submitForm();
1858 |   await expect(page.getByTestId("text")).toHaveText('{"firstname":"James"}');
1859 | });
1860 | 
1861 | test(`submit with type 'items'`, async ({
1862 |   initTestBed,
1863 |   createFormDriver,
1864 |   createButtonDriver,
1865 |   createFormItemDriver,
1866 | }) => {
1867 |   const { testStateDriver } = await initTestBed(`
1868 |       <Form onSubmit="data => testState = data" testId="form">
1869 |         <FormItem testId="formItem" type="items" bindTo="arrayItems" id="arrayItems">
1870 |             <FormItem bindTo="name" testId="text{$itemIndex}"/>
1871 |         </FormItem>
1872 |         <Button testId="addButton" onClick="arrayItems.addItem()"/>
1873 |       </Form>`);
1874 | 
1875 |   await (await createButtonDriver("addButton")).click();
1876 |   await (await createFormItemDriver("text0")).textBox.fill("John");
1877 |   await (await createButtonDriver("addButton")).click();
1878 |   await (await createFormItemDriver("text1")).textBox.fill("Peter");
1879 |   const driver = await createFormDriver("form");
1880 |   await driver.submitForm();
1881 |   await expect.poll(testStateDriver.testState).toStrictEqual({
1882 |     arrayItems: [{ name: "John" }, { name: "Peter" }],
1883 |   });
1884 | });
1885 | 
1886 | test(`submit with type 'items', empty bindTo`, async ({
1887 |   initTestBed,
1888 |   createFormDriver,
1889 |   createButtonDriver,
1890 |   createFormItemDriver,
1891 | }) => {
1892 |   const { testStateDriver } = await initTestBed(`
1893 |       <Form onSubmit="data => testState = data" testId="form">
1894 |         <FormItem testId="formItem" type="items" bindTo="arrayItems" id="arrayItems">
1895 |             <FormItem testId="text{$itemIndex}" bindTo=""/>
1896 |         </FormItem>
1897 |         <Button testId="addButton" onClick="arrayItems.addItem()"/>
1898 |       </Form>`);
1899 | 
1900 |   await (await createButtonDriver("addButton")).click();
1901 |   await (await createFormItemDriver("text0")).textBox.fill("John");
1902 |   await (await createButtonDriver("addButton")).click();
1903 |   await (await createFormItemDriver("text1")).textBox.fill("Peter");
1904 |   const driver = await createFormDriver("form");
1905 |   await driver.submitForm();
1906 |   await expect.poll(testStateDriver.testState).toStrictEqual({
1907 |     arrayItems: ["John", "Peter"],
1908 |   });
1909 | });
1910 | 
1911 | // --- Testing
1912 | 
1913 | // --- --- buttonRowTemplate
1914 | 
1915 | test("buttonRowTemplate can render buttons", async ({ initTestBed, createButtonDriver }) => {
1916 |   await initTestBed(`
1917 |     <Form>
1918 |       <property name="buttonRowTemplate">
1919 |         <Button testId="submitBtn" type="submit" label="Hello Button" />
1920 |       </property>
1921 |     </Form>`);
1922 |   await expect((await createButtonDriver("submitBtn")).component).toBeAttached();
1923 | });
1924 | 
1925 | test("buttonRowTemplate replaces built-in buttons", async ({ initTestBed, createFormDriver }) => {
1926 |   await initTestBed(`
1927 |     <Form testId="form">
1928 |       <property name="buttonRowTemplate">
1929 |         <Button testId="submitBtn" type="submit" label="Hello Button" />
1930 |       </property>
1931 |     </Form>`);
1932 | 
1933 |   const driver = await createFormDriver("form");
1934 |   await expect(driver.submitButton).not.toBeVisible();
1935 |   await expect(driver.cancelButton).not.toBeVisible();
1936 | });
1937 | 
1938 | test("setting buttonRowTemplate without buttons still runs submit on Enter", async ({
1939 |   initTestBed,
1940 |   createFormDriver,
1941 | }) => {
1942 |   const { testStateDriver } = await initTestBed(`
1943 |     <Form onSubmit="testState = true">
1944 |       <property name="buttonRowTemplate">
1945 |         <Fragment />
1946 |       </property>
1947 |       <FormItem bindTo="name" />
1948 |     </Form>
1949 |   `);
1950 |   const driver = await createFormDriver();
1951 | 
1952 |   await driver.submitForm("keypress");
1953 |   await expect.poll(testStateDriver.testState).toBe(true);
1954 | });
1955 | 
1956 | test("data accepts an object", async ({
1957 |   initTestBed,
1958 |   createFormItemDriver,
1959 |   createTextBoxDriver,
1960 | }) => {
1961 |   await initTestBed(`
1962 |     <Form data="{{ field1: 'test' }}">
1963 |       <FormItem testId="inputField" bindTo="field1" />
1964 |     </Form>
1965 |   `);
1966 |   const driver = await createFormItemDriver("inputField");
1967 |   await expect((await createTextBoxDriver(driver.input)).field).toHaveValue("test");
1968 | });
1969 | 
1970 | test(`data accepts primitive`, async ({ initTestBed, createFormDriver }) => {
1971 |   await initTestBed(`
1972 |     <Form data="test">
1973 |       <FormItem bindTo="field1" />
1974 |     </Form>
1975 |   `);
1976 |   const component = (await createFormDriver()).component;
1977 |   await expect(component).toBeAttached();
1978 | });
1979 | 
1980 | test(`data accepts empty array`, async ({ initTestBed, createFormDriver }) => {
1981 |   await initTestBed(`
1982 |     <Form data="{[]}">
1983 |       <FormItem bindTo="field1" />
1984 |     </Form>
1985 |   `);
1986 |   const component = (await createFormDriver()).component;
1987 |   await expect(component).toBeAttached();
1988 | });
1989 | 
1990 | test("data accepts relative URL endpoint", async ({
1991 |   initTestBed,
1992 |   createFormItemDriver,
1993 |   createTextBoxDriver,
1994 | }) => {
1995 |   await initTestBed(
1996 |     `
1997 |       <Form data="/test">
1998 |         <FormItem testId="inputField" bindTo="name" />
1999 |       </Form>`,
2000 |     {
2001 |       apiInterceptor: {
2002 |         operations: {
2003 |           test: {
2004 |             url: "/test",
2005 |             method: "get",
2006 |             handler: `return { name: 'John' };`,
2007 |           },
2008 |         },
2009 |       },
2010 |     },
2011 |   );
2012 |   const driver = await createFormItemDriver("inputField");
2013 |   await expect((await createTextBoxDriver(driver.input)).field).toHaveValue("John");
2014 | });
2015 | 
2016 | test("cancel button and save button use default label", async ({
2017 |   initTestBed,
2018 |   createFormDriver,
2019 | }) => {
2020 |   await initTestBed(`
2021 |       <Form testId="form">
2022 |         <FormItem label="Name" bindTo="name" />
2023 |         <FormItem label="Email" bindTo="email" />
2024 |       </Form>
2025 |     `);
2026 | 
2027 |   const driver = await createFormDriver("form");
2028 |   await expect(driver.cancelButton).toHaveText("Cancel");
2029 |   await expect(driver.submitButton).toHaveText("Save");
2030 | });
2031 | 
2032 | test("cancel button is rendered with cancelLabel", async ({ initTestBed, createFormDriver }) => {
2033 |   await initTestBed(`
2034 |       <Form testId="form" cancelLabel="Abort">
2035 |         <FormItem label="Name" bindTo="name" />
2036 |         <FormItem label="Email" bindTo="email" />
2037 |       </Form>
2038 |     `);
2039 | 
2040 |   const driver = await createFormDriver("form");
2041 |   await expect(driver.cancelButton).toHaveText("Abort");
2042 |   await expect(driver.submitButton).toHaveText("Save");
2043 | });
2044 | 
2045 | test("save button is rendered with saveLabel", async ({ initTestBed, createFormDriver }) => {
2046 |   await initTestBed(`
2047 |       <Form testId="form" saveLabel="Submit">
2048 |         <FormItem label="Name" bindTo="name" />
2049 |         <FormItem label="Email" bindTo="email" />
2050 |       </Form>
2051 |     `);
2052 | 
2053 |   const driver = await createFormDriver("form");
2054 |   await expect(driver.cancelButton).toHaveText("Cancel");
2055 |   await expect(driver.submitButton).toHaveText("Submit");
2056 | });
2057 | 
2058 | // swapCancelAndSave
2059 | 
2060 | test("built-in button row order is default if swapCancelAndSave is false", async ({
2061 |   initTestBed,
2062 |   createFormDriver,
2063 | }) => {
2064 |   await initTestBed(`
2065 |       <Form testId="form" saveLabel="Submit">
2066 |         <FormItem label="Name" bindTo="name" />
2067 |         <FormItem label="Email" bindTo="email" />
2068 |       </Form>
2069 |     `);
2070 | 
2071 |   const driver = await createFormDriver("form");
2072 |   const cancelBox = await driver.cancelButton.boundingBox();
2073 |   const submitBox = await driver.submitButton.boundingBox();
2074 |   expect(cancelBox.x).toBeLessThan(submitBox.x);
2075 | });
2076 | 
2077 | test("built-in button row order flips if swapCancelAndSave is true", async ({
2078 |   initTestBed,
2079 |   createFormDriver,
2080 | }) => {
2081 |   await initTestBed(`
2082 |       <Form testId="form" saveLabel="Submit" swapCancelAndSave="true">
2083 |         <FormItem label="Name" bindTo="name" />
2084 |         <FormItem label="Email" bindTo="email" />
2085 |       </Form>
2086 |     `);
2087 | 
2088 |   const driver = await createFormDriver("form");
2089 |   const cancelBox = await driver.cancelButton.boundingBox();
2090 |   const submitBox = await driver.submitButton.boundingBox();
2091 |   expect(cancelBox.x).toBeGreaterThan(submitBox.x);
2092 | });
2093 | 
2094 | // --- submitUrl
2095 | 
2096 | test("form submits to correct url", async ({ initTestBed, createFormDriver }) => {
2097 |   const endpoint = "/test";
2098 |   await initTestBed(
2099 |     `
2100 |     <Form data="{{ name: 'John' }}" submitUrl="${endpoint}" submitMethod="post">
2101 |       <FormItem bindTo="name" />
2102 |     </Form>`,
2103 |     {
2104 |       apiInterceptor: {
2105 |         operations: {
2106 |           test: {
2107 |             url: endpoint,
2108 |             method: "post",
2109 |             handler: `{ return true; }`,
2110 |           },
2111 |         },
2112 |       },
2113 |     },
2114 |   );
2115 |   const driver = await createFormDriver();
2116 | 
2117 |   await driver.submitForm();
2118 |   const response = await driver.getSubmitResponse(endpoint);
2119 |   expect(response.ok()).toBe(true);
2120 |   expect(new URL(response.url()).pathname).toBe(endpoint);
2121 | });
2122 | 
2123 | // --- submitMethod
2124 | 
2125 | // NOTE: GET doesn't work because GET/HEAD cannot have a 'body'
2126 | ["post", "put", "delete"].forEach((method) => {
2127 |   test(`${method} REST op on submit`, async ({ initTestBed, createFormDriver }) => {
2128 |     await initTestBed(`<Form submitUrl="/test" submitMethod="${method}" />`, {
2129 |       apiInterceptor: {
2130 |         operations: {
2131 |           testPost: {
2132 |             url: "/test",
2133 |             method: "post",
2134 |             handler: `return true;`,
2135 |           },
2136 |           testPut: {
2137 |             url: "/test",
2138 |             method: "put",
2139 |             handler: `return true;`,
2140 |           },
2141 |           testDelete: {
2142 |             url: "/test",
2143 |             method: "delete",
2144 |             handler: `return true;`,
2145 |           },
2146 |         },
2147 |       },
2148 |     });
2149 |     const driver = await createFormDriver();
2150 |     const request = await driver.getSubmitRequest("/test", method, "click");
2151 |     expect(request.failure()).toBeNull();
2152 |   });
2153 | });
2154 | 
2155 | // --- submitting the Form
2156 | 
2157 | test("submit triggers when clicking save/submit button", async ({
2158 |   initTestBed,
2159 |   createFormDriver,
2160 | }) => {
2161 |   await initTestBed(
2162 |     `
2163 |     <Form data="{{ name: 'John' }}" submitUrl="/test" submitMethod="post">
2164 |       <FormItem bindTo="name" />
2165 |     </Form>`,
2166 |     {
2167 |       apiInterceptor: {
2168 |         operations: {
2169 |           test: {
2170 |             url: "/test",
2171 |             method: "post",
2172 |             handler: `return true;`,
2173 |           },
2174 |         },
2175 |       },
2176 |     },
2177 |   );
2178 |   const driver = await createFormDriver();
2179 | 
2180 |   const request = await driver.getSubmitRequest("/test", "POST", "click");
2181 |   expect(request.failure()).toBeNull();
2182 | });
2183 | 
2184 | test("submit triggers when pressing Enter", async ({ initTestBed, createFormDriver }) => {
2185 |   await initTestBed(
2186 |     `
2187 |     <Form data="{{ name: 'John' }}" submitUrl="/test" submitMethod="post">
2188 |       <FormItem bindTo="name" />
2189 |     </Form>`,
2190 |     {
2191 |       apiInterceptor: {
2192 |         operations: {
2193 |           test: {
2194 |             url: "/test",
2195 |             method: "post",
2196 |             handler: `return true;`,
2197 |           },
2198 |         },
2199 |       },
2200 |     },
2201 |   );
2202 |   const driver = await createFormDriver();
2203 | 
2204 |   const request = await driver.getSubmitRequest("/test", "POST", "keypress");
2205 |   expect(request.failure()).toBeNull();
2206 | });
2207 | 
2208 | test("user cannot submit with clientside errors present", async ({
2209 |   initTestBed,
2210 |   createFormDriver,
2211 | }) => {
2212 |   const { testStateDriver } = await initTestBed(`
2213 |     <Form onSubmit="testState = true">
2214 |       <FormItem bindTo="name" required="true" />
2215 |     </Form>
2216 |   `);
2217 |   const driver = await createFormDriver();
2218 | 
2219 |   // The onSubmit event should have been triggered if not for the client error of an empty required field
2220 |   await driver.submitForm("click");
2221 |   await expect.poll(testStateDriver.testState).toEqual(null);
2222 | });
2223 | 
2224 | // --- backend validation summary
2225 | 
2226 | test("submitting with errors shows validation summary", async ({
2227 |   initTestBed,
2228 |   createFormDriver,
2229 | }) => {
2230 |   await initTestBed(`<Form submitUrl="/general-validation-error" submitMethod="post" />`, {
2231 |     apiInterceptor: errorDisplayInterceptor,
2232 |   });
2233 |   const driver = await createFormDriver();
2234 |   await driver.submitForm();
2235 |   await expect(await driver.getValidationSummary()).toBeVisible();
2236 | });
2237 | 
2238 | test("submitting without errors does not show summary", async ({
2239 |   initTestBed,
2240 |   createFormDriver,
2241 | }) => {
2242 |   await initTestBed(`<Form submitUrl="/no-validation-error" submitMethod="post" />`, {
2243 |     apiInterceptor: errorDisplayInterceptor,
2244 |   });
2245 |   const driver = await createFormDriver();
2246 |   await driver.submitForm();
2247 |   await expect(await driver.getValidationSummary()).not.toBeVisible();
2248 | });
2249 | 
2250 | test("general error messages are rendered in the summary", async ({
2251 |   initTestBed,
2252 |   createFormDriver,
2253 |   createValidationDisplayDriver,
2254 | }) => {
2255 |   await initTestBed(`<Form submitUrl="/general-validation-error" submitMethod="post" />`, {
2256 |     apiInterceptor: errorDisplayInterceptor,
2257 |   });
2258 |   const formDriver = await createFormDriver();
2259 |   await formDriver.submitForm();
2260 | 
2261 |   // TODO: strip this down -> it's verbose but hard to read
2262 |   const warningDisplay = await createValidationDisplayDriver(
2263 |     await formDriver.getValidationDisplaysBySeverity("warning"),
2264 |   );
2265 |   const errorDisplay = await createValidationDisplayDriver(
2266 |     await formDriver.getValidationDisplaysBySeverity("error"),
2267 |   );
2268 | 
2269 |   expect(await warningDisplay.getText()).toContain("Warning for the whole form");
2270 |   expect(await errorDisplay.getText()).toContain("Error for the whole form");
2271 | });
2272 | 
2273 | test("field-related errors are rendered at FormItems", async ({
2274 |   initTestBed,
2275 |   createFormDriver,
2276 |   createFormItemDriver,
2277 | }) => {
2278 |   await initTestBed(
2279 |     `
2280 |       <Form submitUrl="/field-validation-error" submitMethod="post">
2281 |         <FormItem testId="testField" bindTo="test" label="test" />
2282 |       </Form>`,
2283 |     {
2284 |       apiInterceptor: errorDisplayInterceptor,
2285 |     },
2286 |   );
2287 |   const formDriver = await createFormDriver();
2288 |   const fieldDriver = await createFormItemDriver("testField");
2289 | 
2290 |   await formDriver.submitForm();
2291 |   await expect(fieldDriver.validationStatusIndicator).toHaveAttribute(
2292 |     fieldDriver.validationStatusTag,
2293 |     "warning",
2294 |   );
2295 | });
2296 | 
2297 | test("field-related errors map to correct FormItems", async ({
2298 |   initTestBed,
2299 |   createFormDriver,
2300 |   createFormItemDriver,
2301 | }) => {
2302 |   await initTestBed(
2303 |     `
2304 |       <Form submitUrl="/field-validation-error" submitMethod="post">
2305 |         <FormItem testId="testField" bindTo="test" label="test" />
2306 |         <FormItem testId="testField2" bindTo="test2" label="test2" />
2307 |       </Form>`,
2308 |     {
2309 |       apiInterceptor: errorDisplayInterceptor,
2310 |     },
2311 |   );
2312 |   const formDriver = await createFormDriver();
2313 |   const fieldDriver = await createFormItemDriver("testField");
2314 | 
2315 |   await formDriver.submitForm();
2316 |   await expect(fieldDriver.validationStatusIndicator).toHaveAttribute(
2317 |     fieldDriver.validationStatusTag,
2318 |     "warning",
2319 |   );
2320 | });
2321 | 
2322 | test.skip("field-related errors disappear if user updates FormItems", async ({
2323 |   initTestBed,
2324 |   page,
2325 |   createFormItemDriver,
2326 | }) => {
2327 |   await initTestBed(
2328 |     `
2329 |       <Form testId="form">
2330 |         <FormItem testId="testField" bindTo="test" label="test" required />
2331 |         <FormItem testId="testField2" bindTo="test2" label="test2" />
2332 |       </Form>`,
2333 |   );
2334 | 
2335 |   const fieldDriver = await createFormItemDriver("testField");
2336 |   const fieldDriver2 = await createFormItemDriver("testField2");
2337 | 
2338 |   await fieldDriver.component.focus();
2339 |   await fieldDriver.textBox.fill("a");
2340 |   await fieldDriver.textBox.fill("");
2341 |   await fieldDriver.textBox.blur();
2342 | 
2343 |   // Should show required error now
2344 |   await expect(fieldDriver.textBox).toHaveValue("");
2345 |   await expect(fieldDriver.validationStatusIndicator).toHaveAttribute(
2346 |     fieldDriver.validationStatusTag,
2347 |     "error",
2348 |   );
2349 | 
2350 |   await fieldDriver.textBox.fill("a");
2351 |   await fieldDriver.textBox.blur();
2352 | 
2353 |   await fieldDriver2.textBox.focus();
2354 | 
2355 |   await expect(fieldDriver2.textBox).toBeFocused();
2356 | 
2357 |   await fieldDriver2.textBox.fill("b");
2358 |   await expect(fieldDriver.validationStatusIndicator).not.toBeVisible();
2359 | });
2360 | 
2361 | const smartCrudInterceptor: ApiInterceptorDefinition = {
2362 |   initialize: `
2363 |     $state.items = {
2364 |       [10]: { name: "Smith", id: 10 }
2365 |     };
2366 |     $state.currentId = 10;
2367 |   `,
2368 |   operations: {
2369 |     create: {
2370 |       url: "/entities",
2371 |       method: "post",
2372 |       handler: `() => {
2373 |         $state.currentId++;
2374 |         $state.items[$state.currentId] = $requestBody;
2375 |         $state.items[$state.currentId].id = $state.currentId;
2376 | 
2377 |         return $state.items[$state.currentId];
2378 |       }`,
2379 |     },
2380 |     read: {
2381 |       url: "/entities/:id",
2382 |       method: "get",
2383 |       handler: `() => {
2384 |         return $state.items[$pathParams.id];
2385 |       }`,
2386 |     },
2387 |     update: {
2388 |       url: "/entities/:id",
2389 |       method: "put",
2390 |       handler: `() => {
2391 |         $state.items[$pathParams.id] = { ...$state.items[$pathParams.id], ...$requestBody };
2392 |         return $state.items[$pathParams.id];
2393 |       }`,
2394 |     },
2395 |   },
2396 | };
2397 | 
2398 | test("create form works with submitUrl", async ({
2399 |   initTestBed,
2400 |   createFormDriver,
2401 |   createFormItemDriver,
2402 |   createTextBoxDriver,
2403 | }) => {
2404 |   await initTestBed(
2405 |     `
2406 |     <Form submitUrl="/entities">
2407 |       <FormItem bindTo="name" testId="nameInput"/>
2408 |     </Form>
2409 |   `,
2410 |     { apiInterceptor: smartCrudInterceptor },
2411 |   );
2412 |   const formDriver = await createFormDriver();
2413 |   const inputElement = (await createFormItemDriver("nameInput")).input;
2414 |   const fieldDriver = await createTextBoxDriver(inputElement);
2415 | 
2416 |   await fieldDriver.field.fill("John");
2417 |   await formDriver.submitForm("click");
2418 | 
2419 |   const response = await formDriver.getSubmitResponse();
2420 |   expect(await response.json()).toEqual({
2421 |     name: "John",
2422 |     id: 11,
2423 |   });
2424 | });
2425 | 
2426 | test("regression: data url through modal context", async ({
2427 |   initTestBed,
2428 |   createButtonDriver,
2429 |   createFormDriver,
2430 |   createFormItemDriver,
2431 |   createTextBoxDriver,
2432 | }) => {
2433 |   await initTestBed(
2434 |     `
2435 |       <Fragment>
2436 |         <Button testId="openModalButton" onClick="modal.open({data: '/entities/10'})"/>
2437 |         <ModalDialog id="modal">
2438 |           <Form testId="modalForm" data="{$param.data}" submitUrl="{$param.submitUrl}">
2439 |              <FormItem bindTo="name" testId="nameInput"/>
2440 |           </Form>
2441 |         </ModalDialog>
2442 |       </Fragment>
2443 |     `,
2444 |     {
2445 |       apiInterceptor: smartCrudInterceptor,
2446 |     },
2447 |   );
2448 |   const formDriver = await createFormDriver("modalForm");
2449 |   const inputElement = (await createFormItemDriver("nameInput")).input;
2450 |   const inputDriver = await createTextBoxDriver(inputElement);
2451 | 
2452 |   await (await createButtonDriver("openModalButton")).click();
2453 | 
2454 |   await expect(inputDriver.field).toHaveValue("Smith");
2455 | 
2456 |   await inputDriver.field.fill("EDITED-Smith");
2457 |   await formDriver.submitForm("click");
2458 | 
2459 |   const response = await formDriver.getSubmitResponse();
2460 |   expect(await response.json()).toEqual({
2461 |     name: "EDITED-Smith",
2462 |     id: 10,
2463 |   });
2464 | });
2465 | 
2466 | // --- Conditional Rendering Cases
2467 | 
2468 | test("can submit with invisible required field", async ({
2469 |   initTestBed,
2470 |   createFormDriver,
2471 |   createFormItemDriver,
2472 |   createTextBoxDriver,
2473 |   page,
2474 | }) => {
2475 |   const { testStateDriver } = await initTestBed(`
2476 |     <Form onSubmit="testState = true">
2477 |       <FormItem testId="select" bindTo="authenticationType"
2478 |         type="select" label="Authentication Type:" initialValue="{0}">
2479 |         <Option value="{0}" label="Password" />
2480 |         <Option value="{1}" label="Public Key" />
2481 |       </FormItem>
2482 |       <FormItem label="name1" testId="name1" bindTo="name1"
2483 |         required="true" when="{$data.authenticationType == 0}"/>
2484 |       <FormItem label="name2" testId="name2" bindTo="name2"
2485 |         required="true" when="{$data.authenticationType == 1}"/>
2486 |     </Form>
2487 |   `);
2488 |   const formDriver = await createFormDriver();
2489 |   const selectDriver = await createFormItemDriver("select");
2490 |   const textfieldElement = (await createFormItemDriver("name2")).input;
2491 |   const textfieldDriver = await createTextBoxDriver(textfieldElement);
2492 | 
2493 |   await selectDriver.component.click();
2494 |   await page.getByText("Public Key").click();
2495 |   await textfieldDriver.field.fill("John");
2496 |   await formDriver.submitForm();
2497 | 
2498 |   await expect.poll(testStateDriver.testState).toEqual(true);
2499 | });
2500 | 
2501 | test("conditional fields keep the state", async ({
2502 |   initTestBed,
2503 |   createFormItemDriver,
2504 |   createOptionDriver,
2505 |   createTextBoxDriver,
2506 | }) => {
2507 |   await initTestBed(`
2508 |     <Form>
2509 |       <FormItem testId="select" bindTo="authenticationType"
2510 |         type="radioGroup" label="Authentication Type:" initialValue="{0}">
2511 |         <Option value="{0}" label="Password" testId="password"/>
2512 |         <Option value="{1}" label="Public Key" testId="publicKey" />
2513 |       </FormItem>
2514 |       <FormItem label="name1" testId="name1" bindTo="name1"
2515 |         required="true" when="{$data.authenticationType == 0}"/>
2516 |       <FormItem label="name2" testId="name2" bindTo="name2"
2517 |         required="true" when="{$data.authenticationType == 1}"/>
2518 |     </Form>
2519 |   `);
2520 |   const option1Driver = await createFormItemDriver("password");
2521 |   const option2Driver = await createOptionDriver("publicKey");
2522 |   const textfield1Element = (await createFormItemDriver("name1")).input;
2523 |   const textfield1Driver = await createTextBoxDriver(textfield1Element);
2524 |   const textfield2Element = (await createFormItemDriver("name2")).input;
2525 |   const textfield2Driver = await createTextBoxDriver(textfield2Element);
2526 | 
2527 |   await textfield1Driver.field.fill("name1");
2528 |   await option2Driver.click();
2529 |   await textfield2Driver.field.fill("name2");
2530 |   await option1Driver.click();
2531 | 
2532 |   await expect(textfield1Driver.field).toHaveValue("name1");
2533 | });
2534 | 
2535 | // =============================================================================
2536 | // BEHAVIORS AND PARTS TESTS
2537 | // =============================================================================
2538 | 
2539 | test.describe("Behaviors and Parts", () => {
2540 |   test("can select part: 'cancelButton'", async ({ page, initTestBed }) => {
2541 |     await initTestBed(`<Form testId="test" />`);
2542 |     const cancelButton = page.locator("[data-part-id='cancelButton']");
2543 |     await expect(cancelButton).toBeVisible();
2544 |     await expect(cancelButton).toHaveText("Cancel");
2545 |   });
2546 | 
2547 |   test("can select part: 'submitButton'", async ({ page, initTestBed }) => {
2548 |     await initTestBed(`<Form testId="test" />`);
2549 |     const submitButton = page.locator("[data-part-id='submitButton']");
2550 |     await expect(submitButton).toBeVisible();
2551 |     await expect(submitButton).toHaveText("Save");
2552 |   });
2553 | 
2554 |   test("cancelButton part is not present when cancelLabel is empty", async ({
2555 |     page,
2556 |     initTestBed,
2557 |   }) => {
2558 |     await initTestBed(`<Form testId="test" cancelLabel="" />`);
2559 |     const cancelButton = page.locator("[data-part-id='cancelButton']");
2560 |     await expect(cancelButton).not.toBeVisible();
2561 |   });
2562 | 
2563 |   test("both parts are visible with default props", async ({ page, initTestBed }) => {
2564 |     await initTestBed(`<Form testId="test" />`);
2565 | 
2566 |     const cancelButton = page.locator("[data-part-id='cancelButton']");
2567 |     const submitButton = page.locator("[data-part-id='submitButton']");
2568 | 
2569 |     await expect(cancelButton).toBeVisible();
2570 |     await expect(submitButton).toBeVisible();
2571 |   });
2572 | });
2573 | 
2574 | // =============================================================================
2575 | // API TESTS
2576 | // =============================================================================
2577 | 
2578 | test.describe("Api", () => {
2579 |   test("getData returns a copy of current form data", async ({ initTestBed, page }) => {
2580 |     const { testStateDriver } = await initTestBed(`
2581 |       <Fragment>
2582 |         <Form id="myForm">
2583 |           <FormItem label="Name" bindTo="name" initialValue="John" />
2584 |           <FormItem label="Email" bindTo="email" initialValue="[email protected]" />
2585 |         </Form>
2586 |         <Button testId="getDataBtn" onClick="testState = myForm.getData()" />
2587 |       </Fragment>
2588 |     `);
2589 | 
2590 |     await page.getByTestId("getDataBtn").click();
2591 |     const data = await testStateDriver.testState();
2592 | 
2593 |     expect(data).toEqual({
2594 |       name: "John",
2595 |       email: "[email protected]",
2596 |     });
2597 |   });
2598 | 
2599 |   test("getData returns updated data after field changes", async ({
2600 |     initTestBed,
2601 |     page,
2602 |     createTextBoxDriver,
2603 |     createFormItemDriver,
2604 |   }) => {
2605 |     const { testStateDriver } = await initTestBed(`
2606 |       <Fragment>
2607 |         <Form id="myForm">
2608 |           <FormItem label="Name" bindTo="name" initialValue="John" testId="nameInput" />
2609 |         </Form>
2610 |         <Button testId="getDataBtn" onClick="testState = myForm.getData()" />
2611 |       </Fragment>
2612 |     `);
2613 | 
2614 |     const inputElement = (await createFormItemDriver("nameInput")).input;
2615 |     const fieldDriver = await createTextBoxDriver(inputElement);
2616 | 
2617 |     await fieldDriver.field.fill("Jane");
2618 |     await page.getByTestId("getDataBtn").click();
2619 |     const data = await testStateDriver.testState();
2620 | 
2621 |     expect(data.name).toEqual("Jane");
2622 |   });
2623 | 
2624 |   test("getData returns empty object for form with no data", async ({ initTestBed, page }) => {
2625 |     const { testStateDriver } = await initTestBed(`
2626 |       <Fragment>
2627 |         <Form id="myForm" />
2628 |         <Button testId="getDataBtn" onClick="testState = myForm.getData()" />
2629 |       </Fragment>
2630 |     `);
2631 | 
2632 |     await page.getByTestId("getDataBtn").click();
2633 |     const data = await testStateDriver.testState();
2634 | 
2635 |     expect(data).toEqual({});
2636 |   });
2637 | 
2638 |   test("getData returns deep clone - modifications do not affect form state", async ({
2639 |     initTestBed,
2640 |     page,
2641 |     createTextBoxDriver,
2642 |     createFormItemDriver,
2643 |   }) => {
2644 |     const { testStateDriver } = await initTestBed(`
2645 |       <Fragment>
2646 |         <Form id="myForm">
2647 |           <FormItem label="Name" bindTo="name" initialValue="John" testId="nameInput" />
2648 |         </Form>
2649 |         <Button testId="getDataBtn" onClick="testState = myForm.getData(); testState.name = 'Modified'" />
2650 |         <Button testId="getDataAgainBtn" onClick="testState = myForm.getData()" />
2651 |       </Fragment>
2652 |     `);
2653 | 
2654 |     await page.getByTestId("getDataBtn").click();
2655 |     let data = await testStateDriver.testState();
2656 |     expect(data.name).toEqual("Modified");
2657 | 
2658 |     // Get data again to verify form still has original value
2659 |     await page.getByTestId("getDataAgainBtn").click();
2660 |     data = await testStateDriver.testState();
2661 |     expect(data.name).toEqual("John");
2662 |   });
2663 | 
2664 |   test("getData excludes unbound fields (fields ending with __UNBOUND_FIELD__)", async ({
2665 |     initTestBed,
2666 |     page,
2667 |   }) => {
2668 |     const { testStateDriver } = await initTestBed(`
2669 |       <Fragment>
2670 |         <Form id="myForm">
2671 |           <FormItem label="Name" bindTo="name" initialValue="John" />
2672 |           <FormItem label="Confirm" bindTo="confirm__UNBOUND_FIELD__" initialValue="yes" />
2673 |         </Form>
2674 |         <Button testId="getDataBtn" onClick="testState = myForm.getData()" />
2675 |       </Fragment>
2676 |     `);
2677 | 
2678 |     await page.getByTestId("getDataBtn").click();
2679 |     const data = await testStateDriver.testState();
2680 | 
2681 |     expect(data).toEqual({
2682 |       name: "John",
2683 |     });
2684 |     expect(data.confirm__UNBOUND_FIELD__).toBeUndefined();
2685 |   });
2686 | 
2687 |   test("getData excludes fields marked with noSubmit", async ({ initTestBed, page }) => {
2688 |     const { testStateDriver } = await initTestBed(`
2689 |       <Fragment>
2690 |         <Form id="myForm">
2691 |           <FormItem label="Name" bindTo="name" initialValue="John" />
2692 |           <FormItem label="Password" bindTo="password" initialValue="secret" noSubmit="true" />
2693 |         </Form>
2694 |         <Button testId="getDataBtn" onClick="testState = myForm.getData()" />
2695 |       </Fragment>
2696 |     `);
2697 | 
2698 |     await page.getByTestId("getDataBtn").click();
2699 |     const data = await testStateDriver.testState();
2700 | 
2701 |     expect(data).toEqual({
2702 |       name: "John",
2703 |     });
2704 |     expect(data.password).toBeUndefined();
2705 |   });
2706 | 
2707 |   test("getData works with complex nested data structures", async ({
2708 |     initTestBed,
2709 |     page,
2710 |     createTextBoxDriver,
2711 |     createFormItemDriver,
2712 |   }) => {
2713 |     const { testStateDriver } = await initTestBed(`
2714 |       <Fragment>
2715 |         <Form id="myForm" data="{{address: {street: '123 Main St', city: 'Springfield'}}}">
2716 |           <FormItem label="Street" bindTo="address.street" testId="streetInput" />
2717 |           <FormItem label="City" bindTo="address.city" testId="cityInput" />
2718 |         </Form>
2719 |         <Button testId="getDataBtn" onClick="testState = myForm.getData()" />
2720 |       </Fragment>
2721 |     `);
2722 | 
2723 |     const streetElement = (await createFormItemDriver("streetInput")).input;
2724 |     const streetDriver = await createTextBoxDriver(streetElement);
2725 |     await streetDriver.field.fill("456 Oak Ave");
2726 | 
2727 |     await page.getByTestId("getDataBtn").click();
2728 |     const data = await testStateDriver.testState();
2729 | 
2730 |     expect(data).toEqual({
2731 |       address: {
2732 |         street: "456 Oak Ave",
2733 |         city: "Springfield",
2734 |       },
2735 |     });
2736 |   });
2737 | 
2738 |   test("getData can be called multiple times without side effects", async ({
2739 |     initTestBed,
2740 |     page,
2741 |   }) => {
2742 |     const { testStateDriver } = await initTestBed(`
2743 |       <Fragment>
2744 |         <Form id="myForm">
2745 |           <FormItem label="Name" bindTo="name" initialValue="John" />
2746 |         </Form>
2747 |         <Button testId="getDataBtn" onClick="testState = (testState || 0) + 1; myForm.getData()" />
2748 |       </Fragment>
2749 |     `);
2750 | 
2751 |     await page.getByTestId("getDataBtn").click();
2752 |     let counter = await testStateDriver.testState();
2753 |     expect(counter).toEqual(1);
2754 | 
2755 |     await page.getByTestId("getDataBtn").click();
2756 |     counter = await testStateDriver.testState();
2757 |     expect(counter).toEqual(2);
2758 | 
2759 |     await page.getByTestId("getDataBtn").click();
2760 |     counter = await testStateDriver.testState();
2761 |     expect(counter).toEqual(3);
2762 |   });
2763 | 
2764 |   test("getData with empty field values", async ({
2765 |     initTestBed,
2766 |     page,
2767 |     createTextBoxDriver,
2768 |     createFormItemDriver,
2769 |   }) => {
2770 |     const { testStateDriver } = await initTestBed(`
2771 |       <Fragment>
2772 |         <Form id="myForm">
2773 |           <FormItem label="Name" bindTo="name" initialValue="John" testId="nameInput" />
2774 |           <FormItem label="Email" bindTo="email" testId="emailInput" />
2775 |         </Form>
2776 |         <Button testId="getDataBtn" onClick="testState = myForm.getData()" />
2777 |       </Fragment>
2778 |     `);
2779 | 
2780 |     const nameElement = (await createFormItemDriver("nameInput")).input;
2781 |     const nameDriver = await createTextBoxDriver(nameElement);
2782 |     await nameDriver.field.fill("");
2783 | 
2784 |     await page.getByTestId("getDataBtn").click();
2785 |     const data = await testStateDriver.testState();
2786 | 
2787 |     expect(data.name).toEqual("");
2788 |     expect(data.email).toBeUndefined();
2789 |   });
2790 | 
2791 |   test("getData with multiple calls returns consistent data", async ({
2792 |     initTestBed,
2793 |     page,
2794 |     createTextBoxDriver,
2795 |     createFormItemDriver,
2796 |   }) => {
2797 |     const { testStateDriver } = await initTestBed(`
2798 |       <Fragment>
2799 |         <Form id="myForm">
2800 |           <FormItem label="Name" bindTo="name" initialValue="John" testId="nameInput" />
2801 |         </Form>
2802 |         <Button testId="getDataBtn" onClick="testState = myForm.getData()" />
2803 |       </Fragment>
2804 |     `);
2805 | 
2806 |     const inputElement = (await createFormItemDriver("nameInput")).input;
2807 |     const fieldDriver = await createTextBoxDriver(inputElement);
2808 |     await fieldDriver.field.fill("Jane");
2809 | 
2810 |     // First call
2811 |     await page.getByTestId("getDataBtn").click();
2812 |     let data = await testStateDriver.testState();
2813 |     expect(data.name).toEqual("Jane");
2814 | 
2815 |     // Second call should return same data
2816 |     await page.getByTestId("getDataBtn").click();
2817 |     data = await testStateDriver.testState();
2818 |     expect(data.name).toEqual("Jane");
2819 |   });
2820 | 
2821 |   // =============================================================================
2822 |   // IMMUTABILITY TESTS - Proves that modifications to returned data don't affect form
2823 |   // =============================================================================
2824 | 
2825 |   test("modifying returned data property does not affect form data - simple property", async ({
2826 |     initTestBed,
2827 |     page,
2828 |   }) => {
2829 |     const { testStateDriver } = await initTestBed(`
2830 |       <Fragment>
2831 |         <Form id="myForm">
2832 |           <FormItem label="Name" bindTo="name" initialValue="John" />
2833 |         </Form>
2834 |         <Button testId="getAndModifyBtn" onClick="const data = myForm.getData(); data.name = 'Hacker'; testState = myForm.getData()" />
2835 |       </Fragment>
2836 |     `);
2837 | 
2838 |     await page.getByTestId("getAndModifyBtn").click();
2839 |     const data = await testStateDriver.testState();
2840 | 
2841 |     expect(data.name).toEqual("John");
2842 |   });
2843 | 
2844 |   test("modifying returned data property does not affect form data - adding new property", async ({
2845 |     initTestBed,
2846 |     page,
2847 |   }) => {
2848 |     const { testStateDriver } = await initTestBed(`
2849 |       <Fragment>
2850 |         <Form id="myForm">
2851 |           <FormItem label="Name" bindTo="name" initialValue="John" />
2852 |         </Form>
2853 |         <Button testId="getAndModifyBtn" onClick="const data = myForm.getData(); data.injected = 'malicious'; testState = myForm.getData()" />
2854 |       </Fragment>
2855 |     `);
2856 | 
2857 |     await page.getByTestId("getAndModifyBtn").click();
2858 |     const data = await testStateDriver.testState();
2859 | 
2860 |     expect(data.name).toEqual("John");
2861 |     expect(data.injected).toBeUndefined();
2862 |   });
2863 | 
2864 |   test("modifying returned data property does not affect form data - nested object", async ({
2865 |     initTestBed,
2866 |     page,
2867 |   }) => {
2868 |     const { testStateDriver } = await initTestBed(`
2869 |       <Fragment>
2870 |         <Form id="myForm" data="{{address: {street: '123 Main St', city: 'Springfield'}}}">
2871 |           <FormItem label="Street" bindTo="address.street" />
2872 |           <FormItem label="City" bindTo="address.city" />
2873 |         </Form>
2874 |         <Button testId="getAndModifyBtn" onClick="const data = myForm.getData(); data.address.street = 'Hacked'; testState = myForm.getData()" />
2875 |       </Fragment>
2876 |     `);
2877 | 
2878 |     await page.getByTestId("getAndModifyBtn").click();
2879 |     const data = await testStateDriver.testState();
2880 | 
2881 |     expect(data.address.street).toEqual("123 Main St");
2882 |     expect(data.address.city).toEqual("Springfield");
2883 |   });
2884 | 
2885 |   test("modifying returned data property does not affect form data - setting nested property to null", async ({
2886 |     initTestBed,
2887 |     page,
2888 |   }) => {
2889 |     const { testStateDriver } = await initTestBed(`
2890 |       <Fragment>
2891 |         <Form id="myForm" data="{{address: {street: '123 Main St', city: 'Springfield'}}}">
2892 |           <FormItem label="Street" bindTo="address.street" />
2893 |           <FormItem label="City" bindTo="address.city" />
2894 |         </Form>
2895 |         <Button testId="getAndModifyBtn" onClick="const data = myForm.getData(); data.address = null; testState = myForm.getData()" />
2896 |       </Fragment>
2897 |     `);
2898 | 
2899 |     await page.getByTestId("getAndModifyBtn").click();
2900 |     const data = await testStateDriver.testState();
2901 | 
2902 |     expect(data.address).not.toBeNull();
2903 |     expect(data.address.street).toEqual("123 Main St");
2904 |   });
2905 | 
2906 |   test("modifying returned data by deleting property does not affect form data", async ({
2907 |     initTestBed,
2908 |     page,
2909 |   }) => {
2910 |     const { testStateDriver } = await initTestBed(`
2911 |       <Fragment>
2912 |         <Form id="myForm">
2913 |           <FormItem label="Name" bindTo="name" initialValue="John" />
2914 |           <FormItem label="Email" bindTo="email" initialValue="[email protected]" />
2915 |         </Form>
2916 |         <Button testId="getAndModifyBtn" onClick="const data = myForm.getData(); delete data.name; testState = myForm.getData()" />
2917 |       </Fragment>
2918 |     `);
2919 | 
2920 |     await page.getByTestId("getAndModifyBtn").click();
2921 |     const data = await testStateDriver.testState();
2922 | 
2923 |     expect(data.name).toEqual("John");
2924 |     expect(data.email).toEqual("[email protected]");
2925 |   });
2926 | 
2927 |   test("modifying returned data array property does not affect form data", async ({
2928 |     initTestBed,
2929 |     page,
2930 |   }) => {
2931 |     const { testStateDriver } = await initTestBed(`
2932 |       <Fragment>
2933 |         <Form id="myForm" data="{{tags: ['tag1', 'tag2']}}">
2934 |           <FormItem label="Tags" bindTo="tags" />
2935 |         </Form>
2936 |         <Button testId="getAndModifyBtn" onClick="const data = myForm.getData(); data.tags.push('hacked'); testState = myForm.getData()" />
2937 |       </Fragment>
2938 |     `);
2939 | 
2940 |     await page.getByTestId("getAndModifyBtn").click();
2941 |     const data = await testStateDriver.testState();
2942 | 
2943 |     expect(data.tags).toEqual(["tag1", "tag2"]);
2944 |     expect(data.tags.length).toEqual(2);
2945 |   });
2946 | 
2947 |   test("modifying returned data by assigning new object does not affect form data", async ({
2948 |     initTestBed,
2949 |     page,
2950 |   }) => {
2951 |     const { testStateDriver } = await initTestBed(`
2952 |       <Fragment>
2953 |         <Form id="myForm">
2954 |           <FormItem label="Name" bindTo="name" initialValue="John" />
2955 |           <FormItem label="Email" bindTo="email" initialValue="[email protected]" />
2956 |         </Form>
2957 |         <Button testId="getAndModifyBtn" onClick="const data = myForm.getData(); data.name = 'Hacker'; data.email = '[email protected]'; testState = myForm.getData()" />
2958 |       </Fragment>
2959 |     `);
2960 | 
2961 |     await page.getByTestId("getAndModifyBtn").click();
2962 |     const data = await testStateDriver.testState();
2963 | 
2964 |     expect(data.name).toEqual("John");
2965 |     expect(data.email).toEqual("[email protected]");
2966 |   });
2967 | 
2968 |   test("each call to getData returns independent copy - modifying first does not affect second", async ({
2969 |     initTestBed,
2970 |     page,
2971 |   }) => {
2972 |     const { testStateDriver } = await initTestBed(`
2973 |       <Fragment>
2974 |         <Form id="myForm">
2975 |           <FormItem label="Name" bindTo="name" initialValue="John" />
2976 |         </Form>
2977 |         <Button testId="testBtn" onClick="const first = myForm.getData(); first.name = 'Modified'; const second = myForm.getData(); testState = {first: first, second: second, current: myForm.getData()}" />
2978 |       </Fragment>
2979 |     `);
2980 | 
2981 |     await page.getByTestId("testBtn").click();
2982 |     const result = await testStateDriver.testState();
2983 | 
2984 |     expect(result.first.name).toEqual("Modified");
2985 |     expect(result.second.name).toEqual("John");
2986 |     expect(result.current.name).toEqual("John");
2987 |   });
2988 | 
2989 |   test("modifying returned data in multiple ways simultaneously does not affect form", async ({
2990 |     initTestBed,
2991 |     page,
2992 |   }) => {
2993 |     const { testStateDriver } = await initTestBed(`
2994 |       <Fragment>
2995 |         <Form id="myForm" data="{{user: {name: 'John', age: 30}, tags: ['a', 'b']}}">
2996 |           <FormItem label="Name" bindTo="user.name" />
2997 |           <FormItem label="Age" bindTo="user.age" />
2998 |           <FormItem label="Tags" bindTo="tags" />
2999 |         </Form>
3000 |         <Button testId="getAndModifyBtn" onClick="const data = myForm.getData(); data.user.name = 'Hacked'; data.user.age = 99; data.tags.push('hacked'); data.newField = 'injected'; testState = myForm.getData()" />
3001 |       </Fragment>
3002 |     `);
3003 | 
3004 |     await page.getByTestId("getAndModifyBtn").click();
3005 |     const data = await testStateDriver.testState();
3006 | 
3007 |     expect(data.user.name).toEqual("John");
3008 |     expect(data.user.age).toEqual(30);
3009 |     expect(data.tags).toEqual(["a", "b"]);
3010 |     expect(data.newField).toBeUndefined();
3011 |   });
3012 | 
3013 |   test("initialValue works with undefined and null fields", async ({ initTestBed, page }) => {
3014 |     const { testStateDriver } = await initTestBed(
3015 |       `
3016 |       <Fragment>
3017 |         <Form id="myForm" data="/formData">
3018 |           <FormItem label="First name" bindTo="firstName" initialValue="Lucy" testId="firstNameField" />
3019 |           <FormItem label="Last name" bindTo="lastName" initialValue="Rose" testId="lastNameField"/>
3020 |         </Form>
3021 |         <Button testId="getBtn" onClick="testState = myForm.getData()" />
3022 |       </Fragment>
3023 |     `,
3024 |       {
3025 |         apiInterceptor: {
3026 |           operations: {
3027 |             read: {
3028 |               url: "/formData",
3029 |               method: "get",
3030 |               handler: `() => {
3031 |                   return {
3032 |                     firstName: null
3033 |                   };
3034 |               }`,
3035 |             },
3036 |           },
3037 |         },
3038 |       },
3039 |     );
3040 | 
3041 |     await expect(page.getByTestId("firstNameField").getByRole("textbox")).toBeEnabled(); //wait for form to load
3042 |     await expect(page.getByTestId("firstNameField").getByRole("textbox")).toHaveValue("Lucy");
3043 |     await expect(page.getByTestId("lastNameField").getByRole("textbox")).toHaveValue("Rose");
3044 | 
3045 |     await page.getByTestId("getBtn").click();
3046 |     const data = await testStateDriver.testState();
3047 | 
3048 |     expect(data.firstName).toEqual("Lucy");
3049 |     expect(data.lastName).toEqual("Rose");
3050 |   });
3051 | });
3052 | 
```
Page 217/224FirstPrevNextLast