From 459574501b5c11d47d93ca20ebe2a73ae8d7330f Mon Sep 17 00:00:00 2001 From: "Bajohr, Rayk" Date: Tue, 28 Jul 2026 14:00:00 +0200 Subject: [PATCH] docs: improve dropdown documentation --- docs/components/buttons-menus/dropdowns.md | 143 +++++++++++++++++- .../dropdown/dropdown-with-overlay.html | 32 ++-- .../dropdown/dropdown-with-overlay.ts | 12 +- 3 files changed, 158 insertions(+), 29 deletions(-) diff --git a/docs/components/buttons-menus/dropdowns.md b/docs/components/buttons-menus/dropdowns.md index 151a3b10d7..01eaa765e8 100644 --- a/docs/components/buttons-menus/dropdowns.md +++ b/docs/components/buttons-menus/dropdowns.md @@ -42,23 +42,150 @@ Interaction states are identical to the [normal buttons](buttons.md). If the overlay should show a contextual menu, please use the [menu component](menu.md). It already implements specific keyboard interactions and aria roles that are needed for a _dropdown menu_. -This chapter describes how to create an interactive contextual overlay using the [CDK Overlay module](https://material.angular.io/cdk/overlay/overview) and the [CDK Focus trap](https://material.angular.io/cdk/a11y/overview#focustrap) -without special keyboard handling and aria-roles. +Use a [button](buttons.md) with the `dropdown-toggle` class as the trigger and place the content in a +`dropdown-menu`. Add a `dropdown-caret` icon to indicate that the button opens an overlay. -The example uses the markup of [Bootstrap dropdown](https://getbootstrap.com/docs/5.1/components/dropdowns/). -It is also possible to use the CDK directives without using markup related to the `dropdown-menu`. +The theme provides the dropdown styles. Use the [CDK Overlay module](https://material.angular.io/cdk/overlay/overview) +to create, position, and dismiss the overlay. The CDK directives can also be used with content that does not use +the `dropdown-menu` class. -The overlay is toggled by the `open` property which needs to be updated properly. -We added a transparent backdrop to the overlay so that we can listen for `(backdropClick)` which should close the overlay. -The listener for `(detach)` is necessary for updating the `open` property after escape was pressed. -In a real world scenario, one has to be careful here, because `(detach)` is always called when the overlay is closed. +### Basic markup + +Use a button for actions and an anchor for navigation. Set `aria-expanded` to the current open state and add +`show` to the trigger while the overlay is open so that the caret rotates. + +```html + + + + + +``` + +The `open` state controls the overlay. Use a transparent backdrop to close it when the user clicks outside. +Also update the state on `(detach)`, which is emitted when the overlay closes, including after pressing Escape. +The `position-static` and `d-block` utility classes allow the CDK to position the rendered menu. For having a proper keyboard interaction, we are using the [CDK Focus trap](https://material.angular.io/cdk/a11y/overview#focustrap). It ensures that the focus will remain within the overlay while it is open. Enabling `[cdkTrapFocusAutoCapture]="true"` ensures, that the focus will be moved into the overlay on creation and returned to the trigger when the overlay is closed. +### Directions + +Wrap the trigger and menu in one of the direction classes to place a statically positioned menu relative to its +trigger. The caret direction changes automatically. + +| Class | Menu placement | +| ----------- | ------------------ | +| `dropdown` | Below the trigger | +| `dropup` | Above the trigger | +| `dropstart` | Before the trigger | +| `dropend` | After the trigger | + +```html +
+ + +
+``` + +When using the CDK Overlay, configure the connected overlay positions instead. The direction classes are useful +for menus that remain in the document flow or when the position is managed by the application. + +### Alignment + +Use an alignment class on the `dropdown-menu` to align it with its trigger. `align-start` is the default placement. + +| Class | Alignment | +| -------------- | ------------------------------------------ | +| `align-start` | Align the menu with the trigger start edge | +| `align-center` | Center the menu on the trigger start edge | +| `align-end` | Align the menu with the trigger end edge | + +```html + +``` + +### Menu content + +Use `dropdown-item` for interactive actions. It supports the `active` and `disabled` states. Use +`dropdown-item-text` for non-interactive content and `dropdown-header` to label a group of items. +Use `dropdown-divider` to separate related groups. + +```html + +``` + +For items with icons, place the icon before an `item-title`. A trailing `menu-end-icon` can communicate an +additional state or a follow-up action. + +```html + +``` + +### Long menus + +`dropdown-menu` scrolls when its height exceeds the viewport. Add `dropdown-menu-scroller` to apply the +recommended bounded height for menus with many items. + +```html + +``` + +### Related components + +Use [button groups](button-group.md) to create split-button patterns. Use the [menu component](menu.md) when the +content, see [content actions](content-actions.md). + diff --git a/src/app/examples/dropdown/dropdown-with-overlay.html b/src/app/examples/dropdown/dropdown-with-overlay.html index 8e1380a487..5eedceb2d6 100644 --- a/src/app/examples/dropdown/dropdown-with-overlay.html +++ b/src/app/examples/dropdown/dropdown-with-overlay.html @@ -3,36 +3,36 @@ type="button" class="btn btn-primary dropdown-toggle" cdkOverlayOrigin - [class.show]="open" - [attr.aria-expanded]="open" - (click)="open = true" + [class.show]="open()" + [attr.aria-expanded]="open()" + (click)="open.set(true)" > Overlay trigger / origin - + diff --git a/src/app/examples/dropdown/dropdown-with-overlay.ts b/src/app/examples/dropdown/dropdown-with-overlay.ts index 91e50cb238..0a0f2e7de9 100644 --- a/src/app/examples/dropdown/dropdown-with-overlay.ts +++ b/src/app/examples/dropdown/dropdown-with-overlay.ts @@ -4,17 +4,19 @@ */ import { A11yModule } from '@angular/cdk/a11y'; import { CdkConnectedOverlay, CdkOverlayOrigin } from '@angular/cdk/overlay'; -import { Component, inject } from '@angular/core'; +import { Component, inject, signal } from '@angular/core'; +import { elementDown2 } from '@siemens/element-icons'; +import { addIcons, SiIconComponent } from '@siemens/element-ng/icon'; import { LOG_EVENT } from '@siemens/live-preview'; @Component({ selector: 'app-sample', - imports: [CdkOverlayOrigin, CdkConnectedOverlay, A11yModule], + imports: [A11yModule, CdkOverlayOrigin, CdkConnectedOverlay, SiIconComponent], templateUrl: './dropdown-with-overlay.html', host: { class: 'p-5' } }) export class SampleComponent { - logEvent = inject(LOG_EVENT); - - open = false; + protected readonly logEvent = inject(LOG_EVENT); + protected readonly icons = addIcons({ elementDown2 }); + protected readonly open = signal(false); }