Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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