Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/dropdown-button-expanded-active.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@siemens/ix': patch
---

Show an active visual state on `ix-dropdown-button` while the dropdown is open (`aria-expanded`), so the trigger stays clearly linked to the menu. Fixes #1402
87 changes: 87 additions & 0 deletions packages/core/src/components/dropdown-button/dropdown-button.ct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { iconCheckboxes } from '@siemens/ix-icons/icons';
import { expect } from '@playwright/test';
import { regressionTest } from '@utils/test';

Expand Down Expand Up @@ -103,6 +104,92 @@ regressionTest(
}
);

regressionTest(
'applies active button appearance while dropdown is expanded',
async ({ page, mount }) => {
await mount(`
<ix-dropdown-button label="Open">
<ix-dropdown-item label="Test"></ix-dropdown-item>
</ix-dropdown-button>
`);

const dropdownButton = page.locator('ix-dropdown-button');
const trigger = dropdownButton.locator('ix-button');

const getTriggerBackground = () =>
trigger.evaluate((el) => {
const button = el.shadowRoot?.querySelector('button');
return button ? getComputedStyle(button).backgroundColor : '';
});

const closedBackground = await getTriggerBackground();

await dropdownButton.click();
await expect(dropdownButton).toHaveAttribute('aria-expanded', 'true');
await expect(dropdownButton.locator('ix-dropdown')).toBeVisible();

const openBackground = await getTriggerBackground();
expect(openBackground).not.toBe(closedBackground);

const activeBackground = await dropdownButton.evaluate((el) => {
const probe = document.createElement('div');
probe.style.backgroundColor =
'var(--theme-btn-primary--background--active)';
el.appendChild(probe);
const value = getComputedStyle(probe).backgroundColor;
probe.remove();
return value;
});

expect(openBackground).toBe(activeBackground);
}
);

regressionTest(
'applies active icon button appearance while dropdown is expanded',
async ({ page, mount }) => {
await mount(
`
<ix-dropdown-button icon="checkboxes">
<ix-dropdown-item label="Test"></ix-dropdown-item>
</ix-dropdown-button>
`,
{
icons: { iconCheckboxes },
}
);

const dropdownButton = page.locator('ix-dropdown-button');
const trigger = dropdownButton.locator('ix-icon-button');

const getTriggerBackground = () =>
trigger.evaluate((el) => {
const button = el.shadowRoot?.querySelector('button');
return button ? getComputedStyle(button).backgroundColor : '';
});

const closedBackground = await getTriggerBackground();

await dropdownButton.click();
await expect(dropdownButton).toHaveAttribute('aria-expanded', 'true');

const openBackground = await getTriggerBackground();
expect(openBackground).not.toBe(closedBackground);

const activeBackground = await dropdownButton.evaluate((el) => {
const probe = document.createElement('div');
probe.style.backgroundColor =
'var(--theme-btn-primary--background--active)';
el.appendChild(probe);
const value = getComputedStyle(probe).backgroundColor;
probe.remove();
return value;
});

expect(openBackground).toBe(activeBackground);
}
);

