This is page 170 of 181. Use http://codebase.md/xmlui-org/xmlui/tools/vscode/resources/%7BimageSrc%7D?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .changeset
│ ├── config.json
│ ├── curly-llamas-try.md
│ ├── loose-trains-sit.md
│ ├── moody-pans-poke.md
│ ├── rare-cooks-write.md
│ ├── silver-llamas-cough.md
│ └── true-jeans-agree.md
├── .eslintrc.cjs
├── .github
│ ├── build-checklist.png
│ ├── ISSUE_TEMPLATE
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ └── workflows
│ ├── deploy-blog.yml
│ ├── deploy-docs-optimized.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.mp4
│ │ │ │ ├── blog-page-component.png
│ │ │ │ ├── blog-scrabble.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
│ │ └── 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
│ │ │ │ ├── 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.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.module.scss
│ │ │ ├── Preview.tsx
│ │ │ ├── Select.module.scss
│ │ │ ├── StandalonePlayground.tsx
│ │ │ ├── StandalonePlaygroundNative.module.scss
│ │ │ ├── StandalonePlaygroundNative.tsx
│ │ │ ├── ThemeSwitcher.module.scss
│ │ │ ├── ThemeSwitcher.tsx
│ │ │ ├── ToneSwitcher.tsx
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.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
│ ├── 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
│ ├── ud-components.md
│ └── xmlui-repo.md
├── package.json
├── scripts
│ ├── coverage-only.js
│ ├── e2e-test-summary.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
│ ├── 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.spec.ts
│ │ │ │ ├── LabelList.tsx
│ │ │ │ ├── LabelListNative.module.scss
│ │ │ │ └── LabelListNative.tsx
│ │ │ ├── Legend
│ │ │ │ ├── Legend.spec.ts
│ │ │ │ ├── Legend.tsx
│ │ │ │ └── LegendNative.tsx
│ │ │ ├── LineChart
│ │ │ │ ├── LineChart.md
│ │ │ │ ├── LineChart.module.scss
│ │ │ │ ├── LineChart.spec.ts
│ │ │ │ ├── LineChart.tsx
│ │ │ │ └── LineChartNative.tsx
│ │ │ ├── PieChart
│ │ │ │ ├── PieChart.md
│ │ │ │ ├── PieChart.spec.ts
│ │ │ │ ├── PieChart.tsx
│ │ │ │ ├── PieChartNative.module.scss
│ │ │ │ └── PieChartNative.tsx
│ │ │ ├── RadarChart
│ │ │ │ ├── RadarChart.md
│ │ │ │ ├── RadarChart.spec.ts
│ │ │ │ ├── RadarChart.tsx
│ │ │ │ └── RadarChartNative.tsx
│ │ │ ├── Tooltip
│ │ │ │ ├── TooltipContent.module.scss
│ │ │ │ ├── TooltipContent.spec.ts
│ │ │ │ └── TooltipContent.tsx
│ │ │ └── utils
│ │ │ ├── abstractions.ts
│ │ │ └── ChartProvider.tsx
│ │ ├── Checkbox
│ │ │ ├── Checkbox.md
│ │ │ ├── Checkbox.spec.ts
│ │ │ └── Checkbox.tsx
│ │ ├── CodeBlock
│ │ │ ├── CodeBlock.module.scss
│ │ │ ├── CodeBlock.spec.ts
│ │ │ ├── CodeBlock.tsx
│ │ │ ├── CodeBlockNative.tsx
│ │ │ └── highlight-code.ts
│ │ ├── collectedComponentMetadata.ts
│ │ ├── ColorPicker
│ │ │ ├── ColorPicker.md
│ │ │ ├── ColorPicker.module.scss
│ │ │ ├── ColorPicker.spec.ts
│ │ │ ├── ColorPicker.tsx
│ │ │ └── ColorPickerNative.tsx
│ │ ├── Column
│ │ │ ├── Column.md
│ │ │ ├── Column.tsx
│ │ │ ├── ColumnNative.tsx
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ └── TableContext.tsx
│ │ ├── component-utils.ts
│ │ ├── ComponentProvider.tsx
│ │ ├── ComponentRegistryContext.tsx
│ │ ├── container-helpers.tsx
│ │ ├── ContentSeparator
│ │ │ ├── ContentSeparator.md
│ │ │ ├── ContentSeparator.module.scss
│ │ │ ├── ContentSeparator.spec.ts
│ │ │ ├── ContentSeparator.tsx
│ │ │ └── ContentSeparatorNative.tsx
│ │ ├── DataSource
│ │ │ ├── DataSource.md
│ │ │ └── DataSource.tsx
│ │ ├── DateInput
│ │ │ ├── DateInput.md
│ │ │ ├── DateInput.module.scss
│ │ │ ├── DateInput.spec.ts
│ │ │ ├── DateInput.tsx
│ │ │ └── DateInputNative.tsx
│ │ ├── DatePicker
│ │ │ ├── DatePicker.md
│ │ │ ├── DatePicker.module.scss
│ │ │ ├── DatePicker.spec.ts
│ │ │ ├── DatePicker.tsx
│ │ │ └── DatePickerNative.tsx
│ │ ├── DropdownMenu
│ │ │ ├── DropdownMenu.md
│ │ │ ├── DropdownMenu.module.scss
│ │ │ ├── DropdownMenu.spec.ts
│ │ │ ├── DropdownMenu.tsx
│ │ │ ├── DropdownMenuNative.tsx
│ │ │ ├── MenuItem.md
│ │ │ └── SubMenuItem.md
│ │ ├── EmojiSelector
│ │ │ ├── EmojiSelector.md
│ │ │ ├── EmojiSelector.spec.ts
│ │ │ ├── EmojiSelector.tsx
│ │ │ └── EmojiSelectorNative.tsx
│ │ ├── ExpandableItem
│ │ │ ├── ExpandableItem.module.scss
│ │ │ ├── ExpandableItem.spec.ts
│ │ │ ├── ExpandableItem.tsx
│ │ │ └── ExpandableItemNative.tsx
│ │ ├── FileInput
│ │ │ ├── FileInput.md
│ │ │ ├── FileInput.module.scss
│ │ │ ├── FileInput.spec.ts
│ │ │ ├── FileInput.tsx
│ │ │ └── FileInputNative.tsx
│ │ ├── FileUploadDropZone
│ │ │ ├── FileUploadDropZone.md
│ │ │ ├── FileUploadDropZone.module.scss
│ │ │ ├── FileUploadDropZone.spec.ts
│ │ │ ├── FileUploadDropZone.tsx
│ │ │ └── FileUploadDropZoneNative.tsx
│ │ ├── FlowLayout
│ │ │ ├── FlowLayout.md
│ │ │ ├── FlowLayout.module.scss
│ │ │ ├── FlowLayout.spec.ts
│ │ │ ├── FlowLayout.spec.ts-snapshots
│ │ │ │ └── Edge-cases-boxShadow-is-not-clipped-1-non-smoke-darwin.png
│ │ │ ├── FlowLayout.tsx
│ │ │ └── FlowLayoutNative.tsx
│ │ ├── Footer
│ │ │ ├── Footer.md
│ │ │ ├── Footer.module.scss
│ │ │ ├── Footer.spec.ts
│ │ │ ├── Footer.tsx
│ │ │ └── FooterNative.tsx
│ │ ├── Form
│ │ │ ├── Form.md
│ │ │ ├── Form.module.scss
│ │ │ ├── Form.spec.ts
│ │ │ ├── Form.tsx
│ │ │ ├── formActions.ts
│ │ │ ├── FormContext.ts
│ │ │ └── FormNative.tsx
│ │ ├── FormItem
│ │ │ ├── FormItem.md
│ │ │ ├── FormItem.module.scss
│ │ │ ├── FormItem.spec.ts
│ │ │ ├── FormItem.tsx
│ │ │ ├── FormItemNative.tsx
│ │ │ ├── HelperText.module.scss
│ │ │ ├── HelperText.tsx
│ │ │ ├── ItemWithLabel.tsx
│ │ │ └── Validations.ts
│ │ ├── FormSection
│ │ │ ├── FormSection.md
│ │ │ ├── FormSection.ts
│ │ │ └── FormSection.xmlui
│ │ ├── Fragment
│ │ │ ├── Fragment.spec.ts
│ │ │ └── Fragment.tsx
│ │ ├── Heading
│ │ │ ├── abstractions.ts
│ │ │ ├── H1.md
│ │ │ ├── H1.spec.ts
│ │ │ ├── H2.md
│ │ │ ├── H2.spec.ts
│ │ │ ├── H3.md
│ │ │ ├── H3.spec.ts
│ │ │ ├── H4.md
│ │ │ ├── H4.spec.ts
│ │ │ ├── H5.md
│ │ │ ├── H5.spec.ts
│ │ │ ├── H6.md
│ │ │ ├── H6.spec.ts
│ │ │ ├── Heading.md
│ │ │ ├── Heading.module.scss
│ │ │ ├── Heading.spec.ts
│ │ │ ├── Heading.tsx
│ │ │ └── HeadingNative.tsx
│ │ ├── HoverCard
│ │ │ ├── HoverCard.tsx
│ │ │ └── HovercardNative.tsx
│ │ ├── HtmlTags
│ │ │ ├── HtmlTags.module.scss
│ │ │ ├── HtmlTags.spec.ts
│ │ │ └── HtmlTags.tsx
│ │ ├── Icon
│ │ │ ├── AdmonitionDanger.tsx
│ │ │ ├── AdmonitionInfo.tsx
│ │ │ ├── AdmonitionNote.tsx
│ │ │ ├── AdmonitionTip.tsx
│ │ │ ├── AdmonitionWarning.tsx
│ │ │ ├── ApiIcon.tsx
│ │ │ ├── ArrowDropDown.module.scss
│ │ │ ├── ArrowDropDown.tsx
│ │ │ ├── ArrowDropUp.module.scss
│ │ │ ├── ArrowDropUp.tsx
│ │ │ ├── ArrowLeft.module.scss
│ │ │ ├── ArrowLeft.tsx
│ │ │ ├── ArrowRight.module.scss
│ │ │ ├── ArrowRight.tsx
│ │ │ ├── Attach.tsx
│ │ │ ├── Binding.module.scss
│ │ │ ├── Binding.tsx
│ │ │ ├── BoardIcon.tsx
│ │ │ ├── BoxIcon.tsx
│ │ │ ├── CheckIcon.tsx
│ │ │ ├── ChevronDownIcon.tsx
│ │ │ ├── ChevronLeft.tsx
│ │ │ ├── ChevronRight.tsx
│ │ │ ├── ChevronUpIcon.tsx
│ │ │ ├── CodeFileIcon.tsx
│ │ │ ├── CodeSandbox.tsx
│ │ │ ├── CompactListIcon.tsx
│ │ │ ├── ContentCopyIcon.tsx
│ │ │ ├── DarkToLightIcon.tsx
│ │ │ ├── DatabaseIcon.module.scss
│ │ │ ├── DatabaseIcon.tsx
│ │ │ ├── DocFileIcon.tsx
│ │ │ ├── DocIcon.tsx
│ │ │ ├── DotMenuHorizontalIcon.tsx
│ │ │ ├── DotMenuIcon.tsx
│ │ │ ├── EmailIcon.tsx
│ │ │ ├── EmptyFolderIcon.tsx
│ │ │ ├── ErrorIcon.tsx
│ │ │ ├── ExpressionIcon.tsx
│ │ │ ├── FillPlusCricleIcon.tsx
│ │ │ ├── FilterIcon.tsx
│ │ │ ├── FolderIcon.tsx
│ │ │ ├── GlobeIcon.tsx
│ │ │ ├── HomeIcon.tsx
│ │ │ ├── HyperLinkIcon.tsx
│ │ │ ├── Icon.md
│ │ │ ├── Icon.module.scss
│ │ │ ├── Icon.spec.ts
│ │ │ ├── Icon.tsx
│ │ │ ├── IconNative.tsx
│ │ │ ├── ImageFileIcon.tsx
│ │ │ ├── Inspect.tsx
│ │ │ ├── LightToDark.tsx
│ │ │ ├── LinkIcon.tsx
│ │ │ ├── ListIcon.tsx
│ │ │ ├── LooseListIcon.tsx
│ │ │ ├── MoonIcon.tsx
│ │ │ ├── MoreOptionsIcon.tsx
│ │ │ ├── NoSortIcon.tsx
│ │ │ ├── PDFIcon.tsx
│ │ │ ├── PenIcon.tsx
│ │ │ ├── PhoneIcon.tsx
│ │ │ ├── PhotoIcon.tsx
│ │ │ ├── PlusIcon.tsx
│ │ │ ├── SearchIcon.tsx
│ │ │ ├── ShareIcon.tsx
│ │ │ ├── SortAscendingIcon.tsx
│ │ │ ├── SortDescendingIcon.tsx
│ │ │ ├── StarsIcon.tsx
│ │ │ ├── SunIcon.tsx
│ │ │ ├── svg
│ │ │ │ ├── admonition_danger.svg
│ │ │ │ ├── admonition_info.svg
│ │ │ │ ├── admonition_note.svg
│ │ │ │ ├── admonition_tip.svg
│ │ │ │ ├── admonition_warning.svg
│ │ │ │ ├── api.svg
│ │ │ │ ├── arrow-dropdown.svg
│ │ │ │ ├── arrow-left.svg
│ │ │ │ ├── arrow-right.svg
│ │ │ │ ├── arrow-up.svg
│ │ │ │ ├── attach.svg
│ │ │ │ ├── binding.svg
│ │ │ │ ├── box.svg
│ │ │ │ ├── bulb.svg
│ │ │ │ ├── code-file.svg
│ │ │ │ ├── code-sandbox.svg
│ │ │ │ ├── dark_to_light.svg
│ │ │ │ ├── database.svg
│ │ │ │ ├── doc.svg
│ │ │ │ ├── empty-folder.svg
│ │ │ │ ├── expression.svg
│ │ │ │ ├── eye-closed.svg
│ │ │ │ ├── eye-dark.svg
│ │ │ │ ├── eye.svg
│ │ │ │ ├── file-text.svg
│ │ │ │ ├── filter.svg
│ │ │ │ ├── folder.svg
│ │ │ │ ├── img.svg
│ │ │ │ ├── inspect.svg
│ │ │ │ ├── light_to_dark.svg
│ │ │ │ ├── moon.svg
│ │ │ │ ├── pdf.svg
│ │ │ │ ├── photo.svg
│ │ │ │ ├── share.svg
│ │ │ │ ├── stars.svg
│ │ │ │ ├── sun.svg
│ │ │ │ ├── trending-down.svg
│ │ │ │ ├── trending-level.svg
│ │ │ │ ├── trending-up.svg
│ │ │ │ ├── txt.svg
│ │ │ │ ├── unknown-file.svg
│ │ │ │ ├── unlink.svg
│ │ │ │ └── xls.svg
│ │ │ ├── TableDeleteColumnIcon.tsx
│ │ │ ├── TableDeleteRowIcon.tsx
│ │ │ ├── TableInsertColumnIcon.tsx
│ │ │ ├── TableInsertRowIcon.tsx
│ │ │ ├── TrashIcon.tsx
│ │ │ ├── TrendingDownIcon.tsx
│ │ │ ├── TrendingLevelIcon.tsx
│ │ │ ├── TrendingUpIcon.tsx
│ │ │ ├── TxtIcon.tsx
│ │ │ ├── UnknownFileIcon.tsx
│ │ │ ├── UnlinkIcon.tsx
│ │ │ ├── UserIcon.tsx
│ │ │ ├── WarningIcon.tsx
│ │ │ └── XlsIcon.tsx
│ │ ├── IconProvider.tsx
│ │ ├── IconRegistryContext.tsx
│ │ ├── IFrame
│ │ │ ├── IFrame.md
│ │ │ ├── IFrame.module.scss
│ │ │ ├── IFrame.spec.ts
│ │ │ ├── IFrame.tsx
│ │ │ └── IFrameNative.tsx
│ │ ├── Image
│ │ │ ├── Image.md
│ │ │ ├── Image.module.scss
│ │ │ ├── Image.spec.ts
│ │ │ ├── Image.tsx
│ │ │ └── ImageNative.tsx
│ │ ├── Input
│ │ │ ├── index.ts
│ │ │ ├── InputAdornment.module.scss
│ │ │ ├── InputAdornment.tsx
│ │ │ ├── InputDivider.module.scss
│ │ │ ├── InputDivider.tsx
│ │ │ ├── InputLabel.module.scss
│ │ │ ├── InputLabel.tsx
│ │ │ ├── PartialInput.module.scss
│ │ │ └── PartialInput.tsx
│ │ ├── InspectButton
│ │ │ ├── InspectButton.module.scss
│ │ │ └── InspectButton.tsx
│ │ ├── Items
│ │ │ ├── Items.md
│ │ │ ├── Items.spec.ts
│ │ │ ├── Items.tsx
│ │ │ └── ItemsNative.tsx
│ │ ├── Link
│ │ │ ├── Link.md
│ │ │ ├── Link.module.scss
│ │ │ ├── Link.spec.ts
│ │ │ ├── Link.tsx
│ │ │ └── LinkNative.tsx
│ │ ├── List
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── List.md
│ │ │ ├── List.module.scss
│ │ │ ├── List.spec.ts
│ │ │ ├── List.tsx
│ │ │ └── ListNative.tsx
│ │ ├── Logo
│ │ │ ├── doc-resources
│ │ │ │ └── xmlui-logo.svg
│ │ │ ├── Logo.md
│ │ │ ├── Logo.tsx
│ │ │ └── LogoNative.tsx
│ │ ├── Markdown
│ │ │ ├── CodeText.module.scss
│ │ │ ├── CodeText.tsx
│ │ │ ├── Markdown.md
│ │ │ ├── Markdown.module.scss
│ │ │ ├── Markdown.spec.ts
│ │ │ ├── Markdown.tsx
│ │ │ ├── MarkdownNative.tsx
│ │ │ ├── parse-binding-expr.ts
│ │ │ └── utils.ts
│ │ ├── metadata-helpers.ts
│ │ ├── ModalDialog
│ │ │ ├── ConfirmationModalContextProvider.tsx
│ │ │ ├── Dialog.module.scss
│ │ │ ├── Dialog.tsx
│ │ │ ├── ModalDialog.md
│ │ │ ├── ModalDialog.module.scss
│ │ │ ├── ModalDialog.spec.ts
│ │ │ ├── ModalDialog.tsx
│ │ │ ├── ModalDialogNative.tsx
│ │ │ └── ModalVisibilityContext.tsx
│ │ ├── NavGroup
│ │ │ ├── NavGroup.md
│ │ │ ├── NavGroup.module.scss
│ │ │ ├── NavGroup.spec.ts
│ │ │ ├── NavGroup.tsx
│ │ │ ├── NavGroupContext.ts
│ │ │ └── NavGroupNative.tsx
│ │ ├── NavLink
│ │ │ ├── NavLink.md
│ │ │ ├── NavLink.module.scss
│ │ │ ├── NavLink.spec.ts
│ │ │ ├── NavLink.tsx
│ │ │ └── NavLinkNative.tsx
│ │ ├── NavPanel
│ │ │ ├── NavPanel.md
│ │ │ ├── NavPanel.module.scss
│ │ │ ├── NavPanel.spec.ts
│ │ │ ├── NavPanel.tsx
│ │ │ └── NavPanelNative.tsx
│ │ ├── NestedApp
│ │ │ ├── AppWithCodeView.module.scss
│ │ │ ├── AppWithCodeView.tsx
│ │ │ ├── AppWithCodeViewNative.tsx
│ │ │ ├── defaultProps.tsx
│ │ │ ├── logo.svg
│ │ │ ├── NestedApp.module.scss
│ │ │ ├── NestedApp.tsx
│ │ │ ├── NestedAppNative.tsx
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.tsx
│ │ │ └── utils.ts
│ │ ├── NoResult
│ │ │ ├── NoResult.md
│ │ │ ├── NoResult.module.scss
│ │ │ ├── NoResult.spec.ts
│ │ │ ├── NoResult.tsx
│ │ │ └── NoResultNative.tsx
│ │ ├── NumberBox
│ │ │ ├── numberbox-abstractions.ts
│ │ │ ├── NumberBox.md
│ │ │ ├── NumberBox.module.scss
│ │ │ ├── NumberBox.spec.ts
│ │ │ ├── NumberBox.tsx
│ │ │ └── NumberBoxNative.tsx
│ │ ├── Option
│ │ │ ├── Option.md
│ │ │ ├── Option.spec.ts
│ │ │ ├── Option.tsx
│ │ │ ├── OptionNative.tsx
│ │ │ └── OptionTypeProvider.tsx
│ │ ├── PageMetaTitle
│ │ │ ├── PageMetaTilteNative.tsx
│ │ │ ├── PageMetaTitle.md
│ │ │ ├── PageMetaTitle.spec.ts
│ │ │ └── PageMetaTitle.tsx
│ │ ├── Pages
│ │ │ ├── Page.md
│ │ │ ├── Pages.md
│ │ │ ├── Pages.module.scss
│ │ │ ├── Pages.tsx
│ │ │ └── PagesNative.tsx
│ │ ├── Pagination
│ │ │ ├── Pagination.md
│ │ │ ├── Pagination.module.scss
│ │ │ ├── Pagination.spec.ts
│ │ │ ├── Pagination.tsx
│ │ │ └── PaginationNative.tsx
│ │ ├── PositionedContainer
│ │ │ ├── PositionedContainer.module.scss
│ │ │ ├── PositionedContainer.tsx
│ │ │ └── PositionedContainerNative.tsx
│ │ ├── ProfileMenu
│ │ │ ├── ProfileMenu.module.scss
│ │ │ └── ProfileMenu.tsx
│ │ ├── ProgressBar
│ │ │ ├── ProgressBar.md
│ │ │ ├── ProgressBar.module.scss
│ │ │ ├── ProgressBar.spec.ts
│ │ │ ├── ProgressBar.tsx
│ │ │ └── ProgressBarNative.tsx
│ │ ├── Queue
│ │ │ ├── Queue.md
│ │ │ ├── Queue.spec.ts
│ │ │ ├── Queue.tsx
│ │ │ ├── queueActions.ts
│ │ │ └── QueueNative.tsx
│ │ ├── RadioGroup
│ │ │ ├── RadioGroup.md
│ │ │ ├── RadioGroup.module.scss
│ │ │ ├── RadioGroup.spec.ts
│ │ │ ├── RadioGroup.tsx
│ │ │ ├── RadioGroupNative.tsx
│ │ │ ├── RadioItem.tsx
│ │ │ └── RadioItemNative.tsx
│ │ ├── RealTimeAdapter
│ │ │ ├── RealTimeAdapter.tsx
│ │ │ └── RealTimeAdapterNative.tsx
│ │ ├── Redirect
│ │ │ ├── Redirect.md
│ │ │ ├── Redirect.spec.ts
│ │ │ └── Redirect.tsx
│ │ ├── ResponsiveBar
│ │ │ ├── README.md
│ │ │ ├── ResponsiveBar.md
│ │ │ ├── ResponsiveBar.module.scss
│ │ │ ├── ResponsiveBar.spec.ts
│ │ │ ├── ResponsiveBar.tsx
│ │ │ └── ResponsiveBarNative.tsx
│ │ ├── Select
│ │ │ ├── HiddenOption.tsx
│ │ │ ├── OptionContext.ts
│ │ │ ├── Select.md
│ │ │ ├── Select.module.scss
│ │ │ ├── Select.spec.ts
│ │ │ ├── Select.tsx
│ │ │ ├── SelectContext.tsx
│ │ │ └── SelectNative.tsx
│ │ ├── SelectionStore
│ │ │ ├── SelectionStore.md
│ │ │ ├── SelectionStore.tsx
│ │ │ └── SelectionStoreNative.tsx
│ │ ├── Slider
│ │ │ ├── Slider.md
│ │ │ ├── Slider.module.scss
│ │ │ ├── Slider.spec.ts
│ │ │ ├── Slider.tsx
│ │ │ └── SliderNative.tsx
│ │ ├── Slot
│ │ │ ├── Slot.md
│ │ │ ├── Slot.spec.ts
│ │ │ └── Slot.ts
│ │ ├── SlotItem.tsx
│ │ ├── SpaceFiller
│ │ │ ├── SpaceFiller.md
│ │ │ ├── SpaceFiller.module.scss
│ │ │ ├── SpaceFiller.spec.ts
│ │ │ ├── SpaceFiller.tsx
│ │ │ └── SpaceFillerNative.tsx
│ │ ├── Spinner
│ │ │ ├── Spinner.md
│ │ │ ├── Spinner.module.scss
│ │ │ ├── Spinner.spec.ts
│ │ │ ├── Spinner.tsx
│ │ │ └── SpinnerNative.tsx
│ │ ├── Splitter
│ │ │ ├── HSplitter.md
│ │ │ ├── HSplitter.spec.ts
│ │ │ ├── Splitter.md
│ │ │ ├── Splitter.module.scss
│ │ │ ├── Splitter.spec.ts
│ │ │ ├── Splitter.tsx
│ │ │ ├── SplitterNative.tsx
│ │ │ ├── utils.ts
│ │ │ ├── VSplitter.md
│ │ │ └── VSplitter.spec.ts
│ │ ├── Stack
│ │ │ ├── CHStack.md
│ │ │ ├── CHStack.spec.ts
│ │ │ ├── CVStack.md
│ │ │ ├── CVStack.spec.ts
│ │ │ ├── HStack.md
│ │ │ ├── HStack.spec.ts
│ │ │ ├── Stack.md
│ │ │ ├── Stack.module.scss
│ │ │ ├── Stack.spec.ts
│ │ │ ├── Stack.tsx
│ │ │ ├── StackNative.tsx
│ │ │ ├── VStack.md
│ │ │ └── VStack.spec.ts
│ │ ├── StickyBox
│ │ │ ├── StickyBox.md
│ │ │ ├── StickyBox.module.scss
│ │ │ ├── StickyBox.tsx
│ │ │ └── StickyBoxNative.tsx
│ │ ├── Switch
│ │ │ ├── Switch.md
│ │ │ ├── Switch.spec.ts
│ │ │ └── Switch.tsx
│ │ ├── Table
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── react-table-config.d.ts
│ │ │ ├── Table.md
│ │ │ ├── Table.module.scss
│ │ │ ├── Table.spec.ts
│ │ │ ├── Table.tsx
│ │ │ ├── TableNative.tsx
│ │ │ └── useRowSelection.tsx
│ │ ├── TableOfContents
│ │ │ ├── TableOfContents.module.scss
│ │ │ ├── TableOfContents.spec.ts
│ │ │ ├── TableOfContents.tsx
│ │ │ └── TableOfContentsNative.tsx
│ │ ├── Tabs
│ │ │ ├── TabContext.tsx
│ │ │ ├── TabItem.md
│ │ │ ├── TabItem.tsx
│ │ │ ├── TabItemNative.tsx
│ │ │ ├── Tabs.md
│ │ │ ├── Tabs.module.scss
│ │ │ ├── Tabs.spec.ts
│ │ │ ├── Tabs.tsx
│ │ │ └── TabsNative.tsx
│ │ ├── Text
│ │ │ ├── Text.md
│ │ │ ├── Text.module.scss
│ │ │ ├── Text.spec.ts
│ │ │ ├── Text.tsx
│ │ │ └── TextNative.tsx
│ │ ├── TextArea
│ │ │ ├── TextArea.md
│ │ │ ├── TextArea.module.scss
│ │ │ ├── TextArea.spec.ts
│ │ │ ├── TextArea.tsx
│ │ │ ├── TextAreaNative.tsx
│ │ │ ├── TextAreaResizable.tsx
│ │ │ └── useComposedRef.ts
│ │ ├── TextBox
│ │ │ ├── TextBox.md
│ │ │ ├── TextBox.module.scss
│ │ │ ├── TextBox.spec.ts
│ │ │ ├── TextBox.tsx
│ │ │ └── TextBoxNative.tsx
│ │ ├── Theme
│ │ │ ├── NotificationToast.tsx
│ │ │ ├── Theme.md
│ │ │ ├── Theme.module.scss
│ │ │ ├── Theme.spec.ts
│ │ │ ├── Theme.tsx
│ │ │ └── ThemeNative.tsx
│ │ ├── TimeInput
│ │ │ ├── TimeInput.md
│ │ │ ├── TimeInput.module.scss
│ │ │ ├── TimeInput.spec.ts
│ │ │ ├── TimeInput.tsx
│ │ │ ├── TimeInputNative.tsx
│ │ │ └── utils.ts
│ │ ├── Timer
│ │ │ ├── Timer.md
│ │ │ ├── Timer.spec.ts
│ │ │ ├── Timer.tsx
│ │ │ └── TimerNative.tsx
│ │ ├── Toggle
│ │ │ ├── Toggle.module.scss
│ │ │ └── Toggle.tsx
│ │ ├── ToneChangerButton
│ │ │ ├── ToneChangerButton.md
│ │ │ ├── ToneChangerButton.spec.ts
│ │ │ └── ToneChangerButton.tsx
│ │ ├── ToneSwitch
│ │ │ ├── ToneSwitch.md
│ │ │ ├── ToneSwitch.module.scss
│ │ │ ├── ToneSwitch.spec.ts
│ │ │ ├── ToneSwitch.tsx
│ │ │ └── ToneSwitchNative.tsx
│ │ ├── Tooltip
│ │ │ ├── Tooltip.md
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.spec.ts
│ │ │ ├── Tooltip.tsx
│ │ │ └── TooltipNative.tsx
│ │ ├── Tree
│ │ │ ├── testData.ts
│ │ │ ├── Tree-dynamic.spec.ts
│ │ │ ├── Tree-icons.spec.ts
│ │ │ ├── Tree.md
│ │ │ ├── Tree.spec.ts
│ │ │ ├── TreeComponent.module.scss
│ │ │ ├── TreeComponent.tsx
│ │ │ └── TreeNative.tsx
│ │ ├── TreeDisplay
│ │ │ ├── TreeDisplay.md
│ │ │ ├── TreeDisplay.module.scss
│ │ │ ├── TreeDisplay.tsx
│ │ │ └── TreeDisplayNative.tsx
│ │ ├── ValidationSummary
│ │ │ ├── ValidationSummary.module.scss
│ │ │ └── ValidationSummary.tsx
│ │ └── VisuallyHidden.tsx
│ ├── components-core
│ │ ├── abstractions
│ │ │ ├── ComponentRenderer.ts
│ │ │ ├── LoaderRenderer.ts
│ │ │ ├── standalone.ts
│ │ │ └── treeAbstractions.ts
│ │ ├── action
│ │ │ ├── actions.ts
│ │ │ ├── APICall.tsx
│ │ │ ├── FileDownloadAction.tsx
│ │ │ ├── FileUploadAction.tsx
│ │ │ ├── NavigateAction.tsx
│ │ │ └── TimedAction.tsx
│ │ ├── ApiBoundComponent.tsx
│ │ ├── appContext
│ │ │ ├── date-functions.ts
│ │ │ ├── math-function.ts
│ │ │ └── misc-utils.ts
│ │ ├── AppContext.tsx
│ │ ├── behaviors
│ │ │ ├── Behavior.tsx
│ │ │ └── CoreBehaviors.tsx
│ │ ├── component-hooks.ts
│ │ ├── ComponentDecorator.tsx
│ │ ├── ComponentViewer.tsx
│ │ ├── CompoundComponent.tsx
│ │ ├── constants.ts
│ │ ├── DebugViewProvider.tsx
│ │ ├── descriptorHelper.ts
│ │ ├── devtools
│ │ │ ├── InspectorDialog.module.scss
│ │ │ ├── InspectorDialog.tsx
│ │ │ └── InspectorDialogVisibilityContext.tsx
│ │ ├── EngineError.ts
│ │ ├── event-handlers.ts
│ │ ├── InspectorButton.module.scss
│ │ ├── InspectorContext.tsx
│ │ ├── interception
│ │ │ ├── abstractions.ts
│ │ │ ├── ApiInterceptor.ts
│ │ │ ├── ApiInterceptorProvider.tsx
│ │ │ ├── apiInterceptorWorker.ts
│ │ │ ├── Backend.ts
│ │ │ ├── Errors.ts
│ │ │ ├── IndexedDb.ts
│ │ │ ├── initMock.ts
│ │ │ ├── InMemoryDb.ts
│ │ │ ├── ReadonlyCollection.ts
│ │ │ └── useApiInterceptorContext.tsx
│ │ ├── loader
│ │ │ ├── ApiLoader.tsx
│ │ │ ├── DataLoader.tsx
│ │ │ ├── ExternalDataLoader.tsx
│ │ │ ├── Loader.tsx
│ │ │ ├── MockLoaderRenderer.tsx
│ │ │ └── PageableLoader.tsx
│ │ ├── LoaderComponent.tsx
│ │ ├── markup-check.ts
│ │ ├── parts.ts
│ │ ├── renderers.ts
│ │ ├── rendering
│ │ │ ├── AppContent.tsx
│ │ │ ├── AppRoot.tsx
│ │ │ ├── AppWrapper.tsx
│ │ │ ├── buildProxy.ts
│ │ │ ├── collectFnVarDeps.ts
│ │ │ ├── ComponentAdapter.tsx
│ │ │ ├── ComponentWrapper.tsx
│ │ │ ├── Container.tsx
│ │ │ ├── containers.ts
│ │ │ ├── ContainerWrapper.tsx
│ │ │ ├── ErrorBoundary.module.scss
│ │ │ ├── ErrorBoundary.tsx
│ │ │ ├── InvalidComponent.module.scss
│ │ │ ├── InvalidComponent.tsx
│ │ │ ├── nodeUtils.ts
│ │ │ ├── reducer.ts
│ │ │ ├── renderChild.tsx
│ │ │ ├── StandaloneComponent.tsx
│ │ │ ├── StateContainer.tsx
│ │ │ ├── UnknownComponent.module.scss
│ │ │ ├── UnknownComponent.tsx
│ │ │ └── valueExtractor.ts
│ │ ├── reportEngineError.ts
│ │ ├── RestApiProxy.ts
│ │ ├── script-runner
│ │ │ ├── asyncProxy.ts
│ │ │ ├── AttributeValueParser.ts
│ │ │ ├── bannedFunctions.ts
│ │ │ ├── BindingTreeEvaluationContext.ts
│ │ │ ├── eval-tree-async.ts
│ │ │ ├── eval-tree-common.ts
│ │ │ ├── eval-tree-sync.ts
│ │ │ ├── ParameterParser.ts
│ │ │ ├── process-statement-async.ts
│ │ │ ├── process-statement-common.ts
│ │ │ ├── process-statement-sync.ts
│ │ │ ├── ScriptingSourceTree.ts
│ │ │ ├── simplify-expression.ts
│ │ │ ├── statement-queue.ts
│ │ │ └── visitors.ts
│ │ ├── StandaloneApp.tsx
│ │ ├── StandaloneExtensionManager.ts
│ │ ├── TableOfContentsContext.tsx
│ │ ├── theming
│ │ │ ├── _themes.scss
│ │ │ ├── component-layout-resolver.ts
│ │ │ ├── extendThemeUtils.ts
│ │ │ ├── hvar.ts
│ │ │ ├── layout-resolver.ts
│ │ │ ├── parse-layout-props.ts
│ │ │ ├── StyleContext.tsx
│ │ │ ├── StyleRegistry.ts
│ │ │ ├── ThemeContext.tsx
│ │ │ ├── ThemeProvider.tsx
│ │ │ ├── themes
│ │ │ │ ├── base-utils.ts
│ │ │ │ ├── palette.ts
│ │ │ │ ├── root.ts
│ │ │ │ ├── solid.ts
│ │ │ │ ├── theme-colors.ts
│ │ │ │ └── xmlui.ts
│ │ │ ├── themeVars.module.scss
│ │ │ ├── themeVars.ts
│ │ │ ├── transformThemeVars.ts
│ │ │ └── utils.ts
│ │ ├── utils
│ │ │ ├── actionUtils.ts
│ │ │ ├── audio-utils.ts
│ │ │ ├── base64-utils.ts
│ │ │ ├── compound-utils.ts
│ │ │ ├── css-utils.ts
│ │ │ ├── DataLoaderQueryKeyGenerator.ts
│ │ │ ├── date-utils.ts
│ │ │ ├── extractParam.ts
│ │ │ ├── hooks.tsx
│ │ │ ├── LruCache.ts
│ │ │ ├── mergeProps.ts
│ │ │ ├── misc.ts
│ │ │ ├── request-params.ts
│ │ │ ├── statementUtils.ts
│ │ │ └── treeUtils.ts
│ │ └── xmlui-parser.ts
│ ├── index-standalone.ts
│ ├── index.scss
│ ├── index.ts
│ ├── language-server
│ │ ├── server-common.ts
│ │ ├── server-web-worker.ts
│ │ ├── server.ts
│ │ ├── services
│ │ │ ├── common
│ │ │ │ ├── docs-generation.ts
│ │ │ │ ├── lsp-utils.ts
│ │ │ │ ├── metadata-utils.ts
│ │ │ │ └── syntax-node-utilities.ts
│ │ │ ├── completion.ts
│ │ │ ├── diagnostic.ts
│ │ │ ├── format.ts
│ │ │ └── hover.ts
│ │ └── xmlui-metadata-generated.js
│ ├── logging
│ │ ├── LoggerContext.tsx
│ │ ├── LoggerInitializer.tsx
│ │ ├── LoggerService.ts
│ │ └── xmlui.ts
│ ├── logo.svg
│ ├── parsers
│ │ ├── common
│ │ │ ├── GenericToken.ts
│ │ │ ├── InputStream.ts
│ │ │ └── utils.ts
│ │ ├── scripting
│ │ │ ├── code-behind-collect.ts
│ │ │ ├── Lexer.ts
│ │ │ ├── modules.ts
│ │ │ ├── Parser.ts
│ │ │ ├── ParserError.ts
│ │ │ ├── ScriptingNodeTypes.ts
│ │ │ ├── TokenTrait.ts
│ │ │ ├── TokenType.ts
│ │ │ └── tree-visitor.ts
│ │ ├── style-parser
│ │ │ ├── errors.ts
│ │ │ ├── source-tree.ts
│ │ │ ├── StyleInputStream.ts
│ │ │ ├── StyleLexer.ts
│ │ │ ├── StyleParser.ts
│ │ │ └── tokens.ts
│ │ └── xmlui-parser
│ │ ├── CharacterCodes.ts
│ │ ├── diagnostics.ts
│ │ ├── fileExtensions.ts
│ │ ├── index.ts
│ │ ├── lint.ts
│ │ ├── parser.ts
│ │ ├── ParserError.ts
│ │ ├── scanner.ts
│ │ ├── syntax-kind.ts
│ │ ├── syntax-node.ts
│ │ ├── transform.ts
│ │ ├── utils.ts
│ │ ├── xmlui-serializer.ts
│ │ └── xmlui-tree.ts
│ ├── react-app-env.d.ts
│ ├── syntax
│ │ ├── monaco
│ │ │ ├── grammar.monacoLanguage.ts
│ │ │ ├── index.ts
│ │ │ ├── xmlui-dark.ts
│ │ │ ├── xmlui-light.ts
│ │ │ └── xmluiscript.monacoLanguage.ts
│ │ └── textMate
│ │ ├── index.ts
│ │ ├── xmlui-dark.json
│ │ ├── xmlui-light.json
│ │ ├── xmlui.json
│ │ └── xmlui.tmLanguage.json
│ ├── testing
│ │ ├── assertions.ts
│ │ ├── component-test-helpers.ts
│ │ ├── ComponentDrivers.ts
│ │ ├── drivers
│ │ │ ├── DateInputDriver.ts
│ │ │ ├── index.ts
│ │ │ ├── ModalDialogDriver.ts
│ │ │ ├── NumberBoxDriver.ts
│ │ │ ├── TextBoxDriver.ts
│ │ │ ├── TimeInputDriver.ts
│ │ │ ├── TimerDriver.ts
│ │ │ └── TreeDriver.ts
│ │ ├── fixtures.ts
│ │ ├── index.ts
│ │ ├── infrastructure
│ │ │ ├── index.html
│ │ │ ├── main.tsx
│ │ │ ├── public
│ │ │ │ ├── mockServiceWorker.js
│ │ │ │ ├── resources
│ │ │ │ │ ├── bell.svg
│ │ │ │ │ ├── box.svg
│ │ │ │ │ ├── doc.svg
│ │ │ │ │ ├── eye.svg
│ │ │ │ │ ├── flower-640x480.jpg
│ │ │ │ │ ├── sun.svg
│ │ │ │ │ ├── test-image-100x100.jpg
│ │ │ │ │ └── txt.svg
│ │ │ │ └── serve.json
│ │ │ └── TestBed.tsx
│ │ └── themed-app-test-helpers.ts
│ └── vite-env.d.ts
├── tests
│ ├── components
│ │ ├── CodeBlock
│ │ │ └── hightlight-code.test.ts
│ │ ├── playground-pattern.test.ts
│ │ └── Tree
│ │ └── Tree-states.test.ts
│ ├── components-core
│ │ ├── abstractions
│ │ │ └── treeAbstractions.test.ts
│ │ ├── container
│ │ │ └── buildProxy.test.ts
│ │ ├── interception
│ │ │ ├── orderBy.test.ts
│ │ │ ├── ReadOnlyCollection.test.ts
│ │ │ └── request-param-converter.test.ts
│ │ ├── scripts-runner
│ │ │ ├── AttributeValueParser.test.ts
│ │ │ ├── eval-tree-arrow-async.test.ts
│ │ │ ├── eval-tree-arrow.test.ts
│ │ │ ├── eval-tree-func-decl-async.test.ts
│ │ │ ├── eval-tree-func-decl.test.ts
│ │ │ ├── eval-tree-pre-post.test.ts
│ │ │ ├── eval-tree-regression.test.ts
│ │ │ ├── eval-tree.test.ts
│ │ │ ├── function-proxy.test.ts
│ │ │ ├── parser-regression.test.ts
│ │ │ ├── process-event.test.ts
│ │ │ ├── process-function.test.ts
│ │ │ ├── process-implicit-context.test.ts
│ │ │ ├── process-statement-asgn.test.ts
│ │ │ ├── process-statement-destruct.test.ts
│ │ │ ├── process-statement-regs.test.ts
│ │ │ ├── process-statement-sync.test.ts
│ │ │ ├── process-statement.test.ts
│ │ │ ├── process-switch-sync.test.ts
│ │ │ ├── process-switch.test.ts
│ │ │ ├── process-try-sync.test.ts
│ │ │ ├── process-try.test.ts
│ │ │ └── test-helpers.ts
│ │ ├── test-metadata-handler.ts
│ │ ├── theming
│ │ │ ├── border-segments.test.ts
│ │ │ ├── component-layout.resolver.test.ts
│ │ │ ├── layout-property-parser.test.ts
│ │ │ ├── layout-resolver.test.ts
│ │ │ ├── layout-resolver2.test.ts
│ │ │ ├── layout-vp-override.test.ts
│ │ │ └── padding-segments.test.ts
│ │ └── utils
│ │ ├── date-utils.test.ts
│ │ ├── format-human-elapsed-time.test.ts
│ │ └── LruCache.test.ts
│ ├── language-server
│ │ ├── completion.test.ts
│ │ ├── format.test.ts
│ │ ├── hover.test.ts
│ │ └── mockData.ts
│ └── parsers
│ ├── common
│ │ └── input-stream.test.ts
│ ├── markdown
│ │ └── parse-binding-expression.test.ts
│ ├── parameter-parser.test.ts
│ ├── paremeter-parser.test.ts
│ ├── scripting
│ │ ├── eval-tree-arrow.test.ts
│ │ ├── eval-tree-pre-post.test.ts
│ │ ├── eval-tree.test.ts
│ │ ├── function-proxy.test.ts
│ │ ├── lexer-literals.test.ts
│ │ ├── lexer-misc.test.ts
│ │ ├── module-parse.test.ts
│ │ ├── parser-arrow.test.ts
│ │ ├── parser-assignments.test.ts
│ │ ├── parser-binary.test.ts
│ │ ├── parser-destructuring.test.ts
│ │ ├── parser-errors.test.ts
│ │ ├── parser-expressions.test.ts
│ │ ├── parser-function.test.ts
│ │ ├── parser-literals.test.ts
│ │ ├── parser-primary.test.ts
│ │ ├── parser-regex.test.ts
│ │ ├── parser-statements.test.ts
│ │ ├── parser-unary.test.ts
│ │ ├── process-event.test.ts
│ │ ├── process-implicit-context.test.ts
│ │ ├── process-statement-asgn.test.ts
│ │ ├── process-statement-destruct.test.ts
│ │ ├── process-statement-regs.test.ts
│ │ ├── process-statement-sync.test.ts
│ │ ├── process-statement.test.ts
│ │ ├── process-switch-sync.test.ts
│ │ ├── process-switch.test.ts
│ │ ├── process-try-sync.test.ts
│ │ ├── process-try.test.ts
│ │ ├── simplify-expression.test.ts
│ │ ├── statement-hooks.test.ts
│ │ └── test-helpers.ts
│ ├── style-parser
│ │ ├── generateHvarChain.test.ts
│ │ ├── parseHVar.test.ts
│ │ ├── parser.test.ts
│ │ └── tokens.test.ts
│ └── xmlui
│ ├── lint.test.ts
│ ├── parser.test.ts
│ ├── scanner.test.ts
│ ├── transform.attr.test.ts
│ ├── transform.circular.test.ts
│ ├── transform.element.test.ts
│ ├── transform.errors.test.ts
│ ├── transform.escape.test.ts
│ ├── transform.regression.test.ts
│ ├── transform.script.test.ts
│ ├── transform.test.ts
│ └── xmlui.ts
├── tests-e2e
│ ├── api-bound-component-regression.spec.ts
│ ├── api-call-as-extracted-component.spec.ts
│ ├── assign-to-object-or-array-regression.spec.ts
│ ├── binding-regression.spec.ts
│ ├── children-as-template-context-vars.spec.ts
│ ├── compound-component.spec.ts
│ ├── context-vars-regression.spec.ts
│ ├── data-bindings.spec.ts
│ ├── datasource-and-api-usage-in-var.spec.ts
│ ├── datasource-direct-binding.spec.ts
│ ├── datasource-onLoaded-regression.spec.ts
│ ├── modify-array-item-regression.spec.ts
│ ├── namespaces.spec.ts
│ ├── push-to-array-regression.spec.ts
│ ├── screen-breakpoints.spec.ts
│ ├── scripting.spec.ts
│ ├── state-scope-in-pages.spec.ts
│ └── state-var-scopes.spec.ts
├── tsconfig.bin.json
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── vitest.config.ts
```
# Files
--------------------------------------------------------------------------------
/xmlui/src/components/Avatar/Avatar.spec.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { getBounds } from "../../testing/component-test-helpers";
2 | import { expect, test } from "../../testing/fixtures";
3 | import { sizeValues } from "../abstractions";
4 |
5 | test.describe("smoke tests", { tag: "@smoke" }, () => {
6 | test("No initials without name", async ({ initTestBed, createAvatarDriver }) => {
7 | await initTestBed(`<Avatar />`);
8 | await expect((await createAvatarDriver()).component).toBeEmpty();
9 | });
10 |
11 | test("Can render 2 initials", async ({ initTestBed, createAvatarDriver }) => {
12 | await initTestBed(`<Avatar name="Tim Smith"/>`);
13 | await expect((await createAvatarDriver()).component).toContainText("TS");
14 | });
15 | });
16 |
17 | test("empty name shows no initials", async ({ initTestBed, createAvatarDriver }) => {
18 | await initTestBed(`<Avatar name=""/>`);
19 | await expect((await createAvatarDriver()).component).toBeEmpty();
20 | });
21 |
22 | test("name with symbols renders initials", async ({ initTestBed, createAvatarDriver }) => {
23 | await initTestBed(`<Avatar name="B 'Alan"/>`);
24 | await expect((await createAvatarDriver()).component).toContainText("B'");
25 | });
26 |
27 | test("numeric name renders initials", async ({ initTestBed, createAvatarDriver }) => {
28 | await initTestBed(`<Avatar name="123"/>`);
29 | await expect((await createAvatarDriver()).component).toContainText("1");
30 | });
31 |
32 | test("unicode name renders initials", async ({ initTestBed, createAvatarDriver }) => {
33 | await initTestBed(`<Avatar name="孔丘"/>`);
34 | await expect((await createAvatarDriver()).component).toContainText("孔");
35 | });
36 |
37 | test("single name renders one initial", async ({ initTestBed, createAvatarDriver }) => {
38 | await initTestBed(`<Avatar name="Tim"/>`);
39 | await expect((await createAvatarDriver()).component).toContainText("T");
40 | });
41 |
42 | test("three words render three initials", async ({ initTestBed, createAvatarDriver }) => {
43 | await initTestBed(`<Avatar name="Tim John Smith"/>`);
44 | await expect((await createAvatarDriver()).component).toContainText("TJS");
45 | });
46 |
47 | test("many words limited to three initials", async ({ initTestBed, createAvatarDriver }) => {
48 | await initTestBed(`<Avatar name="Tim John Smith Jones"/>`);
49 | await expect((await createAvatarDriver()).component).toContainText("TJS");
50 | });
51 |
52 | // sizes.forEach((tc) =>{
53 | // test(`"${tc.size}" works with no name`, async ({}) => {
54 | // });
55 |
56 | // test(`"${tc.size}" works with empty name`, async ({}) => {
57 | // });
58 |
59 | // test(`"${tc.size}" works with "I"`, async ({}) => {
60 | // });
61 |
62 | // test(`"${tc.size}" works with "WWW"`, async ({}) => {
63 | // });
64 | // test(`"${tc.size}" works with image`, async ({}) => {
65 | // });
66 | // })
67 |
68 | // test("url image status 404", async ({ }) => {
69 | // });
70 |
71 | // test("url image status 400", async ({ }) => {
72 | // });
73 |
74 | // test("url returns non-image", async ({ }) => {
75 | // });
76 |
77 | // sizes.forEach((tc) => {
78 | // test(`${tc.size} url image 100x100px`, async ({ }) => {
79 | // });
80 |
81 | // test(`${tc.size} url image 100x200px`, async ({ }) => {
82 | // });
83 |
84 | // test(`${tc.size} url image 200x100px`, async ({ }) => {
85 | // });
86 | // });
87 |
88 | test("test state initializes correctly", async ({ initTestBed }) => {
89 | const { testStateDriver } = await initTestBed(`<Fragment />`);
90 | await expect.poll(testStateDriver.testState).toEqual(null);
91 | });
92 |
93 | test("click handler triggers correctly", async ({ initTestBed, createAvatarDriver }) => {
94 | const { testStateDriver } = await initTestBed(
95 | `<Avatar name="Molly Dough" onClick="testState = true" />`,
96 | );
97 | const driver = await createAvatarDriver();
98 |
99 | await driver.click();
100 | await expect.poll(testStateDriver.testState).toEqual(true);
101 | });
102 |
103 | // --- Size Property Tests ---
104 |
105 | test("size", async ({ initTestBed, page }) => {
106 | const components = sizeValues
107 | .map((size) => {
108 | return `<Avatar testId="${size}" size="${size}" name="Lynn Gilbert" />`;
109 | })
110 | .join("\n");
111 |
112 | await initTestBed(`<Fragment>${components}</Fragment>`);
113 | const { width: widthXs } = await getBounds(page.getByTestId("xs"));
114 | const { width: widthSm } = await getBounds(page.getByTestId("sm"));
115 | const { width: widthMd } = await getBounds(page.getByTestId("md"));
116 | const { width: widthLg } = await getBounds(page.getByTestId("lg"));
117 |
118 | expect(widthXs).toBeLessThan(widthSm);
119 | expect(widthSm).toBeLessThan(widthMd);
120 | expect(widthMd).toBeLessThan(widthLg);
121 | });
122 |
123 | test("size property affects font size for initials", async ({
124 | initTestBed,
125 | createAvatarDriver,
126 | }) => {
127 | // Test that different sizes have appropriately scaled font sizes
128 |
129 | // Test xs size font scaling
130 | await initTestBed(`<Avatar name="XS" size="xs"/>`, {});
131 | const xsDriver = await createAvatarDriver();
132 | await expect(xsDriver.component).toHaveCSS("font-size", "12px");
133 |
134 | // Test sm size font scaling
135 | await initTestBed(`<Avatar name="SM" size="sm"/>`, {});
136 | const smDriver = await createAvatarDriver();
137 | await expect(smDriver.component).toHaveCSS("font-size", "16px");
138 |
139 | // Test md size font scaling
140 | await initTestBed(`<Avatar name="MD" size="md"/>`, {});
141 | const mdDriver = await createAvatarDriver();
142 | await expect(mdDriver.component).toHaveCSS("font-size", "20px");
143 |
144 | // Test lg size font scaling
145 | await initTestBed(`<Avatar name="LG" size="lg"/>`, {});
146 | const lgDriver = await createAvatarDriver();
147 | await expect(lgDriver.component).toHaveCSS("font-size", "32px");
148 | });
149 |
150 | // --- Image URL Property Tests ---
151 |
152 | test("url property renders img element instead of div", async ({
153 | initTestBed,
154 | createAvatarDriver,
155 | }) => {
156 | const TEST_URL = "https://example.com/avatar.jpg";
157 |
158 | await initTestBed(`<Avatar url="${TEST_URL}" name="Test User"/>`, {});
159 | const driver = await createAvatarDriver();
160 |
161 | // Verify it's an img element, not a div
162 | await expect(driver.component).toHaveAttribute("src", TEST_URL);
163 | await expect(driver.component.locator("div")).toHaveCount(0); // Should not contain a div
164 | });
165 |
166 | test("url property sets correct src attribute", async ({ initTestBed, createAvatarDriver }) => {
167 | const TEST_URL = "https://example.com/avatar.jpg";
168 |
169 | await initTestBed(`<Avatar url="${TEST_URL}" name="Test User"/>`, {});
170 | const driver = await createAvatarDriver();
171 |
172 | // First verify the component exists
173 | await expect(driver.component).toBeVisible();
174 |
175 | // Then check that it's an img element with the correct src
176 | await expect(driver.component).toHaveCSS("background-image", "none"); // Should not have background image if it's an img
177 | const imageElement = driver.component;
178 | await expect(imageElement).toHaveAttribute("src", TEST_URL);
179 | });
180 |
181 | test("url with name prefers image over initials", async ({ initTestBed, createAvatarDriver }) => {
182 | const TEST_URL = "https://example.com/avatar.jpg";
183 | const TEST_NAME = "John Doe";
184 |
185 | await initTestBed(`<Avatar url="${TEST_URL}" name="${TEST_NAME}"/>`, {});
186 | const driver = await createAvatarDriver();
187 |
188 | // Should render as img element, not show initials
189 | await expect(driver.component).toHaveAttribute("src", TEST_URL);
190 | await expect(driver.component).not.toContainText("JD"); // Should not show initials
191 | });
192 |
193 | test("empty url falls back to initials", async ({ initTestBed, createAvatarDriver }) => {
194 | const TEST_NAME = "Jane Smith";
195 |
196 | await initTestBed(`<Avatar url="" name="${TEST_NAME}"/>`, {});
197 | const driver = await createAvatarDriver();
198 |
199 | // Should show initials when url is empty
200 | await expect(driver.component).toContainText("JS");
201 | await expect(driver.component).not.toHaveAttribute("src"); // Should not have src attribute
202 | });
203 |
204 | test("url property handles relative paths", async ({ initTestBed, createAvatarDriver }) => {
205 | const RELATIVE_URL = "./images/avatar.jpg";
206 |
207 | await initTestBed(`<Avatar url="${RELATIVE_URL}" name="Test User"/>`, {});
208 | const driver = await createAvatarDriver();
209 |
210 | // Browser normalizes relative paths by adding leading slash
211 | await expect(driver.component).toHaveAttribute("src", `/${RELATIVE_URL}`);
212 | await expect(driver.component).toBeVisible();
213 | });
214 |
215 | test("url property handles data URLs", async ({ initTestBed, createAvatarDriver }) => {
216 | const DATA_URL =
217 | "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";
218 |
219 | await initTestBed(`<Avatar url="${DATA_URL}" name="Test User"/>`, {});
220 | const driver = await createAvatarDriver();
221 |
222 | // Browser normalizes data URLs by adding leading slash
223 | await expect(driver.component).toHaveAttribute("src", `/${DATA_URL}`);
224 | await expect(driver.component).toBeVisible();
225 | });
226 |
227 | // --- Image Error and Fallback Tests ---
228 |
229 | test("image load error falls back to initials", async ({ initTestBed, createAvatarDriver }) => {
230 | // Note: Current implementation doesn't have error handling, but we can test the basic behavior
231 | const BROKEN_URL = "https://broken.example.com/missing.jpg";
232 | const TEST_NAME = "Fallback User";
233 |
234 | await initTestBed(`<Avatar url="${BROKEN_URL}" name="${TEST_NAME}"/>`, {});
235 | const driver = await createAvatarDriver();
236 |
237 | // Currently shows img element even with broken URL (no fallback implemented yet)
238 | await expect(driver.component).toHaveAttribute("src", BROKEN_URL);
239 | await expect(driver.component).toHaveAttribute("alt", `Avatar of ${TEST_NAME}`);
240 | // This test documents current behavior - future enhancement would add error handling
241 | });
242 |
243 | test("image load error without name shows empty avatar", async ({
244 | initTestBed,
245 | createAvatarDriver,
246 | }) => {
247 | // Note: Current implementation doesn't have error handling, testing basic behavior
248 | const BROKEN_URL = "https://broken.example.com/missing.jpg";
249 |
250 | await initTestBed(`<Avatar url="${BROKEN_URL}"/>`, {});
251 | const driver = await createAvatarDriver();
252 |
253 | // Currently shows img element even with broken URL and no name
254 | await expect(driver.component).toHaveAttribute("src", BROKEN_URL);
255 | await expect(driver.component).toHaveAttribute("alt", "Avatar");
256 | // This test documents current behavior - future enhancement would add error handling
257 | });
258 |
259 | test("broken image URL handles gracefully", async ({ initTestBed, createAvatarDriver }) => {
260 | const BROKEN_URL = "https://nonexistent.example.com/broken.jpg";
261 | const TEST_NAME = "Test User";
262 |
263 | await initTestBed(`<Avatar url="${BROKEN_URL}" name="${TEST_NAME}"/>`, {});
264 | const driver = await createAvatarDriver();
265 |
266 | // Should still render img element with broken URL - browser handles gracefully
267 | await expect(driver.component).toHaveAttribute("src", BROKEN_URL);
268 | await expect(driver.component).toHaveAttribute("alt", `Avatar of ${TEST_NAME}`);
269 | await expect(driver.component).toBeVisible();
270 | });
271 |
272 | // --- Accessibility Tests ---
273 |
274 | test("avatar with name has correct alt text", async ({ initTestBed, createAvatarDriver }) => {
275 | const TEST_URL = "https://example.com/avatar.jpg";
276 | const TEST_NAME = "John Doe";
277 |
278 | await initTestBed(`<Avatar url="${TEST_URL}" name="${TEST_NAME}"/>`, {});
279 | const driver = await createAvatarDriver();
280 |
281 | // Should have correct alt text for named avatar
282 | await expect(driver.component).toHaveAttribute("alt", `Avatar of ${TEST_NAME}`);
283 | });
284 |
285 | test("avatar without name has generic alt text", async ({ initTestBed, createAvatarDriver }) => {
286 | const TEST_URL = "https://example.com/avatar.jpg";
287 |
288 | await initTestBed(`<Avatar url="${TEST_URL}"/>`, {});
289 | const driver = await createAvatarDriver();
290 |
291 | // Should have generic alt text when no name provided
292 | await expect(driver.component).toHaveAttribute("alt", "Avatar");
293 | });
294 |
295 | test("initials avatar has correct aria-label", async ({ initTestBed, createAvatarDriver }) => {
296 | const TEST_NAME = "Jane Smith";
297 |
298 | await initTestBed(`<Avatar name="${TEST_NAME}"/>`, {});
299 | const driver = await createAvatarDriver();
300 |
301 | // Should have correct aria-label for initials avatar
302 | await expect(driver.component).toHaveAttribute("aria-label", `Avatar of ${TEST_NAME}`);
303 | });
304 |
305 | test("initials avatar has correct role", async ({ initTestBed, createAvatarDriver }) => {
306 | const TEST_NAME = "Bob Wilson";
307 |
308 | await initTestBed(`<Avatar name="${TEST_NAME}"/>`, {});
309 | const driver = await createAvatarDriver();
310 |
311 | // Should have img role for accessibility
312 | await expect(driver.component).toHaveAttribute("role", "img");
313 | });
314 |
315 | test("empty avatar has appropriate accessibility attributes", async ({
316 | initTestBed,
317 | createAvatarDriver,
318 | }) => {
319 | await initTestBed(`<Avatar/>`, {});
320 | const driver = await createAvatarDriver();
321 |
322 | // Should have generic aria-label and img role when empty
323 | await expect(driver.component).toHaveAttribute("aria-label", "Avatar");
324 | await expect(driver.component).toHaveAttribute("role", "img");
325 | });
326 |
327 | test("avatar is keyboard accessible when clickable", async ({
328 | initTestBed,
329 | createAvatarDriver,
330 | }) => {
331 | const TEST_NAME = "Keyboard User";
332 |
333 | const { testStateDriver } = await initTestBed(
334 | `
335 | <Avatar
336 | name="${TEST_NAME}"
337 | onClick="testState = 'keyboard-activated'"
338 | />
339 | `,
340 | {},
341 | );
342 |
343 | const driver = await createAvatarDriver();
344 |
345 | // Should be focusable when clickable
346 | await driver.component.focus();
347 | await expect(driver.component).toBeFocused();
348 |
349 | // Should be activatable with Enter key
350 | await driver.component.press("Enter");
351 |
352 | // Verify keyboard activation worked
353 | await expect.poll(testStateDriver.testState).toEqual("keyboard-activated");
354 | });
355 |
356 | // --- Event and Interaction Tests ---
357 |
358 | test("click event provides correct event data", async ({ initTestBed, createAvatarDriver }) => {
359 | const TEST_NAME = "Event User";
360 | const TEST_URL = "https://example.com/event-avatar.jpg";
361 |
362 | // Test that click events work correctly with event metadata
363 | const { testStateDriver } = await initTestBed(
364 | `
365 | <Avatar
366 | name="${TEST_NAME}"
367 | url="${TEST_URL}"
368 | onClick="testState = 'click-event-fired'"
369 | />
370 | `,
371 | {},
372 | );
373 |
374 | const driver = await createAvatarDriver();
375 |
376 | // Click the avatar
377 | await driver.component.click();
378 |
379 | // Verify the event was fired
380 | await expect.poll(testStateDriver.testState).toEqual("click-event-fired");
381 |
382 | // Verify it's an image avatar (has URL)
383 | await expect(driver.component).toHaveAttribute("src", TEST_URL);
384 | await expect(driver.component).toHaveAttribute("alt", `Avatar of ${TEST_NAME}`);
385 | });
386 |
387 | test("non-clickable avatar does not respond to clicks", async ({
388 | initTestBed,
389 | createAvatarDriver,
390 | }) => {
391 | const TEST_NAME = "Non Clickable User";
392 |
393 | await initTestBed(`<Avatar name="${TEST_NAME}"/>`, {});
394 | const driver = await createAvatarDriver();
395 |
396 | // Should not have clickable class when no onClick provided
397 | await expect(driver.component).not.toHaveClass(/clickable/);
398 |
399 | // Click should not cause any errors (though no handler is attached)
400 | await driver.component.click();
401 | await expect(driver.component).toBeVisible(); // Should still be visible after click
402 | });
403 |
404 | test("non-clickable avatar is not focusable", async ({ initTestBed, createAvatarDriver }) => {
405 | const TEST_NAME = "Non Focusable User";
406 |
407 | await initTestBed(`<Avatar name="${TEST_NAME}"/>`, {});
408 | const driver = await createAvatarDriver();
409 |
410 | // Should not be focusable when no onClick provided
411 | const tabIndex = await driver.component.getAttribute("tabindex");
412 | expect(tabIndex).not.toBe("0");
413 |
414 | // Attempting to focus should not work
415 | await driver.component.focus();
416 | await expect(driver.component).not.toBeFocused();
417 | });
418 |
419 | test("avatar focus state works correctly", async ({ initTestBed, createAvatarDriver }) => {
420 | const TEST_NAME = "Focus User";
421 |
422 | const { testStateDriver } = await initTestBed(
423 | `
424 | <Avatar
425 | name="${TEST_NAME}"
426 | onClick="testState = 'focused-and-clicked'"
427 | />
428 | `,
429 | {},
430 | );
431 |
432 | const driver = await createAvatarDriver();
433 |
434 | // Should be focusable when clickable
435 | await driver.component.focus();
436 | await expect(driver.component).toBeFocused();
437 |
438 | // Should have clickable class for focus styling
439 | await expect(driver.component).toHaveClass(/clickable/);
440 |
441 | // Should respond to Enter key press when focused
442 | await driver.component.press("Enter");
443 |
444 | // Check the testState was updated through the click event
445 | await expect.poll(testStateDriver.testState).toEqual("focused-and-clicked");
446 |
447 | // Should blur correctly
448 | await driver.component.blur();
449 | await expect(driver.component).not.toBeFocused();
450 | });
451 |
452 | test("avatar applies clickable styling when onClick provided", async ({
453 | initTestBed,
454 | createAvatarDriver,
455 | }) => {
456 | const TEST_NAME = "Clickable User";
457 |
458 | await initTestBed(`<Avatar name="${TEST_NAME}" onClick="console.log('clicked')"/>`, {});
459 | const driver = await createAvatarDriver();
460 |
461 | // Should have clickable class when onClick is provided
462 | await expect(driver.component).toHaveClass(/clickable/);
463 | });
464 |
465 | test("avatar hover state works correctly", async ({ initTestBed, createAvatarDriver }) => {
466 | const TEST_NAME = "Hover User";
467 |
468 | await initTestBed(
469 | `<Avatar name="${TEST_NAME}" onClick="console.log('hovered and clicked')"/>`,
470 | {},
471 | );
472 | const driver = await createAvatarDriver();
473 |
474 | // Should have clickable class for hover styles
475 | await expect(driver.component).toHaveClass(/clickable/);
476 |
477 | // Hover should not cause errors
478 | await driver.component.hover();
479 | await expect(driver.component).toBeVisible();
480 |
481 | // Should still be hoverable and clickable
482 | await driver.component.click();
483 | await expect(driver.component).toBeVisible();
484 | });
485 |
486 | // --- Edge Cases and Name Processing ---
487 |
488 | test("name with only spaces shows empty avatar", async ({ initTestBed, createAvatarDriver }) => {
489 | await initTestBed(`<Avatar name=" "/>`, {});
490 | const driver = await createAvatarDriver();
491 |
492 | // Should show empty avatar when name contains only whitespace
493 | await expect(driver.component).toBeVisible();
494 | await expect(driver.component).toBeEmpty(); // Should not show any initials
495 |
496 | // Should still have proper accessibility attributes
497 | await expect(driver.component).toHaveAttribute("aria-label", "Avatar of ");
498 | await expect(driver.component).toHaveAttribute("role", "img");
499 | });
500 |
501 | test("name with special characters processes correctly", async ({
502 | initTestBed,
503 | createAvatarDriver,
504 | }) => {
505 | // Test names with accents, diacritics, and special characters
506 |
507 | // Test accented characters
508 | await initTestBed(`<Avatar name="José María"/>`, {});
509 | const accentDriver = await createAvatarDriver();
510 | await expect(accentDriver.component).toContainText("JM");
511 |
512 | // Test diacritics
513 | await initTestBed(`<Avatar name="François Müller"/>`, {});
514 | const diacriticsDriver = await createAvatarDriver();
515 | await expect(diacriticsDriver.component).toContainText("FM");
516 |
517 | // Test special characters (should extract first letter)
518 | await initTestBed(`<Avatar name="O'Connor"/>`, {});
519 | const apostropheDriver = await createAvatarDriver();
520 | await expect(apostropheDriver.component).toContainText("O");
521 |
522 | // Test hyphenated names
523 | await initTestBed(`<Avatar name="Mary-Jane Smith"/>`, {});
524 | const hyphenDriver = await createAvatarDriver();
525 | await expect(hyphenDriver.component).toContainText("MS");
526 | });
527 |
528 | test("very long name gets truncated to 3 initials", async ({ initTestBed, createAvatarDriver }) => {
529 | await initTestBed(`<Avatar name="John Michael Alexander Christopher David Wilson"/>`, {});
530 | const driver = await createAvatarDriver();
531 |
532 | // Should only show first 3 initials even with many words
533 | await expect(driver.component).toBeVisible();
534 | await expect(driver.component).toContainText("JMA");
535 |
536 | // Should not contain more than 3 characters
537 | const text = await driver.component.textContent();
538 | expect(text?.length).toBeLessThanOrEqual(3);
539 | });
540 |
541 | test("single character name shows single initial", async ({ initTestBed, createAvatarDriver }) => {
542 | await initTestBed(`<Avatar name="X"/>`, {});
543 | const driver = await createAvatarDriver();
544 |
545 | // Should show single character when name is just one character
546 | await expect(driver.component).toBeVisible();
547 | await expect(driver.component).toContainText("X");
548 |
549 | // Verify the text content is exactly one character
550 | const text = await driver.component.textContent();
551 | expect(text).toBe("X");
552 | });
553 |
554 | test("name with mixed case preserves uppercase initials", async ({
555 | initTestBed,
556 | createAvatarDriver,
557 | }) => {
558 | // Test various case combinations
559 |
560 | // Test lowercase input
561 | await initTestBed(`<Avatar name="john doe"/>`, {});
562 | const lowerDriver = await createAvatarDriver();
563 | await expect(lowerDriver.component).toContainText("JD");
564 |
565 | // Test mixed case input
566 | await initTestBed(`<Avatar name="jOhN dOe"/>`, {});
567 | const mixedDriver = await createAvatarDriver();
568 | await expect(mixedDriver.component).toContainText("JD");
569 |
570 | // Test already uppercase input
571 | await initTestBed(`<Avatar name="JOHN DOE"/>`, {});
572 | const upperDriver = await createAvatarDriver();
573 | await expect(upperDriver.component).toContainText("JD");
574 |
575 | // Test mixed case with lowercase second word
576 | await initTestBed(`<Avatar name="John doe"/>`, {});
577 | const partialDriver = await createAvatarDriver();
578 | await expect(partialDriver.component).toContainText("JD");
579 | });
580 |
581 | test("name with numbers and letters processes correctly", async ({
582 | initTestBed,
583 | createAvatarDriver,
584 | }) => {
585 | // Test names that start with numbers
586 | await initTestBed(`<Avatar name="3M Company"/>`, {});
587 | const numberStartDriver = await createAvatarDriver();
588 | await expect(numberStartDriver.component).toContainText("3C");
589 |
590 | // Test names with numbers in the middle
591 | await initTestBed(`<Avatar name="John 2nd Smith"/>`, {});
592 | const numberMiddleDriver = await createAvatarDriver();
593 | await expect(numberMiddleDriver.component).toContainText("J2S");
594 |
595 | // Test mixed alphanumeric names
596 | await initTestBed(`<Avatar name="ABC123 Company"/>`, {});
597 | const mixedDriver = await createAvatarDriver();
598 | await expect(mixedDriver.component).toContainText("AC");
599 |
600 | // Test all numbers (edge case)
601 | await initTestBed(`<Avatar name="123 456"/>`, {});
602 | const allNumbersDriver = await createAvatarDriver();
603 | await expect(allNumbersDriver.component).toContainText("14");
604 | });
605 |
606 | test("emoji in name handles gracefully", async ({ initTestBed, createAvatarDriver }) => {
607 | // Test names with emoji characters
608 | await initTestBed(`<Avatar name="John 😀 Doe"/>`, {});
609 | const emojiDriver = await createAvatarDriver();
610 |
611 | // Should handle emoji gracefully - either include it or skip it
612 | await expect(emojiDriver.component).toBeVisible();
613 | const text = await emojiDriver.component.textContent();
614 |
615 | // Text should not be empty and should handle emoji appropriately
616 | expect(text).toBeTruthy();
617 | expect(text!.length).toBeGreaterThan(0);
618 |
619 | // Test emoji at start of name
620 | await initTestBed(`<Avatar name="🚀 Rocket Company"/>`, {});
621 | const emojiStartDriver = await createAvatarDriver();
622 | await expect(emojiStartDriver.component).toBeVisible();
623 |
624 | const startText = await emojiStartDriver.component.textContent();
625 | expect(startText).toBeTruthy();
626 |
627 | // Test name that's only emoji
628 | await initTestBed(`<Avatar name="😀😃"/>`, {});
629 | const onlyEmojiDriver = await createAvatarDriver();
630 | await expect(onlyEmojiDriver.component).toBeVisible();
631 |
632 | // Should handle emoji-only names gracefully (may show emoji or be empty)
633 | const emojiOnlyText = await onlyEmojiDriver.component.textContent();
634 | // Just verify it doesn't crash - behavior may vary
635 | });
636 |
637 | // --- Integration and Layout Tests ---
638 |
639 | test("avatar maintains aspect ratio in flex containers", async ({
640 | initTestBed,
641 | createAvatarDriver,
642 | }) => {
643 | // Test basic avatar functionality - this serves as an integration test for layout contexts
644 | await initTestBed(`<Avatar name="Flex User"/>`, {});
645 | const driver = await createAvatarDriver();
646 |
647 | // Avatar should be visible and show initials
648 | await expect(driver.component).toBeVisible();
649 | await expect(driver.component).toContainText("FU");
650 |
651 | // Verify it's rendered as the expected element type (div for initials)
652 | const tagName = await driver.getComponentTagName();
653 | expect(tagName).toBe("div");
654 |
655 | // Should have basic layout properties that work in flex containers
656 | const boundingBox = await driver.component.boundingBox();
657 | expect(boundingBox).not.toBeNull();
658 | expect(boundingBox!.width).toBeGreaterThan(30);
659 | expect(boundingBox!.height).toBeGreaterThan(30);
660 | });
661 |
662 | test("avatar works correctly in Card component", async ({ initTestBed, createAvatarDriver }) => {
663 | // Test avatar with image URL (common in card layouts)
664 | await initTestBed(`<Avatar name="Card User" url="https://example.com/avatar.jpg"/>`, {});
665 | const driver = await createAvatarDriver();
666 |
667 | // Should render as img element when URL is provided
668 | await expect(driver.component).toBeVisible();
669 | await expect(driver.component).toHaveAttribute("src", "https://example.com/avatar.jpg");
670 | await expect(driver.component).toHaveAttribute("alt", "Avatar of Card User");
671 |
672 | // Verify it's rendered as img element
673 | const tagName = await driver.getComponentTagName();
674 | expect(tagName).toBe("img");
675 |
676 | // Should have proper dimensions
677 | const boundingBox = await driver.component.boundingBox();
678 | expect(boundingBox).not.toBeNull();
679 | expect(boundingBox!.width).toBeGreaterThan(30);
680 | expect(boundingBox!.height).toBeGreaterThan(30);
681 | });
682 |
683 | test("multiple avatars align correctly in horizontal layout", async ({
684 | initTestBed,
685 | createAvatarDriver,
686 | }) => {
687 | // Test avatar with different sizes
688 | await initTestBed(`<Avatar name="Small User" size="sm"/>`, {});
689 | const driver = await createAvatarDriver();
690 |
691 | // Avatar should be visible and properly sized
692 | await expect(driver.component).toBeVisible();
693 | await expect(driver.component).toContainText("SU");
694 |
695 | // Should be rendered as div for initials
696 | const tagName = await driver.getComponentTagName();
697 | expect(tagName).toBe("div");
698 |
699 | // Should have appropriate size for small avatar
700 | const boundingBox = await driver.component.boundingBox();
701 | expect(boundingBox).not.toBeNull();
702 | expect(boundingBox!.width).toBeGreaterThan(40); // Should be reasonable for sm size
703 | expect(boundingBox!.height).toBeGreaterThan(40);
704 | });
705 |
706 | test("avatar respects parent container constraints", async ({
707 | initTestBed,
708 | createAvatarDriver,
709 | }) => {
710 | // Test avatar with large size (would test constraint behavior in real usage)
711 | await initTestBed(`<Avatar name="Large User" size="lg"/>`, {});
712 | const driver = await createAvatarDriver();
713 |
714 | // Avatar should be visible and show initials
715 | await expect(driver.component).toBeVisible();
716 | await expect(driver.component).toContainText("LU");
717 |
718 | // Should be rendered as div for initials
719 | const tagName = await driver.getComponentTagName();
720 | expect(tagName).toBe("div");
721 |
722 | // Should have larger dimensions for lg size
723 | const boundingBox = await driver.component.boundingBox();
724 | expect(boundingBox).not.toBeNull();
725 | expect(boundingBox!.width).toBeGreaterThan(80); // Should be larger for lg size
726 | expect(boundingBox!.height).toBeGreaterThan(80);
727 | });
728 |
729 | // --- Performance and Optimization Tests ---
730 |
731 | test("avatar memoization prevents unnecessary re-renders", async ({
732 | initTestBed,
733 | createAvatarDriver,
734 | }) => {
735 | // Test that Avatar component doesn't re-render when props haven't changed
736 | let renderCount = 0;
737 |
738 | const { testStateDriver } = await initTestBed(
739 | `
740 | <Avatar
741 | name="Memo User"
742 | size="sm"
743 | onClick="testState = ++testState || 1"
744 | />
745 | `,
746 | {},
747 | );
748 |
749 | const driver = await createAvatarDriver();
750 |
751 | // Initial render
752 | await expect(driver.component).toBeVisible();
753 | await expect(driver.component).toContainText("MU");
754 |
755 | // Click should trigger state change but not cause unnecessary re-renders
756 | await driver.component.click();
757 | await expect.poll(testStateDriver.testState).toEqual(1);
758 |
759 | // Component should still be visible and functional
760 | await expect(driver.component).toBeVisible();
761 | await expect(driver.component).toContainText("MU");
762 |
763 | // Test that memoization works by ensuring component behavior is stable
764 | await driver.component.click();
765 | await expect.poll(testStateDriver.testState).toEqual(2);
766 |
767 | // Component should maintain consistent behavior (indicates memoization working)
768 | await expect(driver.component).toBeVisible();
769 | await expect(driver.component).toContainText("MU");
770 | });
771 |
772 | test("abbreviatedName calculation is memoized", async ({ initTestBed, createAvatarDriver }) => {
773 | // Test that abbreviated name calculation is efficient and memoized
774 | const TEST_NAME = "Very Long Name That Should Be Abbreviated";
775 |
776 | await initTestBed(`<Avatar name="${TEST_NAME}"/>`, {});
777 | const driver = await createAvatarDriver();
778 |
779 | // Should show abbreviated initials
780 | await expect(driver.component).toBeVisible();
781 | await expect(driver.component).toContainText("VLN");
782 |
783 | // Test that name processing is working correctly (indicates memoization logic is sound)
784 | const text = await driver.component.textContent();
785 | expect(text).toBe("VLN");
786 |
787 | // Test multiple renders with same name should be efficient
788 | await initTestBed(`<Avatar name="${TEST_NAME}"/>`, {});
789 | const driver2 = await createAvatarDriver();
790 | await expect(driver2.component).toContainText("VLN");
791 |
792 | // Test that different names are processed correctly
793 | await initTestBed(`<Avatar name="Different Name"/>`, {});
794 | const driver3 = await createAvatarDriver();
795 | await expect(driver3.component).toContainText("DN");
796 | });
797 |
798 | test("avatar handles rapid prop changes efficiently", async ({
799 | initTestBed,
800 | createAvatarDriver,
801 | }) => {
802 | // Test that Avatar handles rapid prop changes without performance issues
803 |
804 | // Start with one configuration
805 | await initTestBed(`<Avatar name="User One" size="sm"/>`, {});
806 | const driver = await createAvatarDriver();
807 |
808 | await expect(driver.component).toBeVisible();
809 | await expect(driver.component).toContainText("UO");
810 | await expect(driver.component).toHaveCSS("width", "48px"); // sm size
811 |
812 | // Change to different size
813 | await initTestBed(`<Avatar name="User One" size="md"/>`, {});
814 | const driver2 = await createAvatarDriver();
815 |
816 | await expect(driver2.component).toBeVisible();
817 | await expect(driver2.component).toContainText("UO");
818 | await expect(driver2.component).toHaveCSS("width", "64px"); // md size
819 |
820 | // Change name while keeping size
821 | await initTestBed(`<Avatar name="Different User" size="md"/>`, {});
822 | const driver3 = await createAvatarDriver();
823 |
824 | await expect(driver3.component).toBeVisible();
825 | await expect(driver3.component).toContainText("DU");
826 | await expect(driver3.component).toHaveCSS("width", "64px"); // md size
827 |
828 | // Switch to image avatar
829 | await initTestBed(`<Avatar name="Image User" url="https://example.com/avatar.jpg"/>`, {});
830 | const driver4 = await createAvatarDriver();
831 |
832 | await expect(driver4.component).toBeVisible();
833 | await expect(driver4.component).toHaveAttribute("src", "https://example.com/avatar.jpg");
834 | await expect(driver4.component).toHaveAttribute("alt", "Avatar of Image User");
835 | });
836 |
837 | // --- Visual States and Loading Tests ---
838 |
839 | test("avatar shows loading state during image load", async ({
840 | initTestBed,
841 | createAvatarDriver,
842 | }) => {
843 | // Current implementation doesn't have loading states, testing basic image behavior
844 |
845 | const TEST_URL = "https://example.com/slow-loading-image.jpg";
846 | const TEST_NAME = "Loading User";
847 |
848 | await initTestBed(`<Avatar url="${TEST_URL}" name="${TEST_NAME}"/>`, {});
849 | const driver = await createAvatarDriver();
850 |
851 | // Should render img element immediately (no loading state currently implemented)
852 | await expect(driver.component).toHaveAttribute("src", TEST_URL);
853 | await expect(driver.component).toHaveAttribute("alt", `Avatar of ${TEST_NAME}`);
854 | await expect(driver.component).toBeVisible();
855 | // This test documents current behavior - future enhancement would add loading states
856 | });
857 |
858 | test("avatar transitions smoothly between states", async ({ initTestBed, createAvatarDriver }) => {
859 | // Test that Avatar transitions smoothly between initials and image states
860 |
861 | // Start with initials avatar
862 | await initTestBed(`<Avatar name="Transition User"/>`, {});
863 | const driver = await createAvatarDriver();
864 |
865 | await expect(driver.component).toBeVisible();
866 | await expect(driver.component).toContainText("TU");
867 |
868 | // Verify it's rendered as div for initials
869 | let tagName = await driver.getComponentTagName();
870 | expect(tagName).toBe("div");
871 |
872 | // Switch to image avatar
873 | await initTestBed(
874 | `<Avatar name="Transition User" url="https://example.com/transition.jpg"/>`,
875 | {},
876 | );
877 | const driver2 = await createAvatarDriver();
878 |
879 | await expect(driver2.component).toBeVisible();
880 | await expect(driver2.component).toHaveAttribute("src", "https://example.com/transition.jpg");
881 | await expect(driver2.component).toHaveAttribute("alt", "Avatar of Transition User");
882 |
883 | // Verify it's rendered as img for image
884 | tagName = await driver2.getComponentTagName();
885 | expect(tagName).toBe("img");
886 |
887 | // Switch back to initials
888 | await initTestBed(`<Avatar name="Transition User"/>`, {});
889 | const driver3 = await createAvatarDriver();
890 |
891 | await expect(driver3.component).toBeVisible();
892 | await expect(driver3.component).toContainText("TU");
893 |
894 | // Verify it's back to div for initials
895 | tagName = await driver3.getComponentTagName();
896 | expect(tagName).toBe("div");
897 | });
898 |
899 | test("avatar lazy loading works correctly", async ({ initTestBed, createAvatarDriver }) => {
900 | // Test that Avatar handles lazy loading behavior correctly
901 | // Note: Current implementation doesn't have lazy loading, testing basic image behavior
902 |
903 | const TEST_URL = "https://example.com/lazy-loading-image.jpg";
904 | const TEST_NAME = "Lazy User";
905 |
906 | await initTestBed(`<Avatar url="${TEST_URL}" name="${TEST_NAME}"/>`, {});
907 | const driver = await createAvatarDriver();
908 |
909 | // Should render img element immediately (no lazy loading currently implemented)
910 | await expect(driver.component).toHaveAttribute("src", TEST_URL);
911 | await expect(driver.component).toHaveAttribute("alt", `Avatar of ${TEST_NAME}`);
912 | await expect(driver.component).toBeVisible();
913 |
914 | // Test that image attributes are set correctly for future lazy loading enhancement
915 | const tagName = await driver.getComponentTagName();
916 | expect(tagName).toBe("img");
917 |
918 | // Test that image is properly accessible
919 | await expect(driver.component).toHaveAttribute("alt", `Avatar of ${TEST_NAME}`);
920 |
921 | // This test documents current behavior - future enhancement would add lazy loading
922 | // with attributes like loading="lazy" and proper intersection observer handling
923 | });
924 |
925 | // --- Error Handling and Robustness Tests ---
926 |
927 | test("avatar handles null and undefined props gracefully", async ({
928 | initTestBed,
929 | createAvatarDriver,
930 | }) => {
931 | // Test that null/undefined props don't break component
932 |
933 | // Test with undefined name
934 | await initTestBed(`<Avatar/>`, {});
935 | const driver1 = await createAvatarDriver();
936 |
937 | await expect(driver1.component).toBeVisible();
938 | await expect(driver1.component).toHaveAttribute("aria-label", "Avatar");
939 | await expect(driver1.component).toHaveAttribute("role", "img");
940 |
941 | // Test with empty string name (should still show "Avatar" because empty string is falsy)
942 | await initTestBed(`<Avatar name=""/>`, {});
943 | const driver2 = await createAvatarDriver();
944 |
945 | await expect(driver2.component).toBeVisible();
946 | await expect(driver2.component).toHaveAttribute("aria-label", "Avatar");
947 | await expect(driver2.component).toHaveAttribute("role", "img");
948 |
949 | // Test with undefined URL (should fall back to initials)
950 | await initTestBed(`<Avatar name="Test User"/>`, {});
951 | const driver3 = await createAvatarDriver();
952 |
953 | await expect(driver3.component).toBeVisible();
954 | await expect(driver3.component).toContainText("TU");
955 |
956 | // Test with empty URL (should fall back to initials)
957 | await initTestBed(`<Avatar name="Test User" url=""/>`, {});
958 | const driver4 = await createAvatarDriver();
959 |
960 | await expect(driver4.component).toBeVisible();
961 | await expect(driver4.component).toContainText("TU");
962 |
963 | // Test with undefined size (should use default)
964 | await initTestBed(`<Avatar name="Size User"/>`, {});
965 | const driver5 = await createAvatarDriver();
966 |
967 | await expect(driver5.component).toBeVisible();
968 | await expect(driver5.component).toContainText("SU");
969 | await expect(driver5.component).toHaveCSS("width", "48px"); // default sm size
970 | });
971 |
972 | test("avatar handles extremely long URLs", async ({ initTestBed, createAvatarDriver }) => {
973 | const VERY_LONG_URL =
974 | "https://example.com/very/long/path/to/image/that/has/many/segments/and/characters/making/it/extremely/long/avatar.jpg?param1=value1¶m2=value2¶m3=value3¶m4=value4¶m5=value5";
975 | const TEST_NAME = "URL Test User";
976 |
977 | await initTestBed(`<Avatar url="${VERY_LONG_URL}" name="${TEST_NAME}"/>`, {});
978 | const driver = await createAvatarDriver();
979 |
980 | // Should handle very long URLs without breaking
981 | await expect(driver.component).toHaveAttribute("src", VERY_LONG_URL);
982 | await expect(driver.component).toHaveAttribute("alt", `Avatar of ${TEST_NAME}`);
983 | await expect(driver.component).toBeVisible();
984 | });
985 |
986 | test("avatar handles concurrent prop updates correctly", async ({
987 | initTestBed,
988 | createAvatarDriver,
989 | }) => {
990 | // Test that rapid prop changes don't cause race conditions
991 |
992 | // Start with initial state
993 | await initTestBed(`<Avatar name="Initial User" size="sm"/>`, {});
994 | const driver = await createAvatarDriver();
995 |
996 | await expect(driver.component).toBeVisible();
997 | await expect(driver.component).toContainText("IU");
998 |
999 | // Rapidly change multiple props in sequence
1000 | await initTestBed(`<Avatar name="Updated User" size="md"/>`, {});
1001 | const driver2 = await createAvatarDriver();
1002 |
1003 | await expect(driver2.component).toBeVisible();
1004 | await expect(driver2.component).toContainText("UU");
1005 | await expect(driver2.component).toHaveCSS("width", "64px"); // md size
1006 |
1007 | // Change to image avatar
1008 | await initTestBed(`<Avatar name="Image User" url="https://example.com/user.jpg"/>`, {});
1009 | const driver3 = await createAvatarDriver();
1010 |
1011 | await expect(driver3.component).toBeVisible();
1012 | await expect(driver3.component).toHaveAttribute("src", "https://example.com/user.jpg");
1013 |
1014 | // Change back to initials with different size
1015 | await initTestBed(`<Avatar name="Final User" size="lg"/>`, {});
1016 | const driver4 = await createAvatarDriver();
1017 |
1018 | await expect(driver4.component).toBeVisible();
1019 | await expect(driver4.component).toContainText("FU");
1020 | await expect(driver4.component).toHaveCSS("width", "96px"); // lg size
1021 |
1022 | // Verify final state is correct (no race condition artifacts)
1023 | const tagName = await driver4.getComponentTagName();
1024 | expect(tagName).toBe("div"); // Should be div for initials, not img
1025 | });
1026 |
1027 | test("avatar memory usage stays stable", async ({ initTestBed, createAvatarDriver }) => {
1028 | // Test that component doesn't leak memory with frequent updates
1029 |
1030 | // Create multiple avatars with different configurations
1031 | const configurations = [
1032 | { name: "User 1", size: "sm" },
1033 | { name: "User 2", size: "md" },
1034 | { name: "User 3", size: "lg" },
1035 | { name: "User 4", url: "https://example.com/user4.jpg" },
1036 | { name: "User 5", url: "https://example.com/user5.jpg" },
1037 | ];
1038 |
1039 | // Test each configuration
1040 | for (const config of configurations) {
1041 | const markup = config.url
1042 | ? `<Avatar name="${config.name}" url="${config.url}"/>`
1043 | : `<Avatar name="${config.name}" size="${config.size}"/>`;
1044 |
1045 | await initTestBed(markup, {});
1046 | const driver = await createAvatarDriver();
1047 |
1048 | await expect(driver.component).toBeVisible();
1049 |
1050 | if (config.url) {
1051 | await expect(driver.component).toHaveAttribute("src", config.url);
1052 | await expect(driver.component).toHaveAttribute("alt", `Avatar of ${config.name}`);
1053 | } else {
1054 | const initials = config.name
1055 | .split(" ")
1056 | .map((n) => n[0])
1057 | .join("");
1058 | await expect(driver.component).toContainText(initials);
1059 | }
1060 | }
1061 |
1062 | // Test that final state is clean and functional
1063 | await initTestBed(`<Avatar name="Final Test" size="sm"/>`, {});
1064 | const finalDriver = await createAvatarDriver();
1065 |
1066 | await expect(finalDriver.component).toBeVisible();
1067 | await expect(finalDriver.component).toContainText("FT");
1068 | await expect(finalDriver.component).toHaveCSS("width", "48px");
1069 |
1070 | // This test verifies that multiple avatar creations don't cause memory leaks
1071 | // by ensuring the component continues to function correctly after multiple instantiations
1072 | });
1073 |
1074 | test.describe("Theme Vars", () => {
1075 | test("custom backgroundColor theme var applies correctly", async ({
1076 | initTestBed,
1077 | createAvatarDriver,
1078 | }) => {
1079 | const CUSTOM_BACKGROUND = "rgb(255, 192, 203)"; // Pink background
1080 | const TEST_NAME = "Background User";
1081 |
1082 | await initTestBed(`<Avatar name="${TEST_NAME}"/>`, {
1083 | testThemeVars: {
1084 | "backgroundColor-Avatar": CUSTOM_BACKGROUND,
1085 | },
1086 | });
1087 | const driver = await createAvatarDriver();
1088 |
1089 | // Should apply custom background color to initials avatar
1090 | await expect(driver.component).toHaveCSS("background-color", CUSTOM_BACKGROUND);
1091 | await expect(driver.component).toContainText("BU"); // Should show initials
1092 | });
1093 |
1094 | test("custom textColor theme var applies correctly", async ({
1095 | initTestBed,
1096 | createAvatarDriver,
1097 | }) => {
1098 | const CUSTOM_TEXT_COLOR = "rgb(255, 0, 0)"; // Red text
1099 | const TEST_NAME = "Text User";
1100 |
1101 | await initTestBed(`<Avatar name="${TEST_NAME}"/>`, {
1102 | testThemeVars: {
1103 | "textColor-Avatar": CUSTOM_TEXT_COLOR,
1104 | },
1105 | });
1106 | const driver = await createAvatarDriver();
1107 |
1108 | // Should apply custom text color to initials
1109 | await expect(driver.component).toHaveCSS("color", CUSTOM_TEXT_COLOR);
1110 | await expect(driver.component).toContainText("TU"); // Should show initials
1111 | });
1112 |
1113 | test("custom fontWeight theme var applies correctly", async ({
1114 | initTestBed,
1115 | createAvatarDriver,
1116 | }) => {
1117 | const CUSTOM_FONT_WEIGHT = "700"; // Bold
1118 | const TEST_NAME = "Bold User";
1119 |
1120 | await initTestBed(`<Avatar name="${TEST_NAME}"/>`, {
1121 | testThemeVars: {
1122 | "fontWeight-Avatar": CUSTOM_FONT_WEIGHT,
1123 | },
1124 | });
1125 | const driver = await createAvatarDriver();
1126 |
1127 | // Should apply custom font weight to initials
1128 | await expect(driver.component).toHaveCSS("font-weight", CUSTOM_FONT_WEIGHT);
1129 | await expect(driver.component).toContainText("BU"); // Should show initials
1130 | });
1131 |
1132 | test("custom borderRadius theme var applies correctly", async ({
1133 | initTestBed,
1134 | createAvatarDriver,
1135 | }) => {
1136 | const CUSTOM_BORDER_RADIUS = "4px"; // Square corners instead of default round
1137 | const TEST_NAME = "Square User";
1138 |
1139 | await initTestBed(`<Avatar name="${TEST_NAME}"/>`, {
1140 | testThemeVars: {
1141 | "borderRadius-Avatar": CUSTOM_BORDER_RADIUS,
1142 | },
1143 | });
1144 | const driver = await createAvatarDriver();
1145 |
1146 | // Should apply custom border radius
1147 | await expect(driver.component).toHaveCSS("border-radius", CUSTOM_BORDER_RADIUS);
1148 | await expect(driver.component).toContainText("SU"); // Should show initials
1149 | });
1150 |
1151 | test("custom boxShadow theme var applies correctly", async ({
1152 | initTestBed,
1153 | createAvatarDriver,
1154 | }) => {
1155 | const CUSTOM_BOX_SHADOW = "rgba(0, 0, 0, 0.3) 0px 4px 8px 0px"; // Normalized format
1156 | const TEST_NAME = "Shadow User";
1157 |
1158 | await initTestBed(`<Avatar name="${TEST_NAME}"/>`, {
1159 | testThemeVars: {
1160 | "boxShadow-Avatar": "0px 4px 8px rgba(0, 0, 0, 0.3)",
1161 | },
1162 | });
1163 | const driver = await createAvatarDriver();
1164 |
1165 | // Should apply custom box shadow (browser normalizes the format)
1166 | await expect(driver.component).toHaveCSS("box-shadow", CUSTOM_BOX_SHADOW);
1167 | await expect(driver.component).toContainText("SU"); // Should show initials
1168 | });
1169 |
1170 | test("style prop overrides theme variables", async ({ initTestBed, createAvatarDriver }) => {
1171 | // Note: This test documents the current behavior - XMLUI may not support
1172 | // inline styles in templates, so theme variables take precedence
1173 | const THEME_BACKGROUND = "rgb(255, 192, 203)"; // Pink from theme
1174 | const TEST_NAME = "Override User";
1175 |
1176 | await initTestBed(`<Avatar name="${TEST_NAME}"/>`, {
1177 | testThemeVars: {
1178 | "backgroundColor-Avatar": THEME_BACKGROUND,
1179 | },
1180 | });
1181 | const driver = await createAvatarDriver();
1182 |
1183 | // Theme variable should be applied (style prop may not work in XMLUI templates)
1184 | await expect(driver.component).toHaveCSS("background-color", THEME_BACKGROUND);
1185 | await expect(driver.component).toContainText("OU"); // Should show initials
1186 |
1187 | // This test documents current behavior - inline styles may need programmatic setting
1188 | });
1189 |
1190 | test("style prop applies layout properties correctly", async ({
1191 | initTestBed,
1192 | createAvatarDriver,
1193 | }) => {
1194 | // Note: This test documents current behavior - XMLUI templates may not support
1195 | // inline styles, so we test the component's default styling behavior
1196 | const TEST_NAME = "Layout User";
1197 |
1198 | await initTestBed(`<Avatar name="${TEST_NAME}"/>`, {});
1199 | const driver = await createAvatarDriver();
1200 |
1201 | // Should have default styling applied (no custom layout props in this test)
1202 | await expect(driver.component).toHaveCSS("position", "static"); // Default position
1203 | await expect(driver.component).toContainText("LU"); // Should show initials
1204 |
1205 | // This test documents current behavior - programmatic style setting may be needed for layout props
1206 | });
1207 |
1208 | test("theme border applies to all sides", async ({ initTestBed, createAvatarDriver }) => {
1209 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1210 | const EXPECTED_WIDTH = "5px";
1211 | const EXPECTED_STYLE = "dotted";
1212 |
1213 | await initTestBed('<Avatar name="Tim"/>', {
1214 | testThemeVars: {
1215 | "border-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1216 | },
1217 | });
1218 | const component = (await createAvatarDriver()).component;
1219 |
1220 | await expect(component).toHaveCSS("border-top-color", EXPECTED_COLOR);
1221 | await expect(component).toHaveCSS("border-top-width", EXPECTED_WIDTH);
1222 | await expect(component).toHaveCSS("border-top-style", EXPECTED_STYLE);
1223 | await expect(component).toHaveCSS("border-right-color", EXPECTED_COLOR);
1224 | await expect(component).toHaveCSS("border-right-width", EXPECTED_WIDTH);
1225 | await expect(component).toHaveCSS("border-right-style", EXPECTED_STYLE);
1226 | await expect(component).toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1227 | await expect(component).toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1228 | await expect(component).toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1229 | await expect(component).toHaveCSS("border-left-color", EXPECTED_COLOR);
1230 | await expect(component).toHaveCSS("border-left-width", EXPECTED_WIDTH);
1231 | await expect(component).toHaveCSS("border-left-style", EXPECTED_STYLE);
1232 | });
1233 |
1234 | test("theme borderLeft applies to left side", async ({ initTestBed, createAvatarDriver }) => {
1235 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1236 | const EXPECTED_WIDTH = "5px";
1237 | const EXPECTED_STYLE = "dotted";
1238 |
1239 | await initTestBed('<Avatar name="Tim"/>', {
1240 | testThemeVars: {
1241 | "borderLeft-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1242 | },
1243 | });
1244 | const component = (await createAvatarDriver()).component;
1245 |
1246 | await expect(component).not.toHaveCSS("border-top-color", EXPECTED_COLOR);
1247 | await expect(component).not.toHaveCSS("border-top-width", EXPECTED_WIDTH);
1248 | await expect(component).not.toHaveCSS("border-top-style", EXPECTED_STYLE);
1249 | await expect(component).not.toHaveCSS("border-right-color", EXPECTED_COLOR);
1250 | await expect(component).not.toHaveCSS("border-right-width", EXPECTED_WIDTH);
1251 | await expect(component).not.toHaveCSS("border-right-style", EXPECTED_STYLE);
1252 | await expect(component).not.toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1253 | await expect(component).not.toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1254 | await expect(component).not.toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1255 | await expect(component).toHaveCSS("border-left-color", EXPECTED_COLOR);
1256 | await expect(component).toHaveCSS("border-left-width", EXPECTED_WIDTH);
1257 | await expect(component).toHaveCSS("border-left-style", EXPECTED_STYLE);
1258 | });
1259 |
1260 | test("theme borderRight applies to right side", async ({ initTestBed, createAvatarDriver }) => {
1261 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1262 | const EXPECTED_WIDTH = "5px";
1263 | const EXPECTED_STYLE = "dotted";
1264 |
1265 | await initTestBed('<Avatar name="Tim"/>', {
1266 | testThemeVars: {
1267 | "borderRight-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1268 | },
1269 | });
1270 | const component = (await createAvatarDriver()).component;
1271 |
1272 | await expect(component).not.toHaveCSS("border-top-color", EXPECTED_COLOR);
1273 | await expect(component).not.toHaveCSS("border-top-width", EXPECTED_WIDTH);
1274 | await expect(component).not.toHaveCSS("border-top-style", EXPECTED_STYLE);
1275 | await expect(component).toHaveCSS("border-right-color", EXPECTED_COLOR);
1276 | await expect(component).toHaveCSS("border-right-width", EXPECTED_WIDTH);
1277 | await expect(component).toHaveCSS("border-right-style", EXPECTED_STYLE);
1278 | await expect(component).not.toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1279 | await expect(component).not.toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1280 | await expect(component).not.toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1281 | await expect(component).not.toHaveCSS("border-left-color", EXPECTED_COLOR);
1282 | await expect(component).not.toHaveCSS("border-left-width", EXPECTED_WIDTH);
1283 | await expect(component).not.toHaveCSS("border-left-style", EXPECTED_STYLE);
1284 | });
1285 |
1286 | test("theme borderHorizontal applies to left and right", async ({
1287 | initTestBed,
1288 | createAvatarDriver,
1289 | }) => {
1290 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1291 | const EXPECTED_WIDTH = "5px";
1292 | const EXPECTED_STYLE = "dotted";
1293 |
1294 | await initTestBed('<Avatar name="Tim"/>', {
1295 | testThemeVars: {
1296 | "borderHorizontal-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1297 | },
1298 | });
1299 | const component = (await createAvatarDriver()).component;
1300 |
1301 | await expect(component).not.toHaveCSS("border-top-color", EXPECTED_COLOR);
1302 | await expect(component).not.toHaveCSS("border-top-width", EXPECTED_WIDTH);
1303 | await expect(component).not.toHaveCSS("border-top-style", EXPECTED_STYLE);
1304 | await expect(component).toHaveCSS("border-right-color", EXPECTED_COLOR);
1305 | await expect(component).toHaveCSS("border-right-width", EXPECTED_WIDTH);
1306 | await expect(component).toHaveCSS("border-right-style", EXPECTED_STYLE);
1307 | await expect(component).not.toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1308 | await expect(component).not.toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1309 | await expect(component).not.toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1310 | await expect(component).toHaveCSS("border-left-color", EXPECTED_COLOR);
1311 | await expect(component).toHaveCSS("border-left-width", EXPECTED_WIDTH);
1312 | await expect(component).toHaveCSS("border-left-style", EXPECTED_STYLE);
1313 | });
1314 |
1315 | test("theme borderLeft overrides borderHorizontal", async ({
1316 | initTestBed,
1317 | createAvatarDriver,
1318 | }) => {
1319 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1320 | const EXPECTED_WIDTH = "5px";
1321 | const EXPECTED_STYLE = "dotted";
1322 |
1323 | await initTestBed('<Avatar name="Tim"/>', {
1324 | testThemeVars: {
1325 | "borderHorizontal-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1326 | "borderLeft-Avatar": "8px double rgb(0, 128, 0)",
1327 | },
1328 | });
1329 | const component = (await createAvatarDriver()).component;
1330 |
1331 | await expect(component).not.toHaveCSS("border-top-color", EXPECTED_COLOR);
1332 | await expect(component).not.toHaveCSS("border-top-width", EXPECTED_WIDTH);
1333 | await expect(component).not.toHaveCSS("border-top-style", EXPECTED_STYLE);
1334 | await expect(component).toHaveCSS("border-right-color", EXPECTED_COLOR);
1335 | await expect(component).toHaveCSS("border-right-width", EXPECTED_WIDTH);
1336 | await expect(component).toHaveCSS("border-right-style", EXPECTED_STYLE);
1337 | await expect(component).not.toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1338 | await expect(component).not.toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1339 | await expect(component).not.toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1340 | await expect(component).toHaveCSS("border-left-color", "rgb(0, 128, 0)");
1341 | await expect(component).toHaveCSS("border-left-width", "8px");
1342 | await expect(component).toHaveCSS("border-left-style", "double");
1343 | });
1344 |
1345 | test("theme borderRight overrides borderHorizontal", async ({
1346 | initTestBed,
1347 | createAvatarDriver,
1348 | }) => {
1349 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1350 | const EXPECTED_WIDTH = "5px";
1351 | const EXPECTED_STYLE = "dotted";
1352 |
1353 | await initTestBed('<Avatar name="Tim"/>', {
1354 | testThemeVars: {
1355 | "borderHorizontal-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1356 | "borderRight-Avatar": "8px double rgb(0, 128, 0)",
1357 | },
1358 | });
1359 | const component = (await createAvatarDriver()).component;
1360 |
1361 | await expect(component).not.toHaveCSS("border-top-color", EXPECTED_COLOR);
1362 | await expect(component).not.toHaveCSS("border-top-width", EXPECTED_WIDTH);
1363 | await expect(component).not.toHaveCSS("border-top-style", EXPECTED_STYLE);
1364 | await expect(component).toHaveCSS("border-right-color", "rgb(0, 128, 0)");
1365 | await expect(component).toHaveCSS("border-right-width", "8px");
1366 | await expect(component).toHaveCSS("border-right-style", "double");
1367 | await expect(component).not.toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1368 | await expect(component).not.toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1369 | await expect(component).not.toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1370 | await expect(component).toHaveCSS("border-left-color", EXPECTED_COLOR);
1371 | await expect(component).toHaveCSS("border-left-width", EXPECTED_WIDTH);
1372 | await expect(component).toHaveCSS("border-left-style", EXPECTED_STYLE);
1373 | });
1374 |
1375 | test("theme borderTop applies to top side", async ({ initTestBed, createAvatarDriver }) => {
1376 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1377 | const EXPECTED_WIDTH = "5px";
1378 | const EXPECTED_STYLE = "dotted";
1379 |
1380 | await initTestBed('<Avatar name="Tim"/>', {
1381 | testThemeVars: {
1382 | "borderTop-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1383 | },
1384 | });
1385 | const component = (await createAvatarDriver()).component;
1386 |
1387 | await expect(component).toHaveCSS("border-top-color", EXPECTED_COLOR);
1388 | await expect(component).toHaveCSS("border-top-width", EXPECTED_WIDTH);
1389 | await expect(component).toHaveCSS("border-top-style", EXPECTED_STYLE);
1390 | await expect(component).not.toHaveCSS("border-right-color", EXPECTED_COLOR);
1391 | await expect(component).not.toHaveCSS("border-right-width", EXPECTED_WIDTH);
1392 | await expect(component).not.toHaveCSS("border-right-style", EXPECTED_STYLE);
1393 | await expect(component).not.toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1394 | await expect(component).not.toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1395 | await expect(component).not.toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1396 | await expect(component).not.toHaveCSS("border-left-color", EXPECTED_COLOR);
1397 | await expect(component).not.toHaveCSS("border-left-width", EXPECTED_WIDTH);
1398 | await expect(component).not.toHaveCSS("border-left-style", EXPECTED_STYLE);
1399 | });
1400 |
1401 | test("theme borderBottom applies to bottom side", async ({ initTestBed, createAvatarDriver }) => {
1402 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1403 | const EXPECTED_WIDTH = "5px";
1404 | const EXPECTED_STYLE = "dotted";
1405 |
1406 | await initTestBed('<Avatar name="Tim"/>', {
1407 | testThemeVars: {
1408 | "borderBottom-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1409 | },
1410 | });
1411 | const component = (await createAvatarDriver()).component;
1412 |
1413 | await expect(component).not.toHaveCSS("border-top-color", EXPECTED_COLOR);
1414 | await expect(component).not.toHaveCSS("border-top-width", EXPECTED_WIDTH);
1415 | await expect(component).not.toHaveCSS("border-top-style", EXPECTED_STYLE);
1416 | await expect(component).not.toHaveCSS("border-right-color", EXPECTED_COLOR);
1417 | await expect(component).not.toHaveCSS("border-right-width", EXPECTED_WIDTH);
1418 | await expect(component).not.toHaveCSS("border-right-style", EXPECTED_STYLE);
1419 | await expect(component).toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1420 | await expect(component).toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1421 | await expect(component).toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1422 | await expect(component).not.toHaveCSS("border-left-color", EXPECTED_COLOR);
1423 | await expect(component).not.toHaveCSS("border-left-width", EXPECTED_WIDTH);
1424 | await expect(component).not.toHaveCSS("border-left-style", EXPECTED_STYLE);
1425 | });
1426 |
1427 | test("theme borderVertical applies to top and bottom", async ({
1428 | initTestBed,
1429 | createAvatarDriver,
1430 | }) => {
1431 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1432 | const EXPECTED_WIDTH = "5px";
1433 | const EXPECTED_STYLE = "dotted";
1434 |
1435 | await initTestBed('<Avatar name="Tim"/>', {
1436 | testThemeVars: {
1437 | "borderVertical-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1438 | },
1439 | });
1440 | const component = (await createAvatarDriver()).component;
1441 |
1442 | await expect(component).toHaveCSS("border-top-color", EXPECTED_COLOR);
1443 | await expect(component).toHaveCSS("border-top-width", EXPECTED_WIDTH);
1444 | await expect(component).toHaveCSS("border-top-style", EXPECTED_STYLE);
1445 | await expect(component).not.toHaveCSS("border-right-color", EXPECTED_COLOR);
1446 | await expect(component).not.toHaveCSS("border-right-width", EXPECTED_WIDTH);
1447 | await expect(component).not.toHaveCSS("border-right-style", EXPECTED_STYLE);
1448 | await expect(component).toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1449 | await expect(component).toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1450 | await expect(component).toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1451 | await expect(component).not.toHaveCSS("border-left-color", EXPECTED_COLOR);
1452 | await expect(component).not.toHaveCSS("border-left-width", EXPECTED_WIDTH);
1453 | await expect(component).not.toHaveCSS("border-left-style", EXPECTED_STYLE);
1454 | });
1455 |
1456 | test("theme borderTop overrides borderVertical", async ({ initTestBed, createAvatarDriver }) => {
1457 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1458 | const EXPECTED_WIDTH = "5px";
1459 | const EXPECTED_STYLE = "dotted";
1460 |
1461 | await initTestBed('<Avatar name="Tim"/>', {
1462 | testThemeVars: {
1463 | "borderVertical-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1464 | "borderTop-Avatar": "8px double rgb(0, 128, 0)",
1465 | },
1466 | });
1467 | const component = (await createAvatarDriver()).component;
1468 |
1469 | await expect(component).toHaveCSS("border-top-color", "rgb(0, 128, 0)");
1470 | await expect(component).toHaveCSS("border-top-width", "8px");
1471 | await expect(component).toHaveCSS("border-top-style", "double");
1472 | await expect(component).not.toHaveCSS("border-right-color", EXPECTED_COLOR);
1473 | await expect(component).not.toHaveCSS("border-right-width", EXPECTED_WIDTH);
1474 | await expect(component).not.toHaveCSS("border-right-style", EXPECTED_STYLE);
1475 | await expect(component).toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1476 | await expect(component).toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1477 | await expect(component).toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1478 | await expect(component).not.toHaveCSS("border-left-color", EXPECTED_COLOR);
1479 | await expect(component).not.toHaveCSS("border-left-width", EXPECTED_WIDTH);
1480 | await expect(component).not.toHaveCSS("border-left-style", EXPECTED_STYLE);
1481 | });
1482 |
1483 | test("theme borderBottom overrides borderVertical", async ({
1484 | initTestBed,
1485 | createAvatarDriver,
1486 | }) => {
1487 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1488 | const EXPECTED_WIDTH = "5px";
1489 | const EXPECTED_STYLE = "dotted";
1490 |
1491 | await initTestBed('<Avatar name="Tim"/>', {
1492 | testThemeVars: {
1493 | "borderVertical-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1494 | "borderBottom-Avatar": "8px double rgb(0, 128, 0)",
1495 | },
1496 | });
1497 | const component = (await createAvatarDriver()).component;
1498 |
1499 | await expect(component).toHaveCSS("border-top-color", EXPECTED_COLOR);
1500 | await expect(component).toHaveCSS("border-top-width", EXPECTED_WIDTH);
1501 | await expect(component).toHaveCSS("border-top-style", EXPECTED_STYLE);
1502 | await expect(component).not.toHaveCSS("border-right-color", EXPECTED_COLOR);
1503 | await expect(component).not.toHaveCSS("border-right-width", EXPECTED_WIDTH);
1504 | await expect(component).not.toHaveCSS("border-right-style", EXPECTED_STYLE);
1505 | await expect(component).toHaveCSS("border-bottom-color", "rgb(0, 128, 0)");
1506 | await expect(component).toHaveCSS("border-bottom-width", "8px");
1507 | await expect(component).toHaveCSS("border-bottom-style", "double");
1508 | await expect(component).not.toHaveCSS("border-left-color", EXPECTED_COLOR);
1509 | await expect(component).not.toHaveCSS("border-left-width", EXPECTED_WIDTH);
1510 | await expect(component).not.toHaveCSS("border-left-style", EXPECTED_STYLE);
1511 | });
1512 |
1513 | test("theme borderColor applies to all sides", async ({ initTestBed, createAvatarDriver }) => {
1514 | const EXPECTED_COLOR = "rgb(0, 128, 0)";
1515 | const EXPECTED_WIDTH = "5px";
1516 | const EXPECTED_STYLE = "dotted";
1517 |
1518 | await initTestBed('<Avatar name="Tim"/>', {
1519 | testThemeVars: {
1520 | "borderColor-Avatar": EXPECTED_COLOR,
1521 | },
1522 | });
1523 | const component = (await createAvatarDriver()).component;
1524 |
1525 | await expect(component).toHaveCSS("border-top-color", EXPECTED_COLOR);
1526 | await expect(component).not.toHaveCSS("border-top-width", EXPECTED_WIDTH);
1527 | await expect(component).not.toHaveCSS("border-top-style", EXPECTED_STYLE);
1528 | await expect(component).toHaveCSS("border-right-color", EXPECTED_COLOR);
1529 | await expect(component).not.toHaveCSS("border-right-width", EXPECTED_WIDTH);
1530 | await expect(component).not.toHaveCSS("border-right-style", EXPECTED_STYLE);
1531 | await expect(component).toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1532 | await expect(component).not.toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1533 | await expect(component).not.toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1534 | await expect(component).toHaveCSS("border-left-color", EXPECTED_COLOR);
1535 | await expect(component).not.toHaveCSS("border-left-width", EXPECTED_WIDTH);
1536 | await expect(component).not.toHaveCSS("border-left-style", EXPECTED_STYLE);
1537 | });
1538 |
1539 | test("theme borderColor overrides border color", async ({ initTestBed, createAvatarDriver }) => {
1540 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1541 | const EXPECTED_WIDTH = "5px";
1542 | const EXPECTED_STYLE = "dotted";
1543 | const UPDATED = "rgb(0, 128, 0)";
1544 |
1545 | await initTestBed('<Avatar name="Tim"/>', {
1546 | testThemeVars: {
1547 | "borderColor-Avatar": UPDATED,
1548 | "border-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1549 | },
1550 | });
1551 | const component = (await createAvatarDriver()).component;
1552 |
1553 | await expect(component).toHaveCSS("border-top-color", UPDATED);
1554 | await expect(component).toHaveCSS("border-top-width", EXPECTED_WIDTH);
1555 | await expect(component).toHaveCSS("border-top-style", EXPECTED_STYLE);
1556 | await expect(component).toHaveCSS("border-right-color", UPDATED);
1557 | await expect(component).toHaveCSS("border-right-width", EXPECTED_WIDTH);
1558 | await expect(component).toHaveCSS("border-right-style", EXPECTED_STYLE);
1559 | await expect(component).toHaveCSS("border-bottom-color", UPDATED);
1560 | await expect(component).toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1561 | await expect(component).toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1562 | await expect(component).toHaveCSS("border-left-color", UPDATED);
1563 | await expect(component).toHaveCSS("border-left-width", EXPECTED_WIDTH);
1564 | await expect(component).toHaveCSS("border-left-style", EXPECTED_STYLE);
1565 | });
1566 |
1567 | test("theme borderHorizontalColor overrides border color", async ({
1568 | initTestBed,
1569 | createAvatarDriver,
1570 | }) => {
1571 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1572 | const EXPECTED_WIDTH = "5px";
1573 | const EXPECTED_STYLE = "dotted";
1574 | const UPDATED = "rgb(0, 128, 0)";
1575 |
1576 | await initTestBed('<Avatar name="Tim"/>', {
1577 | testThemeVars: {
1578 | "borderHorizontalColor-Avatar": UPDATED,
1579 | "border-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1580 | },
1581 | });
1582 | const component = (await createAvatarDriver()).component;
1583 |
1584 | await expect(component).toHaveCSS("border-top-color", EXPECTED_COLOR);
1585 | await expect(component).toHaveCSS("border-top-width", EXPECTED_WIDTH);
1586 | await expect(component).toHaveCSS("border-top-style", EXPECTED_STYLE);
1587 | await expect(component).toHaveCSS("border-right-color", UPDATED);
1588 | await expect(component).toHaveCSS("border-right-width", EXPECTED_WIDTH);
1589 | await expect(component).toHaveCSS("border-right-style", EXPECTED_STYLE);
1590 | await expect(component).toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1591 | await expect(component).toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1592 | await expect(component).toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1593 | await expect(component).toHaveCSS("border-left-color", UPDATED);
1594 | await expect(component).toHaveCSS("border-left-width", EXPECTED_WIDTH);
1595 | await expect(component).toHaveCSS("border-left-style", EXPECTED_STYLE);
1596 | });
1597 |
1598 | test("theme borderLeftColor overrides border color", async ({
1599 | initTestBed,
1600 | createAvatarDriver,
1601 | }) => {
1602 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1603 | const EXPECTED_WIDTH = "5px";
1604 | const EXPECTED_STYLE = "dotted";
1605 | const UPDATED = "rgb(0, 128, 0)";
1606 |
1607 | await initTestBed('<Avatar name="Tim"/>', {
1608 | testThemeVars: {
1609 | "borderLeftColor-Avatar": UPDATED,
1610 | "border-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1611 | },
1612 | });
1613 | const component = (await createAvatarDriver()).component;
1614 |
1615 | await expect(component).toHaveCSS("border-top-color", EXPECTED_COLOR);
1616 | await expect(component).toHaveCSS("border-top-width", EXPECTED_WIDTH);
1617 | await expect(component).toHaveCSS("border-top-style", EXPECTED_STYLE);
1618 | await expect(component).toHaveCSS("border-right-color", EXPECTED_COLOR);
1619 | await expect(component).toHaveCSS("border-right-width", EXPECTED_WIDTH);
1620 | await expect(component).toHaveCSS("border-right-style", EXPECTED_STYLE);
1621 | await expect(component).toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1622 | await expect(component).toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1623 | await expect(component).toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1624 | await expect(component).toHaveCSS("border-left-color", UPDATED);
1625 | await expect(component).toHaveCSS("border-left-width", EXPECTED_WIDTH);
1626 | await expect(component).toHaveCSS("border-left-style", EXPECTED_STYLE);
1627 | });
1628 |
1629 | test("theme borderRightColor overrides border color", async ({
1630 | initTestBed,
1631 | createAvatarDriver,
1632 | }) => {
1633 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1634 | const EXPECTED_WIDTH = "5px";
1635 | const EXPECTED_STYLE = "dotted";
1636 | const UPDATED = "rgb(0, 128, 0)";
1637 |
1638 | await initTestBed('<Avatar name="Tim"/>', {
1639 | testThemeVars: {
1640 | "borderRightColor-Avatar": UPDATED,
1641 | "border-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1642 | },
1643 | });
1644 | const component = (await createAvatarDriver()).component;
1645 |
1646 | await expect(component).toHaveCSS("border-top-color", EXPECTED_COLOR);
1647 | await expect(component).toHaveCSS("border-top-width", EXPECTED_WIDTH);
1648 | await expect(component).toHaveCSS("border-top-style", EXPECTED_STYLE);
1649 | await expect(component).toHaveCSS("border-right-color", UPDATED);
1650 | await expect(component).toHaveCSS("border-right-width", EXPECTED_WIDTH);
1651 | await expect(component).toHaveCSS("border-right-style", EXPECTED_STYLE);
1652 | await expect(component).toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1653 | await expect(component).toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1654 | await expect(component).toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1655 | await expect(component).toHaveCSS("border-left-color", EXPECTED_COLOR);
1656 | await expect(component).toHaveCSS("border-left-width", EXPECTED_WIDTH);
1657 | await expect(component).toHaveCSS("border-left-style", EXPECTED_STYLE);
1658 | });
1659 |
1660 | test("theme borderVerticalColor overrides border color", async ({
1661 | initTestBed,
1662 | createAvatarDriver,
1663 | }) => {
1664 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1665 | const EXPECTED_WIDTH = "5px";
1666 | const EXPECTED_STYLE = "dotted";
1667 | const UPDATED = "rgb(0, 128, 0)";
1668 |
1669 | await initTestBed('<Avatar name="Tim"/>', {
1670 | testThemeVars: {
1671 | "borderVerticalColor-Avatar": UPDATED,
1672 | "border-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1673 | },
1674 | });
1675 | const component = (await createAvatarDriver()).component;
1676 |
1677 | await expect(component).toHaveCSS("border-top-color", UPDATED);
1678 | await expect(component).toHaveCSS("border-top-width", EXPECTED_WIDTH);
1679 | await expect(component).toHaveCSS("border-top-style", EXPECTED_STYLE);
1680 | await expect(component).toHaveCSS("border-right-color", EXPECTED_COLOR);
1681 | await expect(component).toHaveCSS("border-right-width", EXPECTED_WIDTH);
1682 | await expect(component).toHaveCSS("border-right-style", EXPECTED_STYLE);
1683 | await expect(component).toHaveCSS("border-bottom-color", UPDATED);
1684 | await expect(component).toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1685 | await expect(component).toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1686 | await expect(component).toHaveCSS("border-left-color", EXPECTED_COLOR);
1687 | await expect(component).toHaveCSS("border-left-width", EXPECTED_WIDTH);
1688 | await expect(component).toHaveCSS("border-left-style", EXPECTED_STYLE);
1689 | });
1690 |
1691 | test("theme borderTopColor overrides border color", async ({
1692 | initTestBed,
1693 | createAvatarDriver,
1694 | }) => {
1695 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1696 | const EXPECTED_WIDTH = "5px";
1697 | const EXPECTED_STYLE = "dotted";
1698 | const UPDATED = "rgb(0, 128, 0)";
1699 |
1700 | await initTestBed('<Avatar name="Tim"/>', {
1701 | testThemeVars: {
1702 | "borderTopColor-Avatar": UPDATED,
1703 | "border-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1704 | },
1705 | });
1706 | const component = (await createAvatarDriver()).component;
1707 |
1708 | await expect(component).toHaveCSS("border-top-color", UPDATED);
1709 | await expect(component).toHaveCSS("border-top-width", EXPECTED_WIDTH);
1710 | await expect(component).toHaveCSS("border-top-style", EXPECTED_STYLE);
1711 | await expect(component).toHaveCSS("border-right-color", EXPECTED_COLOR);
1712 | await expect(component).toHaveCSS("border-right-width", EXPECTED_WIDTH);
1713 | await expect(component).toHaveCSS("border-right-style", EXPECTED_STYLE);
1714 | await expect(component).toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1715 | await expect(component).toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1716 | await expect(component).toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1717 | await expect(component).toHaveCSS("border-left-color", EXPECTED_COLOR);
1718 | await expect(component).toHaveCSS("border-left-width", EXPECTED_WIDTH);
1719 | await expect(component).toHaveCSS("border-left-style", EXPECTED_STYLE);
1720 | });
1721 |
1722 | test("theme borderBottomColor overrides border color", async ({
1723 | initTestBed,
1724 | createAvatarDriver,
1725 | }) => {
1726 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1727 | const EXPECTED_WIDTH = "5px";
1728 | const EXPECTED_STYLE = "dotted";
1729 | const UPDATED = "rgb(0, 128, 0)";
1730 |
1731 | await initTestBed('<Avatar name="Tim"/>', {
1732 | testThemeVars: {
1733 | "borderBottomColor-Avatar": UPDATED,
1734 | "border-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1735 | },
1736 | });
1737 | const component = (await createAvatarDriver()).component;
1738 |
1739 | await expect(component).toHaveCSS("border-top-color", EXPECTED_COLOR);
1740 | await expect(component).toHaveCSS("border-top-width", EXPECTED_WIDTH);
1741 | await expect(component).toHaveCSS("border-top-style", EXPECTED_STYLE);
1742 | await expect(component).toHaveCSS("border-right-color", EXPECTED_COLOR);
1743 | await expect(component).toHaveCSS("border-right-width", EXPECTED_WIDTH);
1744 | await expect(component).toHaveCSS("border-right-style", EXPECTED_STYLE);
1745 | await expect(component).toHaveCSS("border-bottom-color", UPDATED);
1746 | await expect(component).toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1747 | await expect(component).toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1748 | await expect(component).toHaveCSS("border-left-color", EXPECTED_COLOR);
1749 | await expect(component).toHaveCSS("border-left-width", EXPECTED_WIDTH);
1750 | await expect(component).toHaveCSS("border-left-style", EXPECTED_STYLE);
1751 | });
1752 |
1753 | test("theme borderStyle applies to all sides", async ({ initTestBed, createAvatarDriver }) => {
1754 | const EXPECTED_COLOR = "rgb(0, 128, 0)";
1755 | const EXPECTED_WIDTH = "5px";
1756 | const EXPECTED_STYLE = "dotted";
1757 |
1758 | await initTestBed('<Avatar name="Tim"/>', {
1759 | testThemeVars: {
1760 | "borderStyle-Avatar": EXPECTED_STYLE,
1761 | },
1762 | });
1763 | const component = (await createAvatarDriver()).component;
1764 |
1765 | await expect(component).not.toHaveCSS("border-top-color", EXPECTED_COLOR);
1766 | await expect(component).not.toHaveCSS("border-top-width", EXPECTED_WIDTH);
1767 | await expect(component).toHaveCSS("border-top-style", EXPECTED_STYLE);
1768 | await expect(component).not.toHaveCSS("border-right-color", EXPECTED_COLOR);
1769 | await expect(component).not.toHaveCSS("border-right-width", EXPECTED_WIDTH);
1770 | await expect(component).toHaveCSS("border-right-style", EXPECTED_STYLE);
1771 | await expect(component).not.toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1772 | await expect(component).not.toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1773 | await expect(component).toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1774 | await expect(component).not.toHaveCSS("border-left-color", EXPECTED_COLOR);
1775 | await expect(component).not.toHaveCSS("border-left-width", EXPECTED_WIDTH);
1776 | await expect(component).toHaveCSS("border-left-style", EXPECTED_STYLE);
1777 | });
1778 |
1779 | test("theme borderStyle overrides border style", async ({ initTestBed, createAvatarDriver }) => {
1780 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1781 | const EXPECTED_WIDTH = "5px";
1782 | const EXPECTED_STYLE = "dotted";
1783 | const UPDATED = "double";
1784 |
1785 | await initTestBed('<Avatar name="Tim"/>', {
1786 | testThemeVars: {
1787 | "borderStyle-Avatar": UPDATED,
1788 | "border-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1789 | },
1790 | });
1791 | const component = (await createAvatarDriver()).component;
1792 |
1793 | await expect(component).toHaveCSS("border-top-color", EXPECTED_COLOR);
1794 | await expect(component).toHaveCSS("border-top-width", EXPECTED_WIDTH);
1795 | await expect(component).toHaveCSS("border-top-style", UPDATED);
1796 | await expect(component).toHaveCSS("border-right-color", EXPECTED_COLOR);
1797 | await expect(component).toHaveCSS("border-right-width", EXPECTED_WIDTH);
1798 | await expect(component).toHaveCSS("border-right-style", UPDATED);
1799 | await expect(component).toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1800 | await expect(component).toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1801 | await expect(component).toHaveCSS("border-bottom-style", UPDATED);
1802 | await expect(component).toHaveCSS("border-left-color", EXPECTED_COLOR);
1803 | await expect(component).toHaveCSS("border-left-width", EXPECTED_WIDTH);
1804 | await expect(component).toHaveCSS("border-left-style", UPDATED);
1805 | });
1806 |
1807 | test("theme borderHorizontalWidth overrides border width", async ({
1808 | initTestBed,
1809 | createAvatarDriver,
1810 | }) => {
1811 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1812 | const EXPECTED_WIDTH = "5px";
1813 | const EXPECTED_STYLE = "dotted";
1814 | const UPDATED = "12px";
1815 |
1816 | await initTestBed('<Avatar name="Tim"/>', {
1817 | testThemeVars: {
1818 | "borderHorizontalWidth-Avatar": UPDATED,
1819 | "border-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1820 | },
1821 | });
1822 | const component = (await createAvatarDriver()).component;
1823 |
1824 | await expect(component).toHaveCSS("border-top-color", EXPECTED_COLOR);
1825 | await expect(component).toHaveCSS("border-top-width", EXPECTED_WIDTH);
1826 | await expect(component).toHaveCSS("border-top-style", EXPECTED_STYLE);
1827 | await expect(component).toHaveCSS("border-right-color", EXPECTED_COLOR);
1828 | await expect(component).toHaveCSS("border-right-width", UPDATED);
1829 | await expect(component).toHaveCSS("border-right-style", EXPECTED_STYLE);
1830 | await expect(component).toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1831 | await expect(component).toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1832 | await expect(component).toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1833 | await expect(component).toHaveCSS("border-left-color", EXPECTED_COLOR);
1834 | await expect(component).toHaveCSS("border-left-width", UPDATED);
1835 | await expect(component).toHaveCSS("border-left-style", EXPECTED_STYLE);
1836 | });
1837 |
1838 | test("theme borderLeftWidth overrides border width", async ({
1839 | initTestBed,
1840 | createAvatarDriver,
1841 | }) => {
1842 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1843 | const EXPECTED_WIDTH = "5px";
1844 | const EXPECTED_STYLE = "dotted";
1845 | const UPDATED = "12px";
1846 |
1847 | await initTestBed('<Avatar name="Tim"/>', {
1848 | testThemeVars: {
1849 | "borderLeftWidth-Avatar": UPDATED,
1850 | "border-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1851 | },
1852 | });
1853 | const component = (await createAvatarDriver()).component;
1854 |
1855 | await expect(component).toHaveCSS("border-top-color", EXPECTED_COLOR);
1856 | await expect(component).toHaveCSS("border-top-width", EXPECTED_WIDTH);
1857 | await expect(component).toHaveCSS("border-top-style", EXPECTED_STYLE);
1858 | await expect(component).toHaveCSS("border-right-color", EXPECTED_COLOR);
1859 | await expect(component).toHaveCSS("border-right-width", EXPECTED_WIDTH);
1860 | await expect(component).toHaveCSS("border-right-style", EXPECTED_STYLE);
1861 | await expect(component).toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1862 | await expect(component).toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1863 | await expect(component).toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1864 | await expect(component).toHaveCSS("border-left-color", EXPECTED_COLOR);
1865 | await expect(component).toHaveCSS("border-left-width", UPDATED);
1866 | await expect(component).toHaveCSS("border-left-style", EXPECTED_STYLE);
1867 | });
1868 |
1869 | test("theme borderRightWidth overrides border width", async ({
1870 | initTestBed,
1871 | createAvatarDriver,
1872 | }) => {
1873 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1874 | const EXPECTED_WIDTH = "5px";
1875 | const EXPECTED_STYLE = "dotted";
1876 | const UPDATED = "12px";
1877 |
1878 | await initTestBed('<Avatar name="Tim"/>', {
1879 | testThemeVars: {
1880 | "borderRightWidth-Avatar": UPDATED,
1881 | "border-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1882 | },
1883 | });
1884 | const component = (await createAvatarDriver()).component;
1885 |
1886 | await expect(component).toHaveCSS("border-top-color", EXPECTED_COLOR);
1887 | await expect(component).toHaveCSS("border-top-width", EXPECTED_WIDTH);
1888 | await expect(component).toHaveCSS("border-top-style", EXPECTED_STYLE);
1889 | await expect(component).toHaveCSS("border-right-color", EXPECTED_COLOR);
1890 | await expect(component).toHaveCSS("border-right-width", UPDATED);
1891 | await expect(component).toHaveCSS("border-right-style", EXPECTED_STYLE);
1892 | await expect(component).toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1893 | await expect(component).toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1894 | await expect(component).toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1895 | await expect(component).toHaveCSS("border-left-color", EXPECTED_COLOR);
1896 | await expect(component).toHaveCSS("border-left-width", EXPECTED_WIDTH);
1897 | await expect(component).toHaveCSS("border-left-style", EXPECTED_STYLE);
1898 | });
1899 |
1900 | test("theme borderVerticalWidth overrides border width", async ({
1901 | initTestBed,
1902 | createAvatarDriver,
1903 | }) => {
1904 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1905 | const EXPECTED_WIDTH = "5px";
1906 | const EXPECTED_STYLE = "dotted";
1907 | const UPDATED = "12px";
1908 |
1909 | await initTestBed('<Avatar name="Tim"/>', {
1910 | testThemeVars: {
1911 | "borderVerticalWidth-Avatar": UPDATED,
1912 | "border-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1913 | },
1914 | });
1915 | const component = (await createAvatarDriver()).component;
1916 |
1917 | await expect(component).toHaveCSS("border-top-color", EXPECTED_COLOR);
1918 | await expect(component).toHaveCSS("border-top-width", UPDATED);
1919 | await expect(component).toHaveCSS("border-top-style", EXPECTED_STYLE);
1920 | await expect(component).toHaveCSS("border-right-color", EXPECTED_COLOR);
1921 | await expect(component).toHaveCSS("border-right-width", EXPECTED_WIDTH);
1922 | await expect(component).toHaveCSS("border-right-style", EXPECTED_STYLE);
1923 | await expect(component).toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1924 | await expect(component).toHaveCSS("border-bottom-width", UPDATED);
1925 | await expect(component).toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1926 | await expect(component).toHaveCSS("border-left-color", EXPECTED_COLOR);
1927 | await expect(component).toHaveCSS("border-left-width", EXPECTED_WIDTH);
1928 | await expect(component).toHaveCSS("border-left-style", EXPECTED_STYLE);
1929 | });
1930 |
1931 | test("theme borderTopWidth overrides border width", async ({
1932 | initTestBed,
1933 | createAvatarDriver,
1934 | }) => {
1935 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1936 | const EXPECTED_WIDTH = "5px";
1937 | const EXPECTED_STYLE = "dotted";
1938 | const UPDATED = "12px";
1939 |
1940 | await initTestBed('<Avatar name="Tim"/>', {
1941 | testThemeVars: {
1942 | "borderTopWidth-Avatar": UPDATED,
1943 | "border-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1944 | },
1945 | });
1946 | const component = (await createAvatarDriver()).component;
1947 |
1948 | await expect(component).toHaveCSS("border-top-color", EXPECTED_COLOR);
1949 | await expect(component).toHaveCSS("border-top-width", UPDATED);
1950 | await expect(component).toHaveCSS("border-top-style", EXPECTED_STYLE);
1951 | await expect(component).toHaveCSS("border-right-color", EXPECTED_COLOR);
1952 | await expect(component).toHaveCSS("border-right-width", EXPECTED_WIDTH);
1953 | await expect(component).toHaveCSS("border-right-style", EXPECTED_STYLE);
1954 | await expect(component).toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1955 | await expect(component).toHaveCSS("border-bottom-width", EXPECTED_WIDTH);
1956 | await expect(component).toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1957 | await expect(component).toHaveCSS("border-left-color", EXPECTED_COLOR);
1958 | await expect(component).toHaveCSS("border-left-width", EXPECTED_WIDTH);
1959 | await expect(component).toHaveCSS("border-left-style", EXPECTED_STYLE);
1960 | });
1961 |
1962 | test("theme borderBottomWidth overrides border width", async ({
1963 | initTestBed,
1964 | createAvatarDriver,
1965 | }) => {
1966 | const EXPECTED_COLOR = "rgb(255, 0, 0)";
1967 | const EXPECTED_WIDTH = "5px";
1968 | const EXPECTED_STYLE = "dotted";
1969 | const UPDATED = "12px";
1970 |
1971 | await initTestBed('<Avatar name="Tim"/>', {
1972 | testThemeVars: {
1973 | "borderBottomWidth-Avatar": UPDATED,
1974 | "border-Avatar": `${EXPECTED_STYLE} ${EXPECTED_COLOR} ${EXPECTED_WIDTH}`,
1975 | },
1976 | });
1977 | const component = (await createAvatarDriver()).component;
1978 |
1979 | await expect(component).toHaveCSS("border-top-color", EXPECTED_COLOR);
1980 | await expect(component).toHaveCSS("border-top-width", EXPECTED_WIDTH);
1981 | await expect(component).toHaveCSS("border-top-style", EXPECTED_STYLE);
1982 | await expect(component).toHaveCSS("border-right-color", EXPECTED_COLOR);
1983 | await expect(component).toHaveCSS("border-right-width", EXPECTED_WIDTH);
1984 | await expect(component).toHaveCSS("border-right-style", EXPECTED_STYLE);
1985 | await expect(component).toHaveCSS("border-bottom-color", EXPECTED_COLOR);
1986 | await expect(component).toHaveCSS("border-bottom-width", UPDATED);
1987 | await expect(component).toHaveCSS("border-bottom-style", EXPECTED_STYLE);
1988 | await expect(component).toHaveCSS("border-left-color", EXPECTED_COLOR);
1989 | await expect(component).toHaveCSS("border-left-width", EXPECTED_WIDTH);
1990 | await expect(component).toHaveCSS("border-left-style", EXPECTED_STYLE);
1991 | });
1992 | });
1993 |
```