This is page 101 of 144. Use http://codebase.md/xmlui-org/xmlui/xmlui-latest.js?lines=false&page={x} to view the full context.
# Directory Structure
```
├── .changeset
│ ├── config.json
│ ├── cyan-tools-design.md
│ ├── every-moments-teach.md
│ ├── fancy-laws-drop.md
│ ├── full-symbols-accept.md
│ └── tricky-zoos-crash.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.yml
│ └── run-smoke-tests.yml
├── .gitignore
├── .prettierrc.js
├── .vscode
│ ├── launch.json
│ └── settings.json
├── blog
│ ├── .gitignore
│ ├── .gitkeep
│ ├── CHANGELOG.md
│ ├── extensions.ts
│ ├── index.html
│ ├── index.ts
│ ├── package.json
│ ├── public
│ │ ├── blog
│ │ │ ├── images
│ │ │ │ ├── an-advanced-codefence.gif
│ │ │ │ ├── an-advanced-codefence.mp4
│ │ │ │ ├── blog-page-component.png
│ │ │ │ ├── blog-scrabble.png
│ │ │ │ ├── codefence-runner.png
│ │ │ │ ├── integrated-blog-search.png
│ │ │ │ ├── lorem-ipsum.png
│ │ │ │ ├── playground-checkbox-source.png
│ │ │ │ ├── playground.png
│ │ │ │ ├── use-xmlui-mcp-to-find-a-howto.png
│ │ │ │ └── xmlui-demo-gallery.png
│ │ │ ├── introducing-xmlui.md
│ │ │ ├── lorem-ipsum.md
│ │ │ ├── newest-post.md
│ │ │ ├── older-post.md
│ │ │ ├── xmlui-playground.md
│ │ │ └── xmlui-powered-blog.md
│ │ ├── mockServiceWorker.js
│ │ ├── resources
│ │ │ ├── favicon.ico
│ │ │ ├── files
│ │ │ │ └── for-download
│ │ │ │ └── xmlui
│ │ │ │ └── xmlui-standalone.umd.js
│ │ │ ├── github.svg
│ │ │ ├── llms.txt
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo.svg
│ │ │ ├── pg-popout.svg
│ │ │ ├── rss.svg
│ │ │ └── xmlui-logo.svg
│ │ ├── serve.json
│ │ ├── staticwebapp.config.json
│ │ └── web.config
│ ├── scripts
│ │ ├── download-latest-xmlui.js
│ │ ├── generate-rss.js
│ │ ├── get-releases.js
│ │ └── utils.js
│ ├── src
│ │ ├── components
│ │ │ ├── BlogOverview.xmlui
│ │ │ ├── BlogPage.xmlui
│ │ │ └── PageNotFound.xmlui
│ │ ├── config.ts
│ │ ├── Main.xmlui
│ │ └── themes
│ │ └── blog-theme.ts
│ └── tsconfig.json
├── CONTRIBUTING.md
├── docs
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── ComponentRefLinks.txt
│ ├── content
│ │ ├── _meta.json
│ │ ├── components
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── APICall.md
│ │ │ ├── App.md
│ │ │ ├── AppHeader.md
│ │ │ ├── AppState.md
│ │ │ ├── AutoComplete.md
│ │ │ ├── Avatar.md
│ │ │ ├── Backdrop.md
│ │ │ ├── Badge.md
│ │ │ ├── BarChart.md
│ │ │ ├── Bookmark.md
│ │ │ ├── Breakout.md
│ │ │ ├── Button.md
│ │ │ ├── Card.md
│ │ │ ├── Carousel.md
│ │ │ ├── ChangeListener.md
│ │ │ ├── Checkbox.md
│ │ │ ├── CHStack.md
│ │ │ ├── ColorPicker.md
│ │ │ ├── Column.md
│ │ │ ├── ContentSeparator.md
│ │ │ ├── CVStack.md
│ │ │ ├── DataSource.md
│ │ │ ├── DateInput.md
│ │ │ ├── DatePicker.md
│ │ │ ├── DonutChart.md
│ │ │ ├── DropdownMenu.md
│ │ │ ├── EmojiSelector.md
│ │ │ ├── ExpandableItem.md
│ │ │ ├── FileInput.md
│ │ │ ├── FileUploadDropZone.md
│ │ │ ├── FlowLayout.md
│ │ │ ├── Footer.md
│ │ │ ├── Form.md
│ │ │ ├── FormItem.md
│ │ │ ├── FormSection.md
│ │ │ ├── Fragment.md
│ │ │ ├── H1.md
│ │ │ ├── H2.md
│ │ │ ├── H3.md
│ │ │ ├── H4.md
│ │ │ ├── H5.md
│ │ │ ├── H6.md
│ │ │ ├── Heading.md
│ │ │ ├── HSplitter.md
│ │ │ ├── HStack.md
│ │ │ ├── Icon.md
│ │ │ ├── IFrame.md
│ │ │ ├── Image.md
│ │ │ ├── Items.md
│ │ │ ├── LabelList.md
│ │ │ ├── Legend.md
│ │ │ ├── LineChart.md
│ │ │ ├── Link.md
│ │ │ ├── List.md
│ │ │ ├── Logo.md
│ │ │ ├── Markdown.md
│ │ │ ├── MenuItem.md
│ │ │ ├── MenuSeparator.md
│ │ │ ├── ModalDialog.md
│ │ │ ├── NavGroup.md
│ │ │ ├── NavLink.md
│ │ │ ├── NavPanel.md
│ │ │ ├── NoResult.md
│ │ │ ├── NumberBox.md
│ │ │ ├── Option.md
│ │ │ ├── Page.md
│ │ │ ├── PageMetaTitle.md
│ │ │ ├── Pages.md
│ │ │ ├── Pagination.md
│ │ │ ├── PasswordInput.md
│ │ │ ├── PieChart.md
│ │ │ ├── ProgressBar.md
│ │ │ ├── Queue.md
│ │ │ ├── RadioGroup.md
│ │ │ ├── RealTimeAdapter.md
│ │ │ ├── Redirect.md
│ │ │ ├── Select.md
│ │ │ ├── Slider.md
│ │ │ ├── Slot.md
│ │ │ ├── SpaceFiller.md
│ │ │ ├── Spinner.md
│ │ │ ├── Splitter.md
│ │ │ ├── Stack.md
│ │ │ ├── StickyBox.md
│ │ │ ├── SubMenuItem.md
│ │ │ ├── Switch.md
│ │ │ ├── TabItem.md
│ │ │ ├── Table.md
│ │ │ ├── TableOfContents.md
│ │ │ ├── Tabs.md
│ │ │ ├── Text.md
│ │ │ ├── TextArea.md
│ │ │ ├── TextBox.md
│ │ │ ├── Theme.md
│ │ │ ├── TimeInput.md
│ │ │ ├── Timer.md
│ │ │ ├── ToneChangerButton.md
│ │ │ ├── ToneSwitch.md
│ │ │ ├── Tooltip.md
│ │ │ ├── Tree.md
│ │ │ ├── VSplitter.md
│ │ │ ├── VStack.md
│ │ │ ├── xmlui-animations
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── Animation.md
│ │ │ │ ├── FadeAnimation.md
│ │ │ │ ├── FadeInAnimation.md
│ │ │ │ ├── FadeOutAnimation.md
│ │ │ │ ├── ScaleAnimation.md
│ │ │ │ └── SlideInAnimation.md
│ │ │ ├── xmlui-pdf
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Pdf.md
│ │ │ ├── xmlui-spreadsheet
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Spreadsheet.md
│ │ │ └── xmlui-website-blocks
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── Carousel.md
│ │ │ ├── HelloMd.md
│ │ │ ├── HeroSection.md
│ │ │ └── ScrollToTop.md
│ │ └── extensions
│ │ ├── _meta.json
│ │ ├── xmlui-animations
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── Animation.md
│ │ │ ├── FadeAnimation.md
│ │ │ ├── FadeInAnimation.md
│ │ │ ├── FadeOutAnimation.md
│ │ │ ├── ScaleAnimation.md
│ │ │ └── SlideInAnimation.md
│ │ └── xmlui-website-blocks
│ │ ├── _meta.json
│ │ ├── _overview.md
│ │ ├── Carousel.md
│ │ ├── 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
│ │ │ │ ├── debounce-user-input-for-api-calls.md
│ │ │ │ ├── debounce-with-changelistener.md
│ │ │ │ ├── debug-a-component.md
│ │ │ │ ├── delay-a-datasource-until-another-datasource-is-ready.md
│ │ │ │ ├── delegate-a-method.md
│ │ │ │ ├── do-custom-form-validation.md
│ │ │ │ ├── expose-a-method-from-a-component.md
│ │ │ │ ├── filter-and-transform-data-from-an-api.md
│ │ │ │ ├── group-items-in-list-by-a-property.md
│ │ │ │ ├── handle-background-operations.md
│ │ │ │ ├── hide-an-element-until-its-datasource-is-ready.md
│ │ │ │ ├── make-a-set-of-equal-width-cards.md
│ │ │ │ ├── make-a-table-responsive.md
│ │ │ │ ├── make-navpanel-width-responsive.md
│ │ │ │ ├── modify-a-value-reported-in-a-column.md
│ │ │ │ ├── paginate-a-list.md
│ │ │ │ ├── pass-data-to-a-modal-dialog.md
│ │ │ │ ├── react-to-button-click-not-keystrokes.md
│ │ │ │ ├── set-the-initial-value-of-a-select-from-fetched-data.md
│ │ │ │ ├── share-a-modaldialog-across-components.md
│ │ │ │ ├── sync-selections-between-table-and-list-views.md
│ │ │ │ ├── update-ui-optimistically.md
│ │ │ │ ├── use-built-in-form-validation.md
│ │ │ │ └── use-the-same-modaldialog-to-add-or-edit.md
│ │ │ ├── howto.md
│ │ │ ├── intro.md
│ │ │ ├── layout.md
│ │ │ ├── markup.md
│ │ │ ├── mcp.md
│ │ │ ├── modal-dialogs.md
│ │ │ ├── news-and-reviews.md
│ │ │ ├── reactive-intro.md
│ │ │ ├── refactoring.md
│ │ │ ├── routing-and-links.md
│ │ │ ├── samples
│ │ │ │ ├── color-palette.xmlui
│ │ │ │ ├── color-values.xmlui
│ │ │ │ ├── shadow-sizes.xmlui
│ │ │ │ ├── spacing-sizes.xmlui
│ │ │ │ ├── swatch.xmlui
│ │ │ │ ├── theme-gallery-brief.xmlui
│ │ │ │ └── theme-gallery.xmlui
│ │ │ ├── scoping.md
│ │ │ ├── scripting.md
│ │ │ ├── styles-and-themes
│ │ │ │ ├── common-units.md
│ │ │ │ ├── layout-props.md
│ │ │ │ ├── theme-variable-defaults.md
│ │ │ │ ├── theme-variables.md
│ │ │ │ └── themes.md
│ │ │ ├── template-properties.md
│ │ │ ├── test.md
│ │ │ ├── tutorial-01.md
│ │ │ ├── tutorial-02.md
│ │ │ ├── tutorial-03.md
│ │ │ ├── tutorial-04.md
│ │ │ ├── tutorial-05.md
│ │ │ ├── tutorial-06.md
│ │ │ ├── tutorial-07.md
│ │ │ ├── tutorial-08.md
│ │ │ ├── tutorial-09.md
│ │ │ ├── tutorial-10.md
│ │ │ ├── tutorial-11.md
│ │ │ ├── tutorial-12.md
│ │ │ ├── universal-properties.md
│ │ │ ├── user-defined-components.md
│ │ │ ├── vscode.md
│ │ │ ├── working-with-markdown.md
│ │ │ ├── working-with-text.md
│ │ │ ├── xmlui-animations
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── Animation.md
│ │ │ │ ├── FadeAnimation.md
│ │ │ │ ├── FadeInAnimation.md
│ │ │ │ ├── FadeOutAnimation.md
│ │ │ │ ├── ScaleAnimation.md
│ │ │ │ └── SlideInAnimation.md
│ │ │ ├── xmlui-charts
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── BarChart.md
│ │ │ │ ├── DonutChart.md
│ │ │ │ ├── LabelList.md
│ │ │ │ ├── Legend.md
│ │ │ │ ├── LineChart.md
│ │ │ │ └── PieChart.md
│ │ │ ├── xmlui-pdf
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Pdf.md
│ │ │ └── xmlui-spreadsheet
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ └── Spreadsheet.md
│ │ ├── resources
│ │ │ ├── devdocs
│ │ │ │ ├── debug-proxy-object-2.png
│ │ │ │ ├── debug-proxy-object.png
│ │ │ │ ├── table_editor_01.png
│ │ │ │ ├── table_editor_02.png
│ │ │ │ ├── table_editor_03.png
│ │ │ │ ├── table_editor_04.png
│ │ │ │ ├── table_editor_05.png
│ │ │ │ ├── table_editor_06.png
│ │ │ │ ├── table_editor_07.png
│ │ │ │ ├── table_editor_08.png
│ │ │ │ ├── table_editor_09.png
│ │ │ │ ├── table_editor_10.png
│ │ │ │ ├── table_editor_11.png
│ │ │ │ ├── table-editor-01.png
│ │ │ │ ├── table-editor-02.png
│ │ │ │ ├── table-editor-03.png
│ │ │ │ ├── table-editor-04.png
│ │ │ │ ├── table-editor-06.png
│ │ │ │ ├── table-editor-07.png
│ │ │ │ ├── table-editor-08.png
│ │ │ │ ├── table-editor-09.png
│ │ │ │ └── xmlui-rendering-of-tiptap-markdown.png
│ │ │ ├── favicon.ico
│ │ │ ├── files
│ │ │ │ ├── clients.json
│ │ │ │ ├── daily-revenue.json
│ │ │ │ ├── dashboard-stats.json
│ │ │ │ ├── demo.xmlui
│ │ │ │ ├── demo.xmlui.xs
│ │ │ │ ├── downloads
│ │ │ │ │ └── downloads.json
│ │ │ │ ├── for-download
│ │ │ │ │ ├── index-with-api.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── mockApi.js
│ │ │ │ │ ├── start-darwin.sh
│ │ │ │ │ ├── start-linux.sh
│ │ │ │ │ ├── start.bat
│ │ │ │ │ └── xmlui
│ │ │ │ │ └── xmlui-standalone.umd.js
│ │ │ │ ├── getting-started
│ │ │ │ │ ├── cl-tutorial-final.zip
│ │ │ │ │ ├── cl-tutorial.zip
│ │ │ │ │ ├── cl-tutorial2.zip
│ │ │ │ │ ├── cl-tutorial3.zip
│ │ │ │ │ ├── cl-tutorial4.zip
│ │ │ │ │ ├── cl-tutorial5.zip
│ │ │ │ │ ├── cl-tutorial6.zip
│ │ │ │ │ ├── getting-started.zip
│ │ │ │ │ ├── hello-xmlui.zip
│ │ │ │ │ ├── xmlui-empty.zip
│ │ │ │ │ └── xmlui-starter.zip
│ │ │ │ ├── howto
│ │ │ │ │ └── component-icons
│ │ │ │ │ └── up-arrow.svg
│ │ │ │ ├── invoices.json
│ │ │ │ ├── monthly-status.json
│ │ │ │ ├── news-and-reviews.json
│ │ │ │ ├── products.json
│ │ │ │ ├── releases.json
│ │ │ │ ├── tutorials
│ │ │ │ │ ├── datasource
│ │ │ │ │ │ └── api.ts
│ │ │ │ │ └── p2do
│ │ │ │ │ ├── api.ts
│ │ │ │ │ └── todo-logo.svg
│ │ │ │ └── xmlui.json
│ │ │ ├── github.svg
│ │ │ ├── images
│ │ │ │ ├── apiaction-tutorial
│ │ │ │ │ ├── add-success.png
│ │ │ │ │ ├── apiaction-param.png
│ │ │ │ │ ├── change-completed.png
│ │ │ │ │ ├── change-in-progress.png
│ │ │ │ │ ├── confirm-delete.png
│ │ │ │ │ ├── data-error.png
│ │ │ │ │ ├── data-progress.png
│ │ │ │ │ ├── data-success.png
│ │ │ │ │ ├── display-1.png
│ │ │ │ │ ├── item-deleted.png
│ │ │ │ │ ├── item-updated.png
│ │ │ │ │ ├── missing-api-key.png
│ │ │ │ │ ├── new-item-added.png
│ │ │ │ │ └── test-message.png
│ │ │ │ ├── chat-api
│ │ │ │ │ └── domain-model.svg
│ │ │ │ ├── components
│ │ │ │ │ ├── image
│ │ │ │ │ │ └── breakfast.jpg
│ │ │ │ │ ├── markdown
│ │ │ │ │ │ └── colors.png
│ │ │ │ │ └── modal
│ │ │ │ │ ├── deep_link_dialog_1.jpg
│ │ │ │ │ └── deep_link_dialog_2.jpg
│ │ │ │ ├── create-apps
│ │ │ │ │ ├── collapsed-vertical.png
│ │ │ │ │ ├── using-forms-warning-dialog.png
│ │ │ │ │ └── using-forms.png
│ │ │ │ ├── datasource-tutorial
│ │ │ │ │ ├── data-with-header.png
│ │ │ │ │ ├── filtered-data.png
│ │ │ │ │ ├── filtered-items.png
│ │ │ │ │ ├── initial-page-items.png
│ │ │ │ │ ├── list-items.png
│ │ │ │ │ ├── next-page-items.png
│ │ │ │ │ ├── no-data.png
│ │ │ │ │ ├── pagination-1.jpg
│ │ │ │ │ ├── pagination-1.png
│ │ │ │ │ ├── polling-1.png
│ │ │ │ │ ├── refetch-data.png
│ │ │ │ │ ├── slow-loading.png
│ │ │ │ │ ├── test-message.png
│ │ │ │ │ ├── Thumbs.db
│ │ │ │ │ ├── unconventional-data.png
│ │ │ │ │ └── unfiltered-items.png
│ │ │ │ ├── flower.jpg
│ │ │ │ ├── get-started
│ │ │ │ │ ├── add-new-contact.png
│ │ │ │ │ ├── app-modified.png
│ │ │ │ │ ├── app-start.png
│ │ │ │ │ ├── app-with-boxes.png
│ │ │ │ │ ├── app-with-toast.png
│ │ │ │ │ ├── boilerplate-structure.png
│ │ │ │ │ ├── cl-initial.png
│ │ │ │ │ ├── cl-start.png
│ │ │ │ │ ├── contact-counts.png
│ │ │ │ │ ├── contact-dialog-title.png
│ │ │ │ │ ├── contact-dialog.png
│ │ │ │ │ ├── contact-menus.png
│ │ │ │ │ ├── contact-predicates.png
│ │ │ │ │ ├── context-menu.png
│ │ │ │ │ ├── dashboard-numbers.png
│ │ │ │ │ ├── default-contact-list.png
│ │ │ │ │ ├── delete-contact.png
│ │ │ │ │ ├── delete-task.png
│ │ │ │ │ ├── detailed-template.png
│ │ │ │ │ ├── edit-contact-details.png
│ │ │ │ │ ├── edited-contact-saved.png
│ │ │ │ │ ├── empty-sections.png
│ │ │ │ │ ├── filter-completed.png
│ │ │ │ │ ├── fullwidth-desktop.png
│ │ │ │ │ ├── fullwidth-mobile.png
│ │ │ │ │ ├── initial-table.png
│ │ │ │ │ ├── items-and-badges.png
│ │ │ │ │ ├── loading-message.png
│ │ │ │ │ ├── new-contact-button.png
│ │ │ │ │ ├── new-contact-saved.png
│ │ │ │ │ ├── no-empty-sections.png
│ │ │ │ │ ├── personal-todo-initial.png
│ │ │ │ │ ├── piechart.png
│ │ │ │ │ ├── review-today.png
│ │ │ │ │ ├── rudimentary-dashboard.png
│ │ │ │ │ ├── section-collapsed.png
│ │ │ │ │ ├── sectioned-items.png
│ │ │ │ │ ├── sections-ordered.png
│ │ │ │ │ ├── spacex-list-with-links.png
│ │ │ │ │ ├── spacex-list.png
│ │ │ │ │ ├── start-personal-todo-1.png
│ │ │ │ │ ├── submit-new-contact.png
│ │ │ │ │ ├── submit-new-task.png
│ │ │ │ │ ├── syntax-highlighting.png
│ │ │ │ │ ├── table-with-badge.png
│ │ │ │ │ ├── template-with-card.png
│ │ │ │ │ ├── test-emulated-api.png
│ │ │ │ │ ├── Thumbs.db
│ │ │ │ │ ├── todo-logo.png
│ │ │ │ │ └── xmlui-tools.png
│ │ │ │ ├── HelloApp.png
│ │ │ │ ├── HelloApp2.png
│ │ │ │ ├── logos
│ │ │ │ │ ├── xmlui1.svg
│ │ │ │ │ ├── xmlui2.svg
│ │ │ │ │ ├── xmlui3.svg
│ │ │ │ │ ├── xmlui4.svg
│ │ │ │ │ ├── xmlui5.svg
│ │ │ │ │ ├── xmlui6.svg
│ │ │ │ │ └── xmlui7.svg
│ │ │ │ ├── pdf
│ │ │ │ │ └── dummy-pdf.jpg
│ │ │ │ ├── rendering-engine
│ │ │ │ │ ├── AppEngine-flow.svg
│ │ │ │ │ ├── Component.svg
│ │ │ │ │ ├── CompoundComponent.svg
│ │ │ │ │ ├── RootComponent.svg
│ │ │ │ │ └── tree-with-containers.svg
│ │ │ │ ├── reviewers-guide
│ │ │ │ │ ├── AppEngine-flow.svg
│ │ │ │ │ └── incbutton-in-action.png
│ │ │ │ ├── tools
│ │ │ │ │ └── boilerplate-structure.png
│ │ │ │ ├── try.svg
│ │ │ │ ├── tutorial
│ │ │ │ │ ├── app-chat-history.png
│ │ │ │ │ ├── app-content-placeholder.png
│ │ │ │ │ ├── app-header-and-content.png
│ │ │ │ │ ├── app-links-channel-selected.png
│ │ │ │ │ ├── app-links-click.png
│ │ │ │ │ ├── app-navigation.png
│ │ │ │ │ ├── finished-ex01.png
│ │ │ │ │ ├── finished-ex02.png
│ │ │ │ │ ├── hello.png
│ │ │ │ │ ├── splash-screen-advanced.png
│ │ │ │ │ ├── splash-screen-after-click.png
│ │ │ │ │ ├── splash-screen-centered.png
│ │ │ │ │ ├── splash-screen-events.png
│ │ │ │ │ ├── splash-screen-expression.png
│ │ │ │ │ ├── splash-screen-reuse-after.png
│ │ │ │ │ ├── splash-screen-reuse-before.png
│ │ │ │ │ └── splash-screen.png
│ │ │ │ └── tutorial-01.png
│ │ │ ├── llms.txt
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo.svg
│ │ │ ├── pg-popout.svg
│ │ │ └── xmlui-logo.svg
│ │ ├── serve.json
│ │ └── web.config
│ ├── scripts
│ │ ├── download-latest-xmlui.js
│ │ ├── generate-rss.js
│ │ ├── get-releases.js
│ │ └── utils.js
│ ├── src
│ │ ├── components
│ │ │ ├── BlogOverview.xmlui
│ │ │ ├── BlogPage.xmlui
│ │ │ ├── Boxes.xmlui
│ │ │ ├── Breadcrumb.xmlui
│ │ │ ├── ChangeLog.xmlui
│ │ │ ├── ColorPalette.xmlui
│ │ │ ├── DocumentLinks.xmlui
│ │ │ ├── DocumentPage.xmlui
│ │ │ ├── DocumentPageNoTOC.xmlui
│ │ │ ├── Icons.xmlui
│ │ │ ├── IncButton.xmlui
│ │ │ ├── IncButton2.xmlui
│ │ │ ├── NameValue.xmlui
│ │ │ ├── PageNotFound.xmlui
│ │ │ ├── PaletteItem.xmlui
│ │ │ ├── Palettes.xmlui
│ │ │ ├── SectionHeader.xmlui
│ │ │ ├── TBD.xmlui
│ │ │ ├── Test.xmlui
│ │ │ ├── ThemesIntro.xmlui
│ │ │ ├── ThousandThemes.xmlui
│ │ │ ├── TubeStops.xmlui
│ │ │ ├── TubeStops.xmlui.xs
│ │ │ └── TwoColumnCode.xmlui
│ │ ├── config.ts
│ │ ├── Main.xmlui
│ │ └── themes
│ │ ├── docs-theme.ts
│ │ ├── earthtone.ts
│ │ ├── xmlui-gray-on-default.ts
│ │ ├── xmlui-green-on-default.ts
│ │ └── xmlui-orange-on-default.ts
│ └── tsconfig.json
├── LICENSE
├── package-lock.json
├── package.json
├── packages
│ ├── tsconfig.json
│ ├── xmlui-animations
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── Animation.tsx
│ │ ├── AnimationNative.tsx
│ │ ├── FadeAnimation.tsx
│ │ ├── FadeInAnimation.tsx
│ │ ├── FadeOutAnimation.tsx
│ │ ├── index.tsx
│ │ ├── ScaleAnimation.tsx
│ │ └── SlideInAnimation.tsx
│ ├── xmlui-devtools
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── devtools
│ │ │ │ ├── DevTools.tsx
│ │ │ │ ├── DevToolsNative.module.scss
│ │ │ │ ├── DevToolsNative.tsx
│ │ │ │ ├── ModalDialog.module.scss
│ │ │ │ ├── ModalDialog.tsx
│ │ │ │ ├── ModalVisibilityContext.tsx
│ │ │ │ ├── Tooltip.module.scss
│ │ │ │ ├── Tooltip.tsx
│ │ │ │ └── utils.ts
│ │ │ ├── editor
│ │ │ │ └── Editor.tsx
│ │ │ └── index.tsx
│ │ └── vite.config-overrides.ts
│ ├── xmlui-hello-world
│ │ ├── .gitignore
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── HelloWorld.module.scss
│ │ ├── HelloWorld.tsx
│ │ ├── HelloWorldNative.tsx
│ │ └── index.tsx
│ ├── xmlui-os-frames
│ │ ├── .gitignore
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── index.tsx
│ │ ├── IPhoneFrame.module.scss
│ │ ├── IPhoneFrame.tsx
│ │ ├── MacOSAppFrame.module.scss
│ │ ├── MacOSAppFrame.tsx
│ │ ├── WindowsAppFrame.module.scss
│ │ └── WindowsAppFrame.tsx
│ ├── xmlui-pdf
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ ├── components
│ │ │ │ └── Pdf.xmlui
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── index.tsx
│ │ ├── LazyPdfNative.tsx
│ │ ├── Pdf.module.scss
│ │ └── Pdf.tsx
│ ├── xmlui-playground
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── hooks
│ │ │ ├── usePlayground.ts
│ │ │ └── useToast.ts
│ │ ├── index.tsx
│ │ ├── playground
│ │ │ ├── Box.module.scss
│ │ │ ├── Box.tsx
│ │ │ ├── CodeSelector.module.scss
│ │ │ ├── CodeSelector.tsx
│ │ │ ├── ConfirmationDialog.module.scss
│ │ │ ├── ConfirmationDialog.tsx
│ │ │ ├── Editor.tsx
│ │ │ ├── Header.module.scss
│ │ │ ├── Header.tsx
│ │ │ ├── Playground.tsx
│ │ │ ├── PlaygroundContent.module.scss
│ │ │ ├── PlaygroundContent.tsx
│ │ │ ├── PlaygroundNative.module.scss
│ │ │ ├── PlaygroundNative.tsx
│ │ │ ├── Preview.tsx
│ │ │ ├── StandalonePlayground.tsx
│ │ │ ├── StandalonePlaygroundNative.module.scss
│ │ │ ├── StandalonePlaygroundNative.tsx
│ │ │ ├── ThemeSwitcher.module.scss
│ │ │ ├── ThemeSwitcher.tsx
│ │ │ └── utils.ts
│ │ ├── providers
│ │ │ ├── Toast.module.scss
│ │ │ └── ToastProvider.tsx
│ │ ├── state
│ │ │ └── store.ts
│ │ ├── themes
│ │ │ └── theme.ts
│ │ └── utils
│ │ └── helpers.ts
│ ├── xmlui-search
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── index.tsx
│ │ ├── Search.module.scss
│ │ └── Search.tsx
│ ├── xmlui-spreadsheet
│ │ ├── .gitignore
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ └── src
│ │ ├── index.tsx
│ │ ├── Spreadsheet.tsx
│ │ └── SpreadsheetNative.tsx
│ └── xmlui-website-blocks
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── demo
│ │ ├── components
│ │ │ ├── HeroBackgroundBreakoutPage.xmlui
│ │ │ ├── HeroBackgroundsPage.xmlui
│ │ │ ├── HeroContentsPage.xmlui
│ │ │ ├── HeroTextAlignPage.xmlui
│ │ │ ├── HeroTextPage.xmlui
│ │ │ └── HeroTonesPage.xmlui
│ │ ├── Main.xmlui
│ │ └── themes
│ │ └── default.ts
│ ├── index.html
│ ├── index.ts
│ ├── meta
│ │ └── componentsMetadata.ts
│ ├── package.json
│ ├── public
│ │ └── resources
│ │ ├── building.jpg
│ │ └── xmlui-logo.svg
│ └── src
│ ├── Carousel
│ │ ├── Carousel.module.scss
│ │ ├── Carousel.tsx
│ │ ├── CarouselContext.tsx
│ │ └── CarouselNative.tsx
│ ├── FancyButton
│ │ ├── FancyButton.module.scss
│ │ ├── FancyButton.tsx
│ │ └── FancyButton.xmlui
│ ├── Hello
│ │ ├── Hello.tsx
│ │ ├── Hello.xmlui
│ │ └── Hello.xmlui.xs
│ ├── HeroSection
│ │ ├── HeroSection.module.scss
│ │ ├── HeroSection.spec.ts
│ │ ├── HeroSection.tsx
│ │ └── HeroSectionNative.tsx
│ ├── index.tsx
│ ├── ScrollToTop
│ │ ├── ScrollToTop.module.scss
│ │ ├── ScrollToTop.tsx
│ │ └── ScrollToTopNative.tsx
│ └── vite-env.d.ts
├── playwright.config.ts
├── README.md
├── tools
│ ├── codefence
│ │ └── xmlui-code-fence-docs.md
│ ├── create-app
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── create-app.ts
│ │ ├── helpers
│ │ │ ├── copy.ts
│ │ │ ├── get-pkg-manager.ts
│ │ │ ├── git.ts
│ │ │ ├── install.ts
│ │ │ ├── is-folder-empty.ts
│ │ │ ├── is-writeable.ts
│ │ │ ├── make-dir.ts
│ │ │ └── validate-pkg.ts
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── templates
│ │ │ ├── default
│ │ │ │ └── ts
│ │ │ │ ├── gitignore
│ │ │ │ ├── index.html
│ │ │ │ ├── index.ts
│ │ │ │ ├── public
│ │ │ │ │ ├── mockServiceWorker.js
│ │ │ │ │ ├── resources
│ │ │ │ │ │ ├── favicon.ico
│ │ │ │ │ │ └── xmlui-logo.svg
│ │ │ │ │ └── serve.json
│ │ │ │ └── src
│ │ │ │ ├── components
│ │ │ │ │ ├── ApiAware.xmlui
│ │ │ │ │ ├── Home.xmlui
│ │ │ │ │ ├── IncButton.xmlui
│ │ │ │ │ └── PagePanel.xmlui
│ │ │ │ ├── config.ts
│ │ │ │ └── Main.xmlui
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ │ └── tsconfig.json
│ ├── create-xmlui-hello-world
│ │ ├── index.js
│ │ └── package.json
│ └── vscode
│ ├── .gitignore
│ ├── .vscode
│ │ ├── launch.json
│ │ └── tasks.json
│ ├── .vscodeignore
│ ├── build.sh
│ ├── CHANGELOG.md
│ ├── esbuild.js
│ ├── eslint.config.mjs
│ ├── formatter-docs.md
│ ├── generate-test-sample.sh
│ ├── LICENSE.md
│ ├── package-lock.json
│ ├── package.json
│ ├── README.md
│ ├── resources
│ │ ├── xmlui-logo.png
│ │ └── xmlui-markup-syntax-highlighting.png
│ ├── src
│ │ ├── extension.ts
│ │ └── server.ts
│ ├── syntaxes
│ │ └── xmlui.tmLanguage.json
│ ├── test-samples
│ │ └── sample.xmlui
│ ├── tsconfig.json
│ └── tsconfig.tsbuildinfo
├── turbo.json
└── xmlui
├── .gitignore
├── bin
│ ├── bootstrap.cjs
│ ├── bootstrap.js
│ ├── build-lib.ts
│ ├── build.ts
│ ├── index.ts
│ ├── preview.ts
│ ├── start.ts
│ ├── vite-xmlui-plugin.ts
│ └── viteConfig.ts
├── CHANGELOG.md
├── conventions
│ ├── component-qa-checklist.md
│ ├── copilot-conventions.md
│ ├── create-xmlui-components.md
│ ├── mermaid.md
│ ├── testing-conventions.md
│ └── xmlui-in-a-nutshell.md
├── dev-docs
│ ├── accessibility.md
│ ├── build-system.md
│ ├── build-xmlui.md
│ ├── component-behaviors.md
│ ├── component-metadata.md
│ ├── components-with-options.md
│ ├── containers.md
│ ├── data-operations.md
│ ├── glossary.md
│ ├── index.md
│ ├── next
│ │ ├── component-dev-guide.md
│ │ ├── configuration-management-enhancement-summary.md
│ │ ├── documentation-scripts-refactoring-complete-summary.md
│ │ ├── documentation-scripts-refactoring-plan.md
│ │ ├── duplicate-pattern-extraction-summary.md
│ │ ├── error-handling-standardization-summary.md
│ │ ├── generating-component-reference.md
│ │ ├── index.md
│ │ ├── logging-consistency-implementation-summary.md
│ │ ├── project-build.md
│ │ ├── project-structure.md
│ │ ├── theme-context.md
│ │ ├── tiptap-design-considerations.md
│ │ ├── working-with-code.md
│ │ ├── xmlui-runtime-architecture
│ │ └── xmlui-wcag-accessibility-report.md
│ ├── react-fundamentals.md
│ ├── release-method.md
│ ├── standalone-app.md
│ ├── theme-variables-refactoring.md
│ ├── ud-components.md
│ └── xmlui-repo.md
├── package.json
├── scripts
│ ├── coverage-only.js
│ ├── e2e-test-summary.js
│ ├── extract-component-metadata.js
│ ├── generate-docs
│ │ ├── build-downloads-map.mjs
│ │ ├── build-pages-map.mjs
│ │ ├── components-config.json
│ │ ├── configuration-management.mjs
│ │ ├── constants.mjs
│ │ ├── create-theme-files.mjs
│ │ ├── DocsGenerator.mjs
│ │ ├── error-handling.mjs
│ │ ├── extensions-config.json
│ │ ├── folders.mjs
│ │ ├── generate-summary-files.mjs
│ │ ├── get-docs.mjs
│ │ ├── input-handler.mjs
│ │ ├── logger.mjs
│ │ ├── logging-standards.mjs
│ │ ├── MetadataProcessor.mjs
│ │ ├── pattern-utilities.mjs
│ │ └── utils.mjs
│ ├── generate-metadata-markdown.js
│ ├── get-langserver-metadata.js
│ ├── inline-links.mjs
│ └── README-e2e-summary.md
├── src
│ ├── abstractions
│ │ ├── _conventions.md
│ │ ├── ActionDefs.ts
│ │ ├── AppContextDefs.ts
│ │ ├── ComponentDefs.ts
│ │ ├── ContainerDefs.ts
│ │ ├── ExtensionDefs.ts
│ │ ├── FunctionDefs.ts
│ │ ├── RendererDefs.ts
│ │ ├── scripting
│ │ │ ├── BlockScope.ts
│ │ │ ├── Compilation.ts
│ │ │ ├── LogicalThread.ts
│ │ │ ├── LoopScope.ts
│ │ │ ├── modules.ts
│ │ │ ├── ScriptParserError.ts
│ │ │ ├── Token.ts
│ │ │ ├── TryScope.ts
│ │ │ └── TryScopeExp.ts
│ │ └── ThemingDefs.ts
│ ├── components
│ │ ├── _conventions.md
│ │ ├── abstractions.ts
│ │ ├── Accordion
│ │ │ ├── Accordion.md
│ │ │ ├── Accordion.module.scss
│ │ │ ├── Accordion.spec.ts
│ │ │ ├── Accordion.tsx
│ │ │ ├── AccordionContext.tsx
│ │ │ ├── AccordionItem.tsx
│ │ │ ├── AccordionItemNative.tsx
│ │ │ └── AccordionNative.tsx
│ │ ├── Animation
│ │ │ └── AnimationNative.tsx
│ │ ├── APICall
│ │ │ ├── APICall.md
│ │ │ ├── APICall.spec.ts
│ │ │ ├── APICall.tsx
│ │ │ └── APICallNative.tsx
│ │ ├── App
│ │ │ ├── App.md
│ │ │ ├── App.module.scss
│ │ │ ├── App.spec.ts
│ │ │ ├── App.tsx
│ │ │ ├── AppLayoutContext.ts
│ │ │ ├── AppNative.tsx
│ │ │ ├── AppStateContext.ts
│ │ │ ├── doc-resources
│ │ │ │ ├── condensed-sticky.xmlui
│ │ │ │ ├── condensed.xmlui
│ │ │ │ ├── horizontal-sticky.xmlui
│ │ │ │ ├── horizontal.xmlui
│ │ │ │ ├── vertical-full-header.xmlui
│ │ │ │ ├── vertical-sticky.xmlui
│ │ │ │ └── vertical.xmlui
│ │ │ ├── IndexerContext.ts
│ │ │ ├── LinkInfoContext.ts
│ │ │ ├── SearchContext.tsx
│ │ │ ├── Sheet.module.scss
│ │ │ └── Sheet.tsx
│ │ ├── AppHeader
│ │ │ ├── AppHeader.md
│ │ │ ├── AppHeader.module.scss
│ │ │ ├── AppHeader.spec.ts
│ │ │ ├── AppHeader.tsx
│ │ │ └── AppHeaderNative.tsx
│ │ ├── AppState
│ │ │ ├── AppState.md
│ │ │ ├── AppState.spec.ts
│ │ │ ├── AppState.tsx
│ │ │ └── AppStateNative.tsx
│ │ ├── AutoComplete
│ │ │ ├── AutoComplete.md
│ │ │ ├── AutoComplete.module.scss
│ │ │ ├── AutoComplete.spec.ts
│ │ │ ├── AutoComplete.tsx
│ │ │ ├── AutoCompleteContext.tsx
│ │ │ └── AutoCompleteNative.tsx
│ │ ├── Avatar
│ │ │ ├── Avatar.md
│ │ │ ├── Avatar.module.scss
│ │ │ ├── Avatar.spec.ts
│ │ │ ├── Avatar.tsx
│ │ │ └── AvatarNative.tsx
│ │ ├── Backdrop
│ │ │ ├── Backdrop.md
│ │ │ ├── Backdrop.module.scss
│ │ │ ├── Backdrop.spec.ts
│ │ │ ├── Backdrop.tsx
│ │ │ └── BackdropNative.tsx
│ │ ├── Badge
│ │ │ ├── Badge.md
│ │ │ ├── Badge.module.scss
│ │ │ ├── Badge.spec.ts
│ │ │ ├── Badge.tsx
│ │ │ └── BadgeNative.tsx
│ │ ├── Bookmark
│ │ │ ├── Bookmark.md
│ │ │ ├── Bookmark.module.scss
│ │ │ ├── Bookmark.spec.ts
│ │ │ ├── Bookmark.tsx
│ │ │ └── BookmarkNative.tsx
│ │ ├── Breakout
│ │ │ ├── Breakout.module.scss
│ │ │ ├── Breakout.spec.ts
│ │ │ ├── Breakout.tsx
│ │ │ └── BreakoutNative.tsx
│ │ ├── Button
│ │ │ ├── Button-style.spec.ts
│ │ │ ├── Button.md
│ │ │ ├── Button.module.scss
│ │ │ ├── Button.spec.ts
│ │ │ ├── Button.tsx
│ │ │ └── ButtonNative.tsx
│ │ ├── Card
│ │ │ ├── Card.md
│ │ │ ├── Card.module.scss
│ │ │ ├── Card.spec.ts
│ │ │ ├── Card.tsx
│ │ │ └── CardNative.tsx
│ │ ├── Carousel
│ │ │ ├── Carousel.md
│ │ │ ├── Carousel.module.scss
│ │ │ ├── Carousel.spec.ts
│ │ │ ├── Carousel.tsx
│ │ │ ├── CarouselContext.tsx
│ │ │ ├── CarouselItem.tsx
│ │ │ ├── CarouselItemNative.tsx
│ │ │ └── CarouselNative.tsx
│ │ ├── ChangeListener
│ │ │ ├── ChangeListener.md
│ │ │ ├── ChangeListener.spec.ts
│ │ │ ├── ChangeListener.tsx
│ │ │ └── ChangeListenerNative.tsx
│ │ ├── chart-color-schemes.ts
│ │ ├── Charts
│ │ │ ├── AreaChart
│ │ │ │ ├── AreaChart.md
│ │ │ │ ├── AreaChart.spec.ts
│ │ │ │ ├── AreaChart.tsx
│ │ │ │ └── AreaChartNative.tsx
│ │ │ ├── BarChart
│ │ │ │ ├── BarChart.md
│ │ │ │ ├── BarChart.module.scss
│ │ │ │ ├── BarChart.spec.ts
│ │ │ │ ├── BarChart.tsx
│ │ │ │ └── BarChartNative.tsx
│ │ │ ├── DonutChart
│ │ │ │ ├── DonutChart.spec.ts
│ │ │ │ └── DonutChart.tsx
│ │ │ ├── LabelList
│ │ │ │ ├── LabelList.module.scss
│ │ │ │ ├── LabelList.spec.ts
│ │ │ │ ├── LabelList.tsx
│ │ │ │ └── LabelListNative.tsx
│ │ │ ├── Legend
│ │ │ │ ├── Legend.spec.ts
│ │ │ │ ├── Legend.tsx
│ │ │ │ └── LegendNative.tsx
│ │ │ ├── LineChart
│ │ │ │ ├── LineChart.md
│ │ │ │ ├── LineChart.module.scss
│ │ │ │ ├── LineChart.spec.ts
│ │ │ │ ├── LineChart.tsx
│ │ │ │ └── LineChartNative.tsx
│ │ │ ├── PieChart
│ │ │ │ ├── PieChart.md
│ │ │ │ ├── PieChart.spec.ts
│ │ │ │ ├── PieChart.tsx
│ │ │ │ ├── PieChartNative.module.scss
│ │ │ │ └── PieChartNative.tsx
│ │ │ ├── RadarChart
│ │ │ │ ├── RadarChart.md
│ │ │ │ ├── RadarChart.spec.ts
│ │ │ │ ├── RadarChart.tsx
│ │ │ │ └── RadarChartNative.tsx
│ │ │ ├── Tooltip
│ │ │ │ ├── TooltipContent.module.scss
│ │ │ │ ├── TooltipContent.spec.ts
│ │ │ │ └── TooltipContent.tsx
│ │ │ └── utils
│ │ │ ├── abstractions.ts
│ │ │ └── ChartProvider.tsx
│ │ ├── Checkbox
│ │ │ ├── Checkbox.md
│ │ │ ├── Checkbox.spec.ts
│ │ │ └── Checkbox.tsx
│ │ ├── CodeBlock
│ │ │ ├── CodeBlock.module.scss
│ │ │ ├── CodeBlock.spec.ts
│ │ │ ├── CodeBlock.tsx
│ │ │ ├── CodeBlockNative.tsx
│ │ │ └── highlight-code.ts
│ │ ├── collectedComponentMetadata.ts
│ │ ├── ColorPicker
│ │ │ ├── ColorPicker.md
│ │ │ ├── ColorPicker.module.scss
│ │ │ ├── ColorPicker.spec.ts
│ │ │ ├── ColorPicker.tsx
│ │ │ └── ColorPickerNative.tsx
│ │ ├── Column
│ │ │ ├── Column.md
│ │ │ ├── Column.tsx
│ │ │ ├── ColumnNative.tsx
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ └── TableContext.tsx
│ │ ├── component-utils.ts
│ │ ├── ComponentProvider.tsx
│ │ ├── ComponentRegistryContext.tsx
│ │ ├── container-helpers.tsx
│ │ ├── ContentSeparator
│ │ │ ├── ContentSeparator.md
│ │ │ ├── ContentSeparator.module.scss
│ │ │ ├── ContentSeparator.spec.ts
│ │ │ ├── ContentSeparator.tsx
│ │ │ ├── ContentSeparatorNative.tsx
│ │ │ └── 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
│ │ ├── PositionedContainer
│ │ │ ├── PositionedContainer.module.scss
│ │ │ ├── PositionedContainer.tsx
│ │ │ └── PositionedContainerNative.tsx
│ │ ├── ProfileMenu
│ │ │ ├── ProfileMenu.module.scss
│ │ │ └── ProfileMenu.tsx
│ │ ├── ProgressBar
│ │ │ ├── ProgressBar.md
│ │ │ ├── ProgressBar.module.scss
│ │ │ ├── ProgressBar.spec.ts
│ │ │ ├── ProgressBar.tsx
│ │ │ └── ProgressBarNative.tsx
│ │ ├── Queue
│ │ │ ├── Queue.md
│ │ │ ├── Queue.spec.ts
│ │ │ ├── Queue.tsx
│ │ │ ├── queueActions.ts
│ │ │ └── QueueNative.tsx
│ │ ├── RadioGroup
│ │ │ ├── RadioGroup.md
│ │ │ ├── RadioGroup.module.scss
│ │ │ ├── RadioGroup.spec.ts
│ │ │ ├── RadioGroup.tsx
│ │ │ ├── RadioGroupNative.tsx
│ │ │ ├── RadioItem.tsx
│ │ │ └── RadioItemNative.tsx
│ │ ├── RealTimeAdapter
│ │ │ ├── RealTimeAdapter.tsx
│ │ │ └── RealTimeAdapterNative.tsx
│ │ ├── Redirect
│ │ │ ├── Redirect.md
│ │ │ ├── Redirect.spec.ts
│ │ │ └── Redirect.tsx
│ │ ├── ResponsiveBar
│ │ │ ├── README.md
│ │ │ ├── ResponsiveBar.md
│ │ │ ├── ResponsiveBar.module.scss
│ │ │ ├── ResponsiveBar.spec.ts
│ │ │ ├── ResponsiveBar.tsx
│ │ │ └── ResponsiveBarNative.tsx
│ │ ├── Select
│ │ │ ├── HiddenOption.tsx
│ │ │ ├── OptionContext.ts
│ │ │ ├── Select.md
│ │ │ ├── Select.module.scss
│ │ │ ├── Select.spec.ts
│ │ │ ├── Select.tsx
│ │ │ ├── SelectContext.tsx
│ │ │ └── SelectNative.tsx
│ │ ├── SelectionStore
│ │ │ ├── SelectionStore.md
│ │ │ ├── SelectionStore.tsx
│ │ │ └── SelectionStoreNative.tsx
│ │ ├── Slider
│ │ │ ├── Slider.md
│ │ │ ├── Slider.module.scss
│ │ │ ├── Slider.spec.ts
│ │ │ ├── Slider.tsx
│ │ │ └── SliderNative.tsx
│ │ ├── Slot
│ │ │ ├── Slot.md
│ │ │ ├── Slot.spec.ts
│ │ │ └── Slot.ts
│ │ ├── SlotItem.tsx
│ │ ├── SpaceFiller
│ │ │ ├── SpaceFiller.md
│ │ │ ├── SpaceFiller.module.scss
│ │ │ ├── SpaceFiller.spec.ts
│ │ │ ├── SpaceFiller.tsx
│ │ │ └── SpaceFillerNative.tsx
│ │ ├── Spinner
│ │ │ ├── Spinner.md
│ │ │ ├── Spinner.module.scss
│ │ │ ├── Spinner.spec.ts
│ │ │ ├── Spinner.tsx
│ │ │ └── SpinnerNative.tsx
│ │ ├── Splitter
│ │ │ ├── HSplitter.md
│ │ │ ├── HSplitter.spec.ts
│ │ │ ├── Splitter.md
│ │ │ ├── Splitter.module.scss
│ │ │ ├── Splitter.spec.ts
│ │ │ ├── Splitter.tsx
│ │ │ ├── SplitterNative.tsx
│ │ │ ├── utils.ts
│ │ │ ├── VSplitter.md
│ │ │ └── VSplitter.spec.ts
│ │ ├── Stack
│ │ │ ├── CHStack.md
│ │ │ ├── CHStack.spec.ts
│ │ │ ├── CVStack.md
│ │ │ ├── CVStack.spec.ts
│ │ │ ├── HStack.md
│ │ │ ├── HStack.spec.ts
│ │ │ ├── Stack.md
│ │ │ ├── Stack.module.scss
│ │ │ ├── Stack.spec.ts
│ │ │ ├── Stack.tsx
│ │ │ ├── StackNative.tsx
│ │ │ ├── VStack.md
│ │ │ └── VStack.spec.ts
│ │ ├── StickyBox
│ │ │ ├── StickyBox.md
│ │ │ ├── StickyBox.module.scss
│ │ │ ├── StickyBox.tsx
│ │ │ └── StickyBoxNative.tsx
│ │ ├── Switch
│ │ │ ├── Switch.md
│ │ │ ├── Switch.spec.ts
│ │ │ └── Switch.tsx
│ │ ├── Table
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── react-table-config.d.ts
│ │ │ ├── Table.md
│ │ │ ├── Table.module.scss
│ │ │ ├── Table.spec.ts
│ │ │ ├── Table.tsx
│ │ │ ├── TableNative.tsx
│ │ │ └── useRowSelection.tsx
│ │ ├── TableOfContents
│ │ │ ├── TableOfContents.module.scss
│ │ │ ├── TableOfContents.spec.ts
│ │ │ ├── TableOfContents.tsx
│ │ │ └── TableOfContentsNative.tsx
│ │ ├── Tabs
│ │ │ ├── TabContext.tsx
│ │ │ ├── TabItem.md
│ │ │ ├── TabItem.tsx
│ │ │ ├── TabItemNative.tsx
│ │ │ ├── Tabs.md
│ │ │ ├── Tabs.module.scss
│ │ │ ├── Tabs.spec.ts
│ │ │ ├── Tabs.tsx
│ │ │ └── TabsNative.tsx
│ │ ├── Text
│ │ │ ├── Text.md
│ │ │ ├── Text.module.scss
│ │ │ ├── Text.spec.ts
│ │ │ ├── Text.tsx
│ │ │ └── TextNative.tsx
│ │ ├── TextArea
│ │ │ ├── TextArea.md
│ │ │ ├── TextArea.module.scss
│ │ │ ├── TextArea.spec.ts
│ │ │ ├── TextArea.tsx
│ │ │ ├── TextAreaNative.tsx
│ │ │ ├── TextAreaResizable.tsx
│ │ │ └── useComposedRef.ts
│ │ ├── TextBox
│ │ │ ├── TextBox.md
│ │ │ ├── TextBox.module.scss
│ │ │ ├── TextBox.spec.ts
│ │ │ ├── TextBox.tsx
│ │ │ └── TextBoxNative.tsx
│ │ ├── Theme
│ │ │ ├── NotificationToast.tsx
│ │ │ ├── Theme.md
│ │ │ ├── Theme.module.scss
│ │ │ ├── Theme.spec.ts
│ │ │ ├── Theme.tsx
│ │ │ └── ThemeNative.tsx
│ │ ├── TimeInput
│ │ │ ├── TimeInput.md
│ │ │ ├── TimeInput.module.scss
│ │ │ ├── TimeInput.spec.ts
│ │ │ ├── TimeInput.tsx
│ │ │ ├── TimeInputNative.tsx
│ │ │ └── utils.ts
│ │ ├── Timer
│ │ │ ├── Timer.md
│ │ │ ├── Timer.spec.ts
│ │ │ ├── Timer.tsx
│ │ │ └── TimerNative.tsx
│ │ ├── Toggle
│ │ │ ├── Toggle.module.scss
│ │ │ └── Toggle.tsx
│ │ ├── ToneChangerButton
│ │ │ ├── ToneChangerButton.md
│ │ │ ├── ToneChangerButton.spec.ts
│ │ │ └── ToneChangerButton.tsx
│ │ ├── ToneSwitch
│ │ │ ├── ToneSwitch.md
│ │ │ ├── ToneSwitch.module.scss
│ │ │ ├── ToneSwitch.spec.ts
│ │ │ ├── ToneSwitch.tsx
│ │ │ └── ToneSwitchNative.tsx
│ │ ├── Tooltip
│ │ │ ├── Tooltip.md
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.spec.ts
│ │ │ ├── Tooltip.tsx
│ │ │ └── TooltipNative.tsx
│ │ ├── Tree
│ │ │ ├── testData.ts
│ │ │ ├── Tree-dynamic.spec.ts
│ │ │ ├── Tree-icons.spec.ts
│ │ │ ├── Tree.md
│ │ │ ├── Tree.spec.ts
│ │ │ ├── TreeComponent.module.scss
│ │ │ ├── TreeComponent.tsx
│ │ │ └── TreeNative.tsx
│ │ ├── TreeDisplay
│ │ │ ├── TreeDisplay.md
│ │ │ ├── TreeDisplay.module.scss
│ │ │ ├── TreeDisplay.tsx
│ │ │ └── TreeDisplayNative.tsx
│ │ ├── ValidationSummary
│ │ │ ├── ValidationSummary.module.scss
│ │ │ └── ValidationSummary.tsx
│ │ └── VisuallyHidden.tsx
│ ├── components-core
│ │ ├── abstractions
│ │ │ ├── ComponentRenderer.ts
│ │ │ ├── LoaderRenderer.ts
│ │ │ ├── standalone.ts
│ │ │ └── treeAbstractions.ts
│ │ ├── action
│ │ │ ├── actions.ts
│ │ │ ├── APICall.tsx
│ │ │ ├── FileDownloadAction.tsx
│ │ │ ├── FileUploadAction.tsx
│ │ │ ├── NavigateAction.tsx
│ │ │ └── TimedAction.tsx
│ │ ├── ApiBoundComponent.tsx
│ │ ├── appContext
│ │ │ ├── date-functions.ts
│ │ │ ├── math-function.ts
│ │ │ └── misc-utils.ts
│ │ ├── AppContext.tsx
│ │ ├── behaviors
│ │ │ ├── Behavior.tsx
│ │ │ └── CoreBehaviors.tsx
│ │ ├── component-hooks.ts
│ │ ├── ComponentDecorator.tsx
│ │ ├── ComponentViewer.tsx
│ │ ├── CompoundComponent.tsx
│ │ ├── constants.ts
│ │ ├── DebugViewProvider.tsx
│ │ ├── descriptorHelper.ts
│ │ ├── devtools
│ │ │ ├── InspectorDialog.module.scss
│ │ │ ├── InspectorDialog.tsx
│ │ │ └── InspectorDialogVisibilityContext.tsx
│ │ ├── EngineError.ts
│ │ ├── event-handlers.ts
│ │ ├── InspectorButton.module.scss
│ │ ├── InspectorContext.tsx
│ │ ├── interception
│ │ │ ├── abstractions.ts
│ │ │ ├── ApiInterceptor.ts
│ │ │ ├── ApiInterceptorProvider.tsx
│ │ │ ├── apiInterceptorWorker.ts
│ │ │ ├── Backend.ts
│ │ │ ├── Errors.ts
│ │ │ ├── IndexedDb.ts
│ │ │ ├── initMock.ts
│ │ │ ├── InMemoryDb.ts
│ │ │ ├── ReadonlyCollection.ts
│ │ │ └── useApiInterceptorContext.tsx
│ │ ├── loader
│ │ │ ├── ApiLoader.tsx
│ │ │ ├── DataLoader.tsx
│ │ │ ├── ExternalDataLoader.tsx
│ │ │ ├── Loader.tsx
│ │ │ ├── MockLoaderRenderer.tsx
│ │ │ └── PageableLoader.tsx
│ │ ├── LoaderComponent.tsx
│ │ ├── markup-check.ts
│ │ ├── parts.ts
│ │ ├── renderers.ts
│ │ ├── rendering
│ │ │ ├── AppContent.tsx
│ │ │ ├── AppRoot.tsx
│ │ │ ├── AppWrapper.tsx
│ │ │ ├── buildProxy.ts
│ │ │ ├── collectFnVarDeps.ts
│ │ │ ├── ComponentAdapter.tsx
│ │ │ ├── ComponentWrapper.tsx
│ │ │ ├── Container.tsx
│ │ │ ├── containers.ts
│ │ │ ├── ContainerWrapper.tsx
│ │ │ ├── ErrorBoundary.module.scss
│ │ │ ├── ErrorBoundary.tsx
│ │ │ ├── InvalidComponent.module.scss
│ │ │ ├── InvalidComponent.tsx
│ │ │ ├── nodeUtils.ts
│ │ │ ├── reducer.ts
│ │ │ ├── renderChild.tsx
│ │ │ ├── StandaloneComponent.tsx
│ │ │ ├── StateContainer.tsx
│ │ │ ├── UnknownComponent.module.scss
│ │ │ ├── UnknownComponent.tsx
│ │ │ └── valueExtractor.ts
│ │ ├── reportEngineError.ts
│ │ ├── RestApiProxy.ts
│ │ ├── script-runner
│ │ │ ├── asyncProxy.ts
│ │ │ ├── AttributeValueParser.ts
│ │ │ ├── bannedFunctions.ts
│ │ │ ├── BindingTreeEvaluationContext.ts
│ │ │ ├── eval-tree-async.ts
│ │ │ ├── eval-tree-common.ts
│ │ │ ├── eval-tree-sync.ts
│ │ │ ├── ParameterParser.ts
│ │ │ ├── process-statement-async.ts
│ │ │ ├── process-statement-common.ts
│ │ │ ├── process-statement-sync.ts
│ │ │ ├── ScriptingSourceTree.ts
│ │ │ ├── simplify-expression.ts
│ │ │ ├── statement-queue.ts
│ │ │ └── visitors.ts
│ │ ├── StandaloneApp.tsx
│ │ ├── StandaloneExtensionManager.ts
│ │ ├── TableOfContentsContext.tsx
│ │ ├── theming
│ │ │ ├── _themes.scss
│ │ │ ├── component-layout-resolver.ts
│ │ │ ├── extendThemeUtils.ts
│ │ │ ├── hvar.ts
│ │ │ ├── layout-resolver.ts
│ │ │ ├── parse-layout-props.ts
│ │ │ ├── StyleContext.tsx
│ │ │ ├── StyleRegistry.ts
│ │ │ ├── ThemeContext.tsx
│ │ │ ├── ThemeProvider.tsx
│ │ │ ├── themes
│ │ │ │ ├── base-utils.ts
│ │ │ │ ├── palette.ts
│ │ │ │ ├── root.ts
│ │ │ │ ├── solid.ts
│ │ │ │ ├── theme-colors.ts
│ │ │ │ └── xmlui.ts
│ │ │ ├── themeVars.module.scss
│ │ │ ├── themeVars.ts
│ │ │ ├── transformThemeVars.ts
│ │ │ └── utils.ts
│ │ ├── utils
│ │ │ ├── actionUtils.ts
│ │ │ ├── audio-utils.ts
│ │ │ ├── base64-utils.ts
│ │ │ ├── compound-utils.ts
│ │ │ ├── css-utils.ts
│ │ │ ├── DataLoaderQueryKeyGenerator.ts
│ │ │ ├── date-utils.ts
│ │ │ ├── extractParam.ts
│ │ │ ├── hooks.tsx
│ │ │ ├── LruCache.ts
│ │ │ ├── mergeProps.ts
│ │ │ ├── misc.ts
│ │ │ ├── request-params.ts
│ │ │ ├── statementUtils.ts
│ │ │ └── treeUtils.ts
│ │ └── xmlui-parser.ts
│ ├── index-standalone.ts
│ ├── index.scss
│ ├── index.ts
│ ├── language-server
│ │ ├── server-common.ts
│ │ ├── server-web-worker.ts
│ │ ├── server.ts
│ │ ├── services
│ │ │ ├── common
│ │ │ │ ├── docs-generation.ts
│ │ │ │ ├── lsp-utils.ts
│ │ │ │ ├── metadata-utils.ts
│ │ │ │ └── syntax-node-utilities.ts
│ │ │ ├── completion.ts
│ │ │ ├── diagnostic.ts
│ │ │ ├── format.ts
│ │ │ └── hover.ts
│ │ └── xmlui-metadata-generated.js
│ ├── logging
│ │ ├── LoggerContext.tsx
│ │ ├── LoggerInitializer.tsx
│ │ ├── LoggerService.ts
│ │ └── xmlui.ts
│ ├── logo.svg
│ ├── parsers
│ │ ├── common
│ │ │ ├── GenericToken.ts
│ │ │ ├── InputStream.ts
│ │ │ └── utils.ts
│ │ ├── scripting
│ │ │ ├── code-behind-collect.ts
│ │ │ ├── Lexer.ts
│ │ │ ├── modules.ts
│ │ │ ├── Parser.ts
│ │ │ ├── ParserError.ts
│ │ │ ├── ScriptingNodeTypes.ts
│ │ │ ├── TokenTrait.ts
│ │ │ ├── TokenType.ts
│ │ │ └── tree-visitor.ts
│ │ ├── style-parser
│ │ │ ├── errors.ts
│ │ │ ├── source-tree.ts
│ │ │ ├── StyleInputStream.ts
│ │ │ ├── StyleLexer.ts
│ │ │ ├── StyleParser.ts
│ │ │ └── tokens.ts
│ │ └── xmlui-parser
│ │ ├── CharacterCodes.ts
│ │ ├── diagnostics.ts
│ │ ├── fileExtensions.ts
│ │ ├── index.ts
│ │ ├── lint.ts
│ │ ├── parser.ts
│ │ ├── ParserError.ts
│ │ ├── scanner.ts
│ │ ├── syntax-kind.ts
│ │ ├── syntax-node.ts
│ │ ├── transform.ts
│ │ ├── utils.ts
│ │ ├── xmlui-serializer.ts
│ │ └── xmlui-tree.ts
│ ├── react-app-env.d.ts
│ ├── syntax
│ │ ├── monaco
│ │ │ ├── grammar.monacoLanguage.ts
│ │ │ ├── index.ts
│ │ │ ├── xmlui-dark.ts
│ │ │ ├── xmlui-light.ts
│ │ │ └── xmluiscript.monacoLanguage.ts
│ │ └── textMate
│ │ ├── index.ts
│ │ ├── xmlui-dark.json
│ │ ├── xmlui-light.json
│ │ ├── xmlui.json
│ │ └── xmlui.tmLanguage.json
│ ├── testing
│ │ ├── assertions.ts
│ │ ├── component-test-helpers.ts
│ │ ├── ComponentDrivers.ts
│ │ ├── drivers
│ │ │ ├── DateInputDriver.ts
│ │ │ ├── index.ts
│ │ │ ├── ModalDialogDriver.ts
│ │ │ ├── NumberBoxDriver.ts
│ │ │ ├── TextBoxDriver.ts
│ │ │ ├── TimeInputDriver.ts
│ │ │ ├── TimerDriver.ts
│ │ │ └── TreeDriver.ts
│ │ ├── fixtures.ts
│ │ ├── index.ts
│ │ ├── infrastructure
│ │ │ ├── index.html
│ │ │ ├── main.tsx
│ │ │ ├── public
│ │ │ │ ├── mockServiceWorker.js
│ │ │ │ ├── resources
│ │ │ │ │ ├── bell.svg
│ │ │ │ │ ├── box.svg
│ │ │ │ │ ├── doc.svg
│ │ │ │ │ ├── eye.svg
│ │ │ │ │ ├── flower-640x480.jpg
│ │ │ │ │ ├── sun.svg
│ │ │ │ │ ├── test-image-100x100.jpg
│ │ │ │ │ └── txt.svg
│ │ │ │ └── serve.json
│ │ │ └── TestBed.tsx
│ │ └── themed-app-test-helpers.ts
│ └── vite-env.d.ts
├── tests
│ ├── components
│ │ ├── CodeBlock
│ │ │ └── hightlight-code.test.ts
│ │ ├── playground-pattern.test.ts
│ │ └── Tree
│ │ └── Tree-states.test.ts
│ ├── components-core
│ │ ├── abstractions
│ │ │ └── treeAbstractions.test.ts
│ │ ├── container
│ │ │ └── buildProxy.test.ts
│ │ ├── interception
│ │ │ ├── orderBy.test.ts
│ │ │ ├── ReadOnlyCollection.test.ts
│ │ │ └── request-param-converter.test.ts
│ │ ├── scripts-runner
│ │ │ ├── AttributeValueParser.test.ts
│ │ │ ├── eval-tree-arrow-async.test.ts
│ │ │ ├── eval-tree-arrow.test.ts
│ │ │ ├── eval-tree-func-decl-async.test.ts
│ │ │ ├── eval-tree-func-decl.test.ts
│ │ │ ├── eval-tree-pre-post.test.ts
│ │ │ ├── eval-tree-regression.test.ts
│ │ │ ├── eval-tree.test.ts
│ │ │ ├── function-proxy.test.ts
│ │ │ ├── parser-regression.test.ts
│ │ │ ├── process-event.test.ts
│ │ │ ├── process-function.test.ts
│ │ │ ├── process-implicit-context.test.ts
│ │ │ ├── process-statement-asgn.test.ts
│ │ │ ├── process-statement-destruct.test.ts
│ │ │ ├── process-statement-regs.test.ts
│ │ │ ├── process-statement-sync.test.ts
│ │ │ ├── process-statement.test.ts
│ │ │ ├── process-switch-sync.test.ts
│ │ │ ├── process-switch.test.ts
│ │ │ ├── process-try-sync.test.ts
│ │ │ ├── process-try.test.ts
│ │ │ └── test-helpers.ts
│ │ ├── test-metadata-handler.ts
│ │ ├── theming
│ │ │ ├── border-segments.test.ts
│ │ │ ├── component-layout.resolver.test.ts
│ │ │ ├── layout-property-parser.test.ts
│ │ │ ├── layout-resolver.test.ts
│ │ │ ├── layout-resolver2.test.ts
│ │ │ ├── layout-vp-override.test.ts
│ │ │ └── padding-segments.test.ts
│ │ └── utils
│ │ ├── date-utils.test.ts
│ │ ├── format-human-elapsed-time.test.ts
│ │ └── LruCache.test.ts
│ ├── language-server
│ │ ├── completion.test.ts
│ │ ├── format.test.ts
│ │ ├── hover.test.ts
│ │ └── mockData.ts
│ └── parsers
│ ├── common
│ │ └── input-stream.test.ts
│ ├── markdown
│ │ └── parse-binding-expression.test.ts
│ ├── parameter-parser.test.ts
│ ├── paremeter-parser.test.ts
│ ├── scripting
│ │ ├── eval-tree-arrow.test.ts
│ │ ├── eval-tree-pre-post.test.ts
│ │ ├── eval-tree.test.ts
│ │ ├── function-proxy.test.ts
│ │ ├── lexer-literals.test.ts
│ │ ├── lexer-misc.test.ts
│ │ ├── module-parse.test.ts
│ │ ├── parser-arrow.test.ts
│ │ ├── parser-assignments.test.ts
│ │ ├── parser-binary.test.ts
│ │ ├── parser-destructuring.test.ts
│ │ ├── parser-errors.test.ts
│ │ ├── parser-expressions.test.ts
│ │ ├── parser-function.test.ts
│ │ ├── parser-literals.test.ts
│ │ ├── parser-primary.test.ts
│ │ ├── parser-regex.test.ts
│ │ ├── parser-statements.test.ts
│ │ ├── parser-unary.test.ts
│ │ ├── process-event.test.ts
│ │ ├── process-implicit-context.test.ts
│ │ ├── process-statement-asgn.test.ts
│ │ ├── process-statement-destruct.test.ts
│ │ ├── process-statement-regs.test.ts
│ │ ├── process-statement-sync.test.ts
│ │ ├── process-statement.test.ts
│ │ ├── process-switch-sync.test.ts
│ │ ├── process-switch.test.ts
│ │ ├── process-try-sync.test.ts
│ │ ├── process-try.test.ts
│ │ ├── simplify-expression.test.ts
│ │ ├── statement-hooks.test.ts
│ │ └── test-helpers.ts
│ ├── style-parser
│ │ ├── generateHvarChain.test.ts
│ │ ├── parseHVar.test.ts
│ │ ├── parser.test.ts
│ │ └── tokens.test.ts
│ └── xmlui
│ ├── lint.test.ts
│ ├── parser.test.ts
│ ├── scanner.test.ts
│ ├── transform.attr.test.ts
│ ├── transform.circular.test.ts
│ ├── transform.element.test.ts
│ ├── transform.errors.test.ts
│ ├── transform.escape.test.ts
│ ├── transform.regression.test.ts
│ ├── transform.script.test.ts
│ ├── transform.test.ts
│ └── xmlui.ts
├── tests-e2e
│ ├── api-bound-component-regression.spec.ts
│ ├── api-call-as-extracted-component.spec.ts
│ ├── assign-to-object-or-array-regression.spec.ts
│ ├── binding-regression.spec.ts
│ ├── children-as-template-context-vars.spec.ts
│ ├── compound-component.spec.ts
│ ├── context-vars-regression.spec.ts
│ ├── data-bindings.spec.ts
│ ├── datasource-and-api-usage-in-var.spec.ts
│ ├── datasource-direct-binding.spec.ts
│ ├── datasource-onLoaded-regression.spec.ts
│ ├── modify-array-item-regression.spec.ts
│ ├── namespaces.spec.ts
│ ├── push-to-array-regression.spec.ts
│ ├── screen-breakpoints.spec.ts
│ ├── scripting.spec.ts
│ ├── state-scope-in-pages.spec.ts
│ └── state-var-scopes.spec.ts
├── tsconfig.json
├── tsdown.config.ts
├── vite.config.ts
└── vitest.config.ts
```
# Files
--------------------------------------------------------------------------------
/xmlui/src/components/Select/Select.spec.ts:
--------------------------------------------------------------------------------
```typescript
import { expect, test } from "../../testing/fixtures";
// =============================================================================
// BASIC FUNCTIONALITY TESTS
// =============================================================================
test.describe("Basic Functionality", () => {
// --- Component rendering
test("dynamic options displayed with Items component", async ({
initTestBed,
createSelectDriver,
page,
}) => {
await initTestBed(`
<Select>
<Items data="{['One', 'Two', 'Three']}" >
<Option value="{$itemIndex}" label="{$item}" />
</Items>
</Select>`);
const driver = await createSelectDriver();
await driver.click();
await expect(page.getByRole("option", { name: "One" })).toBeVisible();
await expect(page.getByRole("option", { name: "Two" })).toBeVisible();
await expect(page.getByRole("option", { name: "Three" })).toBeVisible();
});
test("changing selected option in form", async ({ initTestBed, createSelectDriver }) => {
await initTestBed(`
<Form data="{{sel: 'opt1'}}">
<FormItem testId="mySelect" type="select" bindTo="sel">
<Option value="opt1" label="first"/>
<Option value="opt2" label="second"/>
<Option value="opt3" label="third"/>
</FormItem>
</Form>`);
const driver = await createSelectDriver("mySelect");
await expect(driver.component).toHaveText("first");
await driver.toggleOptionsVisibility();
await driver.selectLabel("second");
await expect(driver.component).toHaveText("second");
});
// --- initialValue prop
test("initialValue set to first valid value", async ({ page, initTestBed }) => {
await initTestBed(`
<Fragment>
<Select id="mySelect" initialValue="{0}">
<Option value="{0}" label="Zero"/>
<Option value="{1}" label="One"/>
<Option value="{2}" label="Two"/>
</Select>
<Text testId="text">Selected value: {mySelect.value}</Text>
</Fragment>
`);
await expect(page.getByTestId("text")).toHaveText("Selected value: 0");
await expect(page.getByText("Zero", { exact: true })).toBeVisible();
await expect(page.getByText("One", { exact: true })).not.toBeVisible();
});
test("initialValue set to non-existant option", async ({ page, initTestBed }) => {
await initTestBed(`
<Fragment>
<Select id="mySelect" initialValue="{42}">
<Option value="{0}" label="Zero"/>
<Option value="{1}" label="One"/>
<Option value="{2}" label="Two"/>
</Select>
<Text testId="text">Selected value: {mySelect.value}</Text>
</Fragment>
`);
await expect(page.getByTestId("text")).toHaveText("Selected value: 42");
});
test("reset works with initialValue", async ({
page,
initTestBed,
createSelectDriver,
createButtonDriver,
}) => {
await initTestBed(`
<Fragment>
<Select id="mySelect" initialValue="{0}">
<Option value="{0}" label="Zero"/>
<Option value="{1}" label="One"/>
<Option value="{2}" label="Two"/>
</Select>
<Button id="resetBtn" label="reset" onClick="mySelect.reset()"/>
<Text testId="text">Selected value: {mySelect.value}</Text>
</Fragment>
`);
const selectDrv = await createSelectDriver("mySelect");
await selectDrv.toggleOptionsVisibility();
await selectDrv.selectLabel("One");
await expect(page.getByTestId("text")).toHaveText("Selected value: 1");
const btnDriver = await createButtonDriver("resetBtn");
await btnDriver.click();
await expect(page.getByTestId("text")).toHaveText("Selected value: 0");
});
test("reset works with no intialValue", async ({
page,
initTestBed,
createSelectDriver,
createButtonDriver,
}) => {
await initTestBed(`
<Fragment>
<Select id="mySelect">
<Option value="{0}" label="Zero"/>
<Option value="{1}" label="One"/>
<Option value="{2}" label="Two"/>
</Select>
<Button id="resetBtn" label="reset" onClick="mySelect.reset()"/>
<Text testId="text">Selected value: {mySelect.value}</Text>
</Fragment>
`);
const selectDrv = await createSelectDriver("mySelect");
await selectDrv.toggleOptionsVisibility();
await selectDrv.selectLabel("One");
await expect(page.getByTestId("text")).toHaveText("Selected value: 1");
const btnDriver = await createButtonDriver("resetBtn");
await btnDriver.click();
await expect(page.getByTestId("text")).not.toContainText("1");
});
// --- enabled prop
test("disabled Select cannot be opened", async ({ page, createSelectDriver, initTestBed }) => {
await initTestBed(`
<Select enabled="{false}">
<Option value="1" label="One"/>
<Option value="2" label="Two"/>
</Select>
`);
const driver = await createSelectDriver();
await driver.click({ force: true });
await expect(page.getByText("One")).not.toBeVisible();
});
// --- readOnly prop
test("readOnly Select shows options, but value cannot be changed", async ({
page,
initTestBed,
createSelectDriver,
}) => {
await initTestBed(`
<Select readOnly initialValue="1">
<Option value="1" label="One"/>
<Option value="2" label="Two"/>
</Select>
`);
const driver = await createSelectDriver();
await expect(driver.component).toHaveText("One");
await driver.toggleOptionsVisibility();
await driver.selectLabel("Two");
await expect(driver.component).toHaveText("One");
// verify dropdown is not visible but value is shown
});
test("readOnly multi-Select shows options, but value cannot be changed", async ({
page,
initTestBed,
createSelectDriver,
}) => {
await initTestBed(`
<Select readOnly initialValue="{[1, 2]}" multiSelect>
<Option value="1" label="One"/>
<Option value="2" label="Two"/>
<Option value="3" label="Three"/>
</Select>
`);
const driver = await createSelectDriver();
await expect(page.getByText("Three")).not.toBeVisible();
await expect(page.getByText("One")).toBeVisible();
await expect(page.getByText("Two")).toBeVisible();
await driver.toggleOptionsVisibility();
await driver.selectLabel("Three");
await expect(page.getByText("Three")).not.toBeVisible();
await expect(page.getByText("One")).toBeVisible();
await expect(page.getByText("Two")).toBeVisible();
});
test("disabled Option cannot be selected", async ({ initTestBed, createSelectDriver, page }) => {
await initTestBed(`
<Select>
<Option value="1" label="One"/>
<Option value="2" label="Two" enabled="{false}"/>
</Select>
`);
await expect(page.getByRole("option", { name: "One" })).not.toBeVisible();
await expect(page.getByRole("option", { name: "Two" })).not.toBeVisible();
const driver = await createSelectDriver();
await driver.toggleOptionsVisibility();
await driver.selectLabel("Two");
await expect(page.getByRole("option", { name: "One" })).toBeVisible();
await expect(page.getByRole("option", { name: "Two" })).toBeVisible();
});
test(
"clicking label brings up the options",
{ tag: "@smoke" },
async ({ initTestBed, page, createSelectDriver }) => {
await initTestBed(`
<Select label="Choose an option">
<Option value="1" label="One"/>
<Option value="2" label="Two"/>
</Select>
`);
await page.getByLabel("Choose an option").click();
await expect(page.getByRole("option", { name: "One" })).toBeVisible();
await expect(page.getByRole("option", { name: "Two" })).toBeVisible();
},
);
test("label displayed for selected numeric value", async ({ page, initTestBed }) => {
await initTestBed(`
<Fragment>
<Select initialValue="{0}" >
<Option value="{0}" label="Zero"/>
<Option value="{1}" label="One"/>
<Option value="{2}" label="Two"/>
</Select>
</Fragment>
`);
await expect(page.getByText("Zero")).toBeVisible();
});
// --- autoFocus prop
test("autoFocus brings the focus to component", async ({
initTestBed,
page,
createSelectDriver,
}) => {
await initTestBed(`
<Select>
<Option value="1" label="One"/>
<Option value="2" label="Two"/>
</Select>
<Select testId="focused-select" autoFocus>
<Option value="1" label="One"/>
<Option value="2" label="Two"/>
</Select>
`);
const driver = await createSelectDriver("focused-select");
await expect(driver.component).toBeFocused();
});
// --- Templates
test("emptyListTemplate shown when wrapped inside an App component", async ({
initTestBed,
page,
createSelectDriver,
}) => {
await initTestBed(`
<App>
<Select testId="mySelect">
<property name="emptyListTemplate">
<Text value="Nothing to see here!" />
</property>
</Select>
</App>
`);
const driver = await createSelectDriver("mySelect");
await driver.click();
await expect(page.getByText("Nothing to see here!", { exact: true })).toBeVisible();
});
test("optionTemplate is shown", async ({ initTestBed, page, createSelectDriver }) => {
await initTestBed(`
<Select>
<Items items="{[
{ value: 'opt1', label: 'first' },
{ value: 'opt2', label: 'second' },
{ value: 'opt3', label: 'third' },
]}">
<Option value="{$item.value}" label="{$item.label}">
<Text>Template for value {$item.value}</Text>
</Option>
</Items>
</Select>
`);
const driver = await createSelectDriver();
await driver.click();
await expect(page.getByText("Template for value opt1")).toBeVisible();
await expect(page.getByText("Template for value opt2")).toBeVisible();
await expect(page.getByText("Template for value opt3")).toBeVisible();
});
// --- placeholder prop
test("placeholder is shown", async ({ initTestBed, page, createSelectDriver }) => {
await initTestBed(`
<Select placeholder="Please select an item">
<Option value="opt1" label="first"/>
<Option value="opt2" label="second"/>
<Option value="opt3" label="third"/>
</Select>
`);
await expect(page.getByText("Please select an item")).toBeVisible();
});
test(
"Option without label and value is not rendered",
{ tag: "@smoke" },
async ({ initTestBed, page, createSelectDriver }) => {
await initTestBed(`
<Select placeholder="Please select an item">
<Option />
<Option />
<Option />
</Select>
`);
const driver = await createSelectDriver();
await driver.click();
await expect(page.getByRole("option")).not.toBeVisible();
},
);
test(
"Option value defaults to label",
{ tag: "@smoke" },
async ({ initTestBed, page, createSelectDriver }) => {
await initTestBed(`
<Fragment>
<Select id="mySelect">
<Option label="Zero"/>
<Option label="One"/>
<Option label="Two"/>
</Select>
<Text testId="text">Selected value: {mySelect.value}</Text>
</Fragment>
`);
const driver = await createSelectDriver("mySelect");
await driver.toggleOptionsVisibility();
await driver.selectLabel("Zero");
await expect(page.getByTestId("text")).toHaveText("Selected value: Zero");
},
);
// --- clearable prop
test("clear button not visible by default (clearable=false)", async ({
initTestBed,
createSelectDriver,
}) => {
await initTestBed(`
<Select initialValue="opt1">
<Option value="opt1" label="first"/>
<Option value="opt2" label="second"/>
</Select>
`);
const driver = await createSelectDriver();
// Clear button should not be visible (default clearable=false)
await expect(driver.clearButton).not.toBeVisible();
});
test("clear button visible when clearable=true and value selected", async ({
initTestBed,
createSelectDriver,
}) => {
await initTestBed(`
<Select clearable="true">
<Option value="opt1" label="first"/>
<Option value="opt2" label="second"/>
</Select>
`);
const driver = await createSelectDriver();
// Clear button should not be visible when no value selected
await expect(driver.clearButton).not.toBeVisible();
// Select a value
await driver.toggleOptionsVisibility();
await driver.selectLabel("first");
// Clear button should now be visible
await expect(driver.clearButton).toBeVisible();
});
test("clicking clear button clears single selection", async ({
initTestBed,
createSelectDriver,
}) => {
await initTestBed(`
<Select id="mySelect" clearable="true">
<Option value="opt1" label="first"/>
<Option value="opt2" label="second"/>
</Select>
`);
const driver = await createSelectDriver();
const select = driver.component;
// Select a value
await driver.toggleOptionsVisibility();
await driver.selectLabel("first");
await expect(select).toHaveText("first");
// Click the clear button
await driver.clearButton.click();
// Value should be cleared
await expect(select).not.toHaveText("first");
await expect(driver.clearButton).not.toBeVisible();
});
test("clear button works with multiSelect", async ({
initTestBed,
createSelectDriver,
page,
}) => {
await initTestBed(`
<Select clearable="true" multiSelect="true">
<Option value="opt1" label="first"/>
<Option value="opt2" label="second"/>
<Option value="opt3" label="third"/>
</Select>
`);
const driver = await createSelectDriver();
// Select multiple values
await driver.toggleOptionsVisibility();
await driver.selectMultipleLabels(["first", "second"]);
await driver.toggleOptionsVisibility();
// Clear button should be visible
await expect(driver.clearButton).toBeVisible();
// Click the clear button
await driver.clearButton.click();
// All values should be cleared
await expect(page.getByText("first")).not.toBeVisible();
await expect(page.getByText("second")).not.toBeVisible();
await expect(driver.clearButton).not.toBeVisible();
});
test("clear button not visible when readOnly=true", async ({
initTestBed,
createSelectDriver,
}) => {
await initTestBed(`
<Select clearable="true" readOnly="true" initialValue="opt1">
<Option value="opt1" label="first"/>
<Option value="opt2" label="second"/>
</Select>
`);
const driver = await createSelectDriver();
// Clear button should not be visible even with clearable=true and value selected
await expect(driver.clearButton).not.toBeVisible();
});
test("clear button not visible when enabled=false", async ({
initTestBed,
createSelectDriver,
}) => {
await initTestBed(`
<Select clearable="true" enabled="false" initialValue="opt1">
<Option value="opt1" label="first"/>
<Option value="opt2" label="second"/>
</Select>
`);
const driver = await createSelectDriver();
// Clear button should not be visible when disabled
await expect(driver.clearButton).not.toBeVisible();
});
test("clear button triggers didChange event", async ({
initTestBed,
createSelectDriver,
page,
}) => {
const { testStateDriver } = await initTestBed(`
<Select clearable="true" onDidChange="testState = 'changed'">
<Option value="opt1" label="first"/>
<Option value="opt2" label="second"/>
</Select>
`);
const driver = await createSelectDriver();
// Select a value
await driver.toggleOptionsVisibility();
await driver.selectLabel("first");
// Reset test state
await page.evaluate(() => (window as any).testState = null);
// Click the clear button
await driver.clearButton.click();
// Event should have fired
await expect.poll(testStateDriver.testState).toEqual("changed");
});
});
// =============================================================================
// LABEL POSITIONING TESTS
// =============================================================================
test.describe("Label", () => {
test("labelBreak prop defaults to false", async ({ initTestBed, page, createSelectDriver }) => {
await page.setViewportSize({ width: 300, height: 720 });
await initTestBed(`
<Select
label="Dignissimos esse quasi esse cupiditate qui qui. Ut provident ad voluptatem tenetur sit consequuntur. Aliquam nisi fugit ut temporibus itaque ducimus rerum. Dolorem reprehenderit qui adipisci. Ullam harum atque ipsa."
>
<Option value="1" label="One"/>
<Option value="2" label="Two"/>
</Select>
`);
const labelWidth = (await page.getByText("Dignissimos esse quasi").boundingBox()).width;
const select = page.getByRole("button").or(page.getByRole("combobox")).first();
const { width: selectWidth } = await select.boundingBox();
expect(labelWidth).toBe(selectWidth);
});
test('labelWidth applies with labelPosition="start"', async ({ initTestBed, page }) => {
await page.setViewportSize({ width: 300, height: 720 });
await initTestBed(`
<Select label="Dignissimos esse quasi" labelWidth="200px" labelPosition="start" >
<Option value="opt1" label="first"/>
<Option value="opt2" label="second"/>
<Option value="opt3" label="third"/>
<Option value="opt4" label="fourth"/>
<Option value="opt5" label="fifth"/>
</Select>
`);
const labelWidth = (await page.getByText("Dignissimos esse quasi").boundingBox()).width;
expect(labelWidth).toBeGreaterThanOrEqual(200);
});
});
// =============================================================================
// SEARCHABLE SELECT TESTS
// =============================================================================
test.describe("searchable select", () => {
test("placeholder is shown", async ({ initTestBed, page, createSelectDriver }) => {
await initTestBed(`
<Select searchable placeholder="Please select an item">
<Option value="opt1" label="first"/>
<Option value="opt2" label="second"/>
<Option value="opt3" label="third"/>
</Select>
`);
await expect(page.getByText("Please select an item")).toBeVisible();
});
test("inProgressNotificationMessage shown when inProgress is true", async ({
initTestBed,
page,
createSelectDriver,
}) => {
await initTestBed(`
<Select searchable inProgress inProgressNotificationMessage="in-progress-msg">
<Option value="opt1" label="first"/>
<Option value="opt2" label="second"/>
<Option value="opt3" label="third"/>
</Select>
`);
const driver = await createSelectDriver();
await driver.click();
await expect(page.getByText("in-progress-msg")).toBeVisible();
});
test("inProgressNotificationMessage not shown when inProgress is false", async ({
initTestBed,
page,
createSelectDriver,
}) => {
await initTestBed(`
<Select searchable inProgress="false" inProgressNotificationMessage="in-progress-msg">
<Option value="opt1" label="first"/>
<Option value="opt2" label="second"/>
<Option value="opt3" label="third"/>
</Select>
`);
const driver = await createSelectDriver();
await driver.click();
await expect(page.getByText("in-progress-msg")).not.toBeVisible();
});
test(
"search filters option labels",
{ tag: "@smoke" },
async ({ initTestBed, page, createSelectDriver }) => {
await initTestBed(`
<Select searchable>
<Option value="opt1" label="first"/>
<Option value="opt2" label="second"/>
<Option value="opt3" label="third"/>
</Select>
`);
const driver = await createSelectDriver();
await driver.toggleOptionsVisibility();
await driver.searchFor("econd");
const options = await page.getByRole("option").all();
expect(options).toHaveLength(1);
await expect(options[0]).toHaveText("second");
},
);
});
// =============================================================================
// MULTISELECT TESTS
// =============================================================================
test.describe("multiSelect", () => {
test("initialValue='{[0]}' works", async ({ page, initTestBed }) => {
await initTestBed(`
<Fragment>
<Select id="mySelect" initialValue="{[0]}" multiSelect>
<Option value="{0}" label="Zero"/>
<Option value="{1}" label="One"/>
<Option value="{2}" label="Two"/>
</Select>
<Text testId="text">Selected value: {mySelect.value}</Text>
</Fragment>
`);
await expect(page.getByTestId("text")).toHaveText("Selected value: 0");
});
test("initialValue='{[0,1]}' works", { tag: "@smoke" }, async ({ page, initTestBed }) => {
await initTestBed(`
<Fragment>
<Select id="mySelect" initialValue="{[0,1]}" multiSelect>
<Option value="{0}" label="Zero"/>
<Option value="{1}" label="One"/>
<Option value="{2}" label="Two"/>
</Select>
<Text testId="text">Selected value: {mySelect.value}</Text>
</Fragment>
`);
await expect(page.getByTestId("text")).toHaveText("Selected value: 0,1");
});
test("select multiple items without closing listbox", async ({
page,
initTestBed,
createSelectDriver,
}) => {
const { testStateDriver } = await initTestBed(`
<Fragment>
<Select id="mySelect" multiSelect>
<Option value="{0}" label="Zero"/>
<Option value="{1}" label="One"/>
<Option value="{2}" label="Two"/>
</Select>
<Text testId="text">Selected value: {mySelect.value}</Text>
</Fragment>
`);
const selectDrv = await createSelectDriver("mySelect");
await selectDrv.toggleOptionsVisibility();
await selectDrv.selectMultipleLabels(["Zero", "One"]);
/* problem is that the listbox closes after the 1st selection is made */
await expect(page.getByTestId("text")).toHaveText("Selected value: 0,1");
});
test(
"clicking label brings up the options",
{ tag: "@smoke" },
async ({ initTestBed, page, createSelectDriver }) => {
await initTestBed(`
<Select label="Choose an option" multiSelect>
<Option value="1" label="One"/>
<Option value="2" label="Two"/>
</Select>
`);
await page.getByLabel("Choose an option").click();
await expect(page.getByRole("option", { name: "One" })).toBeVisible();
await expect(page.getByRole("option", { name: "Two" })).toBeVisible();
},
);
test("labelBreak prop defaults to false", async ({ initTestBed, page, createSelectDriver }) => {
await page.setViewportSize({ width: 300, height: 720 });
await initTestBed(`
<Select
label="Dignissimos esse quasi esse cupiditate qui qui. Ut provident ad voluptatem tenetur sit consequuntur. Aliquam nisi fugit ut temporibus itaque ducimus rerum. Dolorem reprehenderit qui adipisci. Ullam harum atque ipsa."
multiSelect>
<Option value="1" label="One"/>
<Option value="2" label="Two"/>
</Select>
`);
const labelWidth = (await page.getByText("Dignissimos esse quasi").boundingBox()).width;
const select = page.getByRole("button").or(page.getByRole("combobox")).first();
const { width: selectWidth } = await select.boundingBox();
expect(labelWidth).toBe(selectWidth);
});
test('labelPosition="start" is left in ltr language', async ({ initTestBed, page }) => {
await initTestBed(`
<Select multiSelect label="hi there" labelPosition="start" labelBreak="false">
<Option value="1" label="One"/>
<Option value="2" label="Two"/>
</Select>
`);
const { x: labelX } = await page.getByText("hi there").boundingBox();
const select = page.getByRole("button").or(page.getByRole("combobox")).first();
const { x: selectX } = await select.boundingBox();
expect(labelX).toBeLessThan(selectX);
});
test('labelPosition="start" is right in rtl language', async ({ initTestBed, page }) => {
await initTestBed(`
<VStack direction="rtl">
<Select multiSelect label="hi there" labelPosition="start" labelBreak="false">
<Option value="1" label="One" />
<Option value="2" label="Two" />
</Select>
</VStack>
`);
const { x: labelX } = await page.getByText("hi there").boundingBox();
const select = page.getByRole("button").or(page.getByRole("combobox")).first();
const { x: selectX } = await select.boundingBox();
expect(labelX).toBeGreaterThan(selectX);
});
test("multiSelect autoFocus brings the focus to component", async ({
initTestBed,
page,
createSelectDriver,
}) => {
await initTestBed(`
<Select multiSelect>
<Option value="1" label="One"/>
<Option value="2" label="Two"/>
</Select>
<Select testId="focused-select" multmultiSelect autoFocus>
<Option value="1" label="One"/>
<Option value="2" label="Two"/>
</Select>
`);
const driver = await createSelectDriver("focused-select");
await expect(driver.component).toBeFocused();
});
test("autoFocus brings the focus to component", async ({
initTestBed,
page,
createSelectDriver,
}) => {
await initTestBed(`
<Select initialValue="opt1" placeholder="Select..." multiSelect>
<property name="valueTemplate">
<HStack>
<Text>{$item.value}={$item.label}</Text>
<Button
variant="ghost"
icon="close"
size="xs"
testId="remove-item-btn"
onClick="$itemContext.removeItem()"/>
</HStack>
</property>
<Option value="opt1" label="first"/>
<Option value="opt2" label="second"/>
<Option value="opt3" label="third"/>
</Select>
`);
const driver = await createSelectDriver();
await driver.toggleOptionsVisibility();
await driver.selectLabel("first");
await expect(page.getByText("opt1=first", { exact: true })).toBeVisible();
await page.getByTestId("remove-item-btn").click();
await expect(page.getByText("opt1=first", { exact: true })).not.toBeVisible();
});
});
// =============================================================================
// SEARCHABLE MULTISELECT TESTS
// =============================================================================
test.describe("searchable multiselect", { tag: "@smoke" }, () => {
test("searching for and selecting 2 items works", async ({
page,
initTestBed,
createSelectDriver,
}) => {
await initTestBed(`
<Fragment>
<Select id="mySelect" testId="mySelect" multiSelect searchable>
<Option value="{0}" label="Zero"/>
<Option value="{1}" label="One"/>
<Option value="{2}" label="Two"/>
</Select>
<Text testId="text">Selected value: {mySelect.value}</Text>
</Fragment>
`);
const driver = await createSelectDriver("mySelect");
await driver.toggleOptionsVisibility();
await driver.selectFirstLabelPostSearh("One");
await driver.selectFirstLabelPostSearh("Two");
await expect(page.getByTestId("text")).toHaveText("Selected value: 1,2");
});
});
// =============================================================================
// EVENT HANDLING TESTS
// =============================================================================
test.describe("Event Handling", () => {
test("gotFocus event fires on focus", async ({ initTestBed, page }) => {
const { testStateDriver } = await initTestBed(`
<Select onGotFocus="testState = 'focused'">
<Option value="1" label="One"/>
<Option value="2" label="Two"/>
</Select>
`);
const selectButton = page.getByRole("combobox");
await selectButton.focus();
await expect.poll(testStateDriver.testState).toBe("focused");
});
test("gotFocus event fires on label click", async ({ initTestBed, page }) => {
const { testStateDriver } = await initTestBed(`
<Select label="Choose" onGotFocus="testState = 'focused'">
<Option value="1" label="One"/>
<Option value="2" label="Two"/>
</Select>
`);
await page.getByText("Choose").click();
await expect.poll(testStateDriver.testState).toBe("focused");
});
test("lostFocus event fires on blur", async ({ initTestBed, page }) => {
const { testStateDriver } = await initTestBed(`
<Select onLostFocus="testState = 'blurred'">
<Option value="1" label="One"/>
<Option value="2" label="Two"/>
</Select>
`);
const selectButton = page.getByRole("combobox");
await selectButton.focus();
await selectButton.blur();
await expect.poll(testStateDriver.testState).toBe("blurred");
});
});
// =============================================================================
// FORM INTEGRATION TESTS
// =============================================================================
//this is an upstream issue: https://github.com/radix-ui/primitives/issues/3135
test("initialValue honored when used within Form", async ({ initTestBed, page }) => {
await initTestBed(`
<Form>
<Select id="mySelect" initialValue="opt3">
<Option value="opt1" label="first"/>
<Option value="opt2" label="second"/>
<Option value="opt3" label="third"/>
</Select>
<Text testId="text">Selected value: {mySelect.value}</Text>
</Form>`);
await expect(page.getByTestId("text")).toHaveText("Selected value: opt3");
});
// =============================================================================
// VISUAL STATE TESTS
// =============================================================================
test.describe("Visual State", () => {
test("input has correct width in px", async ({ page, initTestBed }) => {
await initTestBed(`<Select width="200px" testId="test"/>`, {});
const input = page.getByTestId("test");
const { width } = await input.boundingBox();
expect(width).toBe(200);
});
test("input with label has correct width in px", async ({ page, initTestBed }) => {
await initTestBed(`<Select width="200px" label="test" testId="test"/>`, {});
const input = page.getByTestId("test");
const { width } = await input.boundingBox();
expect(width).toBe(200);
});
test("input has correct width in %", async ({ page, initTestBed }) => {
await page.setViewportSize({ width: 400, height: 300 });
await initTestBed(`<Select width="50%" testId="test"/>`, {});
const input = page.getByTestId("test");
const { width } = await input.boundingBox();
expect(width).toBe(200);
});
test("input with label has correct width in %", async ({ page, initTestBed }) => {
await page.setViewportSize({ width: 400, height: 300 });
await initTestBed(`<Select width="50%" label="test" testId="test"/>`, {});
const input = page.getByTestId("test");
const { width } = await input.boundingBox();
expect(width).toBe(200);
});
});
// =============================================================================
// Z-INDEX AND MODAL LAYERING TESTS
// =============================================================================
test.describe("Z-Index and Modal Layering", () => {
test("Select dropdown in modal is visible and not covered by modal overlay", async ({
initTestBed,
page,
createSelectDriver,
}) => {
await initTestBed(`
<Fragment>
<Select testId="select">
<Option value="stuff1">option 1</Option>
<Option value="stuff2">option 2</Option>
<Button onClick="modal.open()">BLOW UP</Button>
</Select>
<ModalDialog id="modal" title="Example Dialog">
<Form data="{{ firstName: 'Billy', lastName: 'Bob' }}">
<FormItem bindTo="firstName" required="true" />
<FormItem bindTo="lastName" required="true" />
<FormItem
label="Field to Update"
type="select"
width="200px"
bindTo="fieldToUpdate"
required
initialValue="rate"
testId="modal-select"
>
<Option value="rate">Price</Option>
<Option value="description">Item Description</Option>
<Option value="account_id">Account</Option>
</FormItem>
</Form>
</ModalDialog>
</Fragment>
`);
const selectDriver = await createSelectDriver("select");
await selectDriver.click();
// Click button to open modal
const blowUpButton = page.getByText("BLOW UP");
await blowUpButton.click();
// Wait for modal to be visible
await expect(page.getByRole("dialog", { name: "Example Dialog" })).toBeVisible();
// Open the select in the modal
const modalSelectDriver = await createSelectDriver("modal-select");
await modalSelectDriver.click();
// Check that all options are visible
await expect(page.getByRole("option", { name: "Price" })).toBeVisible();
await expect(page.getByRole("option", { name: "Item Description" })).toBeVisible();
await expect(page.getByRole("option", { name: "Account" })).toBeVisible();
});
});
// =============================================================================
// THEME VARIABLE TESTS
// =============================================================================
test.describe("Theme Variables", () => {
[
{ value: "--default", prop: "" },
{ value: "--warning", prop: 'validationStatus="warning"' },
{ value: "--error", prop: 'validationStatus="error"' },
{ value: "--success", prop: 'validationStatus="valid"' },
].forEach((variant) => {
test(`applies correct borderRadius ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<Select testId="test" ${variant.prop} />`, {
testThemeVars: { [`borderRadius-Select${variant.value}`]: "12px" },
});
await expect(page.getByTestId("test")).toHaveCSS("border-radius", "12px");
});
test(`applies correct borderColor ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<Select testId="test" ${variant.prop} />`, {
testThemeVars: { [`borderColor-Select${variant.value}`]: "rgb(255, 0, 0)" },
});
await expect(page.getByTestId("test")).toHaveCSS("border-color", "rgb(255, 0, 0)");
});
test(`applies correct borderWidth ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<Select testId="test" ${variant.prop} />`, {
testThemeVars: { [`borderWidth-Select${variant.value}`]: "1px" },
});
await expect(page.getByTestId("test")).toHaveCSS("border-width", "1px");
});
test(`applies correct borderStyle ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<Select testId="test" ${variant.prop} />`, {
testThemeVars: { [`borderStyle-Select${variant.value}`]: "dashed" },
});
await expect(page.getByTestId("test")).toHaveCSS("border-style", "dashed");
});
test(`applies correct fontSize ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<Select testId="test" ${variant.prop} />`, {
testThemeVars: { [`fontSize-Select${variant.value}`]: "14px" },
});
await expect(page.getByTestId("test")).toHaveCSS("font-size", "14px");
});
test(`applies correct backgroundColor ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<Select testId="test" ${variant.prop} />`, {
testThemeVars: { [`backgroundColor-Select${variant.value}`]: "rgb(240, 240, 240)" },
});
await expect(page.getByTestId("test")).toHaveCSS("background-color", "rgb(240, 240, 240)");
});
test(`applies correct boxShadow ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<Select testId="test" ${variant.prop} />`, {
testThemeVars: {
[`boxShadow-Select${variant.value}`]: "0 2px 8px rgba(0, 0, 0, 0.1)",
},
});
await expect(page.getByTestId("test")).toHaveCSS(
"box-shadow",
"rgba(0, 0, 0, 0.1) 0px 2px 8px 0px",
);
});
test(`applies correct textColor ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<Select testId="test" ${variant.prop} />`, {
testThemeVars: { [`textColor-Select${variant.value}`]: "rgb(0, 0, 0)" },
});
await expect(page.getByTestId("test")).toHaveCSS("color", "rgb(0, 0, 0)");
});
test(`applies correct borderColor on hover ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<Select testId="test" ${variant.prop} />`, {
testThemeVars: { [`borderColor-Select${variant.value}--hover`]: "rgb(0, 0, 0)" },
});
await page.getByTestId("test").hover();
await expect(page.getByTestId("test")).toHaveCSS("border-color", "rgb(0, 0, 0)");
});
test(`applies correct backgroundColor on hover ${variant.value}`, async ({
initTestBed,
page,
}) => {
await initTestBed(`<Select testId="test" ${variant.prop} />`, {
testThemeVars: { [`backgroundColor-Select${variant.value}--hover`]: "rgb(0, 0, 0)" },
});
await page.getByTestId("test").hover();
await expect(page.getByTestId("test")).toHaveCSS("background-color", "rgb(0, 0, 0)");
});
test(`applies correct boxShadow on hover ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<Select testId="test" ${variant.prop} />`, {
testThemeVars: {
[`boxShadow-Select${variant.value}--hover`]: "0 2px 8px rgba(0, 0, 0, 0.1)",
},
});
await page.getByTestId("test").hover();
await expect(page.getByTestId("test")).toHaveCSS(
"box-shadow",
"rgba(0, 0, 0, 0.1) 0px 2px 8px 0px",
);
});
test(`applies correct textColor on hover ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<Select testId="test" ${variant.prop} />`, {
testThemeVars: { [`textColor-Select${variant.value}--hover`]: "rgb(0, 0, 0)" },
});
await page.getByTestId("test").hover();
await expect(page.getByTestId("test")).toHaveCSS("color", "rgb(0, 0, 0)");
});
});
});
```
--------------------------------------------------------------------------------
/xmlui/src/components/TextBox/TextBox.spec.ts:
--------------------------------------------------------------------------------
```typescript
import { getBounds, SKIP_REASON } from "../../testing/component-test-helpers";
import { test, expect } from "../../testing/fixtures";
// =============================================================================
// BASIC FUNCTIONALITY TESTS
// =============================================================================
test.describe("Basic Functionality", () => {
test("component renders", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" />`);
const driver = await createTextBoxDriver("test");
await expect(driver.component).toBeVisible();
});
test("component renders with label", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" label="Username" />`);
const driver = await createTextBoxDriver("test");
await expect(driver.component).toBeVisible();
await expect(driver.label).toBeVisible();
});
test("initialValue sets field value", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" initialValue="test value" />`);
const driver = await createTextBoxDriver("test");
await expect(driver.input).toHaveValue("test value");
});
test("initialValue accepts empty as empty string", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" initialValue="" />`);
const driver = await createTextBoxDriver("test");
await expect(driver.input).toHaveValue("");
});
test("initialValue accepts different data types", async ({ initTestBed, createTextBoxDriver }) => {
// Test string
await initTestBed(`<TextBox testId="test" initialValue="hello" />`);
const driver1 = await createTextBoxDriver("test");
await expect(driver1.input).toHaveValue("hello");
// Test number
await initTestBed(`<TextBox testId="test" initialValue="{123}" />`);
const driver2 = await createTextBoxDriver("test");
await expect(driver2.input).toHaveValue("123");
// Test boolean
await initTestBed(`<TextBox testId="test" initialValue="{true}" />`);
const driver3 = await createTextBoxDriver("test");
await expect(driver3.input).toHaveValue("true");
});
test("initialValue handles null", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" initialValue="{null}" />`);
const driver = await createTextBoxDriver("test");
await expect(driver.input).toHaveValue("");
});
test("initialValue handles undefined", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" initialValue="{undefined}" />`);
const driver = await createTextBoxDriver("test");
await expect(driver.input).toHaveValue("");
});
test("component accepts user input", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" />`);
const driver = await createTextBoxDriver("test");
await driver.input.fill("user input");
await expect(driver.input).toHaveValue("user input");
});
test("component clears input correctly", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" initialValue="initial text" />`);
const driver = await createTextBoxDriver("test");
await expect(driver.input).toHaveValue("initial text");
await driver.input.clear();
await expect(driver.input).toHaveValue("");
});
test("component required prop adds required attribute", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" required="true" />`);
const driver = await createTextBoxDriver("test");
await expect(driver.input).toHaveAttribute("required");
});
test("enabled=false disables control", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" enabled="false" />`);
const driver = await createTextBoxDriver("test");
await expect(driver.input).toBeDisabled();
});
test("enabled=false prevents user input", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" enabled="false" />`);
const driver = await createTextBoxDriver("test");
await expect(driver.input).not.toBeEditable();
});
test("readOnly prevents editing but allows focus", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" readOnly="true" initialValue="read only text" />`);
const driver = await createTextBoxDriver("test");
await expect(driver.input).toHaveAttribute("readonly");
await expect(driver.input).toHaveValue("read only text");
await expect(driver.input).not.toBeEditable();
await driver.input.focus();
await expect(driver.input).toBeFocused();
});
test("autoFocus focuses input on mount", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" autoFocus="true" />`);
const driver = await createTextBoxDriver("test");
await expect(driver.input).toBeFocused();
});
test("autoFocus focuses input on mount with label", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" autoFocus="true" label="Auto-focused input" />`);
const driver = await createTextBoxDriver("test");
await expect(driver.input).toBeFocused();
});
test("placeholder shows when input is empty", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" placeholder="Enter text here..." />`);
const driver = await createTextBoxDriver("test");
await expect(driver.input).toHaveAttribute("placeholder", "Enter text here...");
});
test("maxLength limits input length", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" maxLength="5" />`);
const driver = await createTextBoxDriver("test");
await driver.input.fill("12345678");
await expect(driver.input).toHaveValue("12345");
});
test("can render startIcon", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" startIcon="search" />`);
const driver = await createTextBoxDriver("test");
await expect(driver.startAdornment).toBeVisible();
});
test("can render endIcon", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" endIcon="search" />`);
const driver = await createTextBoxDriver("test");
await expect(driver.endAdornment).toBeVisible();
});
});
// =============================================================================
// ACCESSIBILITY TESTS
// =============================================================================
test.describe("Accessibility", () => {
test("label is properly associated with input", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" label="Username" />`);
const driver = await createTextBoxDriver("test");
await expect(driver.label).toBeVisible();
});
test("component supports keyboard navigation", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" label="Input" />`);
const driver = await createTextBoxDriver("test");
await driver.input.focus();
await expect(driver.input).toBeFocused();
});
test("component supports keyboard navigation from other elements", async ({
initTestBed,
page,
createTextBoxDriver,
}) => {
await initTestBed(`
<Fragment>
<TextBox testId="first-input" label="First input" />
<TextBox testId="second-input" label="Second input" />
</Fragment>
`);
const firstInput = await createTextBoxDriver("first-input");
const secondInput = await createTextBoxDriver("second-input");
await firstInput.input.focus();
await expect(firstInput.input).toBeFocused();
await page.keyboard.press("Tab", { delay: 100 });
await expect(secondInput.input).toBeFocused();
});
test("required has proper ARIA attributes", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" required="true" label="Required input" />`);
const driver = await createTextBoxDriver("test");
await expect(driver.input).toHaveAttribute("required");
await expect(driver.label).toContainText("*");
});
test("disabled component has proper attribute", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" enabled="false" />`);
const driver = await createTextBoxDriver("test");
await expect(driver.input).toHaveAttribute("disabled");
});
test("readOnly has proper ARIA attributes", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" readOnly="true" label="Read-only input" />`);
const driver = await createTextBoxDriver("test");
await expect(driver.input).toHaveAttribute("readonly");
await expect(driver.label).toContainText("Read-only input");
await expect(driver.input).not.toBeEditable();
});
});
// =============================================================================
// LABEL POSITIONING TESTS
// =============================================================================
test.describe("Label", () => {
test("labelPosition=start positions label before input", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" direction="ltr" label="test" labelPosition="start" />`);
const driver = await createTextBoxDriver("test");
const { left: textboxLeft } = await getBounds(driver.input);
const { right: labelRight } = await getBounds(driver.label);
expect(labelRight).toBeLessThan(textboxLeft);
});
test("labelPosition=end positions label after input", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" direction="ltr" label="test" labelPosition="end" />`);
const driver = await createTextBoxDriver("test");
const { right: textboxRight } = await getBounds(driver.input);
const { left: labelLeft } = await getBounds(driver.label);
expect(labelLeft).toBeGreaterThan(textboxRight);
});
test("labelPosition=top positions label above input", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" label="test" labelPosition="top" />`);
const driver = await createTextBoxDriver("test");
const { top: textboxTop } = await getBounds(driver.input);
const { bottom: labelBottom } = await getBounds(driver.label);
expect(labelBottom).toBeLessThan(textboxTop);
});
test("labelPosition=bottom positions label below input", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" label="test" labelPosition="bottom" />`);
const driver = await createTextBoxDriver("test");
const { bottom: textboxBottom } = await getBounds(driver.input);
const { top: labelTop } = await getBounds(driver.label);
expect(labelTop).toBeGreaterThan(textboxBottom);
});
test("labelBreak enables label line breaks", async ({ initTestBed, createTextBoxDriver }) => {
const labelText = "Very long label text that should break";
const commonProps = `label="${labelText}" labelWidth="100px"`;
await initTestBed(
`<Fragment>
<TextBox ${commonProps} testId="break" labelBreak="{true}" />
<TextBox ${commonProps} testId="oneLine" labelBreak="{false}" />
</Fragment>`,
);
const driverBreak = await createTextBoxDriver("break");
const driverOneLine = await createTextBoxDriver("oneLine");
const { height: heightBreak } = await getBounds(driverBreak.label);
const { height: heightOneLine } = await getBounds(driverOneLine.label);
expect(heightBreak).toBeGreaterThan(heightOneLine);
});
test("component handles invalid labelPosition gracefully", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="test" labelPosition="invalid" label="test" />`);
const driver = await createTextBoxDriver("test");
await expect(driver.label).toBeVisible();
await expect(driver.input).toBeVisible();
});
});
// =============================================================================
// EVENT HANDLING TESTS
// =============================================================================
test.describe("Event Handling", () => {
test("didChange event fires on input change", async ({ initTestBed, createTextBoxDriver }) => {
const { testStateDriver } = await initTestBed(`
<TextBox testId="test" onDidChange="testState = 'changed'" />
`);
const driver = await createTextBoxDriver("test");
await driver.input.fill("test");
await expect.poll(testStateDriver.testState).toEqual("changed");
});
test("didChange event passes new value", async ({ initTestBed, createTextBoxDriver }) => {
const { testStateDriver } = await initTestBed(`
<TextBox testId="test" onDidChange="arg => testState = arg" />
`);
const driver = await createTextBoxDriver("test");
await driver.input.fill("test value");
await expect.poll(testStateDriver.testState).toEqual("test value");
});
test("gotFocus event fires on focus", async ({ initTestBed, createTextBoxDriver }) => {
const { testStateDriver } = await initTestBed(`
<TextBox testId="test" onGotFocus="testState = 'focused'" />
`);
const driver = await createTextBoxDriver("test");
await driver.input.focus();
await expect.poll(testStateDriver.testState).toEqual("focused");
});
test("gotFocus event fires on label click", async ({ initTestBed, page }) => {
const { testStateDriver } = await initTestBed(`
<TextBox label="Username" onGotFocus="testState = 'focused'" />
`);
await page.getByText("Username").click();
await expect.poll(testStateDriver.testState).toEqual("focused");
});
test("component lostFocus event fires on blur", async ({ initTestBed, createTextBoxDriver }) => {
const { testStateDriver } = await initTestBed(`
<TextBox testId="test" onLostFocus="testState = 'blurred'" />
`);
const driver = await createTextBoxDriver("test");
await driver.input.focus();
await driver.input.blur();
await expect.poll(testStateDriver.testState).toEqual("blurred");
});
test("events do not fire when component is disabled", async ({ initTestBed, createTextBoxDriver }) => {
const { testStateDriver } = await initTestBed(`
<TextBox testId="test" enabled="false" didChange="testState = 'changed'" gotFocus="testState = 'focused'" />
`);
const driver = await createTextBoxDriver("test");
await driver.input.focus();
await driver.input.fill("test", { force: true });
await expect.poll(testStateDriver.testState).toEqual(null);
});
});
// =============================================================================
// API TESTS
// =============================================================================
test.describe("Api", () => {
test("component value API returns current state", async ({ initTestBed, page }) => {
await initTestBed(`
<Fragment>
<TextBox id="myTextBox" initialValue="initial" />
<Text testId="value">{myTextBox.value}</Text>
</Fragment>
`);
await expect(page.getByTestId("value")).toHaveText("initial");
});
test("component value API returns state after change", async ({ initTestBed, page, createTextBoxDriver }) => {
await initTestBed(`
<Fragment>
<TextBox id="myTextBox" testId="myTextBox" />
<Text testId="value">{myTextBox.value}</Text>
</Fragment>
`);
const driver = await createTextBoxDriver("myTextBox");
await expect(page.getByTestId("value")).toHaveText("");
await driver.input.fill("new value");
await expect(page.getByTestId("value")).toHaveText("new value");
});
test("component setValue API updates state", async ({ initTestBed, page, createTextBoxDriver }) => {
await initTestBed(`
<Fragment>
<TextBox id="myTextBox" testId="myTextBox" />
<Button testId="setBtn" onClick="myTextBox.setValue('api value')" />
</Fragment>
`);
const driver = await createTextBoxDriver("myTextBox");
await page.getByTestId("setBtn").click();
await expect(driver.input).toHaveValue("api value");
});
test("component setValue API triggers events", async ({ initTestBed, page }) => {
const { testStateDriver } = await initTestBed(`
<Fragment>
<TextBox id="myTextBox" onDidChange="testState = 'api-changed'" />
<Button testId="setBtn" onClick="myTextBox.setValue('test')">Set Value</Button>
</Fragment>
`);
await page.getByTestId("setBtn").click();
await expect.poll(testStateDriver.testState).toEqual("api-changed");
});
test("focus API focuses the input", async ({ initTestBed, page, createTextBoxDriver }) => {
await initTestBed(`
<Fragment>
<TextBox id="myTextBox" />
<Button testId="focusBtn" onClick="myTextBox.focus()">Focus</Button>
</Fragment>
`);
const driver = await createTextBoxDriver("myTextBox");
await expect(driver.input).not.toBeFocused();
await page.getByTestId("focusBtn").click();
await expect(driver.input).toBeFocused();
});
test("focus API does nothing when component is disabled", async ({ initTestBed, page, createTextBoxDriver }) => {
await initTestBed(`
<Fragment>
<TextBox id="myTextBox" enabled="false" />
<Button testId="focusBtn" onClick="myTextBox.focus()">Focus</Button>
</Fragment>
`);
const driver = await createTextBoxDriver("myTextBox");
await page.getByTestId("focusBtn").click();
await expect(driver.input).not.toBeFocused();
});
});
// =============================================================================
// INPUT ADORNMENTS TESTS
// =============================================================================
test.describe("Input Adornments", () => {
test("startText displays at beginning of input", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="input" startText="$" />`);
const driver = await createTextBoxDriver("input");
const { left: compLeft, right: compRight } = await getBounds(driver.input);
const { left: textLeft, right: textRight } = await getBounds(driver.startAdornment);
await expect(driver.startAdornment).toContainText("$");
expect(textRight - compLeft).toBeLessThanOrEqual(compRight - textLeft);
});
test("endText displays at end of input", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="input" endText="USD" />`);
const driver = await createTextBoxDriver("input");
const { left: compLeft, right: compRight } = await getBounds(driver.input);
const { left: textLeft, right: textRight } = await getBounds(driver.endAdornment);
await expect(driver.endAdornment).toContainText("USD");
expect(textRight - compLeft).toBeGreaterThanOrEqual(compRight - textLeft);
});
test("startIcon displays at beginning of input", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="input" startIcon="search" />`);
const driver = await createTextBoxDriver("input");
const { left: compLeft, right: compRight } = await getBounds(driver.input);
const { left: iconLeft, right: iconRight } = await getBounds(driver.startAdornment);
expect(iconRight - compLeft).toBeLessThanOrEqual(compRight - iconLeft);
});
test("endIcon displays at end of input", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="input" endIcon="search" />`);
const driver = await createTextBoxDriver("input");
const { left: compLeft, right: compRight } = await getBounds(driver.input);
const { left: iconLeft, right: iconRight } = await getBounds(driver.endAdornment);
expect(iconRight - compLeft).toBeGreaterThanOrEqual(compRight - iconLeft);
});
test("multiple adornments can be combined", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`
<TextBox testId="input" startText="$" endText="USD" startIcon="search" endIcon="search" />`);
const driver = await createTextBoxDriver("input");
await expect(driver.startAdornment).toContainText("$");
await expect(driver.endAdornment).toContainText("USD");
await expect(driver.startAdornment).toBeVisible();
await expect(driver.endAdornment).toBeVisible();
});
test("all adornments appear in the right place", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`
<TextBox testId="input" startText="$" endText="USD" startIcon="search" endIcon="search" direction="ltr" />
`);
const driver = await createTextBoxDriver("input");
const { left: compLeft, right: compRight } = await getBounds(driver.input);
const { left: startTextLeft, right: startTextRight } = await getBounds(driver.startAdornment);
const { left: endTextLeft, right: endTextRight } = await getBounds(driver.endAdornment);
const { left: startIconLeft, right: startIconRight } = await getBounds(
driver.startAdornment,
);
const { left: endIconLeft, right: endIconRight } = await getBounds(
driver.endAdornment,
);
// Check order of adornments
expect(startTextRight - compLeft).toBeLessThanOrEqual(compRight - startTextLeft);
expect(startIconRight - compLeft).toBeLessThanOrEqual(compRight - startIconLeft);
expect(endTextRight - compLeft).toBeGreaterThanOrEqual(compRight - endTextLeft);
expect(endIconRight - compLeft).toBeGreaterThanOrEqual(compRight - endIconLeft);
});
});
// =============================================================================
// PASSWORD INPUT TESTS
// =============================================================================
test.describe("Password Input", () => {
test("component renders", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<PasswordInput testId="input" />`);
const driver = await createTextBoxDriver("input");
await expect(driver.component).toBeVisible();
});
test("component has password type", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<PasswordInput testId="input" />`);
const driver = await createTextBoxDriver("input");
await expect(driver.input).toHaveAttribute("type", "password");
});
test("component has initial value", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<PasswordInput testId="input" initialValue="secret" />`);
const driver = await createTextBoxDriver("input");
await expect(driver.input).toHaveValue("secret");
});
test("showPasswordToggle displays visibility toggle", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<PasswordInput testId="input" showPasswordToggle="true" />`);
const driver = await createTextBoxDriver("input");
await expect(driver.input).toBeVisible();
});
test("password toggle switches between visible and hidden", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<PasswordInput testId="input" showPasswordToggle="true" />`);
const driver = await createTextBoxDriver("input");
await expect(driver.input).toHaveAttribute("type", "password");
await driver.button.click();
await expect(driver.input).toHaveAttribute("type", "text");
});
test("custom password icons work correctly", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(
`
<PasswordInput
testId="input"
showPasswordToggle="true"
passwordVisibleIcon="test"
passwordHiddenIcon="test"
/>`,
{
resources: {
"icon.test": "resources/bell.svg",
},
},
);
const driver = await createTextBoxDriver("input");
const icon = driver.button;
await expect(icon).toBeVisible();
await icon.click();
await expect(icon).toBeVisible();
});
});
// =============================================================================
// VISUAL STATE TESTS
// =============================================================================
test.describe("Theme Vars", () => {
test("backgroundColor applies correctly", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="input" />`, {
testThemeVars: {
"backgroundColor-TextBox": "rgb(255, 240, 240)",
},
});
const driver = await createTextBoxDriver("input");
await expect(driver.component).toHaveCSS("background-color", "rgb(255, 240, 240)");
});
test("borderColor applies correctly", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="input" />`, {
testThemeVars: {
"borderColor-TextBox": "rgb(255, 0, 0)",
},
});
const driver = await createTextBoxDriver("input");
await expect(driver.component).toHaveCSS("border-color", "rgb(255, 0, 0)");
});
test("textColor applies correctly", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="input" />`, {
testThemeVars: {
"textColor-TextBox": "rgb(0, 0, 255)",
},
});
const driver = await createTextBoxDriver("input");
await expect(driver.component).toHaveCSS("color", "rgb(0, 0, 255)");
});
test("focus borderColor applies on focus", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="input" />`, {
testThemeVars: {
"borderColor-TextBox--focus": "rgb(0, 255, 0)",
},
});
const driver = await createTextBoxDriver("input");
await driver.input.focus();
await expect(driver.component).toHaveCSS("border-color", "rgb(0, 255, 0)");
});
test("disabled backgroundColor applies when disabled", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="input" enabled="false" />`, {
testThemeVars: {
"backgroundColor-TextBox--disabled": "rgb(240, 240, 240)",
},
});
const driver = await createTextBoxDriver("input");
await expect(driver.component).toHaveCSS("background-color", "rgb(240, 240, 240)");
});
test("borderRadius applies correctly", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="input" />`, {
testThemeVars: {
"borderRadius-TextBox": "8px",
},
});
const driver = await createTextBoxDriver("input");
await expect(driver.component).toHaveCSS("border-radius", "8px");
});
test("padding applies correctly", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="input" />`, {
testThemeVars: {
"padding-TextBox": "12px",
},
});
const driver = await createTextBoxDriver("input");
await expect(driver.component).toHaveCSS("padding", "12px");
});
});
// =============================================================================
// VALIDATION STATUS TESTS
// =============================================================================
test.describe("Validation", () => {
test("handles invalid validationStatus gracefully", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="input" validationStatus="invalid" />`, {
testThemeVars: {
"borderColor-TextBox--default": "rgb(0, 0, 0)",
"borderColor-TextBox--error": "rgb(255, 0, 0)",
"borderColor-TextBox--warning": "rgb(255, 165, 0)",
"borderColor-TextBox--success": "rgb(0, 255, 0)",
},
});
const driver = await createTextBoxDriver("input");
await expect(driver.component).not.toHaveCSS("border-color", "rgb(255, 0, 0)");
await expect(driver.component).not.toHaveCSS("border-color", "rgb(255, 165, 0)");
await expect(driver.component).not.toHaveCSS("border-color", "rgb(0, 255, 0)");
await expect(driver.component).toHaveCSS("border-color", "rgb(0, 0, 0)");
});
[
{ value: "--default", prop: "" },
{ value: "--warning", prop: 'validationStatus="warning"' },
{ value: "--error", prop: 'validationStatus="error"' },
{ value: "--success", prop: 'validationStatus="valid"' },
].forEach((variant) => {
test(`applies correct borderRadius ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<TextBox testId="test" ${variant.prop} />`, {
testThemeVars: { [`borderRadius-TextBox${variant.value}`]: "12px" },
});
await expect(page.getByTestId("test")).toHaveCSS("border-radius", "12px");
});
test(`applies correct borderColor ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<TextBox testId="test" ${variant.prop} />`, {
testThemeVars: { [`borderColor-TextBox${variant.value}`]: "rgb(255, 0, 0)" },
});
await expect(page.getByTestId("test")).toHaveCSS("border-color", "rgb(255, 0, 0)");
});
test(`applies correct borderWidth ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<TextBox testId="test" ${variant.prop} />`, {
testThemeVars: { [`borderWidth-TextBox${variant.value}`]: "1px" },
});
await expect(page.getByTestId("test")).toHaveCSS("border-width", "1px");
});
test(`applies correct borderStyle ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<TextBox testId="test" ${variant.prop} />`, {
testThemeVars: { [`borderStyle-TextBox${variant.value}`]: "dashed" },
});
await expect(page.getByTestId("test")).toHaveCSS("border-style", "dashed");
});
test(`applies correct fontSize ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<TextBox testId="test" ${variant.prop} />`, {
testThemeVars: { [`fontSize-TextBox${variant.value}`]: "14px" },
});
await expect(page.getByTestId("test")).toHaveCSS("font-size", "14px");
});
test(`applies correct backgroundColor ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<TextBox testId="test" ${variant.prop} />`, {
testThemeVars: { [`backgroundColor-TextBox${variant.value}`]: "rgb(240, 240, 240)" },
});
await expect(page.getByTestId("test")).toHaveCSS("background-color", "rgb(240, 240, 240)");
});
test(`applies correct boxShadow ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<TextBox testId="test" ${variant.prop} />`, {
testThemeVars: {
[`boxShadow-TextBox${variant.value}`]: "0 2px 8px rgba(0, 0, 0, 0.1)",
},
});
await expect(page.getByTestId("test")).toHaveCSS(
"box-shadow",
"rgba(0, 0, 0, 0.1) 0px 2px 8px 0px",
);
});
test(`applies correct textColor ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<TextBox testId="test" ${variant.prop} />`, {
testThemeVars: { [`textColor-TextBox${variant.value}`]: "rgb(0, 0, 0)" },
});
await expect(page.getByTestId("test")).toHaveCSS("color", "rgb(0, 0, 0)");
});
test(`applies correct borderColor on hover ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<TextBox testId="test" ${variant.prop} />`, {
testThemeVars: { [`borderColor-TextBox${variant.value}--hover`]: "rgb(0, 0, 0)" },
});
await page.getByTestId("test").hover();
await expect(page.getByTestId("test")).toHaveCSS("border-color", "rgb(0, 0, 0)");
});
test(`applies correct backgroundColor on hover ${variant.value}`, async ({
initTestBed,
page,
}) => {
await initTestBed(`<TextBox testId="test" ${variant.prop} />`, {
testThemeVars: { [`backgroundColor-TextBox${variant.value}--hover`]: "rgb(0, 0, 0)" },
});
await page.getByTestId("test").hover();
await expect(page.getByTestId("test")).toHaveCSS("background-color", "rgb(0, 0, 0)");
});
test(`applies correct boxShadow on hover ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<TextBox testId="test" ${variant.prop} />`, {
testThemeVars: {
[`boxShadow-TextBox${variant.value}--hover`]: "0 2px 8px rgba(0, 0, 0, 0.1)",
},
});
await page.getByTestId("test").hover();
await expect(page.getByTestId("test")).toHaveCSS(
"box-shadow",
"rgba(0, 0, 0, 0.1) 0px 2px 8px 0px",
);
});
test(`applies correct textColor on hover ${variant.value}`, async ({ initTestBed, page }) => {
await initTestBed(`<TextBox testId="test" ${variant.prop} />`, {
testThemeVars: { [`textColor-TextBox${variant.value}--hover`]: "rgb(0, 0, 0)" },
});
await page.getByTestId("test").hover();
await expect(page.getByTestId("test")).toHaveCSS("color", "rgb(0, 0, 0)");
});
});
});
// =============================================================================
// EDGE CASE TESTS
// =============================================================================
test.describe("Edge Cases", () => {
test("handle special characters in input", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="input"/>`);
const driver = await createTextBoxDriver("input");
await driver.input.fill("Hello 日本語 @#$%!");
await expect(driver.input).toHaveValue("Hello 日本語 @#$%!");
});
test("handle Unicode characters in input", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="input"/>`);
const driver = await createTextBoxDriver("input");
await driver.input.fill("🚀 Unicode test 🎉");
await expect(driver.input).toHaveValue("🚀 Unicode test 🎉");
});
test("component handles very long input text", async ({ initTestBed, createTextBoxDriver }) => {
const longText =
"This is a very long text that might cause layout or performance issues in the component".repeat(
10,
);
await initTestBed(`<TextBox testId="input"/>`);
const driver = await createTextBoxDriver("input");
await driver.input.fill(longText);
await expect(driver.input).toHaveValue(longText);
});
test("component handles special characters correctly", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="input" label="Input with !@#$%^&*()"/>`, {});
const driver = await createTextBoxDriver("input");
await expect(driver.label).toBeVisible();
});
test("component handles extremely long input values", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<TextBox testId="input"/>`);
const driver = await createTextBoxDriver("input");
const veryLongText = "A".repeat(10000);
await driver.input.fill(veryLongText);
await expect(driver.input).toHaveValue(veryLongText);
});
});
// =============================================================================
// INTEGRATION TESTS
// =============================================================================
test.describe("Integration", () => {
test("component works correctly in Stack layout contexts", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<Stack><TextBox testId="input" /></Stack>`);
const driver = await createTextBoxDriver("input");
const { width, height } = await getBounds(driver.input);
await expect(driver.input).toBeVisible();
expect(width).toBeGreaterThan(0);
expect(height).toBeGreaterThan(0);
});
test("component works correctly in FlowLayout layout contexts", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<FlowLayout><TextBox testId="input" /></FlowLayout>`);
const driver = await createTextBoxDriver("input");
const { width, height } = await getBounds(driver.input);
await expect(driver.input).toBeVisible();
expect(width).toBeGreaterThan(0);
expect(height).toBeGreaterThan(0);
});
test("component integrates with forms correctly", async ({ initTestBed, createTextBoxDriver }) => {
await initTestBed(`<Form><TextBox testId="input" label="Username" /></Form>`);
const driver = await createTextBoxDriver("input");
const { width, height } = await getBounds(driver.input);
await expect(driver.input).toBeVisible();
expect(width).toBeGreaterThan(0);
expect(height).toBeGreaterThan(0);
});
test("component works with conditional rendering", async ({ initTestBed, page, createTextBoxDriver }) => {
await initTestBed(`
<Fragment var.showInput="{true}">
<Fragment when="{showInput}">
<TextBox testId="input" label="Conditional input" />
</Fragment>
<Button testId="toggleBtn" onClick="showInput = !showInput">Toggle</Button>
</Fragment>
`);
const driver = await createTextBoxDriver("input");
await expect(driver.label).toBeVisible();
await page.getByTestId("toggleBtn").click();
await expect(driver.label).not.toBeVisible();
});
});
test("labelWidth applies custom label width", async ({ initTestBed, createTextBoxDriver }) => {
const expected = 200;
await initTestBed(`<TextBox testId="test" label="test test" labelWidth="${expected}px" />`);
const driver = await createTextBoxDriver("test");
const { width } = await getBounds(driver.label);
expect(width).toEqual(expected);
});
// =============================================================================
// VISUAL STATE TESTS
// =============================================================================
test("input has correct width", async ({ initTestBed, page }) => {
await initTestBed(`
<TextBox width="200px" testId="test"/>
`);
const { width } = await page.getByTestId("test").boundingBox();
expect(width).toBe(200);
});
test("input with label has correct width", async ({ initTestBed, page }) => {
await initTestBed(`
<TextBox width="200px" label="test" testId="test"/>
`);
const { width } = await page.getByTestId("test").boundingBox();
expect(width).toBe(200);
});
test("input has correct width in %", async ({ page, initTestBed }) => {
await page.setViewportSize({ width: 400, height: 300});
await initTestBed(`<TextBox width="50%" testId="test"/>`, {});
const input = page.getByTestId("test");
const { width } = await input.boundingBox();
expect(width).toBe(200);
});
test("input with label has correct width in %", async ({ page, initTestBed }) => {
await page.setViewportSize({ width: 400, height: 300});
await initTestBed(`<TextBox width="50%" label="test" testId="test"/>`, {});
const input = page.getByTestId("test");
const { width } = await input.boundingBox();
expect(width).toBe(200);
});
```