Comment thread
alexkaduk marked this conversation as resolved.
regressionTest(
'should not render focus outline on trigger while dropdown is expanded',
async ({ page, mount }) => {
Expand Down
53 changes: 42 additions & 11 deletions packages/core/src/components/dropdown-button/dropdown-button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,66 @@
*/
@use 'misc/common-variables' as vars;
@use 'mixins/shadow-dom/component';
@use './dropdown-button.vars' as *;

@mixin triangle($name: '') {
@if ($name != '') {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

๐Ÿ“ Maintainability & Code Quality | ๐ŸŸก Minor | โšก Quick win

Remove parentheses from the @if condition.

Stylelint reports scss/at-rule-conditional-no-parentheses at Line 14. This file fails the configured lint rule.

Proposed fix
-  `@if` ($name != '') {
+  `@if` $name != '' {
๐Ÿ“ Committable suggestion

โ€ผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@if ($name != '') {
`@if` $name != '' {
๐Ÿงฐ Tools
๐Ÿช› Stylelint (17.14.1)

[error] 14-14: Unexpected () used to surround statements for @-rules (scss/at-rule-conditional-no-parentheses)

(scss/at-rule-conditional-no-parentheses)

๐Ÿค– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/components/dropdown-button/dropdown-button.scss` at line
14, Remove the parentheses surrounding the condition in the `@if` rule at line 14
in the dropdown-button.scss file. The condition `($name != '')` should be
updated to remove the outer parentheses while keeping the condition logic
intact, as required by the scss/at-rule-conditional-no-parentheses linting rule.

Source: Linters/SAST tools

$name: '#{$name}-';
}

.triangle.#{$name}primary {
color: var(--theme-btn-#{$name}primary--color);
color: var(--ix-dropdown-button-#{$name}primary-color);
}
.triangle.#{$name}secondary {
color: var(--theme-btn-#{$name}secondary--color);
color: var(--ix-dropdown-button-#{$name}secondary-color);
}
.triangle.#{$name}tertiary {
color: var(--theme-btn-#{$name}tertiary--color);
color: var(--ix-dropdown-button-#{$name}tertiary-color);
}

.triangle.#{$name}primary.disabled {
color: var(--theme-btn-#{$name}primary--color--disabled);
color: var(--ix-dropdown-button-#{$name}primary-color-disabled);
}
.triangle.#{$name}secondary.disabled {
color: var(--theme-btn-#{$name}secondary--color--disabled);
color: var(--ix-dropdown-button-#{$name}secondary-color-disabled);
}
.triangle.#{$name}tertiary.disabled {
color: var(--theme-btn-#{$name}tertiary--color--disabled);
color: var(--ix-dropdown-button-#{$name}tertiary-color-disabled);
}
}

// Apply Expanded Active look to nested ix-button / ix-icon-button via their theme vars.
@mixin expanded-active-variant($name) {
--theme-btn-#{$name}--background: var(
--ix-dropdown-button-#{$name}-background-active
);
--theme-btn-#{$name}--background--hover: var(
--ix-dropdown-button-#{$name}-background-active
);
--theme-btn-#{$name}--color: var(--ix-dropdown-button-#{$name}-color-active);
--theme-btn-#{$name}--color--hover: var(
--ix-dropdown-button-#{$name}-color-active
);
--theme-btn-#{$name}--border-color: var(
--ix-dropdown-button-#{$name}-border-color-active
);
--theme-btn-#{$name}--border-color--hover: var(
--ix-dropdown-button-#{$name}-border-color-active
);
}

:host {
display: inline-block;
position: relative;
height: vars.$x-large-space;
width: auto;

--ix-dropdown-button-border-radius-left: var(--theme-btn--border-radius);
--ix-dropdown-button-border-radius-right: var(--theme-btn--border-radius);
--ix-dropdown-button-border-radius-left: var(
--ix-dropdown-button-border-radius
);
--ix-dropdown-button-border-radius-right: var(
--ix-dropdown-button-border-radius
);

border-top-left-radius: var(--ix-dropdown-button-border-radius-left);
border-bottom-left-radius: var(--ix-dropdown-button-border-radius-left);
Expand Down Expand Up @@ -97,7 +122,7 @@
border-left: 4px solid transparent;
border-top: 0 solid transparent;
border-bottom: 4px solid;
color: var(--theme-btn-primary--color);
color: var(--ix-dropdown-button-primary-color);
}

@include triangle();
Expand Down Expand Up @@ -130,8 +155,14 @@
}

:host(:focus-visible) {
outline: 1px solid var(--theme-color-focus-bdr);
outline-offset: var(--theme-btn--focus--outline-offset);
outline: 1px solid var(--ix-dropdown-button-focus-outline-color);
outline-offset: var(--ix-dropdown-button-focus-outline-offset);
}

:host([aria-expanded='true']) {
@each $variant in $dropdown-button-variants {
@include expanded-active-variant($variant);
}
}

:host([aria-expanded='true']:focus-visible) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2026 Siemens AG
*
* SPDX-License-Identifier: MIT
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

// Component token aliases. Keep --theme-* references in this file only.

$dropdown-button-variants: primary, secondary, tertiary, subtle-primary,
subtle-secondary, subtle-tertiary, danger-primary, danger-secondary,
danger-tertiary;

@mixin dropdown-button-variant-aliases($name) {
--ix-dropdown-button-#{$name}-color: var(--theme-btn-#{$name}--color);
--ix-dropdown-button-#{$name}-color-disabled: var(
--theme-btn-#{$name}--color--disabled
);
--ix-dropdown-button-#{$name}-background-active: var(
--theme-btn-#{$name}--background--active
);
--ix-dropdown-button-#{$name}-color-active: var(
--theme-btn-#{$name}--color--active
);
--ix-dropdown-button-#{$name}-border-color-active: var(
--theme-btn-#{$name}--border-color--active
);
}

:host {
--ix-dropdown-button-border-radius: var(--theme-btn--border-radius);
--ix-dropdown-button-focus-outline-color: var(--theme-color-focus-bdr);
--ix-dropdown-button-focus-outline-offset: var(
--theme-btn--focus--outline-offset
);

@each $variant in $dropdown-button-variants {
@include dropdown-button-variant-aliases($variant);
}
}
Loading