diff --git a/apps/public-docsite-v9-headless/.storybook/manager-head.html b/apps/public-docsite-v9-headless/.storybook/manager-head.html index 11beec81c25d49..730b487ec19bf7 100644 --- a/apps/public-docsite-v9-headless/.storybook/manager-head.html +++ b/apps/public-docsite-v9-headless/.storybook/manager-head.html @@ -140,4 +140,12 @@ [id*='accessibility-stories'] { display: none !important; } + + /* Target the brand image container */ + .sidebar-header a::after { + content: ' Headless Components'; + display: block; + font-size: 12px; + margin-left: 34px; + } diff --git a/apps/public-docsite-v9-headless/.storybook/preview.js b/apps/public-docsite-v9-headless/.storybook/preview.js index 3cdce74a824fb4..5185ce7b232f73 100644 --- a/apps/public-docsite-v9-headless/.storybook/preview.js +++ b/apps/public-docsite-v9-headless/.storybook/preview.js @@ -8,7 +8,7 @@ export const parameters = { options: { storySort: { method: 'alphabetical', - order: ['Introduction', 'Headless Components'], + order: ['Overview', ['Introduction', 'Getting Started', 'Accessibility'], 'Guides', 'Components', 'Concepts'], }, }, reactStorybookAddon: { diff --git a/apps/public-docsite-v9-headless/src/Accessibility.mdx b/apps/public-docsite-v9-headless/src/Accessibility.mdx new file mode 100644 index 00000000000000..b2e01e69edbeb2 --- /dev/null +++ b/apps/public-docsite-v9-headless/src/Accessibility.mdx @@ -0,0 +1,136 @@ +import { Meta } from '@storybook/addon-docs/blocks'; + + + +# Accessibility + +Fluent UI Headless provides accessible, interactive React components without opinionated styling. Headless components implement WAI-ARIA authoring practices, keyboard navigation patterns, and focus management across a broad range of browsers and assistive technologies. + +**Important:** Headless components provide accessibility infrastructure: ARIA attributes, roles, keyboard event handling, and focus management. They **do not provide visual styling**, so your design system must supply visible focus indicators, sufficient color contrast, and clear interactive state feedback. + +## Built In, And What You Own + +When building custom components with Fluent UI Headless, you own the visual accessibility layer: + +- **Visible focus indicators** — Use CSS to style `:focus-visible` with sufficient contrast and size +- **Color contrast** — Ensure text and interactive elements meet WCAG AA (or AAA) color contrast ratios +- **Visual feedback** — Provide clear indication of hover, active, disabled, and error states +- **Touch targets** — Make interactive elements at least 44×44px for mobile accessibility +- **Motion considerations** — Respect `prefers-reduced-motion` media query for users with vestibular disorders + +## WAI-ARIA Patterns + +Headless components implement standard WAI-ARIA patterns and apply appropriate roles, states, and properties: + +| Component | ARIA Pattern | Key Attributes | +| -------------- | ------------- | ---------------------------------------------------------------- | +| Button | Button / Link | `role="button"`, `aria-pressed` (toggle), `aria-disabled` | +| Checkbox | Checkbox | `role="checkbox"`, `aria-checked` | +| Radio Group | Radio Group | `role="radiogroup"`, `role="radio"`, `aria-checked` | +| Switch | Switch | `role="switch"`, `aria-checked` | +| Accordion | Accordion | `role="button"`, `aria-expanded`, `aria-controls` | +| Dialog | Dialog | `role="dialog"`, `aria-modal`, `aria-labelledby` | +| Tooltip | Tooltip | `role="tooltip"` | +| Tabs | Tab List | `role="tablist"`, `role="tab"`, `aria-selected`, `aria-controls` | +| Menu / Popover | Menu | `role="menu"`, `role="menuitem"` | + +**Note:** When a component is disabled but should remain focusable (for context), headless components set `aria-disabled="true"` while keeping the element keyboard-accessible. Style both `:disabled` pseudo-classes and `[aria-disabled="true"]` states. + +## Keyboard Navigation + +Fluent UI Headless components support keyboard interaction patterns consistent with WAI-ARIA authoring practices: + +- **Tab/Shift+Tab** — Move focus between components; focus management respects `tabindex` +- **Enter/Space** — Activate buttons, checkboxes, switches, and toggle buttons +- **Arrow Keys** — Navigate within components like radio groups, tab lists, menus, and sliders +- **Home/End** — Jump to first/last item in lists, tabs, and menus (where applicable) +- **Escape** — Close overlays (dialogs, popovers, menus, tooltips) + +Each component's keyboard behavior is identical to its styled counterpart in `@fluentui/react-components`. Check individual component docs for pattern-specific details, like arrow key direction for horizontal versus vertical components. + +## Accessible Labels + +Form controls and custom UI patterns require accessible names so screen readers can identify them: + +### Form Controls + +Use the native HTML `` element to associate text with form inputs: + +```tsx +Email address + +``` + +For headless ``, ``, ``, and `` components, wrap them with a `` component or manually associate a label: + +```tsx +import { Field } from '@fluentui/react-headless-components-preview/field'; +import { Input } from '@fluentui/react-headless-components-preview/input'; + + + +; +``` + +### Buttons and Icon-Only Controls + +Buttons must have visible or accessible text: + +```tsx +// ✅ Visible text +Save + +// ✅ aria-label for icon-only buttons +× + +// ❌ Avoid: unlabeled buttons + +``` + +### Custom Controls + +For complex components without standard labels, provide an accessible name using: + +- `aria-label` — Direct label text on the element +- `aria-labelledby` — Reference to another element's id +- `aria-describedby` — Additional description for context + +```tsx + + {/* component content */} + +Start typing to filter the list +``` + +## Focus Management + +Headless hooks automatically manage focus based on user interaction: + +- **Logical tab order** — Focus flows naturally through the page; modify with `tabindex` if needed +- **Focus on interaction** — When a user opens a menu or dialog, focus may move to the first menu item or a default action button +- **Disabled elements** — Disabled elements are removed from tab order (unless `disabledFocusable` prop is used) +- **Trapped focus** — Dialogs and overlays keep focus within their boundaries during keyboard navigation + +**Your responsibility:** Ensure focused elements are **visually obvious** with a `:focus-visible` style. Don't remove the default focus outline without providing an alternative visual indicator. + +## Testing Your Accessibility + +- **Keyboard navigation** — Tab through your interface; verify arrow keys work in appropriate components +- **Screen reader** — Test with NVDA (Windows), JAWS, or VoiceOver (macOS/iOS) +- **Color contrast** — Use tools like WebAIM or Axe to verify WCAG AA or AAA ratios +- **Focus visibility** — Ensure all interactive elements show a clear focus indicator +- **Responsive behavior** — Test touch interactions on mobile; verify 44×44px minimum target size +- **Motion sensitivity** — Test with `prefers-reduced-motion: reduce` enabled in your OS + +Fluent UI Headless components are tested across a wide spectrum of browsers, devices, and assistive technologies. The ARIA patterns are battle-tested; focus on ensuring your **custom styling layer** maintains accessibility. + +## Resources + +- [WAI-ARIA Authoring Practices Guide](https://www.w3.org/WAI/ARIA/apg/) +- [WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG21/quickref/) +- [WebAIM Color Contrast Checker](https://webaim.org/resources/contrastchecker/) +- [APCA (Advanced Perceptual Contrast Algorithm)](https://contrast-ratio.com/#%23ffffff-on-%23000000) diff --git a/apps/public-docsite-v9-headless/src/GettingStarted.mdx b/apps/public-docsite-v9-headless/src/GettingStarted.mdx new file mode 100644 index 00000000000000..9887ad19fe507d --- /dev/null +++ b/apps/public-docsite-v9-headless/src/GettingStarted.mdx @@ -0,0 +1,112 @@ +import { Meta } from '@storybook/addon-docs/blocks'; + + + +# Get started + +## Install the package + +```bash +npm install @fluentui/react-headless-components-preview +``` + +Fluent UI Headless ships as a single package and is tree-shakable, so your bundle includes only the components you import. + +You can also install with your preferred package manager: + +```bash +yarn add @fluentui/react-headless-components-preview +pnpm add @fluentui/react-headless-components-preview +``` + +## Build your first component + +Import a headless component and compose its subcomponents. Headless components provide ARIA attributes, keyboard handling, and interaction logic out of the box. + +You are responsible for the visual layer, including focus indicators, color contrast, and state styling. + +```jsx +import { + Accordion, + AccordionItem, + AccordionHeader, + AccordionPanel, +} from '@fluentui/react-headless-components-preview/accordion'; + +export function MyAccordion() { + return ( + + + Section 1 + Content goes here + + + ); +} +``` + +## Style with your framework + +Fluent UI Headless components are unstyled by default, so you can style them with any approach that fits your stack. + +```jsx +// With CSS modules +import styles from './MyAccordion.module.css'; + +Section 1; +``` + +```css +/* MyAccordion.module.css */ +.header { + padding: 0.75rem 1rem; + border-radius: 0.5rem; +} + +.header:focus-visible { + outline: 2px solid currentColor; + outline-offset: 2px; +} +``` + +```jsx +// With Tailwind + + Section 1 + +``` + +```jsx +// With styled-components +const StyledHeader = styled(AccordionHeader)` + padding: 0.75rem 1rem; + border-radius: 0.5rem; + + &:focus-visible { + outline: 2px solid currentColor; + outline-offset: 2px; + } +`; +``` + +## First-component accessibility check + +Before shipping, verify: + +- Keyboard navigation works with Tab, Shift+Tab, Enter, Space, and arrow keys where expected +- Focus indicators are clearly visible on every interactive element +- Text and UI states meet your target contrast standard (WCAG AA or AAA) +- Interactive targets are large enough for touch usage +- Screen readers announce labels and state changes correctly + +For detailed guidance, review the [Accessibility overview](/?path=/docs/overview-accessibility--docs). + +## Working with LLMs + +Each docs page has a "View as Markdown" link at the top. Share that URL with your AI assistant to provide clean, context-rich documentation for Fluent UI concepts and component APIs. + +Additionally, the "llms.txt" link in the "Handbook" sidebar helps assistants discover and navigate the docs more effectively. + +## Explore more + +Visit the [components documentation](/components) to see all available headless components and their APIs. diff --git a/apps/public-docsite-v9-headless/src/Introduction.mdx b/apps/public-docsite-v9-headless/src/Introduction.mdx index a9ea43fba150fd..2708ebd882d885 100644 --- a/apps/public-docsite-v9-headless/src/Introduction.mdx +++ b/apps/public-docsite-v9-headless/src/Introduction.mdx @@ -1,34 +1,86 @@ import { Meta } from '@storybook/addon-docs/blocks'; - +import pkg from '@fluentui/react-headless-components-preview/package.json'; -## Overview +import { Hero, Features, Feature, WhyGrid, WhyCard } from './Introduction'; +import hero from './assets/images/hero.svg'; +import composition from './assets/images/features-composition.svg'; +import primitives from './assets/images/features-primitives.svg'; +import accessibility from './assets/images/features-accessibility.svg'; +import whyControl from './assets/images/why-control.svg'; +import whyBrand from './assets/images/why-brand.svg'; +import whyAccessibility from './assets/images/why-accessibility.svg'; +import whyBundle from './assets/images/why-bundle.svg'; +import whyFlexibility from './assets/images/why-flexibility.svg'; +import whyBattleTested from './assets/images/why-battle-tested.svg'; -Fluent UI React Headless Components is a set of unstyled, accessible, and composable React components that implement the Fluent UI design patterns without prescribing any specific styling solution. + -These components provide the behavior, accessibility, and state management while giving you full control over the visual presentation using your preferred styling approach (e.g., Tailwind CSS, CSS Modules, or any other solution). + + Fluent UI Headless Components v{pkg.version} + -## Getting Started + -To get started with the headless components package in your project: +# Overview -1. Install the package using `npm` or `yarn`: +**Fluent UI Headless** provides accessible, interactive React components without opinionated styling. Perfect for teams building custom design systems, maintaining brand consistency, or integrating with existing design frameworks. - ``` - npm install @fluentui/react-headless-components-preview - ``` +## What you get -2. Import and use the components: +- **Behavior without styling** — Accessible primitives with full keyboard navigation and ARIA support +- **Interaction logic** — Pre-built state management, event handling, and user interactions +- **Zero visual opinions** — No CSS framework lock-in, style components however you want +- **Enterprise-ready** — WCAG 2.1 AA compliant out of the box +- **Framework agnostic** — Pure JavaScript logic you control completely - ```tsx - import { - Accordion, - AccordionItem, - AccordionHeader, - AccordionPanel, - } from '@fluentui/react-headless-components-preview'; - ``` +## Features -## Contributing + + + Build component hierarchies that match your design system. Slot-based architecture gives you complete control over + structure and content nesting. + + + Start from zero styling constraints. Use CSS-in-JS, CSS modules, Tailwind, or any styling approach without conflicts + or overrides. + + + WCAG 2.1 AA compliant with full keyboard support, screen reader optimization, and semantic HTML. Accessibility is + built in, not bolted on. + + -Refer to the main FluentUI [wiki](https://github.com/microsoft/fluentui/wiki) for detailed instructions on setup and contributing to the package. +## Why choose Fluent UI Headless + +Designed for flexibility, maintainability, and performance without sacrificing accessibility or developer experience. + + + + No predefined colors, spacing, or typography. You own every pixel of your design system implementation. + + + Integrate seamlessly with existing design systems. Apply your brand identity without fighting CSS specificity. + + + WCAG 2.1 AA compliant with keyboard navigation and screen reader support out of the box. + + + Only ship the styles you use. No unused CSS framework bloat in your production bundles. + + + React hooks-based architecture. Easy to adapt patterns to other frameworks if needed. + + + Leverages years of Fluent UI development. Proven interaction patterns for common components. + + + +## Perfect for + +- **Custom design systems** — Maintain complete design system control without style conflicts +- **Multi-brand applications** — Easy theming and brand switching without component rewrites +- **Enterprise products** — WCAG 2.1 AA compliance and mature accessibility patterns +- **Minimalist bundles** — Ship only the code you need, no unused styling framework +- **Design-first teams** — Designers control the visual expression, developers ensure accessibility +- **Micro-frontends** — Self-contained components that don't leak styles to other parts of your app diff --git a/apps/public-docsite-v9-headless/src/Introduction.styles.ts b/apps/public-docsite-v9-headless/src/Introduction.styles.ts new file mode 100644 index 00000000000000..8b61928b2ab146 --- /dev/null +++ b/apps/public-docsite-v9-headless/src/Introduction.styles.ts @@ -0,0 +1,196 @@ +'use client'; + +import { makeStyles } from '@griffel/react'; + +export const useIntroductionStyles = makeStyles({ + hero: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + paddingTop: 'var(--space-8)', + paddingRight: 0, + paddingBottom: 'var(--space-8)', + paddingLeft: 0, + marginBottom: 'var(--space-10)', + '@media (max-width: 768px)': { + paddingTop: 'var(--space-4)', + paddingRight: 'var(--space-2)', + paddingBottom: 'var(--space-4)', + paddingLeft: 'var(--space-2)', + }, + }, + heroImage: { + width: '100%', + height: 'auto', + maxHeight: '500px', + objectFit: 'cover', + objectPosition: 'center', + borderRadius: 'var(--radius-lg)', + borderTopStyle: 'solid', + borderRightStyle: 'solid', + borderBottomStyle: 'solid', + borderLeftStyle: 'solid', + borderTopWidth: '1px', + borderRightWidth: '1px', + borderBottomWidth: '1px', + borderLeftWidth: '1px', + borderTopColor: 'var(--border)', + borderRightColor: 'var(--border)', + borderBottomColor: 'var(--border)', + borderLeftColor: 'var(--border)', + }, + + features: { + display: 'grid', + gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', + gap: 'var(--space-6)', + marginBottom: 'var(--space-10)', + padding: 0, + '@media (max-width: 768px)': { + gridTemplateColumns: '1fr', + }, + }, + card: { + padding: 0, + backgroundColor: 'var(--bg)', + borderTopStyle: 'solid', + borderRightStyle: 'solid', + borderBottomStyle: 'solid', + borderLeftStyle: 'solid', + borderTopWidth: 'var(--stroke-thin)', + borderRightWidth: 'var(--stroke-thin)', + borderBottomWidth: 'var(--stroke-thin)', + borderLeftWidth: 'var(--stroke-thin)', + borderTopColor: 'var(--border)', + borderRightColor: 'var(--border)', + borderBottomColor: 'var(--border)', + borderLeftColor: 'var(--border)', + borderRadius: 'var(--radius-lg)', + transitionProperty: 'all', + transitionDuration: 'var(--duration-medium)', + transitionTimingFunction: 'var(--ease-standard)', + display: 'flex', + flexDirection: 'column', + gap: 0, + boxShadow: 'var(--shadow-1)', + overflow: 'hidden', + ':hover': { + borderTopColor: 'var(--accent)', + borderRightColor: 'var(--accent)', + borderBottomColor: 'var(--accent)', + borderLeftColor: 'var(--accent)', + boxShadow: 'var(--shadow-4)', + transform: 'translateY(-4px)', + }, + }, + cardImage: { + width: '100%', + height: 'auto', + minHeight: '200px', + objectFit: 'cover', + objectPosition: 'center', + borderBottomStyle: 'solid', + borderBottomWidth: '1px', + borderBottomColor: 'var(--border)', + }, + cardTitle: { + fontSize: '1.125rem', + fontWeight: 600, + color: 'var(--text)', + margin: 0, + paddingTop: 'var(--space-4)', + paddingRight: 'var(--space-6)', + paddingLeft: 'var(--space-6)', + paddingBottom: 0, + }, + cardText: { + fontSize: '1rem', + color: 'var(--text-muted)', + margin: 0, + paddingTop: 0, + paddingRight: 'var(--space-6)', + paddingBottom: 'var(--space-6)', + paddingLeft: 'var(--space-6)', + lineHeight: 1.6, + }, + + grid: { + display: 'grid', + gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))', + gap: 'var(--space-6)', + padding: 0, + backgroundColor: 'transparent', + borderRadius: 0, + '@media (max-width: 768px)': { + gridTemplateColumns: '1fr', + paddingTop: 'var(--space-4)', + paddingRight: 'var(--space-4)', + paddingBottom: 'var(--space-4)', + paddingLeft: 'var(--space-4)', + }, + }, + gridCard: { + padding: 0, + backgroundColor: 'var(--bg)', + borderTopStyle: 'solid', + borderRightStyle: 'solid', + borderBottomStyle: 'solid', + borderLeftStyle: 'solid', + borderTopWidth: 'var(--stroke-thin)', + borderRightWidth: 'var(--stroke-thin)', + borderBottomWidth: 'var(--stroke-thin)', + borderLeftWidth: 'var(--stroke-thin)', + borderTopColor: 'var(--border)', + borderRightColor: 'var(--border)', + borderBottomColor: 'var(--border)', + borderLeftColor: 'var(--border)', + borderRadius: 'var(--radius-lg)', + transitionProperty: 'all', + transitionDuration: 'var(--duration-medium)', + transitionTimingFunction: 'var(--ease-standard)', + boxShadow: 'var(--shadow-1)', + overflow: 'hidden', + display: 'flex', + flexDirection: 'column', + ':hover': { + borderTopColor: 'var(--accent)', + borderRightColor: 'var(--accent)', + borderBottomColor: 'var(--accent)', + borderLeftColor: 'var(--accent)', + boxShadow: 'var(--shadow-3)', + transform: 'translateY(-2px)', + }, + }, + gridImage: { + width: '100%', + height: '240px', + objectFit: 'cover', + objectPosition: 'center', + borderBottomStyle: 'solid', + borderBottomWidth: '1px', + borderBottomColor: 'var(--border)', + }, + gridTitle: { + fontSize: '1rem', + fontWeight: 600, + color: 'var(--text)', + marginTop: 0, + marginRight: 0, + marginBottom: 'var(--space-2)', + marginLeft: 0, + paddingTop: 'var(--space-4)', + paddingRight: 'var(--space-4)', + paddingLeft: 'var(--space-4)', + paddingBottom: 0, + }, + gridText: { + fontSize: '0.95rem', + color: 'var(--text-muted)', + margin: 0, + paddingTop: 0, + paddingRight: 'var(--space-4)', + paddingBottom: 'var(--space-4)', + paddingLeft: 'var(--space-4)', + lineHeight: 1.6, + }, +}); diff --git a/apps/public-docsite-v9-headless/src/Introduction.tsx b/apps/public-docsite-v9-headless/src/Introduction.tsx new file mode 100644 index 00000000000000..2c89189f4c27b9 --- /dev/null +++ b/apps/public-docsite-v9-headless/src/Introduction.tsx @@ -0,0 +1,66 @@ +'use client'; + +import * as React from 'react'; + +import { useIntroductionStyles } from './Introduction.styles'; + +export const Hero = ({ src, alt }: { src: string; alt: string }) => { + const styles = useIntroductionStyles(); + return ( + + + + ); +}; + +export const Features = ({ children }: { children: React.ReactNode }) => { + const styles = useIntroductionStyles(); + return {children}; +}; + +export const Feature = ({ + src, + alt, + title, + children, +}: { + src: string; + alt: string; + title: React.ReactNode; + children: React.ReactNode; +}) => { + const styles = useIntroductionStyles(); + return ( + + + {title} + {children} + + ); +}; + +export const WhyGrid = ({ children }: { children: React.ReactNode }) => { + const styles = useIntroductionStyles(); + return {children}; +}; + +export const WhyCard = ({ + src, + alt, + title, + children, +}: { + src: string; + alt: string; + title: React.ReactNode; + children: React.ReactNode; +}) => { + const styles = useIntroductionStyles(); + return ( + + + {title} + {children} + + ); +}; diff --git a/apps/public-docsite-v9-headless/src/assets/images/features-accessibility.svg b/apps/public-docsite-v9-headless/src/assets/images/features-accessibility.svg new file mode 100644 index 00000000000000..e2dbf445e0162b --- /dev/null +++ b/apps/public-docsite-v9-headless/src/assets/images/features-accessibility.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + Tab + + + + + + + + + + + Enter + + + + WCAG 2.1 AA + diff --git a/apps/public-docsite-v9-headless/src/assets/images/features-composition.svg b/apps/public-docsite-v9-headless/src/assets/images/features-composition.svg new file mode 100644 index 00000000000000..9a763e07e09701 --- /dev/null +++ b/apps/public-docsite-v9-headless/src/assets/images/features-composition.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/public-docsite-v9-headless/src/assets/images/features-primitives.svg b/apps/public-docsite-v9-headless/src/assets/images/features-primitives.svg new file mode 100644 index 00000000000000..f3addb428f67d9 --- /dev/null +++ b/apps/public-docsite-v9-headless/src/assets/images/features-primitives.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + unstyled + diff --git a/apps/public-docsite-v9-headless/src/assets/images/hero.svg b/apps/public-docsite-v9-headless/src/assets/images/hero.svg new file mode 100644 index 00000000000000..28a235c1878cfb --- /dev/null +++ b/apps/public-docsite-v9-headless/src/assets/images/hero.svg @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Styles + + + + + + + + + + + HEADLESS + + + + + + + root + + + + + + + + + + + + + icon? + + + + + label + + + + + + + keyboard + + + aria + + + focus + + + state + + + events + + + wcag 2.1 aa + + + Headless Components + + + = + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Your Design System + diff --git a/apps/public-docsite-v9-headless/src/assets/images/why-accessibility.svg b/apps/public-docsite-v9-headless/src/assets/images/why-accessibility.svg new file mode 100644 index 00000000000000..6501d21d21e143 --- /dev/null +++ b/apps/public-docsite-v9-headless/src/assets/images/why-accessibility.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/apps/public-docsite-v9-headless/src/assets/images/why-battle-tested.svg b/apps/public-docsite-v9-headless/src/assets/images/why-battle-tested.svg new file mode 100644 index 00000000000000..da3e0fdcb1419e --- /dev/null +++ b/apps/public-docsite-v9-headless/src/assets/images/why-battle-tested.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + logic + diff --git a/apps/public-docsite-v9-headless/src/assets/images/why-brand.svg b/apps/public-docsite-v9-headless/src/assets/images/why-brand.svg new file mode 100644 index 00000000000000..420b46bfff1c39 --- /dev/null +++ b/apps/public-docsite-v9-headless/src/assets/images/why-brand.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/apps/public-docsite-v9-headless/src/assets/images/why-bundle.svg b/apps/public-docsite-v9-headless/src/assets/images/why-bundle.svg new file mode 100644 index 00000000000000..38a599a4835600 --- /dev/null +++ b/apps/public-docsite-v9-headless/src/assets/images/why-bundle.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/apps/public-docsite-v9-headless/src/assets/images/why-control.svg b/apps/public-docsite-v9-headless/src/assets/images/why-control.svg new file mode 100644 index 00000000000000..146ae4b84c12a3 --- /dev/null +++ b/apps/public-docsite-v9-headless/src/assets/images/why-control.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/apps/public-docsite-v9-headless/src/assets/images/why-flexibility.svg b/apps/public-docsite-v9-headless/src/assets/images/why-flexibility.svg new file mode 100644 index 00000000000000..e0cfa8f18793b4 --- /dev/null +++ b/apps/public-docsite-v9-headless/src/assets/images/why-flexibility.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + R + + V + + TS + + ++ + diff --git a/change/@fluentui-web-components-f10b25db-f9c4-4ce4-a77b-5c3bdf81283e.json b/change/@fluentui-web-components-f10b25db-f9c4-4ce4-a77b-5c3bdf81283e.json new file mode 100644 index 00000000000000..0da3e221a3ffce --- /dev/null +++ b/change/@fluentui-web-components-f10b25db-f9c4-4ce4-a77b-5c3bdf81283e.json @@ -0,0 +1,7 @@ +{ + "type": "none", + "comment": "chore: use component tag names in playwright tests", + "packageName": "@fluentui/web-components", + "email": "863023+radium-v@users.noreply.github.com", + "dependentChangeType": "none" +} diff --git a/packages/react-components/react-headless-components-preview/stories/.storybook/logo.svg b/packages/react-components/react-headless-components-preview/stories/.storybook/logo.svg new file mode 100644 index 00000000000000..226d3cbc4bf594 --- /dev/null +++ b/packages/react-components/react-headless-components-preview/stories/.storybook/logo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/react-components/react-headless-components-preview/stories/.storybook/theme.js b/packages/react-components/react-headless-components-preview/stories/.storybook/theme.js index 2d88b7f13bb96a..b58842d056f06e 100644 --- a/packages/react-components/react-headless-components-preview/stories/.storybook/theme.js +++ b/packages/react-components/react-headless-components-preview/stories/.storybook/theme.js @@ -1,4 +1,5 @@ import { create } from 'storybook/theming'; +import brandImage from './logo.svg'; /** * Custom Storybook chrome for the headless components docsite. @@ -47,6 +48,7 @@ const theme = create({ inputTextColor: '#0a0a0a', inputBorderRadius: 8, // --radius-md + brandImage, brandTitle: 'Fluent UI Headless Components', brandUrl: 'https://github.com/microsoft/fluentui/tree/master/packages/react-components/react-headless-components-preview', diff --git a/packages/react-components/react-headless-components-preview/stories/README.md b/packages/react-components/react-headless-components-preview/stories/README.md index 30edaef2b08741..003e448e761c8d 100644 --- a/packages/react-components/react-headless-components-preview/stories/README.md +++ b/packages/react-components/react-headless-components-preview/stories/README.md @@ -77,7 +77,7 @@ import classes from './my-component.module.css'; export { Default } from './MyComponentDefault.stories'; export default { - title: 'Headless Components/MyComponent', + title: 'Components/MyComponent', component: MyComponent, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Accordion/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Accordion/index.stories.tsx index a67b98651d01b9..e63b01615ed80e 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Accordion/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Accordion/index.stories.tsx @@ -10,7 +10,7 @@ export { Default } from './AccordionDefault.stories'; export { Collapsible } from './AccordionCollapsible.stories'; export default { - title: 'Headless Components/Accordion', + title: 'Components/Accordion', component: Accordion, subcomponents: { AccordionHeader, AccordionItem, AccordionPanel }, parameters: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Avatar/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Avatar/index.stories.tsx index fabf13de0d5f84..d72e519fc2f831 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Avatar/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Avatar/index.stories.tsx @@ -4,7 +4,7 @@ import descriptionMd from './AvatarDescription.md'; export { Default } from './AvatarDefault.stories'; export default { - title: 'Headless Components/Avatar', + title: 'Components/Avatar', component: Avatar, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Badge/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Badge/index.stories.tsx index 4fc5def4b0aa65..0051f90d790eb2 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Badge/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Badge/index.stories.tsx @@ -4,7 +4,7 @@ import descriptionMd from './BadgeDescription.md'; export { Default } from './BadgeDefault.stories'; export default { - title: 'Headless Components/Badge', + title: 'Components/Badge', component: Badge, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/index.stories.tsx index eff2b479f922e0..4bd02ce3ba79eb 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/index.stories.tsx @@ -9,7 +9,7 @@ import descriptionMd from './BreadcrumbDescription.md'; export { Default } from './BreadcrumbDefault.stories'; export default { - title: 'Headless Components/Breadcrumb', + title: 'Components/Breadcrumb', component: Breadcrumb, subcomponents: { BreadcrumbItem, BreadcrumbButton, BreadcrumbDivider }, parameters: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Button/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Button/index.stories.tsx index b8e9ad54c2e8e7..4eada1f1b7ee0a 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Button/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Button/index.stories.tsx @@ -4,7 +4,7 @@ import descriptionMd from './ButtonDescription.md'; export { Default } from './ButtonDefault.stories'; export default { - title: 'Headless Components/Button', + title: 'Components/Button', component: Button, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Card/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Card/index.stories.tsx index ca0505ef173940..1209367a261413 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Card/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Card/index.stories.tsx @@ -6,7 +6,7 @@ export { Selectable } from './CardSelectable.stories'; export { Disabled } from './CardDisabled.stories'; export default { - title: 'Headless Components/Card', + title: 'Components/Card', component: Card, subcomponents: { CardHeader, CardPreview, CardFooter }, parameters: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Checkbox/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Checkbox/index.stories.tsx index bd41f10e2e73da..92f21b32f3828d 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Checkbox/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Checkbox/index.stories.tsx @@ -4,7 +4,7 @@ import descriptionMd from './CheckboxDescription.md'; export { Default } from './CheckboxDefault.stories'; export default { - title: 'Headless Components/Checkbox', + title: 'Components/Checkbox', component: Checkbox, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/index.stories.tsx index 73e33580a16fc8..498fe9f2b21cdb 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/index.stories.tsx @@ -14,7 +14,7 @@ export { FlippingInline } from './PositioningFlippingInline.stories'; export { FlippingCorner } from './PositioningFlippingCorner.stories'; export default { - title: 'Headless Concepts/Positioning', + title: 'Concepts/Positioning', component: Positioning, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/index.stories.tsx index 0adc7e687f13d6..d56ff90f682b4d 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/index.stories.tsx @@ -18,7 +18,7 @@ export { Nested } from './DialogNested.stories'; export { NoTrigger } from './DialogNoTrigger.stories'; export default { - title: 'Headless Components/Dialog', + title: 'Components/Dialog', component: Dialog, subcomponents: { DialogSurface, DialogTrigger, DialogTitle, DialogBody, DialogActions }, parameters: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Divider/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Divider/index.stories.tsx index b0c88506e768f6..931cf531f7c1c6 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Divider/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Divider/index.stories.tsx @@ -5,7 +5,7 @@ export { Default } from './DividerDefault.stories'; export { Vertical } from './DividerVertical.stories'; export default { - title: 'Headless Components/Divider', + title: 'Components/Divider', component: Divider, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Drawer/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Drawer/index.stories.tsx index 1ac4f6e7132e14..764a795148bb54 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Drawer/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Drawer/index.stories.tsx @@ -14,7 +14,7 @@ export { Default } from './DefaultDrawer.stories'; export { Inline } from './InlineDrawer.stories'; export default { - title: 'Headless Components/Drawer', + title: 'Components/Drawer', component: Drawer, subcomponents: { OverlayDrawer, diff --git a/packages/react-components/react-headless-components-preview/stories/src/Field/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Field/index.stories.tsx index 20e119b040b760..7cacfe56899b80 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Field/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Field/index.stories.tsx @@ -4,7 +4,7 @@ import descriptionMd from './FieldDescription.md'; export { Default } from './FieldDefault.stories'; export default { - title: 'Headless Components/Field', + title: 'Components/Field', component: Field, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Image/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Image/index.stories.tsx index f941af327c28c5..115e24277f554a 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Image/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Image/index.stories.tsx @@ -5,7 +5,7 @@ import descriptionMd from './ImageDescription.md'; export { Default } from './ImageDefault.stories'; export default { - title: 'Headless Components/Image', + title: 'Components/Image', component: Image, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Input/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Input/index.stories.tsx index d7283184f4da77..fcc95edf9cfe90 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Input/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Input/index.stories.tsx @@ -5,7 +5,7 @@ export { Default } from './InputDefault.stories'; export { Basic } from './InputBasic.stories'; export default { - title: 'Headless Components/Input', + title: 'Components/Input', component: Input, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Link/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Link/index.stories.tsx index 277c38bb77beb4..72a688a718ff11 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Link/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Link/index.stories.tsx @@ -4,7 +4,7 @@ import descriptionMd from './LinkDescription.md'; export { Default } from './LinkDefault.stories'; export default { - title: 'Headless Components/Link', + title: 'Components/Link', component: Link, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/MessageBar/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/MessageBar/index.stories.tsx index 7f6943fba0b199..618aef5ebe4393 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/MessageBar/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/MessageBar/index.stories.tsx @@ -10,7 +10,7 @@ export { Default } from './MessageBarDefault.stories'; export { Intent } from './MessageBarIntent.stories'; export default { - title: 'Headless Components/MessageBar', + title: 'Components/MessageBar', component: MessageBar, subcomponents: { MessageBarBody, diff --git a/packages/react-components/react-headless-components-preview/stories/src/Persona/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Persona/index.stories.tsx index fcca2f6fc350b9..7811cd51a7082d 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Persona/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Persona/index.stories.tsx @@ -6,7 +6,7 @@ import descriptionMd from './PersonaDescription.md'; export { Default } from './PersonaDefault.stories'; export default { - title: 'Headless Components/Persona', + title: 'Components/Persona', component: Persona, subcomponent: { Avatar }, parameters: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Popover/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Popover/index.stories.tsx index b0ade7f4080f43..f29c8b72892160 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Popover/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Popover/index.stories.tsx @@ -14,7 +14,7 @@ export { Nested } from './PopoverNested.stories'; export { InternalUpdateContent } from './PopoverInternalUpdateContent.stories'; export default { - title: 'Headless Components/Popover', + title: 'Components/Popover', component: Popover, subcomponents: { PopoverTrigger, PopoverSurface }, parameters: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/index.stories.tsx index f351eddf85f4c7..ac5a04a7ef4125 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/index.stories.tsx @@ -4,7 +4,7 @@ import descriptionMd from './ProgressBarDescription.md'; export { Default } from './ProgressBarDefault.stories'; export default { - title: 'Headless Components/ProgressBar', + title: 'Components/ProgressBar', component: ProgressBar, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/index.stories.tsx index fa22cb5b007063..786fe343bcdd72 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/index.stories.tsx @@ -4,7 +4,7 @@ import descriptionMd from './RadioGroupDescription.md'; export { Default } from './RadioGroupDefault.stories'; export default { - title: 'Headless Components/RadioGroup', + title: 'Components/RadioGroup', component: RadioGroup, subcomponents: { Radio }, parameters: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Rating/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Rating/index.stories.tsx index 9c0fc57f4dc39a..495aad592921f4 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Rating/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Rating/index.stories.tsx @@ -4,7 +4,7 @@ import descriptionMd from './RatingDescription.md'; export { Default } from './RatingDefault.stories'; export default { - title: 'Headless Components/Rating', + title: 'Components/Rating', component: Rating, subcomponents: { RatingItem }, parameters: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/index.stories.tsx index 47cdbe210b3f17..366c16d652d69b 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/index.stories.tsx @@ -5,7 +5,7 @@ export { Default } from './RatingDisplayDefault.stories'; export { Compact } from './RatingDisplayCompact.stories'; export default { - title: 'Headless Components/RatingDisplay', + title: 'Components/RatingDisplay', component: RatingDisplay, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/SearchBox/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/SearchBox/index.stories.tsx index 89eb0a93dd14ee..43a6dac694865c 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/SearchBox/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/SearchBox/index.stories.tsx @@ -4,7 +4,7 @@ import descriptionMd from './SearchBoxDescription.md'; export { Default } from './SearchBoxDefault.stories'; export default { - title: 'Headless Components/SearchBox', + title: 'Components/SearchBox', component: SearchBox, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Select/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Select/index.stories.tsx index ab827fa08f0f9b..b7028aa521a495 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Select/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Select/index.stories.tsx @@ -4,7 +4,7 @@ import descriptionMd from './SelectDescription.md'; export { Default } from './SelectDefault.stories'; export default { - title: 'Headless Components/Select', + title: 'Components/Select', component: Select, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Skeleton/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Skeleton/index.stories.tsx index 635ae473eec33d..800bbab1294ea2 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Skeleton/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Skeleton/index.stories.tsx @@ -4,7 +4,7 @@ import descriptionMd from './SkeletonDescription.md'; export { Default } from './SkeletonDefault.stories'; export default { - title: 'Headless Components/Skeleton', + title: 'Components/Skeleton', component: Skeleton, subcomponents: { SkeletonItem }, parameters: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Slider/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Slider/index.stories.tsx index db39864751edce..963e66f0f00a34 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Slider/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Slider/index.stories.tsx @@ -4,7 +4,7 @@ import descriptionMd from './SliderDescription.md'; export { Default } from './SliderDefault.stories'; export default { - title: 'Headless Components/Slider', + title: 'Components/Slider', component: Slider, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/SpinButton/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/SpinButton/index.stories.tsx index 1536ca236f68bd..9a9f6cf0f57577 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/SpinButton/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/SpinButton/index.stories.tsx @@ -4,7 +4,7 @@ import descriptionMd from './SpinButtonDescription.md'; export { Default } from './SpinButtonDefault.stories'; export default { - title: 'Headless Components/SpinButton', + title: 'Components/SpinButton', component: SpinButton, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Spinner/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Spinner/index.stories.tsx index 6c59a6ce188e22..b3717d6d6721cc 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Spinner/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Spinner/index.stories.tsx @@ -5,7 +5,7 @@ export { Default } from './SpinnerDefault.stories'; export { Labels } from './SpinnerLabels.stories'; export default { - title: 'Headless Components/Spinner', + title: 'Components/Spinner', component: Spinner, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Switch/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Switch/index.stories.tsx index a1d5edf795237c..24e6330243b109 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Switch/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Switch/index.stories.tsx @@ -4,7 +4,7 @@ import descriptionMd from './SwitchDescription.md'; export { Default } from './SwitchDefault.stories'; export default { - title: 'Headless Components/Switch', + title: 'Components/Switch', component: Switch, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/TabList/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/TabList/index.stories.tsx index 7b8c4d85ac5e48..3ff4d754c065a0 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/TabList/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/TabList/index.stories.tsx @@ -4,7 +4,7 @@ import descriptionMd from './TabListDescription.md'; export { Default } from './TabListDefault.stories'; export default { - title: 'Headless Components/TabList', + title: 'Components/TabList', component: TabList, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Textarea/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Textarea/index.stories.tsx index f9c7febdffffa5..83514914912dd4 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Textarea/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Textarea/index.stories.tsx @@ -4,7 +4,7 @@ import descriptionMd from './TextareaDescription.md'; export { Default } from './TextareaDefault.stories'; export default { - title: 'Headless Components/Textarea', + title: 'Components/Textarea', component: Textarea, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/index.stories.tsx index 76ea4d5fea976c..8dbad5744e32ae 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/index.stories.tsx @@ -4,7 +4,7 @@ import descriptionMd from './ToggleButtonDescription.md'; export { Default } from './ToggleButtonDefault.stories'; export default { - title: 'Headless Components/ToggleButton', + title: 'Components/ToggleButton', component: ToggleButton, parameters: { docs: { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/index.stories.tsx index 66b4fdd6a11350..2e6891a6c4982f 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/index.stories.tsx @@ -15,7 +15,7 @@ export { Toggle } from './ToolbarToggleButton.stories'; export { RadioButton } from './ToolbarRadioButton.stories'; export default { - title: 'Headless Components/Toolbar', + title: 'Components/Toolbar', component: Toolbar, subcomponents: { ToolbarButton, diff --git a/packages/react-components/react-headless-components-preview/stories/src/Tooltip/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Tooltip/index.stories.tsx index e4b5e33f923cb5..28a0961d476ad3 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Tooltip/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Tooltip/index.stories.tsx @@ -11,7 +11,7 @@ export { RelationshipLabel } from './TooltipRelationshipLabel.stories'; export { RelationshipDescription } from './TooltipRelationshipDescription.stories'; export default { - title: 'Headless Components/Tooltip', + title: 'Components/Tooltip', component: Tooltip, parameters: { docs: { diff --git a/packages/web-components/playwright.config.ts b/packages/web-components/playwright.config.ts index 91b2f75a52d273..dd6a17dc3b0571 100644 --- a/packages/web-components/playwright.config.ts +++ b/packages/web-components/playwright.config.ts @@ -8,6 +8,9 @@ const config: PlaywrightTestConfig = { timeout: process.env.CI ? 10000 : 30000, use: { baseURL: 'http://localhost:5173', + contextOptions: { + reducedMotion: 'reduce', + }, }, projects: [ { @@ -20,7 +23,10 @@ const config: PlaywrightTestConfig = { }, { name: 'webkit', - use: { ...devices['Desktop Safari'] }, + use: { + ...devices['Desktop Safari'], + deviceScaleFactor: 1, + }, }, ], webServer: { diff --git a/packages/web-components/src/accordion-item/accordion-item.spec.ts b/packages/web-components/src/accordion-item/accordion-item.spec.ts index bdfbdd8761a1b4..24c38b413f461e 100644 --- a/packages/web-components/src/accordion-item/accordion-item.spec.ts +++ b/packages/web-components/src/accordion-item/accordion-item.spec.ts @@ -1,12 +1,13 @@ import { expect, test } from '../../test/playwright/index.js'; +import { tagName as AccordionTagName } from '../accordion/accordion.options.js'; import { AccordionItem } from './accordion-item.js'; -import { AccordionItemSize } from './accordion-item.options.js'; +import { AccordionItemSize, tagName } from './accordion-item.options.js'; test.describe('Accordion item', () => { test.use({ innerHTML: 'Hello, World!', - tagName: 'fluent-accordion-item', - waitFor: ['fluent-accordion-item'], + tagName, + waitFor: [AccordionTagName], }); test('should create with document.createElement()', async ({ page, fastPage }) => { @@ -18,9 +19,9 @@ test.describe('Accordion item', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-accordion-item'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -29,12 +30,12 @@ test.describe('Accordion item', () => { const { element } = fastPage; await fastPage.setTemplate(/* html */ ` - - + <${AccordionTagName}> + <${tagName}> Heading 1 Content 1 - - + ${tagName}> + ${AccordionTagName}> `); await expect(element).toHaveJSProperty('headinglevel', 2); @@ -78,7 +79,7 @@ test.describe('Accordion item', () => { fastPage, }) => { const { element } = fastPage; - const button = fastPage.element.locator('button'); + const button = element.locator('button'); await fastPage.setTemplate({ attributes: { disabled: true } }); @@ -95,11 +96,11 @@ test.describe('Accordion item', () => { const { element } = fastPage; await fastPage.setTemplate(/* html */ ` - - Item 1 - Item 2 - Item 3 - + <${AccordionTagName}> + <${tagName}>Item 1${tagName}> + <${tagName} disabled>Item 2${tagName}> + <${tagName}>Item 3${tagName}> + ${AccordionTagName}> `); const firstItem = element.nth(0); const secondItem = element.nth(1); diff --git a/packages/web-components/src/accordion/accordion.spec.ts b/packages/web-components/src/accordion/accordion.spec.ts index 679ca8713b291a..1c6ce16e4a0758 100644 --- a/packages/web-components/src/accordion/accordion.spec.ts +++ b/packages/web-components/src/accordion/accordion.spec.ts @@ -1,9 +1,21 @@ import { expect, test } from '../../test/playwright/index.js'; +import { tagName as AccordionItemTagName } from '../accordion-item/accordion-item.options.js'; +import { tagName } from './accordion.options.js'; test.describe('Accordion', () => { test.use({ - innerHTML: 'Hello, World!', - tagName: 'fluent-accordion', + tagName, + innerHTML: /* html */ ` + <${AccordionItemTagName} tabindex="0"> + Heading 1 + Content 1 + ${AccordionItemTagName}> + <${AccordionItemTagName} tabindex="0"> + Heading 2 + Content 2 + ${AccordionItemTagName}> + `, + waitFor: [AccordionItemTagName], }); test('should create with document.createElement()', async ({ page, fastPage }) => { @@ -15,9 +27,9 @@ test.describe('Accordion', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-accordion'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -27,16 +39,6 @@ test.describe('Accordion', () => { await fastPage.setTemplate({ attributes: { 'expand-mode': 'multi' }, - innerHTML: /* html */ ` - - Heading 1 - Content 1 - - - Heading 2 - Content 2 - - `, }); await expect(element).toHaveAttribute('expand-mode', 'multi'); @@ -44,22 +46,12 @@ test.describe('Accordion', () => { test('should open/close appropriate accordion items on Enter key in single expand mode', async ({ fastPage }) => { const { element } = fastPage; - const accordionItems = element.locator('fluent-accordion-item'); + const accordionItems = element.locator(AccordionItemTagName); const firstItem = accordionItems.nth(0); const secondItem = accordionItems.nth(1); await fastPage.setTemplate({ attributes: { 'expand-mode': 'single' }, - innerHTML: /* html */ ` - - Heading 1 - Content 1 - - - Heading 1 - Content 1 - - `, }); await expect(firstItem).toHaveAttribute('expanded'); @@ -81,22 +73,12 @@ test.describe('Accordion', () => { test('should open/close appropriate accordion items on Enter key in multi expand mode', async ({ fastPage }) => { const { element } = fastPage; - const accordionItems = element.locator('fluent-accordion-item'); + const accordionItems = element.locator(AccordionItemTagName); const firstItem = accordionItems.nth(0); const secondItem = accordionItems.nth(1); await fastPage.setTemplate({ attributes: { 'expand-mode': 'multi' }, - innerHTML: /* html */ ` - - Heading 1 - Content 1 - - - Heading 1 - Content 1 - - `, }); await expect(firstItem).toHaveJSProperty('expanded', false); @@ -124,16 +106,6 @@ test.describe('Accordion', () => { const { element } = fastPage; await fastPage.setTemplate({ attributes: { 'expand-mode': 'single' }, - innerHTML: /* html */ ` - - Heading 1 - Content 1 - - - Heading 2 - Content 2 - - `, }); await expect(element).toHaveAttribute('expand-mode', 'single'); @@ -144,18 +116,7 @@ test.describe('Accordion', () => { }) => { const { element } = fastPage; - await fastPage.setTemplate({ - innerHTML: /* html */ ` - - Heading 1 - Content 1 - - - Heading 2 - Content 2 - - `, - }); + await fastPage.setTemplate(); await expect(element).toHaveJSProperty('expandmode', 'multi'); @@ -164,20 +125,10 @@ test.describe('Accordion', () => { test('should expand/collapse items when clicked in multi mode', async ({ fastPage }) => { const { element } = fastPage; - const items = element.locator('fluent-accordion-item'); + const items = element.locator(AccordionItemTagName); await fastPage.setTemplate({ attributes: { 'expand-mode': 'multi' }, - innerHTML: /* html */ ` - - Heading 1 - Content 1 - - - Heading 2 - Content 2 - - `, }); await items.nth(0).click(); @@ -191,22 +142,12 @@ test.describe('Accordion', () => { test('should only have one expanded item in single mode', async ({ fastPage }) => { const { element } = fastPage; - const items = element.locator('fluent-accordion-item'); + const items = element.locator(AccordionItemTagName); const firstItem = items.nth(0); const secondItem = items.nth(1); await fastPage.setTemplate({ attributes: { 'expand-mode': 'single' }, - innerHTML: /* html */ ` - - Heading 1 - Content 1 - - - Heading 2 - Content 2 - - `, }); await firstItem.click(); @@ -229,19 +170,9 @@ test.describe('Accordion', () => { await fastPage.setTemplate({ attributes: { 'expand-mode': 'single' }, - innerHTML: /* html */ ` - - Heading 1 - Content 1 - - - Heading 2 - Content 2 - - `, }); - const items = element.locator('fluent-accordion-item'); + const items = element.locator(AccordionItemTagName); const firstItem = items.nth(0); @@ -272,19 +203,9 @@ test.describe('Accordion', () => { await fastPage.setTemplate({ attributes: { 'expand-mode': 'single' }, - innerHTML: /* html */ ` - - Heading 1 - Content 1 - - - Heading 2 - Content 2 - - `, }); - const items = element.locator('fluent-accordion-item'); + const items = element.locator(AccordionItemTagName); const firstItem = items.nth(0); @@ -310,19 +231,9 @@ test.describe('Accordion', () => { attributes: { 'expand-mode': 'single', }, - innerHTML: /* html */ ` - - Heading 1 - Content 1 - - - Heading 2 - Content 2 - - `, }); - const items = element.locator('fluent-accordion-item'); + const items = element.locator(AccordionItemTagName); const firstItem = items.nth(0); @@ -347,22 +258,22 @@ test.describe('Accordion', () => { 'expand-mode': 'single', }, innerHTML: /* html */ ` - + <${AccordionItemTagName}> Heading 1 Content 1 - - + ${AccordionItemTagName}> + <${AccordionItemTagName} expanded> Heading 2 Content 2 - - + ${AccordionItemTagName}> + <${AccordionItemTagName} expanded> Heading 3 Content 2 - + ${AccordionItemTagName}> `, }); - const items = element.locator('fluent-accordion-item'); + const items = element.locator(AccordionItemTagName); const firstItem = items.nth(0); @@ -385,22 +296,22 @@ test.describe('Accordion', () => { 'expand-mode': 'single', }, innerHTML: /* html */ ` - + <${AccordionItemTagName}> Heading 1 Content 1 - - + ${AccordionItemTagName}> + <${AccordionItemTagName} expanded disabled> Heading 2 Content 2 - - + ${AccordionItemTagName}> + <${AccordionItemTagName} expanded> Heading 3 Content 2 - + ${AccordionItemTagName}> `, }); - const items = element.locator('fluent-accordion-item'); + const items = element.locator(AccordionItemTagName); const firstItem = items.nth(0); @@ -431,18 +342,18 @@ test.describe('Accordion', () => { await fastPage.setTemplate({ attributes: { 'expand-mode': 'single' }, innerHTML: /* html */ ` - + <${AccordionItemTagName}> Accordion Item 1 Heading Accordion Item 1 Content - - + ${AccordionItemTagName}> + <${AccordionItemTagName}> Accordion Item 2 Heading - A checkbox as content - + A checkbox as content + ${AccordionItemTagName}> `, }); - const item = element.locator('fluent-accordion-item').nth(1); + const item = element.locator(AccordionItemTagName).nth(1); const button = item.locator('button[part="button"]'); @@ -450,7 +361,7 @@ test.describe('Accordion', () => { await expect(item).toHaveAttribute('expanded'); - const checkbox = item.locator('fluent-checkbox'); + const checkbox = item.locator('input[type="checkbox"]'); await checkbox.click(); diff --git a/packages/web-components/src/anchor-button/anchor-button.spec.ts b/packages/web-components/src/anchor-button/anchor-button.spec.ts index f0e0bd22bd21fe..e34999cea9260f 100644 --- a/packages/web-components/src/anchor-button/anchor-button.spec.ts +++ b/packages/web-components/src/anchor-button/anchor-button.spec.ts @@ -1,10 +1,10 @@ import { expect, test } from '../../test/playwright/index.js'; -import { AnchorButtonAppearance, AnchorButtonShape, AnchorButtonSize } from './anchor-button.options.js'; +import { AnchorButtonAppearance, AnchorButtonShape, AnchorButtonSize, tagName } from './anchor-button.options.js'; test.describe('Anchor Button', () => { test.use({ - innerHTML: 'Fluent Anchor Button', - tagName: 'fluent-anchor-button', + innerHTML: 'Anchor Button', + tagName, }); test('should create with document.createElement()', async ({ page, fastPage }) => { @@ -16,9 +16,9 @@ test.describe('Anchor Button', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-anchor-button'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); diff --git a/packages/web-components/src/avatar/avatar.spec.ts b/packages/web-components/src/avatar/avatar.spec.ts index 19c236542e9b0b..0556964a2eb83e 100644 --- a/packages/web-components/src/avatar/avatar.spec.ts +++ b/packages/web-components/src/avatar/avatar.spec.ts @@ -1,9 +1,9 @@ import { expect, test } from '../../test/playwright/index.js'; -import { AvatarAppearance, AvatarColor, AvatarSize } from './avatar.options.js'; +import { AvatarAppearance, AvatarColor, AvatarSize, tagName } from './avatar.options.js'; test.describe('Avatar', () => { test.use({ - tagName: 'fluent-avatar', + tagName, }); test('should create with document.createElement()', async ({ page, fastPage }) => { @@ -15,9 +15,9 @@ test.describe('Avatar', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-avatar'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -25,6 +25,8 @@ test.describe('Avatar', () => { test('should have a `role` of `img`', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('elementInternals.role', 'img'); }); @@ -37,13 +39,13 @@ test.describe('Avatar', () => { await expect(icon).toBeVisible(); await test.step('should NOT render the icon when empty elements are present in the default slot', async () => { - await fastPage.setTemplate({ innerHTML: `\n\n` }); + await fastPage.updateTemplate(element, { innerHTML: `\n\n` }); await expect(icon).toBeHidden(); }); await test.step('should retain comment nodes in the default slot when no name or initials are provided', async () => { - await fastPage.setTemplate({ innerHTML: `\n\n\n` }); + await fastPage.updateTemplate(element, { innerHTML: `\n\n\n` }); await expect(icon).toBeVisible(); }); @@ -102,7 +104,11 @@ test.describe('Avatar', () => { }); test('should have a data-color attribute of `neutral` when no color is provided', async ({ fastPage }) => { - await expect(fastPage.element).toHaveAttribute('data-color', 'neutral'); + const { element } = fastPage; + + await fastPage.setTemplate(); + + await expect(element).toHaveAttribute('data-color', 'neutral'); }); test('should add a data-color attribute of `brand` when `brand is provided as the color', async ({ fastPage }) => { @@ -124,9 +130,11 @@ test.describe('Avatar', () => { test('should set the `data-color` attribute to match the `color` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const color of Object.values(AvatarColor)) { await test.step(`should set the \`color\` property to \`${color}\``, async () => { - await fastPage.setTemplate({ attributes: { color } }); + await fastPage.updateTemplate(element, { attributes: { color } }); await expect(element).toHaveAttribute('color', color); @@ -143,9 +151,11 @@ test.describe('Avatar', () => { test('should set the `size` property to match the `size` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const size of Object.values(AvatarSize)) { await test.step(`should set the \`size\` property to \`${size}\``, async () => { - await fastPage.setTemplate({ attributes: { size: `${size}` } }); + await fastPage.updateTemplate(element, { attributes: { size: `${size}` } }); await expect(element).toHaveAttribute('size', `${size}`); @@ -157,9 +167,11 @@ test.describe('Avatar', () => { test('should set the `appearance` property to match the `appearance` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const appearance of Object.values(AvatarAppearance)) { await test.step(appearance, async () => { - await fastPage.setTemplate({ attributes: { appearance } }); + await fastPage.updateTemplate(element, { attributes: { appearance } }); await expect(element).toHaveJSProperty('appearance', appearance); diff --git a/packages/web-components/src/badge/badge.spec.ts b/packages/web-components/src/badge/badge.spec.ts index 4c84a7271f9782..df4ee10a746c44 100644 --- a/packages/web-components/src/badge/badge.spec.ts +++ b/packages/web-components/src/badge/badge.spec.ts @@ -1,9 +1,9 @@ import { expect, test } from '../../test/playwright/index.js'; -import { BadgeAppearance, BadgeColor, BadgeShape, BadgeSize } from './badge.options.js'; +import { BadgeAppearance, BadgeColor, BadgeShape, BadgeSize, tagName } from './badge.options.js'; test.describe('Badge', () => { test.use({ - tagName: 'fluent-badge', + tagName, innerHTML: 'Badge', }); @@ -16,9 +16,9 @@ test.describe('Badge', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-badge'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); diff --git a/packages/web-components/src/button/button.spec.ts b/packages/web-components/src/button/button.spec.ts index 77f66a0984cdb1..263a8282786752 100644 --- a/packages/web-components/src/button/button.spec.ts +++ b/packages/web-components/src/button/button.spec.ts @@ -1,8 +1,9 @@ import { expect, test } from '../../test/playwright/index.js'; +import { tagName } from './button.options.js'; test.describe('Button', () => { test.use({ - tagName: 'fluent-button', + tagName, innerHTML: 'Button', }); @@ -15,9 +16,9 @@ test.describe('Button', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-button'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -27,7 +28,7 @@ test.describe('Button', () => { await fastPage.setTemplate(/* html */ ` - Button + <${tagName}>Button${tagName}> `); @@ -44,7 +45,7 @@ test.describe('Button', () => { await fastPage.setTemplate(/* html */ ` - Button + <${tagName} type="button">Button${tagName}> `); @@ -61,7 +62,7 @@ test.describe('Button', () => { await fastPage.setTemplate(/* html */ ` - Button + <${tagName} type="reset">Button${tagName}> `); @@ -75,7 +76,7 @@ test.describe('Button', () => { await fastPage.setTemplate(/* html */ ` - Button + <${tagName} type="submit" name="bar" value="baz">Button${tagName}> `); @@ -92,7 +93,7 @@ test.describe('Button', () => { await fastPage.setTemplate(/* html */ ` - Button + <${tagName} type="submit" value="baz">Button${tagName}> `); @@ -104,6 +105,8 @@ test.describe('Button', () => { test('should be focusable by default', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await element.focus(); await expect(element).toBeFocused(); @@ -241,12 +244,13 @@ test.describe('Button', () => { } test('should NOT receive focus when the `tabindex` is manually set to -1', async ({ fastPage, page }) => { - const element = page.locator('fluent-button', { hasText: 'Not Focusable' }); - const focusable = page.locator('fluent-button', { hasText: 'Recieves Focus' }); + const { element } = fastPage; + const focusable = element.nth(0); + const notFocusable = element.nth(1); await fastPage.setTemplate(/* html */ ` - Recieves Focus - Not Focusable + <${tagName}>Recieves Focus${tagName}> + <${tagName} tabindex="-1">Not Focusable${tagName}> `); await focusable.focus(); @@ -255,7 +259,7 @@ test.describe('Button', () => { await focusable.press('Tab'); - await expect(element).not.toBeFocused(); + await expect(notFocusable).not.toBeFocused(); }); test('should focus the element when the `autofocus` attribute is present', async ({ fastPage }) => { @@ -271,7 +275,7 @@ test.describe('Button', () => { await fastPage.setTemplate(/* html */ ` - Submit Button + <${tagName} type="submit">Submit Button${tagName}> `); @@ -290,7 +294,7 @@ test.describe('Button', () => { await fastPage.setTemplate(/* html */ ` - Reset Button + <${tagName} type="reset">Reset Button${tagName}> `); @@ -315,7 +319,7 @@ test.describe('Button', () => { await fastPage.setTemplate(/* html */ ` - Reset Button + <${tagName} type="reset" disabled>Reset Button${tagName}> `); @@ -334,7 +338,7 @@ test.describe('Button', () => { await fastPage.setTemplate(/* html */ ` Unrelated Form - Submit Button + <${tagName} type="submit">Submit Button${tagName}> `); await element.click(); @@ -354,7 +358,7 @@ test.describe('Button', () => { Unrelated Form - Submit Button + <${tagName} type="reset">Submit Button${tagName}> `); await expect(input).toHaveValue(''); @@ -375,7 +379,7 @@ test.describe('Button', () => { await fastPage.setTemplate(/* html */ ` - Submit Button + <${tagName} type="submit" disabled>Submit Button${tagName}> `); @@ -395,7 +399,7 @@ test.describe('Button', () => { - Submit Button + <${tagName} type="submit" form="testform">Submit Button${tagName}> `); await expect(page).not.toHaveURL(/foo/); @@ -410,7 +414,7 @@ test.describe('Button', () => { await fastPage.setTemplate(/* html */ ` - Submit Button + <${tagName} type="submit" formaction="bar">Submit Button${tagName}> `); @@ -431,7 +435,7 @@ test.describe('Button', () => { - Submit Button + <${tagName} type="submit" form="testform" formaction="bar">Submit Button${tagName}> `); await element.click(); @@ -446,7 +450,7 @@ test.describe('Button', () => { await fastPage.setTemplate(/* html */ ` - Submit Button + <${tagName} type="submit" formmethod="post">Submit Button${tagName}> `); @@ -472,7 +476,7 @@ test.describe('Button', () => { - Submit Button + <${tagName} type="submit" form="testform" formmethod="post">Submit Button${tagName}> `); await expect(page).not.toHaveURL(/foo/); @@ -492,7 +496,7 @@ test.describe('Button', () => { await fastPage.setTemplate(/* html */ ` - Submit Button + <${tagName} type="submit" formenctype="plain/text">Submit Button${tagName}> `); @@ -514,7 +518,7 @@ test.describe('Button', () => { - Submit Button + <${tagName} type="submit" form="testform" formenctype="plain/text">Submit Button${tagName}> `); await expect(page).not.toHaveURL(/foo/); @@ -529,7 +533,7 @@ test.describe('Button', () => { await fastPage.setTemplate(/* html */ ` - Submit Button + <${tagName} type="submit" formtarget="_self">Submit Button${tagName}> `); @@ -551,7 +555,7 @@ test.describe('Button', () => { - Submit Button + <${tagName} type="submit" form="testform" formtarget="_self">Submit Button${tagName}> `); await expect(page).not.toHaveURL(/foo/); @@ -572,7 +576,7 @@ test.describe('Button', () => { await fastPage.setTemplate(/* html */ ` - Button + <${tagName} type="submit" formnovalidate>Button${tagName}> `); @@ -602,7 +606,7 @@ test.describe('Button', () => { - Button + <${tagName} type="submit" form="test-form" formnovalidate>Button${tagName}> `); await input.fill('foo'); @@ -620,13 +624,13 @@ test.describe('Button', () => { fastPage, page, }) => { - const button = page.locator('fluent-button'); + const button = page.locator(tagName); const input = page.locator('#text-input'); await fastPage.setTemplate(/* html */ ` - Button + <${tagName} type="submit">Button${tagName}> `); @@ -648,7 +652,7 @@ test.describe('Button', () => { fastPage, page, }) => { - const button = page.locator('fluent-button'); + const button = page.locator(tagName); const input = page.locator('#text-input'); await fastPage.setTemplate(/* html */ ` @@ -656,7 +660,7 @@ test.describe('Button', () => { - Button + <${tagName} type="submit" form="test-form">Button${tagName}> `); await input.fill('foo'); diff --git a/packages/web-components/src/checkbox/checkbox.spec.ts b/packages/web-components/src/checkbox/checkbox.spec.ts index 0abab1d376ac14..26fbbf93209be9 100644 --- a/packages/web-components/src/checkbox/checkbox.spec.ts +++ b/packages/web-components/src/checkbox/checkbox.spec.ts @@ -1,11 +1,11 @@ import { expect, test } from '../../test/playwright/index.js'; + import type { Checkbox } from './checkbox.js'; -import { CheckboxShape, CheckboxSize } from './checkbox.options.js'; +import { CheckboxShape, CheckboxSize, tagName } from './checkbox.options.js'; test.describe('Checkbox', () => { test.use({ - tagName: 'fluent-checkbox', - waitFor: ['fluent-button'], + tagName, }); test('should create with document.createElement()', async ({ page, fastPage }) => { @@ -17,33 +17,37 @@ test.describe('Checkbox', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-checkbox'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); test('should have a role of `checkbox`', async ({ fastPage }) => { - await expect(fastPage.element).toHaveJSProperty('elementInternals.role', 'checkbox'); + const { element } = fastPage; + + await fastPage.setTemplate(); + + await expect(element).toHaveJSProperty('elementInternals.role', 'checkbox'); }); test('should initialize to the initial value if no value property is set', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('value', 'on'); }); test('should set the `shape` property to match the `shape` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const shape of Object.values(CheckboxShape)) { await test.step(`should set the \`shape\` property to "${shape}"`, async () => { - await fastPage.setTemplate({ - attributes: { - shape, - }, - }); + await fastPage.updateTemplate(element, { attributes: { shape } }); await expect(element).toHaveAttribute('shape', shape); @@ -55,9 +59,11 @@ test.describe('Checkbox', () => { test('should set the `size` property to match the `size` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const size of Object.values(CheckboxSize)) { await test.step(`should set the \`size\` property to "${size}"`, async () => { - await fastPage.setTemplate({ attributes: { size: size } }); + await fastPage.updateTemplate(element, { attributes: { size } }); await expect(element).toHaveJSProperty('size', size); @@ -69,6 +75,8 @@ test.describe('Checkbox', () => { test('should set the `ariaChecked` property equal to the `checked` property', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('elementInternals.ariaChecked', 'false'); await element.evaluate((node: Checkbox) => { @@ -81,6 +89,8 @@ test.describe('Checkbox', () => { test('should NOT set a default `aria-required` value when `required` is not defined', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).not.toHaveAttribute('required'); await expect(element).not.toHaveAttribute('aria-required'); @@ -89,6 +99,8 @@ test.describe('Checkbox', () => { test('should be focusable by default', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await element.focus(); await expect(element).toBeFocused(); @@ -109,6 +121,8 @@ test.describe('Checkbox', () => { }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await element.evaluate((node: Checkbox) => { node.indeterminate = true; }); @@ -125,6 +139,8 @@ test.describe('Checkbox', () => { test('should set `indeterminate` to false when the `checked` state changes via click', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await element.evaluate((node: Checkbox) => { node.indeterminate = true; }); @@ -142,7 +158,7 @@ test.describe('Checkbox', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName}>${tagName}> `); @@ -176,22 +192,26 @@ test.describe('Checkbox', () => { await fastPage.setTemplate(''); - const value = await page.evaluate(expectedValue => { - const node = document.createElement('fluent-checkbox') as Checkbox; + const value = await page.evaluate( + ([expectedValue, tagName]) => { + const node = document.createElement(tagName) as Checkbox; - node.setAttribute('value', expectedValue); + node.setAttribute('value', expectedValue); - return node.value; - }, expectedValue); + return node.value; + }, + [expectedValue, tagName], + ); expect(value).toBe(expectedValue); }); test('should initialize to the provided `value` attribute when set post-connection', async ({ fastPage }) => { const { element } = fastPage; - const expectedValue = 'foobar'; + await fastPage.setTemplate(); + await element.evaluate((node: Checkbox, expectedValue) => { node.setAttribute('value', expectedValue); }, expectedValue); @@ -206,13 +226,16 @@ test.describe('Checkbox', () => { const expectedValue = 'foobar'; - const value = await page.evaluate(expectedValue => { - const node = document.createElement('fluent-checkbox') as Checkbox; + const value = await page.evaluate( + ([expectedValue, tagName]) => { + const node = document.createElement(tagName) as Checkbox; - node.value = expectedValue; + node.value = expectedValue; - return node.value; - }, expectedValue); + return node.value; + }, + [expectedValue, tagName], + ); expect(value).toBe(expectedValue); }); @@ -222,7 +245,7 @@ test.describe('Checkbox', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} required>${tagName}> `); @@ -234,7 +257,7 @@ test.describe('Checkbox', () => { await fastPage.setTemplate(/* html */ ` - checkbox + <${tagName} required>checkbox${tagName}> `); @@ -251,7 +274,7 @@ test.describe('Checkbox', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName}>${tagName}> `); @@ -276,7 +299,7 @@ test.describe('Checkbox', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName}>${tagName}> `); @@ -304,7 +327,7 @@ test.describe('Checkbox', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} required>${tagName}> `); @@ -334,7 +357,7 @@ test.describe('Checkbox', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} name="checkbox" value="foo">${tagName}> submit `); @@ -354,8 +377,8 @@ test.describe('Checkbox', () => { await fastPage.setTemplate(/* html */ ` - - + <${tagName} name="checkbox" value="foo">${tagName}> + <${tagName} name="checkbox" value="bar">${tagName}> submit `); @@ -373,13 +396,13 @@ test.describe('Checkbox', () => { page, }) => { const { element } = fastPage; - const submitButton = page.locator('fluent-button'); + const submitButton = page.locator('button[type="submit"]'); await fastPage.setTemplate(/* html */ ` Checkbox - - submit + <${tagName} required name="checkbox" id="checkbox">${tagName}> + submit `); diff --git a/packages/web-components/src/counter-badge/counter-badge.spec.ts b/packages/web-components/src/counter-badge/counter-badge.spec.ts index ff9cf98cd04b01..fc24a15e25f402 100644 --- a/packages/web-components/src/counter-badge/counter-badge.spec.ts +++ b/packages/web-components/src/counter-badge/counter-badge.spec.ts @@ -5,11 +5,12 @@ import { CounterBadgeColor, CounterBadgeShape, CounterBadgeSize, + tagName, } from './counter-badge.options.js'; test.describe('CounterBadge component', () => { test.use({ - tagName: 'fluent-counter-badge', + tagName, }); test('should create with document.createElement()', async ({ page, fastPage }) => { @@ -21,9 +22,9 @@ test.describe('CounterBadge component', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-counter-badge'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -174,9 +175,11 @@ test.describe('CounterBadge component', () => { test('should set the `shape` property to match the `shape` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const shape of Object.values(CounterBadgeShape)) { await test.step(`should set the \`shape\` property to \`${shape}\``, async () => { - await fastPage.setTemplate({ attributes: { shape } }); + await fastPage.updateTemplate(element, { attributes: { shape } }); await expect(element).toHaveAttribute('shape', shape); @@ -188,9 +191,11 @@ test.describe('CounterBadge component', () => { test('should set the `color` property to match the `color` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const color of Object.values(CounterBadgeColor)) { await test.step(`should set the \`color\` property to \`${color}\``, async () => { - await fastPage.setTemplate({ attributes: { color } }); + await fastPage.updateTemplate(element, { attributes: { color } }); await expect(element).toHaveAttribute('color', color); @@ -202,9 +207,11 @@ test.describe('CounterBadge component', () => { test('should set the `size` property to match the `size` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const size of Object.values(CounterBadgeSize)) { await test.step(`should set the \`size\` property to "${size}"`, async () => { - await fastPage.setTemplate({ attributes: { size } }); + await fastPage.updateTemplate(element, { attributes: { size } }); await expect(element).toHaveAttribute('size', size); @@ -216,9 +223,11 @@ test.describe('CounterBadge component', () => { test('should set the `appearance` property to match the `appearance` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const appearance of Object.values(CounterBadgeAppearance)) { await test.step(appearance, async () => { - await fastPage.setTemplate({ attributes: { appearance } }); + await fastPage.updateTemplate(element, { attributes: { appearance } }); await expect(element).toHaveJSProperty('appearance', appearance); diff --git a/packages/web-components/src/dialog/dialog.spec.ts b/packages/web-components/src/dialog/dialog.spec.ts index d5aaf22c9048ef..967a655033e40e 100644 --- a/packages/web-components/src/dialog/dialog.spec.ts +++ b/packages/web-components/src/dialog/dialog.spec.ts @@ -1,22 +1,24 @@ import type { Locator } from '@playwright/test'; import { expect, test } from '../../test/playwright/index.js'; +import { tagName as DialogBodyTagName } from '../dialog-body/dialog-body.options.js'; import type { Dialog } from './dialog.js'; +import { tagName } from './dialog.options.js'; test.describe('Dialog', () => { test.use({ - tagName: 'fluent-dialog', + tagName, innerHTML: /* html */ `Dialog Body`, - waitFor: ['fluent-button', 'fluent-dialog-body'], + waitFor: [DialogBodyTagName], }); async function getPointOutside(element: Locator) { // Get the bounding box of the element - const boundingBox = (await element.boundingBox()) as { x: number; y: number; width: number; height: number }; + const boundingBox = await element.boundingBox(); // Calculate a point outside the bounding box return { - x: boundingBox.x + boundingBox.width + 10, // 10 pixels to the right - y: boundingBox.y + boundingBox.height + 10, // 10 pixels below + x: boundingBox!.x + boundingBox!.width + 10, // 10 pixels to the right + y: boundingBox!.y + boundingBox!.height + 10, // 10 pixels below }; } @@ -29,9 +31,9 @@ test.describe('Dialog', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-dialog'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -40,6 +42,8 @@ test.describe('Dialog', () => { const { element } = fastPage; const content = element.locator('#content'); + await fastPage.setTemplate(); + await test.step('should show the dialog content when the dialog is shown', async () => { await expect(content).toBeHidden(); @@ -66,6 +70,8 @@ test.describe('Dialog', () => { const { element } = fastPage; const content = element.locator('#content'); + await fastPage.setTemplate(); + await element.evaluate((node: Dialog) => { node.show(); }); @@ -116,6 +122,8 @@ test.describe('Dialog', () => { const { element } = fastPage; const content = element.locator('#content'); + await fastPage.setTemplate(); + await element.evaluate((node: Dialog) => { node.setAttribute('type', 'alert'); node.show(); @@ -139,6 +147,8 @@ test.describe('Dialog', () => { const { element } = fastPage; const content = element.locator('#content'); + await fastPage.setTemplate(); + await element.evaluate((node: Dialog) => { node.show(); }); @@ -152,17 +162,17 @@ test.describe('Dialog', () => { test('should close after a button is slotted into the close slot and clicked', async ({ fastPage, page }) => { const { element } = fastPage; - const closeButton = element.locator('fluent-button[slot="close"]'); + const closeButton = element.locator('button[slot="close"]'); const content = element.locator('#content'); - await fastPage.setTemplate(/* html */ ` - - - Close - content - - - `); + await fastPage.setTemplate({ + innerHTML: /* html */ ` + <${DialogBodyTagName}> + Close + content + ${DialogBodyTagName}> + `, + }); await element.evaluate((node: Dialog) => { node.show(); @@ -177,17 +187,18 @@ test.describe('Dialog', () => { test('should NOT close after a slotted button is clicked', async ({ fastPage, page }) => { const { element } = fastPage; - const genericButton = element.locator('fluent-button'); + const genericButton = element.locator('button'); const content = element.locator('#content'); - await fastPage.setTemplate(/* html */ ` - - - Close - content - - - `); + await fastPage.setTemplate({ + attributes: { type: 'non-modal' }, + innerHTML: /* html */ ` + <${DialogBodyTagName}> + Close + content + ${DialogBodyTagName}> + `, + }); await element.evaluate((node: Dialog) => { node.show(); @@ -292,6 +303,8 @@ test.describe('Dialog', () => { const { element } = fastPage; const dialog = element.locator('dialog'); + await fastPage.setTemplate(); + await expect(dialog).not.toHaveAttribute('aria-labelledby'); await element.evaluate(node => { @@ -307,6 +320,8 @@ test.describe('Dialog', () => { const { element } = fastPage; const dialog = element.locator('dialog'); + await fastPage.setTemplate(); + await expect(dialog).not.toHaveAttribute('aria-describedby'); await element.evaluate(node => { @@ -322,6 +337,8 @@ test.describe('Dialog', () => { const { element } = fastPage; const dialog = element.locator('dialog'); + await fastPage.setTemplate(); + await expect(dialog).not.toHaveAttribute('aria-label'); await element.evaluate(node => { @@ -337,14 +354,14 @@ test.describe('Dialog', () => { const label = page.locator('label'); const input = page.locator('input'); - await fastPage.setTemplate(/* html */ ` - - + await fastPage.setTemplate({ + innerHTML: /* html */ ` + <${DialogBodyTagName} id="content"> Label - - - `); + ${DialogBodyTagName}> + `, + }); await element.evaluate((node: Dialog) => { node.show(); diff --git a/packages/web-components/src/divider/divider.spec.ts b/packages/web-components/src/divider/divider.spec.ts index 1683f67aebd357..035c94ad2ddbd9 100644 --- a/packages/web-components/src/divider/divider.spec.ts +++ b/packages/web-components/src/divider/divider.spec.ts @@ -1,9 +1,9 @@ import { expect, test } from '../../test/playwright/index.js'; import type { Divider } from './divider.js'; -import { DividerAlignContent, DividerAppearance, DividerOrientation, DividerRole } from './divider.options.js'; +import { DividerAlignContent, DividerAppearance, DividerOrientation, DividerRole, tagName } from './divider.options.js'; test.describe('Divider', () => { - test.use({ tagName: 'fluent-divider' }); + test.use({ tagName }); test('should create with document.createElement()', async ({ page, fastPage }) => { await fastPage.setTemplate(); @@ -14,9 +14,9 @@ test.describe('Divider', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-divider'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -24,15 +24,19 @@ test.describe('Divider', () => { test('should set a default `role` attribute of "separator"', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('elementInternals.role', 'separator'); }); test('should set the internal `role` property to match the `role` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const role of Object.values(DividerRole)) { await test.step(`role="${role}"`, async () => { - await fastPage.setTemplate({ attributes: { role } }); + await fastPage.updateTemplate(element, { attributes: { role } }); await expect(element).toHaveJSProperty('elementInternals.role', role); }); @@ -42,9 +46,11 @@ test.describe('Divider', () => { test('should set the `aria-orientation` attribute equal to the `orientation` value', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const orientation of Object.values(DividerOrientation)) { await test.step(`orientation="${orientation}"`, async () => { - await fastPage.setTemplate({ attributes: { orientation } }); + await fastPage.updateTemplate(element, { attributes: { orientation } }); await expect(element).toHaveJSProperty('elementInternals.ariaOrientation', orientation); }); @@ -83,9 +89,11 @@ test.describe('Divider', () => { test('should initialize to the provided value attribute if set post-connection', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const alignment of Object.values(DividerAlignContent)) { await test.step(`alignContent="${alignment}"`, async () => { - await fastPage.setTemplate({ attributes: { 'align-content': alignment } }); + await fastPage.updateTemplate(element, { attributes: { 'align-content': alignment } }); await expect(element).toHaveJSProperty('alignContent', alignment); }); @@ -93,14 +101,14 @@ test.describe('Divider', () => { for (const appearance of Object.values(DividerAppearance)) { await test.step(`appearance="${appearance}"`, async () => { - await fastPage.setTemplate({ attributes: { appearance } }); + await fastPage.updateTemplate(element, { attributes: { appearance } }); await expect(element).toHaveJSProperty('appearance', appearance); }); } await test.step('inset', async () => { - await fastPage.setTemplate({ attributes: { inset: true } }); + await fastPage.updateTemplate(element, { attributes: { inset: true } }); await expect(element).toHaveJSProperty('inset', true); }); diff --git a/packages/web-components/src/drawer/drawer.spec.ts b/packages/web-components/src/drawer/drawer.spec.ts index 60b00ce2770e6c..159706f678c3a8 100644 --- a/packages/web-components/src/drawer/drawer.spec.ts +++ b/packages/web-components/src/drawer/drawer.spec.ts @@ -1,11 +1,12 @@ import { expect, test } from '../../test/playwright/index.js'; -import type { Drawer } from './drawer.js'; -import { DrawerPosition } from './drawer.options.js'; -import { DrawerSize, DrawerType } from './drawer.options.js'; +import { tagName as DrawerBodyTagName } from '../drawer-body/drawer-body.options.js'; +import { Drawer } from './drawer.js'; +import { DrawerPosition, DrawerSize, DrawerType, tagName } from './drawer.options.js'; test.describe('Drawer', () => { test.use({ - tagName: 'fluent-drawer', + tagName, + waitFor: [DrawerBodyTagName], }); for (const type of Object.values(DrawerType)) { @@ -28,9 +29,9 @@ test.describe('Drawer', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-drawer'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -38,9 +39,11 @@ test.describe('Drawer', () => { test('should set the `size` property to match the `size` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const size of Object.values(DrawerSize)) { await test.step(`should set the \`size\` property to \`${size}\``, async () => { - await fastPage.setTemplate({ attributes: { size } }); + await fastPage.updateTemplate(element, { attributes: { size } }); await expect(element).toHaveAttribute('size', size); @@ -52,9 +55,11 @@ test.describe('Drawer', () => { test('should set the `position` property to match the `position` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const position of Object.values(DrawerPosition)) { await test.step(`should set the \`position\` property to \`${position}\``, async () => { - await fastPage.setTemplate({ attributes: { position } }); + await fastPage.updateTemplate(element, { attributes: { position } }); await expect(element).toHaveAttribute('position', position); @@ -122,6 +127,8 @@ test.describe('Drawer', () => { test('should emit a `toggle` event when the `show` method is called', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + const wasOpened = element.evaluate( node => new Promise(resolve => node.addEventListener('toggle', () => resolve(true))), ); @@ -136,6 +143,8 @@ test.describe('Drawer', () => { test('should emit a `beforetoggle` event before open property changes', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + const wasOpened = element.evaluate( node => new Promise(resolve => node.addEventListener('beforetoggle', () => resolve(true))), ); @@ -150,6 +159,8 @@ test.describe('Drawer', () => { test('should emit a `cancel` event when a `cancel` event is invoked on the document', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await element.evaluate((node: Drawer) => { node.show(); }); @@ -167,17 +178,17 @@ test.describe('Drawer', () => { test('should close after a button is slotted into the close slot and clicked', async ({ fastPage, page }) => { const { element } = fastPage; - const closeButton = element.locator('fluent-button[slot="close"]'); + const closeButton = element.locator('button[slot="close"]'); const content = element.locator('#content'); - await fastPage.setTemplate(/* html */ ` - - - Close - content - - - `); + await fastPage.setTemplate({ + innerHTML: /* html */ ` + <${DrawerBodyTagName}> + Close + content + ${DrawerBodyTagName}> + `, + }); await element.evaluate((node: Drawer) => { node.show(); diff --git a/packages/web-components/src/dropdown/dropdown.spec.ts b/packages/web-components/src/dropdown/dropdown.spec.ts index dca5c0a00159ce..cfc2ecec632fb6 100644 --- a/packages/web-components/src/dropdown/dropdown.spec.ts +++ b/packages/web-components/src/dropdown/dropdown.spec.ts @@ -1,22 +1,25 @@ import { expect, test } from '../../test/playwright/index.js'; +import { tagName as ListboxTagName } from '../listbox/listbox.options.js'; +import { tagName as OptionTagName } from '../option/option.options.js'; import type { Dropdown } from './dropdown.js'; +import { tagName } from './dropdown.options.js'; test.describe('Dropdown', () => { test.use({ - tagName: 'fluent-dropdown', + tagName, innerHTML: /* html */ ` - - Apple - Banana - Orange - Mango - Kiwi - Cherry - Grapefruit - Papaya - + <${ListboxTagName}> + <${OptionTagName} value="apple">Apple${OptionTagName}> + <${OptionTagName} value="banana">Banana${OptionTagName}> + <${OptionTagName} value="orange">Orange${OptionTagName}> + <${OptionTagName} value="mango">Mango${OptionTagName}> + <${OptionTagName} value="kiwi">Kiwi${OptionTagName}> + <${OptionTagName} value="cherry">Cherry${OptionTagName}> + <${OptionTagName} value="grapefruit">Grapefruit${OptionTagName}> + <${OptionTagName} value="papaya">Papaya${OptionTagName}> + ${ListboxTagName}> `, - waitFor: ['fluent-listbox', 'fluent-option'], + waitFor: [ListboxTagName, OptionTagName], }); test('should create with document.createElement()', async ({ page, fastPage }) => { @@ -28,9 +31,9 @@ test.describe('Dropdown', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-dropdown'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -38,12 +41,16 @@ test.describe('Dropdown', () => { test('should render a dropdown', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveCount(1); }); test('should render a dropdown with options', async ({ fastPage }) => { const { element } = fastPage; - const options = element.locator('fluent-option'); + const options = element.locator(OptionTagName); + + await fastPage.setTemplate(); await expect(options).toHaveCount(8); }); @@ -52,6 +59,8 @@ test.describe('Dropdown', () => { const { element } = fastPage; const button = element.locator('button'); + await fastPage.setTemplate(); + await expect(button).toHaveCount(1); }); @@ -66,7 +75,9 @@ test.describe('Dropdown', () => { test('should open the dropdown on click', async ({ fastPage }) => { const { element } = fastPage; - const listbox = element.locator('fluent-listbox'); + const listbox = element.locator(ListboxTagName); + + await fastPage.setTemplate(); await expect(listbox).toBeHidden(); @@ -77,9 +88,11 @@ test.describe('Dropdown', () => { test('should close the dropdown on click', async ({ fastPage }) => { const { element } = fastPage; - const listbox = element.locator('fluent-listbox'); + const listbox = element.locator(ListboxTagName); const button = element.locator('[role=combobox]'); + await fastPage.setTemplate(); + await button.click(); await expect(listbox).toBeVisible(); @@ -91,9 +104,11 @@ test.describe('Dropdown', () => { test('should open the dropdown when the space key is pressed', async ({ fastPage }) => { const { element } = fastPage; - const listbox = element.locator('fluent-listbox'); + const listbox = element.locator(ListboxTagName); const button = element.locator('[role=combobox]'); + await fastPage.setTemplate(); + await button.press(' '); await expect(listbox).toBeVisible(); @@ -101,7 +116,7 @@ test.describe('Dropdown', () => { test("should set the `name` property on options when it's set on the dropdown", async ({ fastPage }) => { const { element } = fastPage; - const options = element.locator('fluent-option'); + const options = element.locator(OptionTagName); await fastPage.setTemplate({ attributes: { name: 'fruit' } }); @@ -117,29 +132,29 @@ test.describe('Dropdown', () => { await fastPage.setTemplate(/* html */ ` - - - Apple - Banana - Orange - Mango - Kiwi - Cherry - Grapefruit - Papaya - - + <${tagName} name="fruit"> + <${ListboxTagName}> + <${OptionTagName} value="apple">Apple${OptionTagName}> + <${OptionTagName} value="banana">Banana${OptionTagName}> + <${OptionTagName} value="orange">Orange${OptionTagName}> + <${OptionTagName} value="mango">Mango${OptionTagName}> + <${OptionTagName} value="kiwi">Kiwi${OptionTagName}> + <${OptionTagName} value="cherry">Cherry${OptionTagName}> + <${OptionTagName} value="grapefruit">Grapefruit${OptionTagName}> + <${OptionTagName} value="papaya">Papaya${OptionTagName}> + ${ListboxTagName}> + ${tagName}> Submit `); await element.click(); - await expect(element.locator('fluent-listbox')).toBeVisible(); + await expect(element.locator(ListboxTagName)).toBeVisible(); - await element.locator('fluent-option[value=cherry]').click(); + await element.locator(`${OptionTagName}[value=cherry]`).click(); - await expect(element.locator('fluent-option[value=cherry]')).toHaveJSProperty('selected', true); + await expect(element.locator(`${OptionTagName}[value=cherry]`)).toHaveJSProperty('selected', true); await submitButton.click(); @@ -152,25 +167,25 @@ test.describe('Dropdown', () => { await fastPage.setTemplate(/* html */ ` - - - Apple - Banana - Orange - Mango - Kiwi - Cherry - Grapefruit - Papaya - - + <${tagName} name="fruit" disabled> + <${ListboxTagName}> + <${OptionTagName} value="apple">Apple${OptionTagName}> + <${OptionTagName} value="banana">Banana${OptionTagName}> + <${OptionTagName} value="orange">Orange${OptionTagName}> + <${OptionTagName} value="mango">Mango${OptionTagName}> + <${OptionTagName} value="kiwi">Kiwi${OptionTagName}> + <${OptionTagName} value="cherry" selected>Cherry${OptionTagName}> + <${OptionTagName} value="grapefruit">Grapefruit${OptionTagName}> + <${OptionTagName} value="papaya">Papaya${OptionTagName}> + ${ListboxTagName}> + ${tagName}> Submit `); await element.click(); - await expect(element.locator('fluent-listbox')).toBeHidden(); + await expect(element.locator(ListboxTagName)).toBeHidden(); await submitButton.click(); @@ -186,25 +201,25 @@ test.describe('Dropdown', () => { await fastPage.setTemplate(/* html */ ` - - - Apple - Banana - Orange - Mango - Kiwi - Cherry - Grapefruit - Papaya - - + <${tagName} name="fruit"> + <${ListboxTagName}> + <${OptionTagName} value="apple">Apple${OptionTagName}> + <${OptionTagName} value="banana">Banana${OptionTagName}> + <${OptionTagName} value="orange">Orange${OptionTagName}> + <${OptionTagName} value="mango" selected>Mango${OptionTagName}> + <${OptionTagName} value="kiwi" selected>Kiwi${OptionTagName}> + <${OptionTagName} value="cherry">Cherry${OptionTagName}> + <${OptionTagName} value="grapefruit">Grapefruit${OptionTagName}> + <${OptionTagName} value="papaya">Papaya${OptionTagName}> + ${ListboxTagName}> + ${tagName}> Submit `); await element.click(); - await expect(element.locator('fluent-listbox')).toBeVisible(); + await expect(element.locator(ListboxTagName)).toBeVisible(); await submitButton.click(); @@ -222,27 +237,27 @@ test.describe('Dropdown', () => { await fastPage.setTemplate(/* html */ ` - - - Apple - Banana - Orange - Mango - Kiwi - Cherry - Grapefruit - Papaya - - + <${tagName} name="fruit" multiple> + <${ListboxTagName}> + <${OptionTagName} value="apple">Apple${OptionTagName}> + <${OptionTagName} value="banana">Banana${OptionTagName}> + <${OptionTagName} value="orange">Orange${OptionTagName}> + <${OptionTagName} value="mango" selected>Mango${OptionTagName}> + <${OptionTagName} value="kiwi" selected>Kiwi${OptionTagName}> + <${OptionTagName} value="cherry">Cherry${OptionTagName}> + <${OptionTagName} value="grapefruit">Grapefruit${OptionTagName}> + <${OptionTagName} value="papaya">Papaya${OptionTagName}> + ${ListboxTagName}> + ${tagName}> Submit `); - await expect(element.locator('fluent-option[value=mango]')).toHaveJSProperty('selected', true); - await expect(element.locator('fluent-option[value=mango]')).toHaveAttribute('selected'); + await expect(element.locator(`${OptionTagName}[value=mango]`)).toHaveJSProperty('selected', true); + await expect(element.locator(`${OptionTagName}[value=mango]`)).toHaveAttribute('selected'); - await expect(element.locator('fluent-option[value=kiwi]')).toHaveJSProperty('selected', true); - await expect(element.locator('fluent-option[value=kiwi]')).toHaveAttribute('selected'); + await expect(element.locator(`${OptionTagName}[value=kiwi]`)).toHaveJSProperty('selected', true); + await expect(element.locator(`${OptionTagName}[value=kiwi]`)).toHaveAttribute('selected'); await submitButton.click(); @@ -257,37 +272,37 @@ test.describe('Dropdown', () => { await fastPage.setTemplate(/* html */ ` - - - Apple - Banana - Orange - Mango - Kiwi - Cherry - Grapefruit - Papaya - - + <${tagName} name="fruit"> + <${ListboxTagName}> + <${OptionTagName} value="apple">Apple${OptionTagName}> + <${OptionTagName} value="banana">Banana${OptionTagName}> + <${OptionTagName} value="orange">Orange${OptionTagName}> + <${OptionTagName} value="mango" selected>Mango${OptionTagName}> + <${OptionTagName} value="kiwi">Kiwi${OptionTagName}> + <${OptionTagName} value="cherry">Cherry${OptionTagName}> + <${OptionTagName} value="grapefruit">Grapefruit${OptionTagName}> + <${OptionTagName} value="papaya">Papaya${OptionTagName}> + ${ListboxTagName}> + ${tagName}> Reset `); await element.click(); - await expect(element.locator('fluent-listbox')).toBeVisible(); + await expect(element.locator(ListboxTagName)).toBeVisible(); - await element.locator('fluent-option[value=kiwi]').click(); + await element.locator(`${OptionTagName}[value=kiwi]`).click(); - await expect(element.locator('fluent-option[value=kiwi]')).toHaveJSProperty('selected', true); + await expect(element.locator(`${OptionTagName}[value=kiwi]`)).toHaveJSProperty('selected', true); - await expect(element.locator('fluent-option[value=mango]')).toHaveJSProperty('selected', false); + await expect(element.locator(`${OptionTagName}[value=mango]`)).toHaveJSProperty('selected', false); await resetButton.click(); - await expect(element.locator('fluent-option[value=mango]')).toHaveJSProperty('selected', true); + await expect(element.locator(`${OptionTagName}[value=mango]`)).toHaveJSProperty('selected', true); - await expect(element.locator('fluent-option[value=kiwi]')).toHaveJSProperty('selected', false); + await expect(element.locator(`${OptionTagName}[value=kiwi]`)).toHaveJSProperty('selected', false); }); test('should reset the values when the form is reset and the `multiple` attribute is present', async ({ @@ -299,41 +314,41 @@ test.describe('Dropdown', () => { await fastPage.setTemplate(/* html */ ` - - - Apple - Banana - Orange - Mango - Kiwi - Cherry - Grapefruit - Papaya - - + <${tagName} name="fruit" multiple> + <${ListboxTagName}> + <${OptionTagName} value="apple">Apple${OptionTagName}> + <${OptionTagName} value="banana">Banana${OptionTagName}> + <${OptionTagName} value="orange">Orange${OptionTagName}> + <${OptionTagName} value="mango" selected>Mango${OptionTagName}> + <${OptionTagName} value="kiwi" selected>Kiwi${OptionTagName}> + <${OptionTagName} value="cherry">Cherry${OptionTagName}> + <${OptionTagName} value="grapefruit">Grapefruit${OptionTagName}> + <${OptionTagName} value="papaya">Papaya${OptionTagName}> + ${ListboxTagName}> + ${tagName}> Reset `); await element.click(); - await expect(element.locator('fluent-listbox')).toBeVisible(); + await expect(element.locator(ListboxTagName)).toBeVisible(); - await element.locator('fluent-option[value=apple]').click(); + await element.locator(`${OptionTagName}[value=apple]`).click(); - await expect(element.locator('fluent-option[value=kiwi]')).toHaveJSProperty('selected', true); + await expect(element.locator(`${OptionTagName}[value=kiwi]`)).toHaveJSProperty('selected', true); - await expect(element.locator('fluent-option[value=mango]')).toHaveJSProperty('selected', true); + await expect(element.locator(`${OptionTagName}[value=mango]`)).toHaveJSProperty('selected', true); - await expect(element.locator('fluent-option[value=apple]')).toHaveJSProperty('selected', true); + await expect(element.locator(`${OptionTagName}[value=apple]`)).toHaveJSProperty('selected', true); await resetButton.click(); - await expect(element.locator('fluent-option[value=mango]')).toHaveJSProperty('selected', true); + await expect(element.locator(`${OptionTagName}[value=mango]`)).toHaveJSProperty('selected', true); - await expect(element.locator('fluent-option[value=kiwi]')).toHaveJSProperty('selected', true); + await expect(element.locator(`${OptionTagName}[value=kiwi]`)).toHaveJSProperty('selected', true); - await expect(element.locator('fluent-option[value=apple]')).toHaveJSProperty('selected', false); + await expect(element.locator(`${OptionTagName}[value=apple]`)).toHaveJSProperty('selected', false); }); test('should display a validation message when the dropdown is required and the form is submitted without a value', async ({ @@ -342,7 +357,7 @@ test.describe('Dropdown', () => { browserName, }) => { const { element } = fastPage; - const options = element.locator('fluent-option'); + const options = element.locator(OptionTagName); const submitButton = page.locator('button[type=submit]'); const messages: Record = { @@ -354,18 +369,18 @@ test.describe('Dropdown', () => { await fastPage.setTemplate(/* html */ ` - - - Apple - Banana - Orange - Mango - Kiwi - Cherry - Grapefruit - Papaya - - + <${tagName} name="fruit" required> + <${ListboxTagName}> + <${OptionTagName} value="apple">Apple${OptionTagName}> + <${OptionTagName} value="banana">Banana${OptionTagName}> + <${OptionTagName} value="orange">Orange${OptionTagName}> + <${OptionTagName} value="mango">Mango${OptionTagName}> + <${OptionTagName} value="kiwi">Kiwi${OptionTagName}> + <${OptionTagName} value="cherry">Cherry${OptionTagName}> + <${OptionTagName} value="grapefruit">Grapefruit${OptionTagName}> + <${OptionTagName} value="papaya">Papaya${OptionTagName}> + ${ListboxTagName}> + ${tagName}> Submit `); @@ -386,8 +401,8 @@ test.describe('Dropdown', () => { test('should select an option when the user types the value', async ({ fastPage }) => { const { element } = fastPage; const input = element.locator('input'); - const listbox = element.locator('fluent-listbox'); - const kiwiOption = element.locator('fluent-option[value=kiwi]'); + const listbox = element.locator(ListboxTagName); + const kiwiOption = element.locator(`${OptionTagName}[value=kiwi]`); await fastPage.setTemplate({ attributes: { type: 'combobox' } }); @@ -414,7 +429,7 @@ test.describe('Dropdown', () => { }) => { const { element } = fastPage; const input = element.locator('input'); - const listbox = element.locator('fluent-listbox'); + const listbox = element.locator(ListboxTagName); await fastPage.setTemplate({ attributes: { type: 'combobox' } }); @@ -441,7 +456,9 @@ test.describe('Dropdown', () => { page, }) => { const { element } = fastPage; - const listbox = element.locator('fluent-listbox'); + const listbox = element.locator(ListboxTagName); + + await fastPage.setTemplate(); await element.click(); @@ -464,7 +481,9 @@ test.describe('Dropdown', () => { test('should emit a `change` event when the value is confirmed by pressing Enter', async ({ fastPage }) => { const { element } = fastPage; - const listbox = element.locator('fluent-listbox'); + const listbox = element.locator(ListboxTagName); + + await fastPage.setTemplate(); await element.click(); @@ -502,6 +521,8 @@ test.describe('Dropdown', () => { test('should NOT emit a `change` event when the value is changed programmatically', async ({ fastPage, page }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await element.evaluate((el: Dropdown) => { el.addEventListener('change', () => el.insertAdjacentText('afterend', 'changed'), { once: true }); }); @@ -514,13 +535,15 @@ test.describe('Dropdown', () => { await expect(element).toHaveJSProperty('value', 'kiwi'); - await expect(element.locator('fluent-option[value=kiwi]')).toHaveJSProperty('selected', true); + await expect(element.locator(`${OptionTagName}[value=kiwi]`)).toHaveJSProperty('selected', true); }); test('should not focus listbox when tabbing from dropdown', async ({ fastPage, page }) => { const { element } = fastPage; - const listbox = element.locator('fluent-listbox'); + await fastPage.setTemplate(); + + const listbox = element.locator(ListboxTagName); await element.focus(); await page.keyboard.press('Tab'); diff --git a/packages/web-components/src/field/field.spec.ts b/packages/web-components/src/field/field.spec.ts index fa7a2cff709a72..68edb224a408d5 100644 --- a/packages/web-components/src/field/field.spec.ts +++ b/packages/web-components/src/field/field.spec.ts @@ -1,11 +1,13 @@ import { expect, test } from '../../test/playwright/index.js'; import type { TextInput } from '../text-input/text-input.js'; +import { tagName as TextInputTagName } from '../text-input/text-input.options.js'; import type { Field } from './field.js'; +import { tagName } from './field.options.js'; test.describe('Field', () => { test.use({ - tagName: 'fluent-field', - waitFor: ['fluent-text-input'], + tagName, + waitFor: [TextInputTagName], }); test('should create with document.createElement()', async ({ page, fastPage }) => { @@ -17,9 +19,9 @@ test.describe('Field', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-field'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -388,7 +390,7 @@ test.describe('Field', () => { await fastPage.setTemplate({ innerHTML: /* html */ ` - + <${TextInputTagName} id="input" slot="input" type="text">${TextInputTagName}> I have no idea how you managed to do this. `, }); diff --git a/packages/web-components/src/image/image.spec.ts b/packages/web-components/src/image/image.spec.ts index 19c5e6c068e3bd..0db5aeda36711b 100644 --- a/packages/web-components/src/image/image.spec.ts +++ b/packages/web-components/src/image/image.spec.ts @@ -1,10 +1,10 @@ import { expect, test } from '../../test/playwright/index.js'; import type { Image } from './image.js'; -import { ImageFit, ImageShape } from './image.options.js'; +import { ImageFit, ImageShape, tagName } from './image.options.js'; test.describe('Image', () => { test.use({ - tagName: 'fluent-image', + tagName, innerHTML: /* html */ ` `, @@ -19,9 +19,9 @@ test.describe('Image', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-image'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -83,9 +83,11 @@ test.describe('Image', () => { test('should set the `fit` property to match the `fit` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const fit of Object.values(ImageFit)) { await test.step(`should set the \`fit\` property to "${fit}"`, async () => { - await fastPage.setTemplate({ attributes: { fit } }); + await fastPage.updateTemplate(element, { attributes: { fit } }); await expect(element).toHaveAttribute('fit', fit); @@ -97,9 +99,11 @@ test.describe('Image', () => { test('should set the `shape` property to match the `shape` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const shape of Object.values(ImageShape)) { await test.step(`should set the \`shape\` property to "${shape}"`, async () => { - await fastPage.setTemplate({ attributes: { shape } }); + await fastPage.updateTemplate(element, { attributes: { shape } }); await expect(element).toHaveAttribute('shape', shape); diff --git a/packages/web-components/src/label/label.spec.ts b/packages/web-components/src/label/label.spec.ts index 2dff43462aed66..56f17a8f56bfba 100644 --- a/packages/web-components/src/label/label.spec.ts +++ b/packages/web-components/src/label/label.spec.ts @@ -1,16 +1,21 @@ import { expect, test } from '../../test/playwright/index.js'; import type { Label } from './label.js'; -import { LabelSize, LabelWeight } from './label.options.js'; +import { LabelSize, LabelWeight, tagName } from './label.options.js'; test.describe('Label', () => { - test.use({ tagName: 'fluent-label' }); + test.use({ + tagName, + innerHTML: 'Label', + }); test('should set the `size` property to match the `size` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const size of Object.values(LabelSize)) { await test.step(`should set the \`size\` property to "${size}"`, async () => { - await fastPage.setTemplate({ attributes: { size } }); + await fastPage.updateTemplate(element, { attributes: { size } }); await expect(element).toHaveAttribute('size', size); @@ -28,9 +33,9 @@ test.describe('Label', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-label'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -38,9 +43,11 @@ test.describe('Label', () => { test('should set the `weight` property to match the `weight` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const weight of Object.values(LabelWeight)) { await test.step(`should set the \`weight\` property to "${weight}"`, async () => { - await fastPage.setTemplate({ attributes: { weight } }); + await fastPage.updateTemplate(element, { attributes: { weight } }); await expect(element).toHaveAttribute('weight', weight); diff --git a/packages/web-components/src/link/link.spec.ts b/packages/web-components/src/link/link.spec.ts index aa6d26f2a2cd1e..877c8df6637021 100644 --- a/packages/web-components/src/link/link.spec.ts +++ b/packages/web-components/src/link/link.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from '../../test/playwright/index.js'; -import type { Link } from './link.js'; -import { LinkAppearance } from './link.options.js'; +import { Link } from './link.js'; +import { LinkAppearance, tagName } from './link.options.js'; const attributes = { download: 'download', @@ -14,7 +14,9 @@ const attributes = { }; test.describe('Link', () => { - test.use({ tagName: 'fluent-link' }); + test.use({ + tagName, + }); test('should create with document.createElement()', async ({ page, fastPage }) => { await fastPage.setTemplate(); @@ -25,9 +27,9 @@ test.describe('Link', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-link'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -36,9 +38,11 @@ test.describe('Link', () => { const { element } = fastPage; const anchor = element.locator('a'); + await fastPage.setTemplate(); + for (const [attribute, value] of Object.entries(attributes)) { await test.step(`should set the \`${attribute}\` property to match the \`${attribute}\` attribute`, async () => { - await fastPage.setTemplate({ attributes: { [attribute]: value } }); + await fastPage.updateTemplate(element, { attributes: { [attribute]: value } }); await expect(element).toHaveAttribute(attribute, value); @@ -54,9 +58,11 @@ test.describe('Link', () => { test('should set the `appearance` property to match the `appearance` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const appearance of Object.values(LinkAppearance)) { await test.step(appearance, async () => { - await fastPage.setTemplate({ attributes: { appearance } }); + await fastPage.updateTemplate(element, { attributes: { appearance } }); await expect(element).toHaveJSProperty('appearance', appearance); @@ -68,6 +74,8 @@ test.describe('Link', () => { test('should add an "inline" attribute when the `inline` property is true', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await element.evaluate((node: Link) => { node.inline = true; }); diff --git a/packages/web-components/src/listbox/listbox.spec.ts b/packages/web-components/src/listbox/listbox.spec.ts index 402f6282e8194d..ad4d49a7271e58 100644 --- a/packages/web-components/src/listbox/listbox.spec.ts +++ b/packages/web-components/src/listbox/listbox.spec.ts @@ -1,20 +1,22 @@ import { expect, test } from '../../test/playwright/index.js'; +import { tagName as OptionTagName } from '../option/option.options.js'; import type { Listbox } from './listbox.js'; +import { tagName } from './listbox.options.js'; test.describe('Listbox', () => { test.use({ - tagName: 'fluent-listbox', + tagName, innerHTML: /* html */ ` - Apple - Banana - Orange - Mango - Kiwi - Cherry - Grapefruit - Papaya + <${OptionTagName} value="apple">Apple${OptionTagName}> + <${OptionTagName} value="banana">Banana${OptionTagName}> + <${OptionTagName} value="orange">Orange${OptionTagName}> + <${OptionTagName} value="mango">Mango${OptionTagName}> + <${OptionTagName} value="kiwi">Kiwi${OptionTagName}> + <${OptionTagName} value="cherry">Cherry${OptionTagName}> + <${OptionTagName} value="grapefruit">Grapefruit${OptionTagName}> + <${OptionTagName} value="papaya">Papaya${OptionTagName}> `, - waitFor: ['fluent-option'], + waitFor: [OptionTagName], }); test('should create with document.createElement()', async ({ page, fastPage }) => { @@ -26,9 +28,9 @@ test.describe('Listbox', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-listbox'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -75,7 +77,7 @@ test.describe('Listbox', () => { fastPage, }) => { const { element } = fastPage; - const options = element.locator('fluent-option'); + const options = element.locator(OptionTagName); await fastPage.setTemplate(); @@ -98,7 +100,7 @@ test.describe('Listbox', () => { test('should set the `ariaPosInSet` and `ariaSetSize` properties on the options', async ({ fastPage }) => { const { element } = fastPage; - const options = element.locator('fluent-option'); + const options = element.locator(OptionTagName); await fastPage.setTemplate(); @@ -113,8 +115,8 @@ test.describe('Listbox', () => { } await test.step('should update the `ariaPosInSet` and `ariaSetSize` properties when the options change', async () => { - await element.evaluate((node: Listbox) => { - const newOption = document.createElement('fluent-option'); + await element.evaluate((node: Listbox, OptionTagName) => { + const newOption = document.createElement(OptionTagName); newOption.textContent = 'New Option'; node.appendChild(newOption); @@ -122,7 +124,7 @@ test.describe('Listbox', () => { for (let i = node.children.length; i >= 0; i--) { node.appendChild(node.children[(Math.random() * i) | 0]); } - }); + }, OptionTagName); const newOptionsCount = await options.count(); diff --git a/packages/web-components/src/menu-list/menu-list.spec.ts b/packages/web-components/src/menu-list/menu-list.spec.ts index be9d7281371c3d..25372a141be4b9 100644 --- a/packages/web-components/src/menu-list/menu-list.spec.ts +++ b/packages/web-components/src/menu-list/menu-list.spec.ts @@ -1,16 +1,18 @@ import { expect, test } from '../../test/playwright/index.js'; +import { tagName as DividerTagName } from '../divider/divider.options.js'; import type { MenuItem } from '../menu-item/menu-item.js'; -import { MenuItemRole } from '../menu-item/menu-item.options.js'; +import { MenuItemRole, tagName as MenuItemTagName } from '../menu-item/menu-item.options.js'; +import { tagName } from './menu-list.options.js'; test.describe('MenuList', () => { test.use({ - tagName: 'fluent-menu-list', - waitFor: ['fluent-menu-item'], + tagName, + waitFor: [MenuItemTagName, DividerTagName], innerHTML: /* html */ ` - Menu item 1 - Menu item 2 - Menu item 3 - Menu item 4 + <${MenuItemTagName}>Menu item 1${MenuItemTagName}> + <${MenuItemTagName}>Menu item 2${MenuItemTagName}> + <${MenuItemTagName}>Menu item 3${MenuItemTagName}> + <${MenuItemTagName}>Menu item 4${MenuItemTagName}> `, }); @@ -23,9 +25,9 @@ test.describe('MenuList', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-menu-list'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -33,34 +35,39 @@ test.describe('MenuList', () => { test('should have a role of `menu`', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('elementInternals.role', 'menu'); }); test('should set `tabindex` of the first focusable menu item to 0', async ({ fastPage }) => { const { element } = fastPage; - const menuItems = element.locator('fluent-menu-item'); + const menuItems = element.locator(MenuItemTagName); + + await fastPage.setTemplate(); await expect(menuItems.first()).toHaveAttribute('tabindex', '0'); }); test('should NOT set any `tabindex` on non-menu-item elements', async ({ fastPage }) => { const { element } = fastPage; + const divider = element.locator('div.divider'); await fastPage.setTemplate({ innerHTML: /* html */ ` - Menu item + <${MenuItemTagName}>Menu item${MenuItemTagName}> Not a menu item `, }); - const divider = element.locator('div.divider'); - await expect(divider).not.toHaveAttribute('tabindex'); }); test('should focus on first menu item when `focus()` is called', async ({ fastPage }) => { const { element } = fastPage; - const firstItem = element.locator('fluent-menu-item').first(); + const firstItem = element.locator(MenuItemTagName).first(); + + await fastPage.setTemplate(); await expect(firstItem).toHaveAttribute('tabindex', '0'); @@ -88,30 +95,29 @@ test.describe('MenuList', () => { await fastPage.setTemplate(''); - await page.evaluate(() => { - const menu = document.createElement('fluent-menu-list'); + await page.evaluate(tagName => { + const menu = document.createElement(tagName); menu.focus(); document.body.append(menu); - }); + }, tagName); await expect(element).not.toBeFocused(); }); test('should focus disabled items', async ({ fastPage }) => { const { element } = fastPage; - const menuItems = element.locator('fluent-menu-item'); + const menuItems = element.locator(MenuItemTagName); + const firstMenuItem = menuItems.first(); await fastPage.setTemplate({ innerHTML: /* html */ ` - Menu item - Menu item + <${MenuItemTagName} disabled>Menu item${MenuItemTagName}> + <${MenuItemTagName}>Menu item${MenuItemTagName}> `, }); - const firstMenuItem = menuItems.first(); - await expect(firstMenuItem).toHaveAttribute('disabled'); await expect(firstMenuItem).toHaveJSProperty('elementInternals.ariaDisabled', 'true'); @@ -134,16 +140,9 @@ test.describe('MenuList', () => { test('should not navigate to hidden items when changed after connection', async ({ fastPage }) => { const { element } = fastPage; - const menuItems = element.locator('fluent-menu-item'); + const menuItems = element.locator(MenuItemTagName); - await fastPage.setTemplate({ - innerHTML: /* html */ ` - Menu item 1 - Menu item 2 - Menu item 3 - Menu item 4 - `, - }); + await fastPage.setTemplate(); await expect(menuItems).toHaveCount(4); @@ -190,14 +189,14 @@ test.describe('MenuList', () => { test('should treat all checkbox menu items as individually selectable items', async ({ fastPage }) => { const { element } = fastPage; - const menuItems = element.locator('fluent-menu-item'); + const menuItems = element.locator(MenuItemTagName); await fastPage.setTemplate({ innerHTML: /* html */ ` - Menu item 1 - Menu item 2 - Menu item 3 - Menu item 4 + <${MenuItemTagName} role="menuitemcheckbox">Menu item 1${MenuItemTagName}> + <${MenuItemTagName} role="menuitemcheckbox">Menu item 2${MenuItemTagName}> + <${MenuItemTagName} role="menuitemcheckbox">Menu item 3${MenuItemTagName}> + <${MenuItemTagName} role="menuitemcheckbox">Menu item 4${MenuItemTagName}> `, }); @@ -218,13 +217,13 @@ test.describe('MenuList', () => { fastPage, }) => { const { element } = fastPage; - const menuItems = element.locator('fluent-menu-item'); + const menuItems = element.locator(MenuItemTagName); await fastPage.setTemplate({ innerHTML: /* html */ ` - Menu item 1 - Menu item 2 - Menu item 3 + <${MenuItemTagName} role="menuitemradio">Menu item 1${MenuItemTagName}> + <${MenuItemTagName} role="menuitemradio">Menu item 2${MenuItemTagName}> + <${MenuItemTagName} role="menuitemradio">Menu item 3${MenuItemTagName}> `, }); @@ -257,15 +256,15 @@ test.describe('MenuList', () => { fastPage, }) => { const { element } = fastPage; - const menuItems = element.locator('fluent-menu-item'); + const menuItems = element.locator(MenuItemTagName); await fastPage.setTemplate({ innerHTML: /* html */ ` - Menu item 1 - Menu item 2 - - Menu item 3 - Menu item 4 + <${MenuItemTagName} role="menuitemradio">Menu item 1${MenuItemTagName}> + <${MenuItemTagName} role="menuitemradio">Menu item 2${MenuItemTagName}> + <${DividerTagName} role="separator">${DividerTagName}> + <${MenuItemTagName} role="menuitemradio">Menu item 3${MenuItemTagName}> + <${MenuItemTagName} role="menuitemradio">Menu item 4${MenuItemTagName}> `, }); @@ -308,7 +307,9 @@ test.describe('MenuList', () => { test('should navigate the menu on arrow up/down keys', async ({ fastPage }) => { const { element } = fastPage; - const menuItems = element.locator('fluent-menu-item'); + const menuItems = element.locator(MenuItemTagName); + + await fastPage.setTemplate(); await element.evaluate(node => { node.focus(); @@ -335,18 +336,18 @@ test.describe('MenuList', () => { fastPage, }) => { const { element } = fastPage; - const menuItems = element.locator('fluent-menu-item'); + const menuItems = element.locator(MenuItemTagName); await fastPage.setTemplate({ innerHTML: /* html */ ` - Menu item 1 - - Menu item 1.1 - Menu item 1.2 - Menu item 1.3 - - + <${tagName} slot="submenu"> + <${MenuItemTagName}>Menu item 1.1${MenuItemTagName}> + <${MenuItemTagName}>Menu item 1.2${MenuItemTagName}> + <${MenuItemTagName}>Menu item 1.3${MenuItemTagName}> + ${tagName}> + ${MenuItemTagName}> `, }); @@ -365,14 +366,14 @@ test.describe('MenuList', () => { test('should not navigate to hidden items when set before connection', async ({ fastPage }) => { const { element } = fastPage; - const menuItems = element.locator('fluent-menu-item'); + const menuItems = element.locator(MenuItemTagName); await fastPage.setTemplate({ innerHTML: /* html */ ` - Menu item 1 - Menu item 2 - Menu item 3 - Menu item 4 + <${MenuItemTagName}>Menu item 1${MenuItemTagName}> + <${MenuItemTagName} hidden="hidden">Menu item 2${MenuItemTagName}> + <${MenuItemTagName}>Menu item 3${MenuItemTagName}> + <${MenuItemTagName}>Menu item 4${MenuItemTagName}> `, }); @@ -403,7 +404,9 @@ test.describe('MenuList', () => { fastPage, }) => { const { element } = fastPage; - const menuItems = element.locator('fluent-menu-item'); + const menuItems = element.locator(MenuItemTagName); + + await fastPage.setTemplate(); for (const item of await menuItems.all()) { await expect(item).toHaveAttribute('data-indent', '0'); @@ -414,14 +417,14 @@ test.describe('MenuList', () => { fastPage, }) => { const { element } = fastPage; - const menuItems = element.locator('fluent-menu-item'); + const menuItems = element.locator(MenuItemTagName); await fastPage.setTemplate({ innerHTML: /* html */ ` - - Menu item 2 - Menu item 3 - Menu item 4 + <${MenuItemTagName} role="menuitemcheckbox">${MenuItemTagName}> + <${MenuItemTagName}>Menu item 2${MenuItemTagName}> + <${MenuItemTagName}>Menu item 3${MenuItemTagName}> + <${MenuItemTagName}>Menu item 4${MenuItemTagName}> `, }); @@ -434,14 +437,14 @@ test.describe('MenuList', () => { fastPage, }) => { const { element } = fastPage; - const menuItems = element.locator('fluent-menu-item'); + const menuItems = element.locator(MenuItemTagName); await fastPage.setTemplate({ innerHTML: /* html */ ` - - Menu item 2 - Menu item 3 - Menu item 4 + <${MenuItemTagName} role="menuitemradio">${MenuItemTagName}> + <${MenuItemTagName}>Menu item 2${MenuItemTagName}> + <${MenuItemTagName}>Menu item 3${MenuItemTagName}> + <${MenuItemTagName}>Menu item 4${MenuItemTagName}> `, }); @@ -454,17 +457,17 @@ test.describe('MenuList', () => { fastPage, }) => { const { element } = fastPage; - const menuItems = element.locator('fluent-menu-item'); + const menuItems = element.locator(MenuItemTagName); await fastPage.setTemplate({ innerHTML: /* html */ ` - + <${MenuItemTagName} role="menuitemcheckbox"> Item 1 Icon - - Menu item 2 - Menu item 3 - Menu item 4 + ${MenuItemTagName}> + <${MenuItemTagName}>Menu item 2${MenuItemTagName}> + <${MenuItemTagName}>Menu item 3${MenuItemTagName}> + <${MenuItemTagName}>Menu item 4${MenuItemTagName}> `, }); @@ -477,14 +480,14 @@ test.describe('MenuList', () => { fastPage, }) => { const { element } = fastPage; - const menuItems = element.locator('fluent-menu-item'); + const menuItems = element.locator(MenuItemTagName); await fastPage.setTemplate({ innerHTML: /* html */ ` - Item 1 Icon - Menu item 2 - Menu item 3 - Menu item 4 + <${MenuItemTagName} role="menuitemradio"> Item 1 Icon ${MenuItemTagName}> + <${MenuItemTagName}>Menu item 2${MenuItemTagName}> + <${MenuItemTagName}>Menu item 3${MenuItemTagName}> + <${MenuItemTagName}>Menu item 4${MenuItemTagName}> `, }); @@ -497,21 +500,21 @@ test.describe('MenuList', () => { fastPage, }) => { const { element } = fastPage; + const menuItems = element.locator(MenuItemTagName); await fastPage.setTemplate({ innerHTML: '' }); - await element.evaluate(node => { + await element.evaluate((node, MenuItemTagName) => { const items = ['item 1', 'item 2', 'item 3']; items.forEach(item => { - const menuItem = document.createElement('fluent-menu-item'); + const menuItem = document.createElement(MenuItemTagName); menuItem.role = 'menuitemradio'; menuItem.textContent = item; node.append(menuItem); }); - }); + }, MenuItemTagName); - const menuItems = element.locator('fluent-menu-item'); await expect(menuItems).toHaveCount(3); for (const item of await menuItems.all()) { @@ -523,24 +526,24 @@ test.describe('MenuList', () => { fastPage, }) => { const { element } = fastPage; + const menuItems = element.locator(MenuItemTagName); await fastPage.setTemplate({ innerHTML: '' }); - await element.evaluate(node => { + await element.evaluate((node, MenuItemTagName) => { const fragment = document.createDocumentFragment(); const items = ['item 1', 'item 2', 'item 3']; items.forEach(item => { - const menuItem = document.createElement('fluent-menu-item'); + const menuItem = document.createElement(MenuItemTagName); menuItem.role = 'menuitemradio'; menuItem.textContent = item; fragment.append(menuItem); }); node.append(fragment); - }); + }, MenuItemTagName); - const menuItems = element.locator('fluent-menu-item'); await expect(menuItems).toHaveCount(3); for (const item of await menuItems.all()) { @@ -552,7 +555,9 @@ test.describe('MenuList', () => { fastPage, }) => { const { element } = fastPage; - const menuItems = element.locator('fluent-menu-item'); + const menuItems = element.locator(MenuItemTagName); + + await fastPage.setTemplate(); await test.step('all plain menuitems should start with data-indent 0', async () => { for (const item of await menuItems.all()) { @@ -561,12 +566,12 @@ test.describe('MenuList', () => { }); await test.step('appending a menuitemradio should update all items to data-indent 1', async () => { - await element.evaluate(node => { - const menuItem = document.createElement('fluent-menu-item'); + await element.evaluate((node, MenuItemTagName) => { + const menuItem = document.createElement(MenuItemTagName); menuItem.role = 'menuitemradio'; menuItem.textContent = 'Radio item'; node.append(menuItem); - }); + }, MenuItemTagName); await expect(menuItems).toHaveCount(5); @@ -589,16 +594,16 @@ test.describe('MenuList', () => { test.describe('`change` event', () => { test('should emit `change` event when `checked` property changed', async ({ fastPage }) => { const { element } = fastPage; - const menuItems = element.locator('fluent-menu-item'); + const menuItems = element.locator(MenuItemTagName); - await fastPage.setTemplate(/* html */ ` - - Menu Item 1 - Menu item 2 - Menu item 3 - Menu item 4 - - `); + await fastPage.setTemplate({ + innerHTML: /* html */ ` + <${MenuItemTagName} role="menuitemradio">Menu Item 1${MenuItemTagName}> + <${MenuItemTagName}>Menu item 2${MenuItemTagName}> + <${MenuItemTagName}>Menu item 3${MenuItemTagName}> + <${MenuItemTagName}>Menu item 4${MenuItemTagName}> + `, + }); const [wasChanged] = await Promise.all([ menuItems @@ -616,16 +621,18 @@ test.describe('MenuList', () => { test('should emit change event when menu-item checked and unchecked', async ({ fastPage }) => { const { element } = fastPage; - const menuItems = element.locator('fluent-menu-item'); - - await fastPage.setTemplate(/* html */ ` - - Menu Item 1 - Menu item 2 - Menu item 3 - Menu item 4 - - `); + const menuItems = element.locator(MenuItemTagName); + + await fastPage.setTemplate({ + innerHTML: /* html */ ` + <${tagName}> + <${MenuItemTagName} role="menuitemradio">Menu Item 1${MenuItemTagName}> + <${MenuItemTagName} checked role="menuitemradio">Menu item 2${MenuItemTagName}> + <${MenuItemTagName} role="menuitemradio">Menu item 3${MenuItemTagName}> + <${MenuItemTagName} role="menuitemradio">Menu item 4${MenuItemTagName}> + ${tagName}> + `, + }); let wasChanged = menuItems.nth(0).evaluate((node: MenuItem) => { return new Promise(resolve => { diff --git a/packages/web-components/src/menu/menu.spec.ts b/packages/web-components/src/menu/menu.spec.ts index 90aa168ed5d2de..01aa6d3a6b0c4f 100644 --- a/packages/web-components/src/menu/menu.spec.ts +++ b/packages/web-components/src/menu/menu.spec.ts @@ -1,18 +1,24 @@ import { expect, test } from '../../test/playwright/index.js'; +import { tagName as ButtonTagName } from '../button/button.options.js'; +import { tagName as DividerTagName } from '../divider/divider.options.js'; +import { tagName as MenuButtonTagName } from '../menu-button/menu-button.options.js'; +import { tagName as MenuItemTagName } from '../menu-item/menu-item.options.js'; +import { tagName as MenuListTagName } from '../menu-list/menu-list.options.js'; +import { tagName } from './menu.options.js'; test.describe('Menu', () => { test.use({ innerHTML: /* html */ ` - Toggle Menu - - Menu item 1 - Menu item 2 - Menu item 3 - Menu item 4 - + <${MenuButtonTagName} appearance="primary" slot="trigger">Toggle Menu${MenuButtonTagName}> + <${MenuListTagName}> + <${MenuItemTagName}>Menu item 1${MenuItemTagName}> + <${MenuItemTagName}>Menu item 2${MenuItemTagName}> + <${MenuItemTagName}>Menu item 3${MenuItemTagName}> + <${MenuItemTagName}>Menu item 4${MenuItemTagName}> + ${MenuListTagName}> `, - tagName: 'fluent-menu', - waitFor: ['fluent-menu-list', 'fluent-menu-item', 'fluent-menu-button'], + tagName, + waitFor: [MenuListTagName, MenuItemTagName, MenuButtonTagName, DividerTagName, ButtonTagName], }); test('should create with document.createElement()', async ({ page, fastPage }) => { @@ -24,24 +30,28 @@ test.describe('Menu', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-menu'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); test('should have menu-list be initially hidden', async ({ fastPage }) => { const { element } = fastPage; - const menuList = element.locator('fluent-menu-list'); + const menuList = element.locator(MenuListTagName); + + await fastPage.setTemplate(); await expect(menuList).toBeHidden(); }); test('should be visible when the button is clicked', async ({ fastPage }) => { const { element } = fastPage; - const menuButton = element.locator('fluent-menu-button'); - const menuList = element.locator('fluent-menu-list'); + const menuButton = element.locator(MenuButtonTagName); + const menuList = element.locator(MenuListTagName); + + await fastPage.setTemplate(); await menuButton.click(); @@ -50,8 +60,10 @@ test.describe('Menu', () => { test('should be hidden when the button is clicked again', async ({ fastPage }) => { const { element } = fastPage; - const menuButton = element.locator('fluent-menu-button'); - const menuList = element.locator('fluent-menu-list'); + const menuButton = element.locator(MenuButtonTagName); + const menuList = element.locator(MenuListTagName); + + await fastPage.setTemplate(); await menuButton.click(); @@ -64,14 +76,18 @@ test.describe('Menu', () => { test('should be hidden when an item is clicked', async ({ fastPage }) => { const { element } = fastPage; - const menuButton = element.locator('fluent-menu-button'); - const menuList = element.locator('fluent-menu-list'); - const menuItems = menuList.locator('fluent-menu-item'); + const menuButton = element.locator(MenuButtonTagName); + const menuList = element.locator(MenuListTagName); + const menuItems = menuList.locator(MenuItemTagName); + + await fastPage.setTemplate(); await menuButton.click(); await expect(menuList).toBeVisible(); + await expect(menuItems.first()).toBeFocused(); + await menuItems.first().click(); await expect(menuList).toBeHidden(); @@ -79,15 +95,17 @@ test.describe('Menu', () => { test('should close when an item is focused and the enter key is pressed', async ({ fastPage, page }) => { const { element } = fastPage; - const menuButton = element.locator('fluent-menu-button'); - const menuList = element.locator('fluent-menu-list'); - const menuItems = menuList.locator('fluent-menu-item'); + const menuButton = element.locator(MenuButtonTagName); + const menuList = element.locator(MenuListTagName); + const menuItems = menuList.locator(MenuItemTagName); + + await fastPage.setTemplate(); await menuButton.click(); await expect(menuList).toBeVisible(); - await menuItems.first().focus(); + await expect(menuItems.first()).toBeFocused(); await page.keyboard.press('Enter'); @@ -96,15 +114,17 @@ test.describe('Menu', () => { test('should close when an item is focused and the escape key is pressed', async ({ fastPage, page }) => { const { element } = fastPage; - const menuButton = element.locator('fluent-menu-button'); - const menuList = element.locator('fluent-menu-list'); - const menuItems = menuList.locator('fluent-menu-item'); + const menuButton = element.locator(MenuButtonTagName); + const menuList = element.locator(MenuListTagName); + const menuItems = menuList.locator(MenuItemTagName); + + await fastPage.setTemplate(); await menuButton.click(); await expect(menuList).toBeVisible(); - await menuItems.first().focus(); + await expect(menuItems.first()).toBeFocused(); await page.keyboard.press('Escape'); @@ -113,13 +133,18 @@ test.describe('Menu', () => { test('should close when the mouse is clicked outside the menu', async ({ fastPage, page }) => { const { element } = fastPage; - const menuButton = element.locator('fluent-menu-button'); - const menuList = element.locator('fluent-menu-list'); + const menuButton = element.locator(MenuButtonTagName); + const menuList = element.locator(MenuListTagName); + const menuItems = menuList.locator(MenuItemTagName); + + await fastPage.setTemplate(); await menuButton.click(); await expect(menuList).toBeVisible(); + await expect(menuItems.first()).toBeFocused(); + await page.mouse.click(0, 0); await expect(menuList).toBeHidden(); @@ -127,13 +152,18 @@ test.describe('Menu', () => { test('should close when the escape key is pressed', async ({ fastPage, page }) => { const { element } = fastPage; - const menuButton = element.locator('fluent-menu-button'); - const menuList = element.locator('fluent-menu-list'); + const menuButton = element.locator(MenuButtonTagName); + const menuList = element.locator(MenuListTagName); + const menuItems = menuList.locator(MenuItemTagName); + + await fastPage.setTemplate(); await menuButton.click(); await expect(menuList).toBeVisible(); + await expect(menuItems.first()).toBeFocused(); + await page.keyboard.press('Escape'); await expect(menuList).toBeHidden(); @@ -141,13 +171,16 @@ test.describe('Menu', () => { test('should close when the menu list loses keyboard focus', async ({ fastPage, page }) => { const { element } = fastPage; - const menuButton = element.locator('fluent-menu-button'); - const menuList = element.locator('fluent-menu-list'); - const menuItems = element.locator('fluent-menu-item'); + const menuButton = element.locator(MenuButtonTagName); + const menuList = element.locator(MenuListTagName); + const menuItems = element.locator(MenuItemTagName); + + await fastPage.setTemplate(); await menuButton.click(); await expect(menuList).toBeVisible(); + await expect(menuItems.nth(0)).toBeFocused(); await page.keyboard.press('Tab'); @@ -157,8 +190,10 @@ test.describe('Menu', () => { test('should NOT open on hover when the `openOnHover` property is false', async ({ fastPage }) => { const { element } = fastPage; - const menuButton = element.locator('fluent-menu-button'); - const menuList = element.locator('fluent-menu-list'); + const menuButton = element.locator(MenuButtonTagName); + const menuList = element.locator(MenuListTagName); + + await fastPage.setTemplate(); await expect(menuList).toBeHidden(); @@ -169,8 +204,8 @@ test.describe('Menu', () => { test('should open on hover when the `openOnHover` property is true', async ({ fastPage }) => { const { element } = fastPage; - const menuButton = element.locator('fluent-menu-button'); - const menuList = element.locator('fluent-menu-list'); + const menuButton = element.locator(MenuButtonTagName); + const menuList = element.locator(MenuListTagName); await fastPage.setTemplate({ attributes: { 'open-on-hover': true } }); @@ -183,19 +218,22 @@ test.describe('Menu', () => { test('should NOT open on context when the `openOnContext` property is false', async ({ fastPage }) => { const { element } = fastPage; - const menuButton = element.locator('fluent-menu-button'); - const menuList = element.locator('fluent-menu-list'); + const menuButton = element.locator(MenuButtonTagName); + const menuList = element.locator(MenuListTagName); + + await fastPage.setTemplate(); await expect(menuList).toBeHidden(); await menuButton.click({ button: 'right' }); + await expect(menuList).toBeHidden(); }); test('should open on context when the `openOnContext` property is true', async ({ fastPage }) => { const { element } = fastPage; - const menuButton = element.locator('fluent-menu-button'); - const menuList = element.locator('fluent-menu-list'); + const menuButton = element.locator(MenuButtonTagName); + const menuList = element.locator(MenuListTagName); await fastPage.setTemplate({ attributes: { 'open-on-context': true } }); @@ -208,67 +246,64 @@ test.describe('Menu', () => { test('should set popover attribute on slotted submenu', async ({ fastPage }) => { const { element } = fastPage; - - const menuButton = element.locator('fluent-menu-button'); - const menuList = element.locator('fluent-menu-list:not([slot])'); - const menuItems = menuList.locator('fluent-menu-item'); - - const submenuList = element.locator('fluent-menu-list[slot="submenu"]'); + const menuButton = element.locator(MenuButtonTagName); + const menuList = element.locator(`${MenuListTagName}:not([slot])`); + const menuItems = menuList.locator(MenuItemTagName); + const submenuList = element.locator(`${MenuListTagName}[slot="submenu"]`); await fastPage.setTemplate({ innerHTML: /* html */ ` - Toggle Menu - - + <${MenuButtonTagName} appearance="primary" slot="trigger">Toggle Menu${MenuButtonTagName}> + <${MenuListTagName}> + <${MenuItemTagName}> Menu item 1 - - Subitem 1 - Subitem 2 - Subitem 3 - - - Menu item 2 - Menu item 3 - Menu item 4 - + <${MenuListTagName} slot="submenu"> + <${MenuItemTagName}> Subitem 1 ${MenuItemTagName}> + <${MenuItemTagName}> Subitem 2 ${MenuItemTagName}> + <${MenuItemTagName}> Subitem 3 ${MenuItemTagName}> + ${MenuListTagName}> + ${MenuItemTagName}> + <${MenuItemTagName}>Menu item 2${MenuItemTagName}> + <${MenuItemTagName}>Menu item 3${MenuItemTagName}> + <${MenuItemTagName}>Menu item 4${MenuItemTagName}> + ${MenuListTagName}> `, }); await menuButton.click(); - await menuItems.first().focus(); + await expect(menuItems.first()).toBeFocused(); await element.press('ArrowRight'); await expect(submenuList).toBeVisible(); + await expect(submenuList).toHaveAttribute('popover'); }); test('should focus the first item when a submenu is closed', async ({ fastPage }) => { const { element } = fastPage; - - const menuButton = element.locator('fluent-menu-button'); - const menuList = element.locator('fluent-menu-list:not([slot])'); - const menuItems = menuList.locator('fluent-menu-item'); - - const submenuList = element.locator('fluent-menu-list[slot="submenu"]'); + const menuButton = element.locator(MenuButtonTagName); + const menuList = element.locator(`${MenuListTagName}:not([slot])`); + const menuItems = menuList.locator(MenuItemTagName); + const submenuList = element.locator(`${MenuListTagName}[slot="submenu"]`); await fastPage.setTemplate({ innerHTML: /* html */ ` - Toggle Menu - - + <${MenuButtonTagName} appearance="primary" slot="trigger">Toggle Menu${MenuButtonTagName}> + <${MenuListTagName}> + <${MenuItemTagName}> Menu item 1 - - Subitem 1 - Subitem 2 - Subitem 3 - - - Menu item 2 - Menu item 3 - Menu item 4 - + <${MenuListTagName} slot="submenu"> + <${MenuItemTagName}> Subitem 1 ${MenuItemTagName}> + <${MenuItemTagName}> Subitem 2 ${MenuItemTagName}> + <${MenuItemTagName}> Subitem 3 ${MenuItemTagName}> + ${MenuListTagName}> + ${MenuItemTagName}> + <${MenuItemTagName}>Menu item 2${MenuItemTagName}> + <${MenuItemTagName}>Menu item 3${MenuItemTagName}> + <${MenuItemTagName}>Menu item 4${MenuItemTagName}> + ${MenuListTagName}> `, }); @@ -278,7 +313,7 @@ test.describe('Menu', () => { await expect(submenuList).toBeHidden(); - await menuItems.first().focus(); + await expect(menuItems.first()).toBeFocused(); await element.press('ArrowRight'); @@ -295,38 +330,42 @@ test.describe('Menu', () => { test('should focus trigger after menu is closed', async ({ fastPage, page }) => { const { element } = fastPage; - const menuButton = element.locator('fluent-menu-button'); - - await fastPage.setTemplate(/* html */ ` - - - Primary Action - - + const menuButton = element.locator(MenuButtonTagName); + const menuList = element.locator(MenuListTagName); + const menuItems = menuList.locator(MenuItemTagName); + + await fastPage.setTemplate({ + innerHTML: /* html */ ` + <${MenuButtonTagName} appearance="primary" slot="trigger" icon-only>${MenuButtonTagName}> + <${ButtonTagName} appearance="primary" slot="primary-action">Primary Action${ButtonTagName}> + <${MenuListTagName}> + <${MenuItemTagName}> Item 1 - - Subitem 1 - Subitem 2 - - + <${MenuListTagName} slot="submenu"> + <${MenuItemTagName}> Subitem 1 ${MenuItemTagName}> + <${MenuItemTagName}> Subitem 2 ${MenuItemTagName}> + ${MenuListTagName}> + ${MenuItemTagName}> - Item 2 - Item 3 + <${MenuItemTagName} role="menuitemcheckbox"> Item 2 ${MenuItemTagName}> + <${MenuItemTagName} role="menuitemcheckbox"> Item 3 ${MenuItemTagName}> - + <${DividerTagName} role="separator" aria-orientation="horizontal" orientation="horizontal">${DividerTagName}> - Menu item 4 - Menu item 5 - Menu item 6 + <${MenuItemTagName}>Menu item 4${MenuItemTagName}> + <${MenuItemTagName}>Menu item 5${MenuItemTagName}> + <${MenuItemTagName}>Menu item 6${MenuItemTagName}> - Menu item 7 - Menu item 8 - - - `); + <${MenuItemTagName}>Menu item 7${MenuItemTagName}> + <${MenuItemTagName}>Menu item 8${MenuItemTagName}> + ${MenuListTagName}> + `, + }); await menuButton.click(); + await expect(menuItems.nth(0)).toBeFocused(); + await page.keyboard.press('Escape'); await expect(menuButton).toBeFocused(); diff --git a/packages/web-components/src/message-bar/message-bar.spec.ts b/packages/web-components/src/message-bar/message-bar.spec.ts index e255b2d3b73c91..907210cc913d92 100644 --- a/packages/web-components/src/message-bar/message-bar.spec.ts +++ b/packages/web-components/src/message-bar/message-bar.spec.ts @@ -1,11 +1,11 @@ import { expect, test } from '../../test/playwright/index.js'; import type { MessageBar } from './message-bar.js'; -import { MessageBarIntent, MessageBarLayout, MessageBarShape } from './message-bar.options.js'; +import { MessageBarIntent, MessageBarLayout, MessageBarShape, tagName } from './message-bar.options.js'; test.describe('Message Bar', () => { test.use({ - tagName: 'fluent-message-bar', - waitFor: ['fluent-button'], + tagName, + innerHTML: 'Message Bar', }); test('should create with document.createElement()', async ({ page, fastPage }) => { @@ -17,9 +17,9 @@ test.describe('Message Bar', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-message-bar'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -27,15 +27,19 @@ test.describe('Message Bar', () => { test('should include a role of status', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('elementInternals.role', 'status'); }); test('should set the `intent` property to match the `intent` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const intent of Object.values(MessageBarIntent)) { await test.step(intent, async () => { - await fastPage.setTemplate({ attributes: { intent } }); + await fastPage.updateTemplate(element, { attributes: { intent } }); await expect(element).toHaveJSProperty('intent', intent); @@ -47,9 +51,11 @@ test.describe('Message Bar', () => { test('should set the `shape` property to match the `shape` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const shape of Object.values(MessageBarShape)) { await test.step(shape, async () => { - await fastPage.setTemplate({ attributes: { shape } }); + await fastPage.updateTemplate(element, { attributes: { shape } }); await expect(element).toHaveJSProperty('shape', shape); @@ -58,13 +64,14 @@ test.describe('Message Bar', () => { } }); - // @FIXME: This test is failing on OSX - https://github.com/microsoft/fluentui/issues/33172 test('should set the `layout` property to match the `layout` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const layout of Object.values(MessageBarLayout)) { await test.step(layout, async () => { - await fastPage.setTemplate({ attributes: { layout } }); + await fastPage.updateTemplate(element, { attributes: { layout } }); await expect(element).toHaveJSProperty('layout', layout); @@ -76,6 +83,8 @@ test.describe('Message Bar', () => { test('should emit a `dismiss` event when `dismissMessageBar()` is called', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + const didDismiss = element.evaluate( node => new Promise(resolve => node.addEventListener('dismiss', () => resolve(true))), ); diff --git a/packages/web-components/src/option/option.spec.ts b/packages/web-components/src/option/option.spec.ts index 13574e5ca3869f..f7617f3ca42f81 100644 --- a/packages/web-components/src/option/option.spec.ts +++ b/packages/web-components/src/option/option.spec.ts @@ -1,9 +1,10 @@ import { expect, test } from '../../test/playwright/index.js'; import type { DropdownOption } from './option.js'; +import { tagName } from './option.options.js'; test.describe('DropdownOption', () => { test.use({ - tagName: 'fluent-option', + tagName, innerHTML: 'Option', }); @@ -16,9 +17,9 @@ test.describe('DropdownOption', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-option'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -160,7 +161,7 @@ test.describe('DropdownOption', () => { await fastPage.setTemplate(/* html */ ` - Hello + <${tagName} name="option" value="hello" selected>Hello${tagName}> `); diff --git a/packages/web-components/src/progress-bar/progress-bar.spec.ts b/packages/web-components/src/progress-bar/progress-bar.spec.ts index fa6f1823f02960..66e5bab756af15 100644 --- a/packages/web-components/src/progress-bar/progress-bar.spec.ts +++ b/packages/web-components/src/progress-bar/progress-bar.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from '../../test/playwright/index.js'; import type { ProgressBar } from './progress-bar.js'; -import { ProgressBarShape, ProgressBarThickness, ProgressBarValidationState } from './progress-bar.options.js'; +import { ProgressBarShape, ProgressBarThickness, ProgressBarValidationState, tagName } from './progress-bar.options.js'; interface BoundingBox { width: number; @@ -8,7 +8,7 @@ interface BoundingBox { test.describe('Progress Bar', () => { test.use({ - tagName: 'fluent-progress-bar', + tagName, }); test('should create with document.createElement()', async ({ page, fastPage }) => { @@ -20,9 +20,9 @@ test.describe('Progress Bar', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-progress-bar'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -30,6 +30,8 @@ test.describe('Progress Bar', () => { test('should include a role of progressbar', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('elementInternals.role', 'progressbar'); }); @@ -59,6 +61,9 @@ test.describe('Progress Bar', () => { test('should set indicator width to be 1/3 of the container width if `value` is missing', async ({ fastPage }) => { const { element } = fastPage; + + await fastPage.setTemplate(); + await element.evaluate(node => { node.style.setProperty('width', '100px'); }); diff --git a/packages/web-components/src/radio-group/radio-group.spec.ts b/packages/web-components/src/radio-group/radio-group.spec.ts index 56dea1212bc9ff..6b581d079c232b 100644 --- a/packages/web-components/src/radio-group/radio-group.spec.ts +++ b/packages/web-components/src/radio-group/radio-group.spec.ts @@ -1,12 +1,13 @@ import { expect, test } from '../../test/playwright/index.js'; import type { Radio } from '../radio/index.js'; +import { tagName as RadioTagName } from '../radio/radio.options.js'; import type { RadioGroup } from './radio-group.js'; +import { tagName } from './radio-group.options.js'; test.describe('RadioGroup', () => { test.use({ - tagName: 'fluent-radio-group', - waitFor: ['fluent-radio'], - innerHTML: '', + tagName, + waitFor: [RadioTagName], }); test('should create with document.createElement()', async ({ page, fastPage }) => { @@ -18,9 +19,9 @@ test.describe('RadioGroup', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-radio-group'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -67,13 +68,13 @@ test.describe('RadioGroup', () => { test('should set the `aria-setsize` and `aria-posinset` attributes on the radios', async ({ fastPage }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); await fastPage.setTemplate({ innerHTML: /* html */ ` - - - + <${RadioTagName}>${RadioTagName}> + <${RadioTagName}>${RadioTagName}> + <${RadioTagName}>${RadioTagName}> `, }); @@ -91,16 +92,16 @@ test.describe('RadioGroup', () => { fastPage, }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); const firstRadio = radios.nth(0); const secondRadio = radios.nth(1); const thirdRadio = radios.nth(2); await fastPage.setTemplate({ innerHTML: /* html */ ` - - - + <${RadioTagName}>${RadioTagName}> + <${RadioTagName} disabled>${RadioTagName}> + <${RadioTagName}>${RadioTagName}> `, }); @@ -133,11 +134,11 @@ test.describe('RadioGroup', () => { await fastPage.setTemplate(/* html */ ` First - - - - - + <${tagName} disabled> + <${RadioTagName}>${RadioTagName}> + <${RadioTagName}>${RadioTagName}> + <${RadioTagName}>${RadioTagName}> + ${tagName}> Second `); @@ -154,16 +155,16 @@ test.describe('RadioGroup', () => { test('should NOT be focusable via click when disabled', async ({ fastPage, page }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); const button = page.locator('button', { hasText: 'Button' }); await fastPage.setTemplate(/* html */ ` Button - - - - - + <${tagName}> + <${RadioTagName}>${RadioTagName}> + <${RadioTagName}>${RadioTagName}> + <${RadioTagName}>${RadioTagName}> + ${tagName}> `); await button.focus(); @@ -200,16 +201,16 @@ test.describe('RadioGroup', () => { test('should set tabindex of 0 to a child radio with a matching `value`', async ({ fastPage }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); await fastPage.setTemplate({ attributes: { value: 'foo', }, innerHTML: /* html */ ` - - - + <${RadioTagName} value="foo">${RadioTagName}> + <${RadioTagName} value="bar">${RadioTagName}> + <${RadioTagName} value="baz">${RadioTagName}> `, }); @@ -218,13 +219,13 @@ test.describe('RadioGroup', () => { test("should set tabindex of 0 to a child radio that's initially checked", async ({ fastPage }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); await fastPage.setTemplate({ innerHTML: /* html */ ` - - - + <${RadioTagName} value="foo">${RadioTagName}> + <${RadioTagName} value="bar" checked>${RadioTagName}> + <${RadioTagName} value="baz">${RadioTagName}> `, }); @@ -233,13 +234,13 @@ test.describe('RadioGroup', () => { test('should check the first radio with a matching `value`', async ({ fastPage }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); await fastPage.setTemplate(/* html */ ` - - - - + <${tagName} value="bar"> + <${RadioTagName} id="radio-1" name="radio" value="foo">${RadioTagName}> + <${RadioTagName} id="radio-2" name="radio" value="bar">${RadioTagName}> + <${RadioTagName} id="radio-3" name="radio" value="baz">${RadioTagName}> `); await expect(radios.nth(0)).toHaveJSProperty('checked', false); @@ -254,14 +255,14 @@ test.describe('RadioGroup', () => { page, }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); await fastPage.setTemplate(/* html */ ` - - - - - + <${tagName}> + <${RadioTagName} id="radio-1" name="radio" value="foo">${RadioTagName}> + <${RadioTagName} id="radio-2" name="radio" value="bar">${RadioTagName}> + <${RadioTagName} id="radio-3" name="radio" value="baz">${RadioTagName}> + ${tagName}> `); await radios.nth(0).evaluate((node: Radio) => { @@ -283,16 +284,16 @@ test.describe('RadioGroup', () => { }); test('should emit `change` event when using keyboard', async ({ fastPage, page }) => { - const element = page.locator('fluent-radio-group'); - const radios = element.locator('fluent-radio'); + const element = page.locator(tagName); + const radios = element.locator(RadioTagName); - await fastPage.setTemplate(/* html */ ` - - - - - - `); + await fastPage.setTemplate({ + innerHTML: /* html */ ` + <${RadioTagName} id="radio-1" name="radio" value="foo">${RadioTagName}> + <${RadioTagName} id="radio-2" name="radio" value="bar">${RadioTagName}> + <${RadioTagName} id="radio-3" name="radio" value="baz">${RadioTagName}> + `, + }); const wasChanged = element.evaluate((node: RadioGroup) => { return new Promise(resolve => { @@ -308,16 +309,16 @@ test.describe('RadioGroup', () => { test('should set a child radio with a matching `value` to `checked` when value changes', async ({ fastPage }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); await fastPage.setTemplate({ attributes: { value: 'foo', }, innerHTML: /* html */ ` - - - + <${RadioTagName} value="foo">${RadioTagName}> + <${RadioTagName} value="bar">${RadioTagName}> + <${RadioTagName} value="baz">${RadioTagName}> `, }); @@ -334,13 +335,13 @@ test.describe('RadioGroup', () => { test('should mark only the last radio defaulted to checked as checked', async ({ fastPage }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); await fastPage.setTemplate({ innerHTML: /* html */ ` - - - + <${RadioTagName} value="foo" checked>${RadioTagName}> + <${RadioTagName} value="bar" checked>${RadioTagName}> + <${RadioTagName} value="baz" checked>${RadioTagName}> `, }); @@ -355,14 +356,14 @@ test.describe('RadioGroup', () => { test('should mark radio matching value on radio-group over any checked attributes', async ({ fastPage }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); await fastPage.setTemplate({ attributes: { value: 'foo' }, innerHTML: /* html */ ` - - - + <${RadioTagName} value="foo">${RadioTagName}> + <${RadioTagName} value="bar" checked>${RadioTagName}> + <${RadioTagName} value="baz">${RadioTagName}> `, }); @@ -379,15 +380,15 @@ test.describe('RadioGroup', () => { test('should allow resetting of elements by the parent form', async ({ fastPage, page }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); await fastPage.setTemplate(/* html */ ` - - - - - + <${tagName}> + <${RadioTagName} name="radio" value="foo">${RadioTagName}> + <${RadioTagName} name="radio" value="bar" checked>${RadioTagName}> + <${RadioTagName} name="radio" value="baz">${RadioTagName}> + ${tagName}> `); @@ -420,13 +421,13 @@ test.describe('RadioGroup', () => { test('should focus the first radio when the radio group is focused', async ({ fastPage, page }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); await fastPage.setTemplate({ innerHTML: /* html */ ` - - - + <${RadioTagName}>${RadioTagName}> + <${RadioTagName}>${RadioTagName}> + <${RadioTagName}>${RadioTagName}> `, }); @@ -440,13 +441,13 @@ test.describe('RadioGroup', () => { page, }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); await fastPage.setTemplate({ innerHTML: /* html */ ` - - - + <${RadioTagName} disabled>${RadioTagName}> + <${RadioTagName}>${RadioTagName}> + <${RadioTagName}>${RadioTagName}> `, }); @@ -460,13 +461,13 @@ test.describe('RadioGroup', () => { page, }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); await fastPage.setTemplate({ innerHTML: /* html */ ` - - - + <${RadioTagName} disabled>${RadioTagName}> + <${RadioTagName} disabled>${RadioTagName}> + <${RadioTagName}>${RadioTagName}> `, }); @@ -483,9 +484,9 @@ test.describe('RadioGroup', () => { await fastPage.setTemplate({ innerHTML: /* html */ ` - - - + <${RadioTagName} disabled>${RadioTagName}> + <${RadioTagName} disabled>${RadioTagName}> + <${RadioTagName} disabled>${RadioTagName}> `, }); @@ -494,21 +495,20 @@ test.describe('RadioGroup', () => { await expect(element).not.toBeFocused(); }); - // @FIXME: This test is failing on OSX - https://github.com/microsoft/fluentui/issues/33172 test('should move focus to the next radio when the radio group is focused and the arrow down key is pressed', async ({ fastPage, page, }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); await fastPage.setTemplate(/* html */ ` before - - - - - + <${tagName}> + <${RadioTagName}>${RadioTagName}> + <${RadioTagName}>${RadioTagName}> + <${RadioTagName}>${RadioTagName}> + ${tagName}> `); await page.getByTestId('before').focus(); @@ -540,9 +540,9 @@ test.describe('RadioGroup', () => { await fastPage.setTemplate({ innerHTML: /* html */ ` - - - + <${RadioTagName} name="foo">${RadioTagName}> + <${RadioTagName} name="foo">${RadioTagName}> + <${RadioTagName} name="foo">${RadioTagName}> `, }); @@ -556,28 +556,27 @@ test.describe('RadioGroup', () => { await fastPage.setTemplate({ innerHTML: /* html */ ` - - - + <${RadioTagName} name="foo">${RadioTagName}> + <${RadioTagName} name="bar">${RadioTagName}> + <${RadioTagName} name="baz">${RadioTagName}> `, }); await expect(element).not.toHaveAttribute('name'); }); - // @FIXME: This test is failing on OSX - https://github.com/microsoft/fluentui/issues/33172 test('should set the `name` attribute of the radios to the `name` attribute of the radio group', async ({ fastPage, }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); await fastPage.setTemplate({ attributes: { name: 'foo' }, innerHTML: /* html */ ` - - - + <${RadioTagName}>${RadioTagName}> + <${RadioTagName}>${RadioTagName}> + <${RadioTagName}>${RadioTagName}> `, }); @@ -592,14 +591,14 @@ test.describe('RadioGroup', () => { fastPage, }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); await fastPage.setTemplate({ attributes: { name: 'foo' }, innerHTML: /* html */ ` - - - + <${RadioTagName} name="bar">${RadioTagName}> + <${RadioTagName} name="baz">${RadioTagName}> + <${RadioTagName} name="qux">${RadioTagName}> `, }); @@ -617,15 +616,15 @@ test.describe('RadioGroup', () => { page, }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); await fastPage.setTemplate(/* html */ ` - - - - - + <${tagName} name="radio"> + <${RadioTagName} value="foo">${RadioTagName}> + <${RadioTagName} value="bar">${RadioTagName}> + <${RadioTagName} value="baz">${RadioTagName}> + ${tagName}> submit `); @@ -644,15 +643,15 @@ test.describe('RadioGroup', () => { page, }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); await fastPage.setTemplate(/* html */ ` - - - - - + <${tagName} name="radio" disabled> + <${RadioTagName} value="foo">${RadioTagName}> + <${RadioTagName} value="bar">${RadioTagName}> + <${RadioTagName} value="baz">${RadioTagName}> + ${tagName}> submit `); @@ -671,15 +670,15 @@ test.describe('RadioGroup', () => { page, }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); await fastPage.setTemplate(/* html */ ` - - - - - + <${tagName}> + <${RadioTagName} value="foo">${RadioTagName}> + <${RadioTagName} value="bar">${RadioTagName}> + <${RadioTagName} value="baz">${RadioTagName}> + ${tagName}> submit `); @@ -699,7 +698,7 @@ test.describe('RadioGroup', () => { }) => { await fastPage.setTemplate(/* html */ ` - + <${tagName} name="radio" value="foo">${tagName}> submit `); @@ -716,16 +715,16 @@ test.describe('RadioGroup', () => { page, }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); const button = page.locator('button'); await fastPage.setTemplate(/* html */ ` - - - - - + <${tagName} name="radio"> + <${RadioTagName} disabled value="foo">${RadioTagName}> + <${RadioTagName} disabled value="bar">${RadioTagName}> + <${RadioTagName} disabled value="baz">${RadioTagName}> + ${tagName}> submit `); @@ -744,16 +743,16 @@ test.describe('RadioGroup', () => { page, }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); const before = page.getByTestId('before'); await fastPage.setTemplate(/* html */ ` before - - - - - + <${tagName} name="radio"> + <${RadioTagName} value="foo">${RadioTagName}> + <${RadioTagName} value="bar">${RadioTagName}> + <${RadioTagName} value="baz">${RadioTagName}> + ${tagName}> `); await before.focus(); @@ -771,16 +770,16 @@ test.describe('RadioGroup', () => { page, }) => { const { element } = fastPage; - const radios = element.locator('fluent-radio'); + const radios = element.locator(RadioTagName); const before = page.getByTestId('before'); await fastPage.setTemplate(/* html */ ` before - - - - - + <${tagName} name="radio"> + <${RadioTagName} value="foo">${RadioTagName}> + <${RadioTagName} value="bar">${RadioTagName}> + <${RadioTagName} value="baz">${RadioTagName}> + ${tagName}> `); await before.focus(); diff --git a/packages/web-components/src/radio/radio.spec.ts b/packages/web-components/src/radio/radio.spec.ts index e66920b60ab9da..7d329a057054de 100644 --- a/packages/web-components/src/radio/radio.spec.ts +++ b/packages/web-components/src/radio/radio.spec.ts @@ -1,8 +1,11 @@ import { expect, test } from '../../test/playwright/index.js'; import type { Radio } from './radio.js'; +import { tagName } from './radio.options.js'; test.describe('Radio', () => { - test.use({ tagName: 'fluent-radio' }); + test.use({ + tagName, + }); test('should have a role of `radio`', async ({ fastPage }) => { const { element } = fastPage; @@ -21,9 +24,9 @@ test.describe('Radio', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-radio'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -87,12 +90,12 @@ test.describe('Radio', () => { test('should initialize to the provided value attribute if set pre-connection', async ({ fastPage, page }) => { await fastPage.setTemplate(''); - const value = await page.evaluate(() => { - const radio = document.createElement('fluent-radio') as Radio; + const value = await page.evaluate(tagName => { + const radio = document.createElement(tagName) as Radio; radio.setAttribute('value', 'foo'); return radio.value; - }); + }, tagName); expect(value).toBe('foo'); }); @@ -157,7 +160,7 @@ test.describe('Radio', () => { await fastPage.setTemplate(/* html */ ` - Radio + <${tagName}>Radio${tagName}> `); @@ -180,7 +183,7 @@ test.describe('Radio', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} checked>${tagName}> `); @@ -212,7 +215,7 @@ test.describe('Radio', () => { await fastPage.setTemplate(/* html */ ` - Radio + <${tagName}>Radio${tagName}> `); @@ -242,7 +245,7 @@ test.describe('Radio', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName}>${tagName}> `); @@ -267,7 +270,7 @@ test.describe('Radio', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName}>${tagName}> `); @@ -295,7 +298,7 @@ test.describe('Radio', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} required>${tagName}> `); @@ -325,7 +328,7 @@ test.describe('Radio', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} name="radio" value="foo">${tagName}> submit `); diff --git a/packages/web-components/src/rating-display/rating-display.spec.ts b/packages/web-components/src/rating-display/rating-display.spec.ts index 388350316676ea..d21bac63afafe8 100644 --- a/packages/web-components/src/rating-display/rating-display.spec.ts +++ b/packages/web-components/src/rating-display/rating-display.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from '../../test/playwright/index.js'; -import { RatingDisplaySize } from './rating-display.options.js'; import type { RatingDisplay } from './rating-display.js'; +import { RatingDisplaySize, tagName } from './rating-display.options.js'; function encodedSvg(str: string, browserName: string) { return encodeURIComponent(str) @@ -10,7 +10,7 @@ function encodedSvg(str: string, browserName: string) { test.describe('Rating Display', () => { test.use({ - tagName: 'fluent-rating-display', + tagName, }); test('should create with document.createElement()', async ({ page, fastPage }) => { @@ -22,15 +22,18 @@ test.describe('Rating Display', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-rating-display'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); test('should not set any default attributes and custom states', async ({ fastPage }) => { const { element } = fastPage; + + await fastPage.setTemplate(); + await expect(element).toBeVisible(); for (const attribute of ['color', 'compact', 'count', 'icon-view-box', 'max', 'size']) { await expect(element).not.toHaveAttribute(attribute); diff --git a/packages/web-components/src/slider/slider.spec.ts b/packages/web-components/src/slider/slider.spec.ts index e49fa9e18ef10d..01b35c8aa758e6 100644 --- a/packages/web-components/src/slider/slider.spec.ts +++ b/packages/web-components/src/slider/slider.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from '../../test/playwright/index.js'; import type { Slider } from './slider.js'; -import { SliderSize } from './slider.options.js'; +import { SliderSize, tagName } from './slider.options.js'; interface BoundingBox { x: number; @@ -11,11 +11,9 @@ interface BoundingBox { test.describe('Slider', () => { test.use({ - tagName: 'fluent-slider', + tagName, }); - // Foundation tests - test('should create with document.createElement()', async ({ page, fastPage }) => { await fastPage.setTemplate(); @@ -25,9 +23,9 @@ test.describe('Slider', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-slider'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -35,6 +33,8 @@ test.describe('Slider', () => { test('should have a default role of `slider`', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('elementInternals.role', 'slider'); }); @@ -43,6 +43,8 @@ test.describe('Slider', () => { }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('min', ''); await expect(element).toHaveJSProperty('max', ''); @@ -55,7 +57,7 @@ test.describe('Slider', () => { await fastPage.setTemplate(/* html */ ` Label 1 - + <${tagName} id="slider">${tagName}> Label 2 `); @@ -69,6 +71,8 @@ test.describe('Slider', () => { test('should set a `tabindex` of 0', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveAttribute('tabindex', '0'); }); @@ -77,12 +81,16 @@ test.describe('Slider', () => { }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('elementInternals.ariaOrientation', 'horizontal'); }); test('should initialize to the initial value if no value property is set', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('value', '50'); }); @@ -91,6 +99,8 @@ test.describe('Slider', () => { }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('elementInternals.ariaDisabled', 'false'); }); @@ -105,6 +115,8 @@ test.describe('Slider', () => { test('should set the `elementInternals.ariaDisabled` when `disabled` property is true', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('elementInternals.ariaDisabled', 'false'); await element.evaluate((node: Slider) => { @@ -129,7 +141,7 @@ test.describe('Slider', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName}>${tagName}> `); @@ -219,6 +231,8 @@ test.describe('Slider', () => { test('should allow setting value with number', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await element.evaluate((node: Slider) => { node.valueAsNumber = 8; }); @@ -240,6 +254,8 @@ test.describe('Slider', () => { }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await element.evaluate((node: Slider) => { node.valueTextFormatter = () => 'Seventy Five Years'; }); @@ -317,7 +333,7 @@ test.describe('Slider', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} min="0" max="100">${tagName}> `); @@ -343,7 +359,7 @@ test.describe('Slider', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} min="0" max="100">${tagName}> `); @@ -437,13 +453,13 @@ test.describe('Slider', () => { await fastPage.setTemplate(''); - await page.evaluate(() => { - const slider = document.createElement('fluent-slider') as Slider; + await page.evaluate(tagName => { + const slider = document.createElement(tagName) as Slider; slider.value = '3'; document.body.appendChild(slider); - }); + }, tagName); await expect(element).toHaveJSProperty('value', '3'); await expect(element).toHaveJSProperty('elementInternals.ariaValueNow', '3'); @@ -452,6 +468,8 @@ test.describe('Slider', () => { test('should initialize to the provided value attribute when set post-connection', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await element.evaluate((node: Slider) => { node.setAttribute('value', '3'); }); @@ -488,7 +506,7 @@ test.describe('Slider', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName}>${tagName}> `); @@ -515,7 +533,7 @@ test.describe('Slider', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} min="0" max="100">${tagName}> `); @@ -548,7 +566,7 @@ test.describe('Slider', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} min="0" max="100">${tagName}> `); @@ -580,6 +598,8 @@ test.describe('Slider', () => { test('should emit `change` event when `value` property changed', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + const wasChanged = element.evaluate( node => new Promise(resolve => node.addEventListener('change', () => resolve(true))), ); @@ -594,6 +614,8 @@ test.describe('Slider', () => { test('should emit `change` event if the `value` attribute changed', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + const wasChanged = element.evaluate( node => new Promise(resolve => node.addEventListener('change', () => resolve(true))), ); @@ -662,6 +684,8 @@ test.describe('Slider', () => { }) => { const { element } = fastPage; + await fastPage.setTemplate(); + const track = element.locator('.track'); const thumb = element.locator('.thumb-container'); const trackBox = (await track.boundingBox()) as BoundingBox; @@ -816,7 +840,7 @@ test.describe('Slider', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName}>${tagName}> `); @@ -847,6 +871,8 @@ test.describe('Slider', () => { const { element } = fastPage; const thumb = element.locator('.thumb-container'); + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('valueAsNumber', 50); await thumb.click(); diff --git a/packages/web-components/src/spinner/spinner.spec.ts b/packages/web-components/src/spinner/spinner.spec.ts index 693e25f65ec57d..ce446dbc7bbff3 100644 --- a/packages/web-components/src/spinner/spinner.spec.ts +++ b/packages/web-components/src/spinner/spinner.spec.ts @@ -1,9 +1,9 @@ import { expect, test } from '../../test/playwright/index.js'; -import { SpinnerAppearance, SpinnerSize } from './spinner.options.js'; +import { SpinnerAppearance, SpinnerSize, tagName } from './spinner.options.js'; test.describe('Spinner', () => { test.use({ - tagName: 'fluent-spinner', + tagName, }); test('should create with document.createElement()', async ({ page, fastPage }) => { @@ -15,9 +15,9 @@ test.describe('Spinner', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-spinner'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -25,9 +25,11 @@ test.describe('Spinner', () => { test('should set the `appearance` property to match the `appearance` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const appearance of Object.values(SpinnerAppearance)) { await test.step(appearance, async () => { - await fastPage.setTemplate({ attributes: { appearance } }); + await fastPage.updateTemplate(element, { attributes: { appearance } }); await expect(element).toHaveJSProperty('appearance', appearance); @@ -39,9 +41,11 @@ test.describe('Spinner', () => { test('should set the `size` property to match the `size` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const size of Object.values(SpinnerSize)) { await test.step(size, async () => { - await fastPage.setTemplate({ attributes: { size } }); + await fastPage.updateTemplate(element, { attributes: { size } }); await expect(element).toHaveJSProperty('size', size); diff --git a/packages/web-components/src/switch/switch.spec.ts b/packages/web-components/src/switch/switch.spec.ts index 3411289c7e1605..98478ce7fabb8d 100644 --- a/packages/web-components/src/switch/switch.spec.ts +++ b/packages/web-components/src/switch/switch.spec.ts @@ -1,8 +1,9 @@ import { expect, test } from '../../test/playwright/index.js'; import type { Switch } from './switch.js'; +import { tagName } from './switch.options.js'; test.describe('Switch', () => { - test.use({ tagName: 'fluent-switch' }); + test.use({ tagName: tagName }); test('should create with document.createElement()', async ({ page, fastPage }) => { await fastPage.setTemplate(); @@ -13,9 +14,9 @@ test.describe('Switch', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-switch'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -23,12 +24,16 @@ test.describe('Switch', () => { test('should have a role of `switch`', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('elementInternals.role', 'switch'); }); test('should set the `ariaChecked` property to `false` when `checked` is not defined', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).not.toHaveAttribute('checked'); await expect(element).toHaveJSProperty('elementInternals.ariaChecked', 'false'); @@ -51,6 +56,8 @@ test.describe('Switch', () => { test('should NOT set a default `aria-required` value when `required` is not defined', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).not.toHaveAttribute('required'); await expect(element).not.toHaveAttribute('aria-required'); @@ -59,6 +66,8 @@ test.describe('Switch', () => { test('should be focusable by default', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await element.focus(); await expect(element).toBeFocused(); @@ -77,6 +86,8 @@ test.describe('Switch', () => { test('should initialize to the initial value if no `value` property is set', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('value', 'on'); }); @@ -93,6 +104,8 @@ test.describe('Switch', () => { const expectedValue = 'foobar'; + await fastPage.setTemplate(); + await element.evaluate((node: Switch, expectedValue) => { node.setAttribute('value', expectedValue); }, expectedValue); @@ -101,17 +114,20 @@ test.describe('Switch', () => { }); test('should initialize to the provided `value` property when set pre-connection', async ({ fastPage, page }) => { - await fastPage.setTemplate(''); - const expectedValue = 'foobar'; - const value = await page.evaluate(expectedValue => { - const node = document.createElement('fluent-switch') as Switch; + await fastPage.setTemplate(''); + + const value = await page.evaluate( + ([expectedValue, tagName]) => { + const node = document.createElement(tagName) as Switch; - node.value = expectedValue; + node.value = expectedValue; - return node.value; - }, expectedValue); + return node.value; + }, + [expectedValue, tagName], + ); expect(value).toBe(expectedValue); }); @@ -121,7 +137,7 @@ test.describe('Switch', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} required>${tagName}> `); @@ -133,7 +149,7 @@ test.describe('Switch', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} required>${tagName}> `); @@ -150,7 +166,7 @@ test.describe('Switch', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName}>${tagName}> `); @@ -175,7 +191,7 @@ test.describe('Switch', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName}>${tagName}> `); @@ -203,7 +219,7 @@ test.describe('Switch', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} required>${tagName}> `); @@ -233,7 +249,7 @@ test.describe('Switch', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} name="switch" value="foo">${tagName}> submit `); @@ -246,15 +262,15 @@ test.describe('Switch', () => { }); test('should submit the values of multiple switches when checked', async ({ fastPage, page }) => { - const switches = page.locator('fluent-switch'); + const switches = page.locator(tagName); const element1 = switches.nth(0); const element2 = switches.nth(1); const submitButton = page.locator('button'); await fastPage.setTemplate(/* html */ ` - - + <${tagName} name="switch" value="foo">${tagName}> + <${tagName} name="switch" value="bar">${tagName}> submit `); diff --git a/packages/web-components/src/tab/tab.spec.ts b/packages/web-components/src/tab/tab.spec.ts index 2b46355e147092..5e905f3e874651 100644 --- a/packages/web-components/src/tab/tab.spec.ts +++ b/packages/web-components/src/tab/tab.spec.ts @@ -1,7 +1,11 @@ import { expect, test } from '../../test/playwright/index.js'; +import { tagName } from './tab.options.js'; test.describe('Tab', () => { - test.use({ tagName: 'fluent-tab' }); + test.use({ + tagName, + innerHTML: 'Tab', + }); test('should create with document.createElement()', async ({ page, fastPage }) => { await fastPage.setTemplate(''); @@ -12,9 +16,9 @@ test.describe('Tab', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-tab'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); diff --git a/packages/web-components/src/tablist/tablist.spec.ts b/packages/web-components/src/tablist/tablist.spec.ts index 7cbf89f3ea5d9e..6878e54aa64f96 100644 --- a/packages/web-components/src/tablist/tablist.spec.ts +++ b/packages/web-components/src/tablist/tablist.spec.ts @@ -1,20 +1,21 @@ import { expect, test } from '../../test/playwright/index.js'; import type { Tab } from '../tab/tab.js'; +import { tagName as TabTagName } from '../tab/tab.options.js'; import type { Tablist } from './tablist.js'; -import { TablistAppearance, TablistSize } from './tablist.options.js'; +import { TablistAppearance, TablistSize, tagName } from './tablist.options.js'; test.describe('Tablist', () => { test.use({ - tagName: 'fluent-tablist', - waitFor: ['fluent-tab'], + tagName, innerHTML: /* html */ ` - Tab one - Tab two - Tab three + <${TabTagName}>Tab one${TabTagName}> + <${TabTagName}>Tab two${TabTagName}> + <${TabTagName}>Tab three${TabTagName}> `, + waitFor: [TabTagName], }); - test('should create with document.createElement()', async ({ page, fastPage }) => { + test('should create with document.createElement()', async ({ page, fastPage, innerHTML }) => { await fastPage.setTemplate(); let hasError = false; @@ -23,9 +24,9 @@ test.describe('Tablist', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-tablist'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -33,6 +34,8 @@ test.describe('Tablist', () => { test('should have reflect disabled attribute on control', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).not.toHaveAttribute('disabled'); await element.evaluate((node: Tablist) => { @@ -44,7 +47,9 @@ test.describe('Tablist', () => { test('should set aria-disabled on individual tabs when tablist is disabled', async ({ fastPage }) => { const { element } = fastPage; - const tabs = element.locator('fluent-tab'); + const tabs = element.locator(TabTagName); + + await fastPage.setTemplate(); // Initially tabs should not have aria-disabled await expect(tabs.nth(0)).not.toHaveAttribute('aria-disabled'); @@ -75,6 +80,8 @@ test.describe('Tablist', () => { test('should have role of `tablist`', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveAttribute('role', 'tablist'); }); @@ -83,12 +90,16 @@ test.describe('Tablist', () => { }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('orientation', 'horizontal'); }); test('should set an `id` attribute on the active tab when an `id` is provided', async ({ fastPage }) => { const { element } = fastPage; - const tabs = element.locator('fluent-tab'); + const tabs = element.locator(TabTagName); + + await fastPage.setTemplate(); const tabCount = await tabs.count(); @@ -108,7 +119,9 @@ test.describe('Tablist', () => { fastPage, }) => { const { element } = fastPage; - const tabs = element.locator('fluent-tab'); + const tabs = element.locator(TabTagName); + + await fastPage.setTemplate(); const tabCount = await tabs.count(); @@ -128,7 +141,9 @@ test.describe('Tablist', () => { test('should default the first tab as the active index if `activeid` is NOT provided', async ({ fastPage }) => { const { element } = fastPage; - const tabs = element.locator('fluent-tab'); + const tabs = element.locator(TabTagName); + + await fastPage.setTemplate(); await expect(tabs.nth(0)).toHaveAttribute('aria-selected', 'true'); }); @@ -139,7 +154,9 @@ test.describe('Tablist', () => { fastPage, }) => { const { element } = fastPage; - const tabs = element.locator('fluent-tab'); + const tabs = element.locator(TabTagName); + + await fastPage.setTemplate(); const secondTab = tabs.nth(1); @@ -156,7 +173,9 @@ test.describe('Tablist', () => { fastPage, }) => { const { element } = fastPage; - const tabs = element.locator('fluent-tab'); + const tabs = element.locator(TabTagName); + + await fastPage.setTemplate(); await expect(tabs.nth(0)).toHaveAttribute('aria-selected', 'true'); @@ -175,9 +194,11 @@ test.describe('Tablist', () => { test('should set the `appearance` property to match the `appearance` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const appearance of Object.values(TablistAppearance)) { await test.step(appearance, async () => { - await fastPage.setTemplate({ attributes: { appearance } }); + await fastPage.updateTemplate(element, { attributes: { appearance } }); await expect(element).toHaveJSProperty('appearance', appearance); @@ -189,9 +210,11 @@ test.describe('Tablist', () => { test('should set the `size` property to match the `size` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const size of Object.values(TablistSize)) { await test.step(size, async () => { - await fastPage.setTemplate({ attributes: { size } }); + await fastPage.updateTemplate(element, { attributes: { size } }); await expect(element).toHaveJSProperty('size', size); @@ -202,13 +225,13 @@ test.describe('Tablist', () => { test('should not allow selecting a tab that has been disabled after it has been connected', async ({ fastPage }) => { const { element } = fastPage; - const tabs = element.locator('fluent-tab'); + const tabs = element.locator(TabTagName); await fastPage.setTemplate({ innerHTML: /* html */ ` - Tab one - Tab two - Tab three + <${TabTagName} id="tab-1">Tab one${TabTagName}> + <${TabTagName} id="tab-2">Tab two${TabTagName}> + <${TabTagName} id="tab-3">Tab three${TabTagName}> `, }); @@ -236,13 +259,13 @@ test.describe('Tablist', () => { test('should allow selecting tab that has been enabled after it has been connected', async ({ fastPage }) => { const { element } = fastPage; - const tabs = element.locator('fluent-tab'); + const tabs = element.locator(TabTagName); await fastPage.setTemplate({ innerHTML: /* html */ ` - Tab one - Tab two - Tab three + <${TabTagName}>Tab one${TabTagName}> + <${TabTagName} disabled>Tab two${TabTagName}> + <${TabTagName}>Tab three${TabTagName}> `, }); @@ -276,13 +299,13 @@ test.describe('Tablist', () => { test('should keep disabled selected tab focusable until it loses selected state', async ({ fastPage }) => { const { element, page } = fastPage; - const tabs = element.locator('fluent-tab'); + const tabs = element.locator(TabTagName); await fastPage.setTemplate({ innerHTML: /* html */ ` - Tab one - Tab two - Tab three + <${TabTagName}>Tab one${TabTagName}> + <${TabTagName}>Tab two${TabTagName}> + <${TabTagName}>Tab three${TabTagName}> `, }); @@ -307,13 +330,13 @@ test.describe('Tablist', () => { test('should not allow selecting hidden tab using arrow keys', async ({ fastPage }) => { const { element } = fastPage; - const tabs = element.locator('fluent-tab'); + const tabs = element.locator(TabTagName); await fastPage.setTemplate({ innerHTML: /* html */ ` - Tab one - Tab two - Tab three + <${TabTagName}>Tab one${TabTagName}> + <${TabTagName} hidden>Tab two${TabTagName}> + <${TabTagName}>Tab three${TabTagName}> `, }); @@ -335,13 +358,13 @@ test.describe('Tablist', () => { test('should not allow selecting hidden tab by pressing End', async ({ fastPage }) => { const { element } = fastPage; - const tabs = element.locator('fluent-tab'); + const tabs = element.locator(TabTagName); await fastPage.setTemplate({ innerHTML: /* html */ ` - Tab one - Tab two - Tab three + <${TabTagName}>Tab one${TabTagName}> + <${TabTagName}>Tab two${TabTagName}> + <${TabTagName} hidden>Tab three${TabTagName}> `, }); @@ -364,11 +387,11 @@ test.describe('Tablist', () => { test('should associate panel elements with `aria-controls` attributes', async ({ fastPage, page }) => { const { element } = fastPage; await fastPage.setTemplate(` - - Tab one - Tab two - Tab three - + <${tagName}> + <${TabTagName} aria-controls="panel1">Tab one${TabTagName}> + <${TabTagName} aria-controls="panel2">Tab two${TabTagName}> + <${TabTagName} aria-controls="panel3">Tab three${TabTagName}> + ${tagName}> Panel one Panel two Panel three @@ -401,12 +424,12 @@ test.describe('Tablist', () => { await fastPage.setTemplate({ attributes: { orientation: 'vertical' }, innerHTML: /* html */ ` - Tab one - TTab two - Tab three + <${TabTagName}>Tab one${TabTagName}> + <${TabTagName}>TTab two${TabTagName}> + <${TabTagName}>Tab three${TabTagName}> `, }); - const tabs = element.locator('fluent-tab'); + const tabs = element.locator(TabTagName); await expect(tabs.nth(0)).toHaveAttribute('data-hasIndent'); await expect(tabs.nth(1)).toHaveAttribute('data-hasIndent'); diff --git a/packages/web-components/src/text-input/text-input.spec.ts b/packages/web-components/src/text-input/text-input.spec.ts index 4ef00687ec7953..f88e019b164bd0 100644 --- a/packages/web-components/src/text-input/text-input.spec.ts +++ b/packages/web-components/src/text-input/text-input.spec.ts @@ -1,9 +1,11 @@ import { expect, test } from '../../test/playwright/index.js'; import type { TextInput } from './text-input.js'; -import { ImplicitSubmissionBlockingTypes } from './text-input.options.js'; +import { ImplicitSubmissionBlockingTypes, tagName } from './text-input.options.js'; test.describe('TextInput', () => { - test.use({ tagName: 'fluent-text-input' }); + test.use({ + tagName, + }); test('should create with document.createElement()', async ({ page, fastPage }) => { await fastPage.setTemplate(); @@ -14,9 +16,9 @@ test.describe('TextInput', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-text-input'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -178,6 +180,8 @@ test.describe('TextInput', () => { test('should have an undefined `value` property when no `value` attribute is set', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('value', undefined); }); @@ -194,13 +198,13 @@ test.describe('TextInput', () => { await fastPage.setTemplate(''); - await page.evaluate(() => { - const textInput = document.createElement('fluent-text-input') as TextInput; + await page.evaluate(tagName => { + const textInput = document.createElement(tagName) as TextInput; textInput.value = 'foo'; document.body.append(textInput); - }); + }, tagName); await expect(element).toHaveJSProperty('value', 'foo'); }); @@ -209,6 +213,8 @@ test.describe('TextInput', () => { const { element } = fastPage; const control = element.locator('input'); + await fastPage.setTemplate(); + await element.evaluate(node => { node.setAttribute('value', 'foo'); }); @@ -224,6 +230,8 @@ test.describe('TextInput', () => { const { element } = fastPage; const control = element.locator('input'); + await fastPage.setTemplate(); + await control.fill('bar'); await element.evaluate(node => { @@ -337,6 +345,8 @@ test.describe('TextInput', () => { test('should fire a `change` event when the internal control emits a `change` event', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + const wasChanged = element.evaluate( node => new Promise(resolve => { @@ -528,7 +538,7 @@ test.describe('TextInput', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName}>${tagName}> `); @@ -538,6 +548,8 @@ test.describe('TextInput', () => { test('should set the `form` property to `null` when the element is not in a form', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('form', null); }); @@ -548,7 +560,7 @@ test.describe('TextInput', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} form="foo">${tagName}> `); @@ -562,7 +574,7 @@ test.describe('TextInput', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} form="bar">${tagName}> `); @@ -578,7 +590,7 @@ test.describe('TextInput', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} name="testinput">${tagName}> `); @@ -599,7 +611,7 @@ test.describe('TextInput', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} name="testinput">${tagName}> Submit `); @@ -620,7 +632,7 @@ test.describe('TextInput', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} name="testinput">${tagName}> Reset `); @@ -641,7 +653,7 @@ test.describe('TextInput', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} name="testinput">${tagName}> `); @@ -663,7 +675,7 @@ test.describe('TextInput', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} name="testinput" required>${tagName}> `); @@ -678,7 +690,7 @@ test.describe('TextInput', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} name="testinput" readonly>${tagName}> `); @@ -696,7 +708,7 @@ test.describe('TextInput', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} name="testinput" multiple type="email">${tagName}> `); @@ -748,7 +760,7 @@ test.describe('TextInput', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} name="testinput">${tagName}> Reset `); @@ -765,6 +777,8 @@ test.describe('TextInput', () => { test('should change the `value` property when the `current-value` attribute changes', async ({ fastPage, page }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await element.evaluate(node => { node.setAttribute('current-value', 'foo'); }); @@ -775,6 +789,8 @@ test.describe('TextInput', () => { test('should change the `value` property when the `currentValue` property changes', async ({ fastPage, page }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await element.evaluate((node: TextInput) => { node.currentValue = 'foo'; }); @@ -785,6 +801,8 @@ test.describe('TextInput', () => { test('should set the `current-value` attribute to match the `value` property', async ({ fastPage, page }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).not.toHaveAttribute('current-value'); await element.evaluate((node: TextInput) => { @@ -797,6 +815,8 @@ test.describe('TextInput', () => { test('should set the `currentValue` property to match the `value` property', async ({ fastPage, page }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('currentValue', undefined); await element.evaluate((node: TextInput) => { diff --git a/packages/web-components/src/text/text.spec.ts b/packages/web-components/src/text/text.spec.ts index 1e8567164f29f9..63beb0a5047826 100644 --- a/packages/web-components/src/text/text.spec.ts +++ b/packages/web-components/src/text/text.spec.ts @@ -1,8 +1,11 @@ import { expect, test } from '../../test/playwright/index.js'; -import { TextAlign, TextFont, TextSize, TextWeight } from './text.options.js'; +import { tagName, TextAlign, TextFont, TextSize, TextWeight } from './text.options.js'; test.describe('Text Component', () => { - test.use({ tagName: 'fluent-text' }); + test.use({ + tagName, + innerHTML: 'Text', + }); test('should create with document.createElement()', async ({ page, fastPage }) => { await fastPage.setTemplate(); @@ -13,9 +16,9 @@ test.describe('Text Component', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-text'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -28,7 +31,7 @@ test.describe('Text Component', () => { await expect(element).toHaveJSProperty('nowrap', true); await test.step('should set the `nowrap` property to false when the `nowrap` attribute is removed', async () => { - await fastPage.setTemplate({ attributes: {} }); + await element.evaluate(node => node.removeAttribute('nowrap')); await expect(element).toHaveJSProperty('nowrap', false); }); @@ -44,7 +47,7 @@ test.describe('Text Component', () => { await expect(element).toHaveJSProperty('truncate', true); await test.step('should set the `truncate` property to false when the `truncate` attribute is removed', async () => { - await fastPage.setTemplate({ attributes: {} }); + await element.evaluate(node => node.removeAttribute('truncate')); await expect(element).toHaveJSProperty('truncate', false); }); @@ -58,7 +61,7 @@ test.describe('Text Component', () => { await expect(element).toHaveJSProperty('italic', true); await test.step('should set the `italic` property to false when the `italic` attribute is removed', async () => { - await fastPage.setTemplate({ attributes: {} }); + await element.evaluate(node => node.removeAttribute('italic')); await expect(element).toHaveJSProperty('italic', false); }); @@ -74,7 +77,7 @@ test.describe('Text Component', () => { await expect(element).toHaveJSProperty('underline', true); await test.step('should set the `underline` property to false when the `underline` attribute is removed', async () => { - await fastPage.setTemplate({ attributes: {} }); + await element.evaluate(node => node.removeAttribute('underline')); await expect(element).toHaveJSProperty('underline', false); }); @@ -90,7 +93,7 @@ test.describe('Text Component', () => { await expect(element).toHaveJSProperty('strikethrough', true); await test.step('should set the `strikethrough` property to false when the `strikethrough` attribute is removed', async () => { - await fastPage.setTemplate({ attributes: {} }); + await element.evaluate(node => node.removeAttribute('strikethrough')); await expect(element).toHaveJSProperty('strikethrough', false); }); diff --git a/packages/web-components/src/textarea/textarea.spec.ts b/packages/web-components/src/textarea/textarea.spec.ts index 8da1fb69475ece..5d9bff314906f4 100644 --- a/packages/web-components/src/textarea/textarea.spec.ts +++ b/packages/web-components/src/textarea/textarea.spec.ts @@ -1,11 +1,12 @@ import { expect, test } from '../../test/playwright/index.js'; -import { TextAreaAppearance, TextAreaResize, TextAreaSize } from './textarea.options.js'; +import { tagName as LabelTagName } from '../label/label.options.js'; import type { TextArea } from './textarea.js'; +import { tagName, TextAreaAppearance, TextAreaResize, TextAreaSize } from './textarea.options.js'; test.describe('TextArea', () => { test.use({ - tagName: 'fluent-textarea', - waitFor: ['fluent-label'], + tagName, + waitFor: [LabelTagName], }); test('should create with document.createElement()', async ({ page, fastPage }) => { @@ -17,9 +18,9 @@ test.describe('TextArea', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-textarea'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -28,12 +29,16 @@ test.describe('TextArea', () => { test('should not have a role on element internals', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('elementInternals.role', null); }); test("should always return 'textarea' for the `type` prop", async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('type', 'textarea'); }); @@ -69,8 +74,8 @@ test.describe('TextArea', () => { await fastPage.setTemplate(/* html */ ` Text area - - Text area + <${tagName} id="textarea">${tagName}> + Text area `); const label1El = await page.getByTestId('label1').evaluate(node => node); @@ -86,7 +91,7 @@ test.describe('TextArea', () => { await fastPage.setTemplate(/* html */ ` Text area - + <${tagName} id="textarea">${tagName}> `); const label = page.getByTestId('label'); @@ -113,9 +118,11 @@ test.describe('TextArea', () => { test('should set the `appearance` property to match the `appearance` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const appearance of Object.values(TextAreaAppearance)) { await test.step(appearance, async () => { - await fastPage.setTemplate({ attributes: { appearance } }); + await fastPage.updateTemplate(element, { attributes: { appearance } }); await expect(element).toHaveJSProperty('appearance', appearance); @@ -195,9 +202,11 @@ test.describe('TextArea', () => { test('should set the `resize` property to match the `resize` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const resize of Object.values(TextAreaResize)) { await test.step(resize, async () => { - await fastPage.setTemplate({ attributes: { resize } }); + await fastPage.updateTemplate(element, { attributes: { resize } }); await expect(element).toHaveJSProperty('resize', resize); @@ -209,9 +218,11 @@ test.describe('TextArea', () => { test('should set the `size` property to match the `size` attribute', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + for (const size of Object.values(TextAreaSize)) { await test.step(size, async () => { - await fastPage.setTemplate({ attributes: { size } }); + await fastPage.updateTemplate(element, { attributes: { size } }); await expect(element).toHaveJSProperty('size', size); @@ -248,6 +259,8 @@ test.describe('TextArea', () => { const { element } = fastPage; const label = element.locator('label'); + await fastPage.setTemplate(); + await expect(label).toBeHidden(); }); @@ -258,7 +271,7 @@ test.describe('TextArea', () => { await fastPage.setTemplate({ innerHTML: /* html */ ` - Details + <${LabelTagName} slot="label">Details${LabelTagName}> `, }); @@ -271,7 +284,7 @@ test.describe('TextArea', () => { await fastPage.setTemplate({ innerHTML: /* html */ ` - Details + <${LabelTagName} slot="label">Details${LabelTagName}> Some text `, }); @@ -281,12 +294,12 @@ test.describe('TextArea', () => { test('should downstream `required` to the label', async ({ fastPage }) => { const { element } = fastPage; - const label = element.locator('fluent-label'); + const label = element.locator(LabelTagName); await fastPage.setTemplate({ attributes: { required: true }, innerHTML: /* html */ ` - Details + <${LabelTagName} slot="label">Details${LabelTagName}> `, }); @@ -301,12 +314,12 @@ test.describe('TextArea', () => { test('should downstream `disabled` to the label', async ({ fastPage }) => { const { element } = fastPage; - const label = element.locator('fluent-label'); + const label = element.locator(LabelTagName); await fastPage.setTemplate({ attributes: { disabled: true }, innerHTML: /* html */ ` - Details + <${LabelTagName} slot="label">Details${LabelTagName}> `, }); @@ -322,12 +335,12 @@ test.describe('TextArea', () => { for (const size of Object.values(TextAreaSize)) { test(`should downstream \`${size}\` size to the label`, async ({ fastPage }) => { const { element } = fastPage; - const label = element.locator('fluent-label'); + const label = element.locator(LabelTagName); await fastPage.setTemplate({ attributes: { size }, innerHTML: /* html */ ` - Details + <${LabelTagName} slot="label">Details${LabelTagName}> `, }); @@ -340,6 +353,8 @@ test.describe('TextArea', () => { test('should have `defaultValue` as empty string if no children', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('defaultValue', ''); await expect(element).toHaveJSProperty('value', ''); }); @@ -369,6 +384,8 @@ test.describe('TextArea', () => { test('should have `defaultValue` as set to `defaultValue` prop', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await element.evaluate((el: TextArea) => { el.defaultValue = 'some text'; }); @@ -380,6 +397,8 @@ test.describe('TextArea', () => { test('should not have `defaultValue` as set to `value` prop', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await element.evaluate((el: TextArea) => { el.value = 'some text'; }); @@ -391,6 +410,8 @@ test.describe('TextArea', () => { test('should have `defaultValue` as set to `innerText`', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await element.evaluate((el: TextArea) => { el.innerText = ' some\ntext '; }); @@ -402,6 +423,8 @@ test.describe('TextArea', () => { test('should have `defaultValue` as set to `textContent`', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await element.evaluate((el: TextArea) => { el.textContent = ' some\ntext '; }); @@ -417,11 +440,11 @@ test.describe('TextArea', () => { const { element } = fastPage; await fastPage.setTemplate(''); - await page.evaluate(() => { - const textarea = document.createElement('fluent-textarea') as TextArea; + await page.evaluate(tagName => { + const textarea = document.createElement(tagName) as TextArea; textarea.defaultValue = 'some text'; document.body.append(textarea); - }); + }, tagName); await expect(element).toHaveJSProperty('defaultValue', 'some text'); await expect(element).toHaveJSProperty('value', 'some text'); @@ -432,11 +455,11 @@ test.describe('TextArea', () => { const control = element.locator('textarea'); await fastPage.setTemplate(''); - await page.evaluate(() => { - const textarea = document.createElement('fluent-textarea') as TextArea; + await page.evaluate(tagName => { + const textarea = document.createElement(tagName) as TextArea; textarea.value = 'some text'; document.body.append(textarea); - }); + }, tagName); await expect(element).toHaveJSProperty('defaultValue', ''); await expect(element).toHaveJSProperty('value', 'some text'); @@ -494,6 +517,8 @@ test.describe('TextArea', () => { const { element } = fastPage; const control = element.locator('textarea'); + await fastPage.setTemplate(); + await control.fill('123456\n7890 '); await expect(element).toHaveJSProperty('textLength', 12); @@ -605,9 +630,12 @@ test.describe('TextArea', () => { test('should always be valid if read-only', async ({ fastPage }) => { const { element } = fastPage; - await fastPage.setTemplate(/* html */ ` - - `); + await fastPage.setTemplate({ + attributes: { + required: true, + readonly: true, + }, + }); await expect(element).toHaveJSProperty('validity.valid', true); @@ -679,6 +707,8 @@ test.describe('TextArea', () => { webkit: 'Use no more than 7 characters', }; + await fastPage.setTemplate(); + await control.fill('12345678'); await element.evaluate((node: TextArea) => { @@ -707,7 +737,7 @@ test.describe('TextArea', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName}>${tagName}> `); @@ -728,7 +758,7 @@ test.describe('TextArea', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName}>${tagName}> `); @@ -767,7 +797,7 @@ test.describe('TextArea', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName}>${tagName}> `); @@ -788,7 +818,7 @@ test.describe('TextArea', () => { await fastPage.setTemplate(/* html */ ` - 1234 + <${tagName}>1234${tagName}> `); @@ -811,7 +841,7 @@ test.describe('TextArea', () => { await fastPage.setTemplate(/* html */ ` - 1234 + <${tagName}>1234${tagName}> `); @@ -837,7 +867,7 @@ test.describe('TextArea', () => { await fastPage.setTemplate(/* html */ ` - + <${tagName} name="content">${tagName}> `); diff --git a/packages/web-components/src/toggle-button/toggle-button.spec.ts b/packages/web-components/src/toggle-button/toggle-button.spec.ts index e5f4a6b13d2fd0..5a9fb32b948602 100644 --- a/packages/web-components/src/toggle-button/toggle-button.spec.ts +++ b/packages/web-components/src/toggle-button/toggle-button.spec.ts @@ -1,7 +1,11 @@ import { expect, test } from '../../test/playwright/index.js'; +import { tagName } from './toggle-button.options.js'; test.describe('Toggle Button', () => { - test.use({ tagName: 'fluent-toggle-button' }); + test.use({ + tagName, + innerHTML: 'Toggle Button', + }); test('should create with document.createElement()', async ({ page, fastPage }) => { await fastPage.setTemplate(); @@ -12,9 +16,9 @@ test.describe('Toggle Button', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-toggle-button'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -22,6 +26,8 @@ test.describe('Toggle Button', () => { test('should have the `aria-pressed` attribute set to `false` by default', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('elementInternals.ariaPressed', 'false'); }); @@ -38,6 +44,8 @@ test.describe('Toggle Button', () => { test('should toggle the `pressed` attribute when clicked', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('elementInternals.ariaPressed', 'false'); await expect(element).not.toHaveCustomState('pressed'); diff --git a/packages/web-components/src/tooltip/tooltip.spec.ts b/packages/web-components/src/tooltip/tooltip.spec.ts index e9ec16c2bbe9fb..48161f4e710344 100644 --- a/packages/web-components/src/tooltip/tooltip.spec.ts +++ b/packages/web-components/src/tooltip/tooltip.spec.ts @@ -1,9 +1,13 @@ import { expect, test } from '../../test/playwright/index.js'; import type { Tooltip } from './tooltip.js'; import type { TooltipPositioningOption } from './tooltip.options.js'; +import { tagName } from './tooltip.options.js'; test.describe('Tooltip', () => { - test.use({ tagName: 'fluent-tooltip' }); + test.use({ + tagName, + innerHTML: 'This is a tooltip', + }); test('should create with document.createElement()', async ({ page, fastPage }) => { await fastPage.setTemplate(); @@ -14,19 +18,13 @@ test.describe('Tooltip', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-tooltip'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); - /** - * ARIA APG Tooltip Pattern {@link https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/ } - * ESC dismisses the tooltip. - * The element that serves as the tooltip container has role tooltip. - * The element that triggers the tooltip references the tooltip element with aria-describedby. - */ test('escape key should hide the tooltip', async ({ fastPage, page }) => { const { element } = fastPage; const button = page.locator('button'); @@ -34,7 +32,7 @@ test.describe('Tooltip', () => { await fastPage.setTemplate(/* html */ ` Target - This is a tooltip + <${tagName} anchor="target">This is a tooltip${tagName}> `); @@ -50,6 +48,8 @@ test.describe('Tooltip', () => { test('should have the role set to `tooltip`', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toHaveJSProperty('elementInternals.role', 'tooltip'); }); @@ -60,7 +60,7 @@ test.describe('Tooltip', () => { await fastPage.setTemplate(/* html */ ` Target - This is a tooltip + <${tagName} anchor="target">This is a tooltip${tagName}> `); @@ -74,6 +74,8 @@ test.describe('Tooltip', () => { test('should not be visible by default', async ({ fastPage }) => { const { element } = fastPage; + await fastPage.setTemplate(); + await expect(element).toBeHidden(); }); @@ -84,7 +86,7 @@ test.describe('Tooltip', () => { await fastPage.setTemplate(/* html */ ` Target - This is a tooltip + <${tagName} anchor="target">This is a tooltip${tagName}> `); @@ -102,7 +104,7 @@ test.describe('Tooltip', () => { await fastPage.setTemplate(/* html */ ` Target - This is a tooltip + <${tagName} anchor="target">This is a tooltip${tagName}> `); @@ -124,7 +126,7 @@ test.describe('Tooltip', () => { await fastPage.setTemplate(/* html */ ` Target - This is a tooltip + <${tagName} anchor="target">This is a tooltip${tagName}> `); @@ -146,7 +148,7 @@ test.describe('Tooltip', () => { await fastPage.setTemplate(/* html */ ` Target - This is a tooltip + <${tagName} anchor="target">This is a tooltip${tagName}> `); @@ -172,7 +174,7 @@ test.describe('Tooltip', () => { await fastPage.setTemplate(/* html */ ` Target - This is a tooltip + <${tagName} anchor="target" positioning="below">This is a tooltip${tagName}> `); @@ -193,7 +195,7 @@ test.describe('Tooltip', () => { await fastPage.setTemplate(/* html */ ` Target - This is a tooltip + <${tagName} anchor="target" positioning="before">This is a tooltip${tagName}> `); @@ -214,7 +216,7 @@ test.describe('Tooltip', () => { await fastPage.setTemplate(/* html */ ` Target - This is a tooltip + <${tagName} anchor="target" positioning="after">This is a tooltip${tagName}> `); diff --git a/packages/web-components/src/tree-item/tree-item.spec.ts b/packages/web-components/src/tree-item/tree-item.spec.ts index 6c22a6a2fcde1d..0138747b5b085e 100644 --- a/packages/web-components/src/tree-item/tree-item.spec.ts +++ b/packages/web-components/src/tree-item/tree-item.spec.ts @@ -1,9 +1,10 @@ import { expect, test } from '../../test/playwright/index.js'; import type { BaseTreeItem } from './tree-item.base.js'; +import { tagName } from './tree-item.options.js'; test.describe('Tree Item', () => { test.use({ - tagName: 'fluent-tree-item', + tagName, }); test('should create with document.createElement()', async ({ page, fastPage }) => { @@ -15,9 +16,9 @@ test.describe('Tree Item', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-tree-item'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -34,11 +35,11 @@ test.describe('Tree Item', () => { const { element } = fastPage; await fastPage.setTemplate({ innerHTML: /* html */ ` - Nested Item A + <${tagName}>Nested Item A${tagName}> `, }); - const nestedItem = element.nth(0).locator('fluent-tree-item'); + const nestedItem = element.nth(0).locator(tagName); await expect(nestedItem).toHaveCount(1); await expect(nestedItem).toHaveText('Nested Item A'); }); @@ -58,13 +59,13 @@ test.describe('Tree Item', () => { await fastPage.setTemplate({ innerHTML: /* html */ ` Item 1 - Nested Item A + <${tagName}>Nested Item A${tagName}> `, }); await expect(element.nth(0)).not.toHaveAttribute('expanded'); - const nestedItems = element.locator('fluent-tree-item'); + const nestedItems = element.locator(tagName); await expect(nestedItems).toBeHidden(); await element.nth(0).evaluate((node: BaseTreeItem) => { @@ -97,13 +98,13 @@ test.describe('Tree Item', () => { await fastPage.setTemplate({ innerHTML: /* html */ ` Item 1 - + <${tagName}> Nested Item A - + <${tagName}> Nested Item B - Nested Item C - - + <${tagName} selected>Nested Item C${tagName}> + ${tagName}> + ${tagName}> `, }); const selectedItems = element.locator('[selected]'); @@ -118,16 +119,16 @@ test.describe('Tree Item', () => { attributes: { expanded: true }, innerHTML: /* html */ ` Item 1 - + <${tagName}> Nested Item A - + <${tagName}> Nested Item B - Nested Item C - - + <${tagName}>Nested Item C${tagName}> + ${tagName}> + ${tagName}> `, }); - const nestedItem = element.nth(0).locator('fluent-tree-item').nth(0); + const nestedItem = element.nth(0).locator(tagName).nth(0); await expect(element.nth(0)).toHaveAttribute('expanded'); await expect(nestedItem).toBeVisible(); }); diff --git a/packages/web-components/src/tree/tree.spec.ts b/packages/web-components/src/tree/tree.spec.ts index a7e3da14a1fca1..72df168c459ea8 100644 --- a/packages/web-components/src/tree/tree.spec.ts +++ b/packages/web-components/src/tree/tree.spec.ts @@ -1,8 +1,11 @@ import { expect, test } from '../../test/playwright/index.js'; +import { tagName as TreeItemTagName } from '../tree-item/tree-item.options.js'; +import { tagName } from './tree.options.js'; test.describe('Tree', () => { test.use({ - tagName: 'fluent-tree', + tagName, + waitFor: [TreeItemTagName], }); test('should create with document.createElement()', async ({ page, fastPage }) => { @@ -14,9 +17,9 @@ test.describe('Tree', () => { hasError = true; }); - await page.evaluate(() => { - document.createElement('fluent-tree'); - }); + await page.evaluate(tagName => { + document.createElement(tagName); + }, tagName); expect(hasError).toBe(false); }); @@ -26,12 +29,12 @@ test.describe('Tree', () => { await fastPage.setTemplate({ innerHTML: /* html */ ` - Item 1 - Item 2 - Item 3 + <${TreeItemTagName}>Item 1${TreeItemTagName}> + <${TreeItemTagName}>Item 2${TreeItemTagName}> + <${TreeItemTagName}>Item 3${TreeItemTagName}> `, }); - const treeItems = element.locator('fluent-tree-item'); + const treeItems = element.locator(TreeItemTagName); await expect(element).toHaveCount(1); await expect(treeItems).toHaveCount(3); await expect(treeItems.nth(0)).toHaveText('Item 1'); @@ -44,32 +47,30 @@ test.describe('Tree', () => { await fastPage.setTemplate({ innerHTML: /* html */ ` - + <${TreeItemTagName}> Item 1 - Nested Item A - - + <${TreeItemTagName}>Nested Item A${TreeItemTagName}> + ${TreeItemTagName}> + <${TreeItemTagName}> Item 2 - Nested Item B - + <${TreeItemTagName}>Nested Item B${TreeItemTagName}> + ${TreeItemTagName}> `, }); - const treeItems = element.locator('fluent-tree-item'); + const treeItems = element.locator(TreeItemTagName); await expect(element).toHaveCount(1); await expect(treeItems).toHaveCount(4); - const nestedItems = treeItems.nth(0).locator('fluent-tree-item'); + const nestedItems = treeItems.nth(0).locator(TreeItemTagName); await expect(nestedItems).toHaveCount(1); }); test('works with size variants - small', async ({ fastPage }) => { const { element } = fastPage; - const treeItemEl = element.locator('fluent-tree-item'); + const treeItemEl = element.locator(TreeItemTagName); await fastPage.setTemplate({ attributes: { size: 'small' }, - innerHTML: /* html */ ` - Item 1 - `, + innerHTML: /* html */ ` <${TreeItemTagName}>Item 1${TreeItemTagName}> `, }); await expect(treeItemEl).toHaveCount(1); @@ -80,13 +81,11 @@ test.describe('Tree', () => { test('works with size variants - medium', async ({ fastPage }) => { const { element } = fastPage; - const treeItemEl = element.locator('fluent-tree-item'); + const treeItemEl = element.locator(TreeItemTagName); await fastPage.setTemplate({ attributes: { size: 'medium' }, - innerHTML: /* html */ ` - Item 1 - `, + innerHTML: /* html */ ` <${TreeItemTagName}>Item 1${TreeItemTagName}> `, }); await expect(treeItemEl).toHaveCount(1); await expect(treeItemEl).toHaveAttribute('size', 'medium'); @@ -96,12 +95,10 @@ test.describe('Tree', () => { test('works with appearance variants - subtle', async ({ fastPage }) => { const { element } = fastPage; - const treeItemEl = element.locator('fluent-tree-item'); + const treeItemEl = element.locator(TreeItemTagName); await fastPage.setTemplate({ - innerHTML: /* html */ ` - Item 1 - `, + innerHTML: /* html */ ` <${TreeItemTagName}>Item 1${TreeItemTagName}> `, }); await expect(treeItemEl).toHaveAttribute('appearance', 'subtle'); @@ -109,12 +106,12 @@ test.describe('Tree', () => { test('works with appearance variants - subtle-alpha', async ({ fastPage }) => { const { element } = fastPage; - const treeItemEl = element.locator('fluent-tree-item'); + const treeItemEl = element.locator(TreeItemTagName); await fastPage.setTemplate({ attributes: { appearance: 'subtle-alpha' }, innerHTML: /* html */ ` - Item 1 + <${TreeItemTagName}>Item 1${TreeItemTagName}> `, }); @@ -123,12 +120,12 @@ test.describe('Tree', () => { test('works with appearance variants - transparent', async ({ fastPage }) => { const { element } = fastPage; - const treeItemEl = element.locator('fluent-tree-item'); + const treeItemEl = element.locator(TreeItemTagName); await fastPage.setTemplate({ attributes: { appearance: 'transparent' }, innerHTML: /* html */ ` - Item 1 + <${TreeItemTagName}>Item 1${TreeItemTagName}> `, }); @@ -141,10 +138,10 @@ test.describe('Tree', () => { await fastPage.setTemplate({ innerHTML: /* html */ ` - - Item 1 - Nested Item A - + <${TreeItemTagName} id="click-tree-item"> + Item 1 + <${TreeItemTagName}>Nested Item A${TreeItemTagName}> + ${TreeItemTagName}> `, }); @@ -166,11 +163,11 @@ test.describe('Tree', () => { await fastPage.setTemplate({ innerHTML: /* html */ ` - Item 1 - Item 2 + <${TreeItemTagName}>Item 1${TreeItemTagName}> + <${TreeItemTagName}>Item 2${TreeItemTagName}> `, }); - const treeItemEl = element.locator('fluent-tree-item'); + const treeItemEl = element.locator(TreeItemTagName); await expect(element).toHaveCount(1); await expect(treeItemEl).toHaveCount(2); await expect(treeItemEl.nth(0)).not.toHaveAttribute('selected'); @@ -194,7 +191,7 @@ test.describe('Tree', () => { await fastPage.setTemplate({ innerHTML: /* html */ ` - Item 1 + <${TreeItemTagName}>Item 1${TreeItemTagName}> `, }); // mock scroll event @@ -202,14 +199,14 @@ test.describe('Tree', () => { element.evaluate(node => new Promise(resolve => node.addEventListener('scroll', () => resolve(true)))), new Promise(resolve => setTimeout(() => resolve(false), 10)), ]); - await element.evaluate(node => { + await element.evaluate((node, tagName) => { for (let i = 0; i < 30; i++) { - node.appendChild(document.createElement('fluent-tree-item')); + node.appendChild(document.createElement(tagName)); } - }); + }, TreeItemTagName); expect(await element.evaluate(node => node.children.length)).toBe(31); - const treeItem1 = element.locator('fluent-tree-item').nth(0); + const treeItem1 = element.locator(TreeItemTagName).nth(0); expect(await treeItem1.getAttribute('selected')).toBeNull(); await treeItem1.focus(); @@ -224,20 +221,19 @@ test.describe('Tree', () => { page, browserName, }) => { - const { element } = fastPage; const anchor = page.locator('a'); await fastPage.setTemplate({ innerHTML: /* html */ ` - - Item1 - Link1 - - Item 2 + <${TreeItemTagName}> + Item1 + Link1 + ${TreeItemTagName}> + <${TreeItemTagName}>Item 2${TreeItemTagName}> `, }); - const treeItems = page.locator('fluent-tree-item'); + const treeItems = page.locator(TreeItemTagName); await treeItems.nth(0).focus(); await expect(treeItems.nth(0)).toBeFocused(); await page.keyboard.press('Enter'); diff --git a/packages/web-components/test/playwright/index.ts b/packages/web-components/test/playwright/index.ts index 16791a9789f911..aea5b357777a88 100644 --- a/packages/web-components/test/playwright/index.ts +++ b/packages/web-components/test/playwright/index.ts @@ -34,7 +34,6 @@ export const test = baseTest.extend({ await fastPage.goto(); await page.emulateMedia({ reducedMotion: 'reduce' }); await fastPage.waitForCustomElement(tagName, ...waitFor); - await fastPage.setTemplate(); await use(fastPage); },
Start typing to filter the list
{children}