feat(select): support constructions of custom select#1852
feat(select): support constructions of custom select#1852spike-rabbit wants to merge 1 commit intomainfrom
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a new set of components and directives—SiCustomSelectDirective, SiSelectComboboxComponent, and SiSelectDropdownDirective—to enable the creation of custom select components like tree-selects. The implementation includes ControlValueAccessor and SiFormItemControl integration, comprehensive unit tests, and a usage example. Feedback focuses on improving consistency with the standard select component through enhanced keyboard navigation and visual alignment, ensuring Server-Side Rendering (SSR) safety for browser-specific APIs, and refining the ControlValueAccessor implementation and resource cleanup.
| '(click)': 'open()', | ||
| '(keydown.enter)': 'open()', | ||
| '(keydown.space)': 'open()', | ||
| '(keydown.arrowDown)': 'open()' |
| } | ||
|
|
||
| /** Opens the dropdown overlay. */ | ||
| open(): void { |
There was a problem hiding this comment.
The open method uses browser-dependent APIs like getBoundingClientRect and the CDK Overlay service. To ensure compatibility with Server-Side Rendering (SSR), these calls should be guarded by a check for the browser platform using isPlatformBrowser.
References
- When using browser-dependent UI features like Angular CDK Overlay, ensure they are only executed in a browser environment. Use a check like isPlatformBrowser to prevent errors during Server-Side Rendering (SSR).
| hasBackdrop: true, | ||
| backdropClass: 'cdk-overlay-transparent-backdrop', | ||
| panelClass: ['dropdown-menu', 'show'], | ||
| minWidth: width |
| this.isOpen.set(false); | ||
| this.disposeOverlay(); | ||
| this.openChange.emit(false); | ||
| this.elementRef.nativeElement.focus(); |
There was a problem hiding this comment.
To adhere to the ControlValueAccessor contract and ensure proper form integration (e.g., triggering validation or marking the control as touched), onTouched() should be called when the dropdown is closed. Additionally, the focus() call should be guarded for SSR safety using isPlatformBrowser.
| this.elementRef.nativeElement.focus(); | |
| if (isPlatformBrowser(this.platformId)) { | |
| this.elementRef.nativeElement.focus(); | |
| } | |
| this.onTouched(); |
References
- When using browser-dependent UI features like Angular CDK Overlay, ensure they are only executed in a browser environment. Use a check like isPlatformBrowser to prevent errors during Server-Side Rendering (SSR).
|
|
||
| private disposeOverlay(): void { | ||
| if (this.overlayRef) { | ||
| this.closeOverlay$.next(); |
| block-size: 100%; | ||
| color: variables.$element-text-primary; | ||
| padding-block: map.get(variables.$spacers, 2); | ||
| padding-inline-end: calc(map.get(variables.$spacers, 3) + var(--si-feedback-icon-offset, 0px)); |
There was a problem hiding this comment.
For visual consistency with the standard si-select-input, the padding-inline-end should use $spacers, 5 instead of $spacers, 3. This ensures the dropdown caret is positioned identically across all select variants.
| padding-inline-end: calc(map.get(variables.$spacers, 3) + var(--si-feedback-icon-offset, 0px)); | |
| padding-inline-end: calc(map.get(variables.$spacers, 5) + var(--si-feedback-icon-offset, 0px)); |
Adding a set of utilities that allows customers to create selects with custom backed value selection, for instance using `si-tree-view`. A very simple version of it can look like this: ```ts @component({ selector: 'app-tree-select', imports: [SiSelectComboboxComponent, SiSelectDropdownDirective, SiTreeViewComponent], template: ` <si-select-combobox> @if (select.value(); as val) { {{ val }} } @else { <span class="text-secondary">Select a location...</span> } </si-select-combobox> <ng-template si-select-dropdown> <si-tree-view class="d-block" ariaLabel="Locations" [items]="items()" [enableSelection]="true" [singleSelectMode]="true" [isVirtualized]="false" (treeItemClicked)="selectItem($event)" /> </ng-template> `, changeDetection: ChangeDetectionStrategy.OnPush, hostDirectives: [ { directive: SiCustomSelectDirective, inputs: ['disabled', 'readonly', 'value'], outputs: ['valueChange'] } ] }) export class TreeSelectComponent { protected readonly select = inject(SiCustomSelectDirective); /** The tree items to display. */ readonly items = input<TreeItem[]>([]); selectItem(item: TreeItem): void { if (item.label) { this.select.updateValue(item.label as string); this.select.close(); } } } ``` The goal is to empower applications to build selects with whatever content they need while we take care of accesibility and proper appereance. Closes #1840
b9201af to
8aad9d9
Compare
Adding a set of utilities that allows customers
to create selects with custom backed value selection, for instance using
si-tree-view.A very simple version of it can look like this:
The goal is to empower applications
to build selects with whatever content they need
while we take care of accesibility and proper appereance.
Closes #1840
Documentation.
Examples.
Dashboards Demo.
Playwright report.
Coverage Reports: