Merge branch 'next' into tree-item-spinner

This commit is contained in:
Cory LaViska
2025-11-18 15:31:42 -05:00
committed by GitHub
7 changed files with 132 additions and 5 deletions

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

@@ -16,6 +16,7 @@ Components with the <wa-badge variant="warning">Experimental</wa-badge> badge sh
- 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-tree-item>` that caused the spinner to not show when lazy loading [issue:1678]
- 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

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

@@ -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;