Compare commits

..

11 Commits

Author SHA1 Message Date
Cory LaViska
4bbcc719f6 remove offending property; fixes #1614 2025-11-18 15:28:45 -05:00
Jesse Jurman
e2f2a8a0d3 Fix: Allow wa-hide to be cancelled in WaDropdown (#1774)
* Fix: Allow wa-hide to be cancelled in WaDropdown

* add tests around open attribute / property

* update changelog
2025-11-18 14:07:53 -05:00
Christian Oliff
86293cc3e1 Remove width property from select component (#1736)
Deleted the duplicate 'width: 100%' style from the select component (already defined on line 56). Also fixed a typo in a comment in the docs CSS. You should setup Stylelint to prevent these issues :-)
2025-11-18 10:03:29 -05:00
Fred
8fb521d9ef Adding a reflect property option to the name property (#1716) 2025-11-17 22:15:18 +00:00
Kelsey Jackson
2491ca45ac Fix appearance="filled-outlined" in <wa-card> (#1694)
* upated filled-outlined attribute for cards

* updated selector

* updated changelog

* updated heading

* updated css

* remove final `~=` selector (thanks @Copilot)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Lindsay M <126139086+lindsaym-fa@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-17 15:12:23 -05:00
Cory LaViska
411d385d65 improve icon perf; closes #1729 (#1783) 2025-11-17 14:49:45 -05:00
Dimitri
634828d89a fix(wa-icon): add waitUntilFirstUpdate: true option to the setIcon() watch decorator (#1738) 2025-11-17 14:49:32 -05:00
Cory LaViska
17fd3d238c fix typo; closes #1713 2025-11-17 14:38:40 -05:00
Konnor Rogers
ab0f2a1411 add manifest json file copying (#1780)
* add manifest json file copying

* update comment

* prettier
2025-11-17 11:31:26 -05:00
Cory LaViska
4069360242 fix card slot detection (#1779) 2025-11-17 09:51:11 -05:00
Owarie
8c51c5b933 fix: re-add page metadata (#1776) 2025-11-17 09:47:14 -05:00
18 changed files with 243 additions and 287 deletions

View File

@@ -124,6 +124,22 @@ export default async function (eleventyConfig) {
eleventyConfig.addFilter('stripExtension', string => path.parse(string + '').name);
eleventyConfig.addFilter('stripPrefix', content => content.replace(/^wa-/, ''));
eleventyConfig.addFilter('uniqueId', (_value, length = 8) => nanoid(length));
eleventyConfig.addGlobalData('eleventyComputed', {
// Page title with smart + default site name formatting
pageTitle: data => composePageTitle(data.title),
// Open Graph title with smart + default site name formatting
ogTitle: data => composePageTitle(data.ogTitle || data.title),
ogDescription: data => data.ogDescription || data.description,
ogImage: data => data.ogImage || siteMetadata.image,
ogUrl: data => {
if (data.ogUrl) return data.ogUrl;
const url = data.page?.url || '';
return url ? `${siteMetadata.url}${url}` : siteMetadata.url;
},
ogType: data => data.ogType || 'website',
});
// Trims whitespace and pipes from the start and end of a string. Useful for CEM types, which can be pipe-delimited.
// With Prettier 3, this means a leading pipe will exist be present when the line wraps.
eleventyConfig.addFilter('trimPipes', content => {
@@ -297,18 +313,19 @@ export default async function (eleventyConfig) {
// This needs to happen in "eleventy.after" otherwise incremental builds never update.
eleventyConfig.on('eleventy.after', function () {
let assetsDir = path.join(process.env.BASE_DIR || 'docs', 'assets');
const baseDir = process.env.BASE_DIR || 'docs';
let assetsDir = path.join(baseDir, 'assets');
const siteAssetsDir = path.join(eleventyConfig.directories.output, 'assets');
fs.cpSync(assetsDir, siteAssetsDir, { recursive: true });
// Passthrough copy for manifest.json (PWA manifest file)
fs.cpSync(path.join(baseDir, 'manifest.json'), path.join(eleventyConfig.directories.output, 'manifest.json'));
});
for (let glob of passThrough) {
eleventyConfig.addPassthroughCopy(glob);
}
// Passthrough copy for manifest.json (PWA manifest file)
eleventyConfig.addPassthroughCopy('manifest.json');
// // SSR plugin
// if (!isDev) {
// //

View File

@@ -33,9 +33,9 @@ unlisted: true
--secondary-opacity: 1.0;
}
wa-page.background-overlay-grid {
--grid-opacity: 0.04;
--subgrid-opacity: 0.02;
wa-page.background-grid {
--grid-line-color: color-mix(in oklab, var(--wa-color-text-normal), transparent 96%);
--subgrid-line-color: color-mix(in oklab, var(--wa-color-text-normal), transparent 98%);
}
wa-page > [slot='main-footer'] {
@@ -209,7 +209,7 @@ unlisted: true
.vehicle wa-icon[name="bulldozer"] { animation: rumble-bulldozer 0.1s ease-in-out infinite; }
</style>
<wa-page disable-sticky="header" class="background-overlay-grid">
<wa-page disable-sticky="header" class="background-grid">
<header slot="header">
<div class="header-content wa-split" style="flex-wrap: nowrap;">
<wa-icon variant="brands" name="web-awesome" class="icon-brand-logo"></wa-icon>

View File

@@ -2,7 +2,7 @@
{%- if req.stripe.discount.active and not currentUser.hasPro -%}
<wa-dialog id="dialog-site" light-dismiss without-header>
<div class="background-overlay-wa-pattern" style="justify-content: center; margin-inline: calc(var(--spacing) * -1); margin-block-start: calc(var(--spacing) * -1); margin-block-end: var(--spacing); background: linear-gradient(to bottom, var(--wa-color-brand), var(--wa-color-brand-50)); color: var(--wa-color-brand-on-loud); padding: var(--wa-space-3xl) var(--spacing); --background-opacity: 0.2;">
<div class="background-wa-pattern" style="justify-content: center; margin-inline: calc(var(--spacing) * -1); margin-block-start: calc(var(--spacing) * -1); margin-block-end: var(--spacing); background: linear-gradient(to bottom, var(--wa-color-brand), var(--wa-color-brand-50)); color: var(--wa-color-brand-on-loud); padding: var(--wa-space-3xl) var(--spacing); --background-pattern-opacity: 0.2; --background-pattern-image: url('/assets/images/bg-wa-pattern.svg');">
<div class="wa-stack wa-align-items-center" style="text-align: center;">
<wa-icon name="party-horn" family="duotone" variant="solid" style="font-size: var(--wa-font-size-3xl); --secondary-color: var(--wa-color-brand-40); --secondary-opacity: 1.0;"></wa-icon>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 27 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -38,7 +38,7 @@ wa-page > [slot='banner'] {
}
&.banner-wa-launch {
/* custom brand colors carrried over from theme-site for the banner */
/* custom brand colors carried over from theme-site for the banner */
--wa-color-brand-95: #fef0ec;
--wa-color-brand-90: #fce0d8;
--wa-color-brand-80: #f8bcac;

View File

@@ -5,7 +5,6 @@
border-radius: var(--wa-border-radius-l);
padding: 0;
margin: 0 auto;
overflow: hidden;
&::part(dialog) {
margin-block-start: 10vh;

View File

@@ -178,107 +178,76 @@
}
}
/* background overlay base */
.background-overlay,
.background-overlay-grid,
.background-overlay-dot-grid,
.background-overlay-wa-pattern {
--background-blend-mode: normal;
--background-blend-mode-dark: var(--background-blend-mode);
--background-color: transparent;
--background-overlay-content-z-index: 1;
--background-overlay-z-index: 0;
/* grid background */
.background-grid {
--grid-spacing: var(--wa-space-2xl);
--grid-line-color: color-mix(in oklab, var(--wa-color-text-normal), transparent 90%);
--grid-line-width: var(--wa-border-width-s);
--subgrid-spacing: calc(var(--grid-spacing) / 2);
--subgrid-line-color: color-mix(in oklab, var(--wa-color-text-normal), transparent 95%);
--subgrid-line-width: var(--wa-border-width-s);
background-image:
/* main grid - vertical lines */
linear-gradient(to right, var(--grid-line-color) var(--grid-line-width), transparent var(--grid-line-width)),
/* main grid - horizontal lines */
linear-gradient(to bottom, var(--grid-line-color) var(--grid-line-width), transparent var(--grid-line-width)),
/* sub-grid - vertical lines (offset by half the main grid spacing) */
linear-gradient(
to right,
var(--subgrid-line-color) var(--subgrid-line-width),
transparent var(--subgrid-line-width)
),
/* sub-grid - horizontal lines (offset by half the main grid spacing) */
linear-gradient(
to bottom,
var(--subgrid-line-color) var(--subgrid-line-width),
transparent var(--subgrid-line-width)
);
background-size:
var(--grid-spacing) var(--grid-spacing),
var(--grid-spacing) var(--grid-spacing),
var(--subgrid-spacing) var(--subgrid-spacing),
var(--subgrid-spacing) var(--subgrid-spacing);
background-position:
0 0,
0 0,
calc(var(--grid-spacing) / 2) calc(var(--grid-spacing) / 2),
calc(var(--grid-spacing) / 2) calc(var(--grid-spacing) / 2);
}
/* dot grid background */
.background-dot-grid {
--dot-spacing: 1.5rem;
--dot-radius: 1.5px;
--dot-color: color-mix(in oklab, var(--wa-color-text-normal), transparent 85%);
background-image: radial-gradient(circle, var(--dot-color) var(--dot-radius), transparent var(--dot-radius));
background-size: var(--dot-spacing) var(--dot-spacing);
}
/* wa illustration background pattern */
.background-wa-pattern {
position: relative;
& > * {
position: relative;
z-index: var(--background-overlay-content-z-index);
z-index: 1;
}
&::before,
&::after {
background-color: var(--background-color);
content: '';
inset: 0;
mix-blend-mode: var(--background-blend-mode);
--background-pattern-image: url('/assets/images/bg-wa-pattern.svg');
position: absolute;
z-index: var(--background-overlay-z-index);
}
.wa-dark & {
&::before,
&::after {
mix-blend-mode: var(--background-blend-mode-dark);
}
}
}
/* grid background overlay */
.background-overlay-grid {
--grid-line-color: var(--wa-color-text-normal);
--grid-line-width: var(--wa-border-width-s);
--grid-opacity: 0.1;
--grid-spacing: var(--wa-space-2xl);
--subgrid-line-color: var(--grid-line-color);
--subgrid-line-width: var(--grid-line-width);
--subgrid-opacity: 0.05;
--subgrid-spacing: calc(var(--grid-spacing) / 2);
&::before {
background-image:
linear-gradient(to right, var(--grid-line-color) var(--grid-line-width), transparent var(--grid-line-width)),
linear-gradient(to bottom, var(--grid-line-color) var(--grid-line-width), transparent var(--grid-line-width));
background-size: var(--grid-spacing) var(--grid-spacing);
opacity: var(--grid-opacity);
}
&::after {
background-image:
linear-gradient(to right, var(--subgrid-line-color) var(--subgrid-line-width), transparent var(--subgrid-line-width)),
linear-gradient(to bottom, var(--subgrid-line-color) var(--subgrid-line-width), transparent var(--subgrid-line-width));
background-position: calc(var(--grid-spacing) / 2) calc(var(--grid-spacing) / 2);
background-size: var(--subgrid-spacing) var(--subgrid-spacing);
opacity: var(--subgrid-opacity);
}
.wa-dark & {
--grid-opacity: 0.15;
--subgrid-opacity: 0.08;
}
}
/* dot grid background overlay */
.background-overlay-dot-grid {
--dot-color: var(--wa-color-text-normal);
--dot-opacity: 0.15;
--dot-radius: 1.5px;
--dot-spacing: 1.5rem;
&::after {
background-image: radial-gradient(circle, var(--dot-color) var(--dot-radius), transparent var(--dot-radius));
background-size: var(--dot-spacing) var(--dot-spacing);
opacity: var(--dot-opacity);
}
.wa-dark & {
--dot-opacity: 0.2;
}
}
/* wa illustration background pattern overlay */
.background-overlay-wa-pattern {
--background-image: url('/assets/images/bg-wa-pattern.svg');
--background-opacity: 0.15;
--background-size: 640px;
&::after {
background-image: var(--background-image);
background-size: var(--background-size);
opacity: var(--background-opacity);
}
.wa-dark & {
--background-opacity: 0.2;
inset: 0;
background-color: var(--background-pattern-color, transparent);
background-image: var(--background-pattern-image);
background-repeat: repeat;
content: '';
opacity: var(--background-pattern-opacity, 0.3);
z-index: 0;
}
}
/* #endregion */

View File

@@ -13,7 +13,11 @@ Components with the <wa-badge variant="warning">Experimental</wa-badge> badge sh
## Next
- 🚨 BREAKING: Changed `appearance="filled outlined"` to `appearance="filled-outlined"` in `<wa-card>` [issue:1671]
- Fixed a bug in `<wa-slider>` that caused some touch devices to end up with the incorrect value [issue:1703]
- Fixed a bug in `<wa-card>` that prevented some slots from being detected correctly [discuss:1450]
- Fixed a bug in `<wa-dropdown>` that caused the browser to hang when cancelling the `wa-hide` event [issue:1483]
- Improved performance of `<wa-icon>` so initial rendering occurs faster, especially with multiple icons on the page [issue:1729]
## 3.0.0
@@ -21,6 +25,7 @@ Components with the <wa-badge variant="warning">Experimental</wa-badge> badge sh
- `<wa-badge>`
- `<wa-button>`
- `<wa-callout>`
- `<wa-card>`
- `<wa-details>`
- `<wa-input>`
- `<wa-select>`

View File

@@ -4,173 +4,6 @@ description: Learn more about using custom elements.
layout: page-outline
---
<!-- WA Pattern Background Examples -->
<div class="example">
<h3>WA Pattern: Default</h3>
<div class="background-overlay-wa-pattern example-content"
style="--background-color: var(--wa-color-neutral-border-quiet);">
<p>Default settings with normal blend mode</p>
</div>
</div>
<div class="example">
<h3>WA Pattern: Custom Opacity (Subtle)</h3>
<div class="background-overlay-wa-pattern example-content"
style="--background-color: var(--wa-color-neutral-border-quiet);
--background-opacity: 0.1;">
<p>Very subtle pattern with 0.1 opacity</p>
</div>
</div>
<div class="example">
<h3>WA Pattern: Custom Opacity (Prominent)</h3>
<div class="background-overlay-wa-pattern example-content"
style="--background-color: var(--wa-color-neutral-border-quiet);
--background-opacity: 0.3;">
<p>More prominent pattern with 0.3 opacity</p>
</div>
</div>
<div class="example">
<h3>WA Pattern: Background Color - Brand</h3>
<div class="background-overlay-wa-pattern example-content"
style="--background-color: var(--wa-color-brand-border-normal);">
<p>Pattern with brand color - automatically blends with multiply (light) or screen (dark)</p>
</div>
</div>
<div class="example">
<h3>WA Pattern: Multiply/Screen Blend Mode</h3>
<div class="background-overlay-wa-pattern example-content"
style="--background-blend-mode: multiply;
--background-blend-mode-dark: screen;
--background-color: var(--wa-color-neutral-border-quiet);
--background-opacity: 0.15;">
<p>Multiply blend mode in light mode, automatically switches to screen blend mode in dark mode. Set both <code>--background-blend-mode: multiply</code> and <code>--background-blend-mode-dark: screen</code>.</p>
</div>
</div>
<div class="example">
<h3>WA Pattern: Grayscale (Luminosity Blend Mode)</h3>
<div class="background-overlay-wa-pattern example-content"
style="--background-blend-mode: luminosity;
--background-color: transparent;
--background-opacity: 0.2;">
<p>Luminosity blend mode creates a grayscale effect that uses the pattern's brightness to modulate the content behind it, while preserving the color of the background content.</p>
</div>
</div>
<!-- Grid Background Examples -->
<div class="example">
<h3>Grid: Default</h3>
<div class="background-overlay-grid example-content">
<p>Default settings with normal blend mode</p>
</div>
</div>
<div class="example">
<h3>Grid: Custom Opacity (Subtle)</h3>
<div class="background-overlay-grid example-content"
style="--grid-opacity: 0.05;
--subgrid-opacity: 0.025;">
<p>Very subtle grid with reduced opacity</p>
</div>
</div>
<div class="example">
<h3>Grid: Custom Opacity (Prominent)</h3>
<div class="background-overlay-grid example-content"
style="--grid-opacity: 0.2;
--subgrid-opacity: 0.1;">
<p>More prominent grid with increased opacity</p>
</div>
</div>
<div class="example">
<h3>Grid: Background Color - Brand</h3>
<div class="background-overlay-grid example-content"
style="--background-color: var(--wa-color-brand-fill-quiet);
--grid-line-color: var(--wa-color-brand-border-normal);
--subgrid-line-color: var(--wa-color-brand-border-normal);">
<p>Grid with brand background and line colors - automatically blends with multiply (light) or screen (dark)</p>
</div>
</div>
<div class="example">
<h3>Grid: Multiply/Screen Blend Mode</h3>
<div class="background-overlay-grid example-content"
style="--background-blend-mode: multiply;
--background-blend-mode-dark: screen;
--grid-opacity: 0.15;
--subgrid-opacity: 0.08;">
<p>Multiply blend mode in light mode, automatically switches to screen blend mode in dark mode. Set both <code>--background-blend-mode: multiply</code> and <code>--background-blend-mode-dark: screen</code>.</p>
</div>
</div>
<div class="example">
<h3>Grid: Grayscale (Luminosity Blend Mode)</h3>
<div class="background-overlay-grid example-content"
style="--background-blend-mode: luminosity;
--background-color: transparent;
--grid-opacity: 0.2;
--subgrid-opacity: 0.1;">
<p>Luminosity blend mode creates a grayscale effect that uses the grid's brightness to modulate the content behind it, while preserving the color of the background content.</p>
</div>
</div>
<!-- Dot Grid Background Examples -->
<div class="example">
<h3>Dot Grid: Default</h3>
<div class="background-overlay-dot-grid example-content">
<p>Default settings with normal blend mode</p>
</div>
</div>
<div class="example">
<h3>Dot Grid: Custom Opacity (Subtle)</h3>
<div class="background-overlay-dot-grid example-content"
style="--dot-opacity: 0.05;">
<p>Very subtle dots with 0.05 opacity</p>
</div>
</div>
<div class="example">
<h3>Dot Grid: Custom Opacity (Prominent)</h3>
<div class="background-overlay-dot-grid example-content"
style="--dot-opacity: 0.3;">
<p>More prominent dots with 0.3 opacity</p>
</div>
</div>
<div class="example">
<h3>Dot Grid: Background Color - Brand</h3>
<div class="background-overlay-dot-grid example-content"
style="--background-color: var(--wa-color-brand-fill-quiet);
--dot-color: var(--wa-color-brand-border-normal);">
<p>Dots with brand background and dot colors - automatically blends with multiply (light) or screen (dark)</p>
</div>
</div>
<div class="example">
<h3>Dot Grid: Multiply/Screen Blend Mode</h3>
<div class="background-overlay-dot-grid example-content"
style="--background-blend-mode: multiply;
--background-blend-mode-dark: screen;
--dot-opacity: 0.2;">
<p>Multiply blend mode in light mode, automatically switches to screen blend mode in dark mode. Set both <code>--background-blend-mode: multiply</code> and <code>--background-blend-mode-dark: screen</code>.</p>
</div>
</div>
<div class="example">
<h3>Dot Grid: Grayscale (Luminosity Blend Mode)</h3>
<div class="background-overlay-dot-grid example-content"
style="--background-blend-mode: luminosity;
--background-color: transparent;
--dot-opacity: 0.2;">
<p>Luminosity blend mode creates a grayscale effect that uses the dot grid's brightness to modulate the content behind it, while preserving the color of the background content.</p>
</div>
</div>
Web Awesome components are just regular HTML elements, or [custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) to be precise. You can use them like any other element. Each component has detailed documentation that describes its full API, including properties, events, methods, and more.
If you're new to custom elements, often referred to as "web components," this section will familiarize you with how to use them.
@@ -392,4 +225,3 @@ If you are using types from multiple projects, you can add an array of reference
### Other Editors
Most popular editors support custom code completion with a bit of configuration. Please [submit a feature request](https://github.com/shoelace-style/webawesome/issues/new/choose) for your editor of choice. PRs are also welcome!

View File

@@ -22,7 +22,7 @@ As soon as all elements are registered _or_ after two seconds have elapsed, the
:::details Are you using Turbo in your app?
If you're using [Turbo](https://turbo.hotwired.dev/) to serve a multi-page application (MPA) as a single page application (SPA), you might notice FOUCE when navigating from page to page. This is because Turbo renders the new page's content before the autoloader has a change to register new components.
If you're using [Turbo](https://turbo.hotwired.dev/) to serve a multi-page application (MPA) as a single page application (SPA), you might notice FOUCE when navigating from page to page. This is because Turbo renders the new page's content before the autoloader has a chance to register new components.
The following function acts as a middleware to ensure components are registered _before_ the page shows, eliminating FOUCE for page-to-page navigation with Turbo.

View File

@@ -16,27 +16,28 @@
}
/* Appearance modifiers */
:host([appearance~='plain']) {
:host([appearance='plain']) {
background-color: transparent;
border-color: transparent;
box-shadow: none;
}
:host([appearance~='outlined']) {
:host([appearance='outlined']) {
background-color: var(--wa-color-surface-default);
border-color: var(--wa-color-surface-border);
}
:host([appearance~='filled']) {
:host([appearance='filled']) {
background-color: var(--wa-color-neutral-fill-quiet);
border-color: transparent;
}
:host([appearance~='filled'][appearance~='outlined']) {
border-color: var(--wa-color-neutral-border-quiet);
:host([appearance='filled-outlined']) {
background-color: var(--wa-color-neutral-fill-quiet);
border-color: var(--wa-color-surface-border);
}
:host([appearance~='accent']) {
:host([appearance='accent']) {
color: var(--wa-color-neutral-on-loud);
background-color: var(--wa-color-neutral-fill-loud);
border-color: transparent;

View File

@@ -30,11 +30,19 @@ import styles from './card.css';
export default class WaCard extends WebAwesomeElement {
static css = [sizeStyles, styles];
private readonly hasSlotController = new HasSlotController(this, 'footer', 'header', 'media');
private readonly hasSlotController = new HasSlotController(
this,
'footer',
'header',
'media',
'header-actions',
'footer-actions',
'actions',
);
/** The card's visual appearance. */
@property({ reflect: true })
appearance: 'accent' | 'filled' | 'outlined' | 'plain' = 'outlined';
appearance: 'accent' | 'filled' | 'outlined' | 'filled-outlined' | 'plain' = 'outlined';
/** Renders the card with a header. Only needed for SSR, otherwise is automatically added. */
@property({ attribute: 'with-header', type: Boolean, reflect: true }) withHeader = false;

View File

@@ -1,4 +1,6 @@
import { expect, fixture, html } from '@open-wc/testing';
import { aTimeout, expect, fixture, html, waitUntil } from '@open-wc/testing';
import sinon from 'sinon';
import type WaDropdown from './dropdown.js';
describe('<wa-dropdown>', () => {
it('should render a component', async () => {
@@ -6,4 +8,112 @@ describe('<wa-dropdown>', () => {
expect(el).to.exist;
});
it('should respect the open attribute when included', async () => {
const el = await fixture<WaDropdown>(html`
<wa-dropdown open>
<wa-button slot="trigger">Dropdown</wa-button>
<wa-dropdown-item>One</wa-dropdown-item>
</wa-dropdown>
`);
await el.updateComplete;
await aTimeout(200);
expect(el.open).to.be.true;
});
it('should fire a single show/after-show and hide/after-hide in normal open/close flow', async () => {
const el = await fixture<WaDropdown>(html`
<wa-dropdown>
<wa-button slot="trigger">Dropdown</wa-button>
<wa-dropdown-item>One</wa-dropdown-item>
<wa-dropdown-item>Two</wa-dropdown-item>
</wa-dropdown>
`);
// setup spies to track how often we see different show/hide events
const showSpy = sinon.spy();
const afterShowSpy = sinon.spy();
const hideSpy = sinon.spy();
const afterHideSpy = sinon.spy();
el.addEventListener('wa-show', showSpy);
el.addEventListener('wa-after-show', afterShowSpy);
el.addEventListener('wa-hide', hideSpy);
el.addEventListener('wa-after-hide', afterHideSpy);
// open the dropdown by triggering a click on the trigger
const trigger = el.querySelector<HTMLElement>('[slot="trigger"]')!;
trigger.click();
await waitUntil(() => showSpy.calledOnce);
await waitUntil(() => afterShowSpy.calledOnce);
expect(showSpy.callCount).to.equal(1);
expect(afterShowSpy.callCount).to.equal(1);
expect(el.open).to.be.true;
// close the dropdown by clicking the trigger again
trigger.click();
await waitUntil(() => hideSpy.calledOnce);
await waitUntil(() => afterHideSpy.calledOnce);
expect(hideSpy.callCount).to.equal(1);
expect(afterHideSpy.callCount).to.equal(1);
expect(el.open).to.be.false;
});
it('should fire a single show/after-show and hide/after-hide when wa-hide event is cancelled', async () => {
const el = await fixture<WaDropdown>(html`
<wa-dropdown>
<wa-button slot="trigger">Dropdown</wa-button>
<wa-dropdown-item>One</wa-dropdown-item>
<wa-dropdown-item>Two</wa-dropdown-item>
</wa-dropdown>
`);
// setup spies to track how often we see different show/hide events
const showSpy = sinon.spy();
const afterShowSpy = sinon.spy();
const hideSpy = sinon.spy();
const afterHideSpy = sinon.spy();
el.addEventListener('wa-show', showSpy);
el.addEventListener('wa-after-show', afterShowSpy);
// Intercept wa-hide and prevent it
el.addEventListener('wa-hide', event => {
event.preventDefault();
hideSpy(event);
});
el.addEventListener('wa-after-hide', afterHideSpy);
// open the dropdown by triggering a click on the trigger
const trigger = el.querySelector<HTMLElement>('[slot="trigger"]')!;
trigger.click();
await waitUntil(() => showSpy.calledOnce);
await waitUntil(() => afterShowSpy.calledOnce);
expect(showSpy.callCount).to.equal(1);
expect(afterShowSpy.callCount).to.equal(1);
expect(el.open).to.be.true;
// click on the trigger (which should do nothing to the open state)
trigger.click();
await waitUntil(() => hideSpy.calledOnce);
expect(hideSpy.callCount).to.equal(1);
// after-hide should not have been called if hide is cancelled
expect(afterHideSpy.callCount).to.equal(0);
expect(el.open).to.be.true;
});
});

View File

@@ -109,6 +109,18 @@ export default class WaDropdown extends WebAwesomeElement {
async updated(changedProperties: PropertyValues) {
if (changedProperties.has('open')) {
const previousOpen = changedProperties.get('open');
// check if the previous value is the same
// (if they are, do not trigger menu showing / hiding)
if (previousOpen === this.open) {
return;
}
// check if we are changing from undefined to false
// (if we are, we can skip menu hiding)
if (previousOpen === undefined && this.open === false) {
return;
}
this.customStates.set('open', this.open);
if (this.open) {
@@ -227,6 +239,12 @@ export default class WaDropdown extends WebAwesomeElement {
return;
}
// if this dropdown is already open, do nothing
// (this can happen when wa-hide was cancelled)
if (this.popup.active) {
return;
}
openDropdowns.forEach(dropdown => (dropdown.open = false));
this.popup.active = true; // Use wa-popup's active property instead of showPopover

View File

@@ -187,7 +187,7 @@ export default class WaIcon extends WebAwesomeElement {
}
}
@watch(['family', 'name', 'library', 'variant', 'src', 'autoWidth', 'swapOpacity'])
@watch(['family', 'name', 'library', 'variant', 'src', 'autoWidth', 'swapOpacity'], { waitUntilFirstUpdate: true })
async setIcon() {
const { url, fromLibrary } = this.getIconSource();
const library = fromLibrary ? getIconLibrary(this.library) : undefined;

View File

@@ -74,7 +74,6 @@
padding: 0 var(--wa-form-control-padding-inline);
position: relative;
vertical-align: middle;
width: 100%;
transition:
background-color var(--wa-transition-normal),
border var(--wa-transition-normal),

View File

@@ -119,7 +119,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
@state() optionValues: Set<string | null> | undefined;
/** The name of the select, submitted as a name/value pair with form data. */
@property() name = '';
@property({ reflect: true }) name = '';
private _defaultValue: null | string | string[] = null;