Skip to content

Commit

Permalink
feat: allow to specify autofocus for buttons (#634)
Browse files Browse the repository at this point in the history
* feat: allow to specify autofocus for buttons

* fix: fixed lint

* fix: storybook

* fix: reverted ngIf removal
  • Loading branch information
markuczy authored Dec 10, 2024
1 parent 740ac32 commit 39230d6
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@
<div class="w-full flex-row justify-content-end flex flex-wrap gap-2">
<ng-container *ngFor="let button of leftCustomButtons; index as i">
<div [ngStyle]="{'margin-right': (i === leftCustomButtons.length - 1) ? 'auto' : 0}">
<ng-container *ngTemplateOutlet="customButton; context: {button: button}"> </ng-container>
<ng-container *ngTemplateOutlet="customButtonTemplate; context: {button: button}"> </ng-container>
</div>
</ng-container>
<ng-container *ngFor="let button of rightCustomButtons">
<div>
<ng-container *ngTemplateOutlet="customButton; context: {button: button}"> </ng-container>
<ng-container *ngTemplateOutlet="customButtonTemplate; context: {button: button}"> </ng-container>
</div>
</ng-container>
<div>
<button
#secondaryButton
id="{{dialogData.config.secondaryButtonDetails?.id ?? 'buttonDialogSecondaryButton'}}"
pButton
*ngIf="dialogData.config.secondaryButtonIncluded"
Expand All @@ -30,9 +31,9 @@
</div>
<div>
<button
#primaryButton
id="{{dialogData.config.primaryButtonDetails?.id ?? 'buttonDialogPrimaryButton'}}"
pButton
autofocus
[icon]="dialogData.config.primaryButtonDetails!.icon !== undefined ? dialogData.config.primaryButtonDetails!.icon : ''"
(click)="primaryButtonAction()"
[label]="dialogData.config.primaryButtonDetails!.key | translate:dialogData.config.primaryButtonDetails?.parameters"
Expand All @@ -45,8 +46,9 @@
</div>
</div>

<ng-template #customButton let-button="button">
<ng-template #customButtonTemplate let-button="button">
<button
#customButton
id="{{button.id}}"
pButton
[icon]="button.icon !== undefined ? button.icon : ''"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import {
AfterViewInit,
Component,
ComponentRef,
ElementRef,
EventEmitter,
Input,
OnInit,
Output,
QueryList,
ViewChild,
ViewChildren,
ViewContainerRef,
} from '@angular/core'
import { BehaviorSubject, Observable, from, isObservable, map, of, startWith, withLatestFrom } from 'rxjs'
Expand Down Expand Up @@ -33,7 +37,7 @@ import {
templateUrl: './button-dialog.component.html',
styleUrls: ['./button-dialog.component.scss'],
})
export class ButtonDialogComponent implements OnInit {
export class ButtonDialogComponent implements OnInit, AfterViewInit {
defaultPrimaryButtonDetails: ButtonDialogButtonDetails = {
key: 'OCX_BUTTON_DIALOG.CONFIRM',
}
Expand All @@ -58,6 +62,19 @@ export class ButtonDialogComponent implements OnInit {

@ViewChild('container', { static: true, read: ViewContainerRef })
dialogHost!: ViewContainerRef
@ViewChild('primaryButton', { static: true, read: ViewContainerRef })
primaryButton!: ViewContainerRef
_secondaryButton!: ViewContainerRef
@ViewChild('secondaryButton', { static: false, read: ViewContainerRef })
set secondaryButton(content: ViewContainerRef) {
if (content) {
this._secondaryButton = content
}
}
get secondaryButton(): ViewContainerRef {
return this._secondaryButton
}
@ViewChildren('customButton') customButtons!: QueryList<ElementRef>

dialogData: ButtonDialogData = this.defaultDialogData
componentRef!: ComponentRef<any>
Expand All @@ -71,6 +88,20 @@ export class ButtonDialogComponent implements OnInit {
public dynamicDialogConfig: DynamicDialogConfig,
public dynamicDialogRef: DynamicDialogRef
) {}
ngAfterViewInit(): void {
if (this.dialogData.config.autoFocusButton === 'primary' || !this.dialogData.config.autoFocusButton) {
this.primaryButton.element.nativeElement.focus()
} else if (this.dialogData.config.autoFocusButton === 'secondary') {
this.secondaryButton.element.nativeElement.focus()
} else if (this.dialogData.config.autoFocusButton === 'custom') {
const button = this.customButtons.find((customButton) => {
return customButton.nativeElement.id === this.dialogData.config.autoFocusButtonCustomId
})
setTimeout(() => {
button?.nativeElement.focus()
})
}
}

ngOnInit(): void {
this.loadComponent()
Expand Down Expand Up @@ -113,6 +144,8 @@ export class ButtonDialogComponent implements OnInit {
if (!!dialogConfig.secondaryButtonDetails && !!dialogConfig.secondaryButtonDetails.key) {
this.dialogData.config.secondaryButtonDetails = dialogConfig.secondaryButtonDetails
}
this.dialogData.config.autoFocusButton = dialogConfig.autoFocusButton
this.dialogData.config.autoFocusButtonCustomId = dialogConfig.autoFocusButtonCustomId
}
if (dynamicConfigData.component) {
this.dialogData.component = dynamicConfigData.component
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Type } from '@angular/core'
import { PrimeIcon } from '@onecx/angular-accelerator'
import { DialogButton } from '../services/portal-dialog.service'

/**
* Object describing details for button rendering containing key for translation, optional icon and optional parameters for translation
Expand Down Expand Up @@ -39,6 +40,8 @@ export interface ButtonDialogConfig {
secondaryButtonIncluded?: boolean
secondaryButtonDetails?: ButtonDialogButtonDetails
customButtons?: ButtonDialogCustomButtonDetails[]
autoFocusButton?: DialogButton
autoFocusButtonCustomId?: string
}

export interface ButtonDialogData {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { Component, Input, importProvidersFrom } from '@angular/core'
import { Component, EventEmitter, Input, OnInit, importProvidersFrom } from '@angular/core'
import { Meta, applicationConfig, argsToTemplate, componentWrapperDecorator, moduleMetadata } from '@storybook/angular'
import { DialogService, DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'
import { BrowserModule } from '@angular/platform-browser'
import { ButtonModule } from 'primeng/button'
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
import { PortalDialogService } from './portal-dialog.service'
import { DialogCustomButtonsDisabled, PortalDialogService } from './portal-dialog.service'
import { ButtonDialogComponent } from '../core/components/button-dialog/button-dialog.component'
import { StorybookTranslateModule } from '../core/storybook-translate.module'
import { DialogMessageContentComponent } from '../core/components/button-dialog/dialog-message-content/dialog-message-content.component'
import { PrimeIcons } from 'primeng/api'
import { TooltipModule } from 'primeng/tooltip'

@Component({
selector: 'ocx-ocx-button-dialog-with-portal-dialog-service',
selector: 'ocx-button-dialog-with-portal-dialog-service',
template: `<button (click)="openDialog()">Open dialog</button>`,
})
class ButtonDialogWithPortalDialogServiceComponent {
Expand Down Expand Up @@ -88,6 +88,27 @@ export const CustomData = {
},
}

export const CustomAutofocus = {
render: (args: any) => ({
props: {
...args,
},
template: `
<ocx-button-dialog-with-portal-dialog-service ${argsToTemplate(args)}>
</ocx-button-dialog-with-portal-dialog-service>
`,
}),
args: {
title: 'Custom title',
messageOrComponent: 'Custom message',
primaryKey: 'Primary Button',
secondaryKey: 'Secondary Button',
extras: {
autoFocusButton: 'secondary',
},
},
}

export const CustomDataWithExtendedButtons = {
render: (args: any) => ({
props: {
Expand Down Expand Up @@ -153,3 +174,97 @@ export const ComponentDisplayed = {
extras: {},
},
}

@Component({
selector: 'ocx-my-component-to-display',
template: `<p>Hello, its my component to display custom buttons</p>`,
})
class ComponentToDisplayCustomButtonsComponent implements DialogCustomButtonsDisabled, OnInit {
customButtonEnabled: EventEmitter<{ id: string; enabled: boolean }> = new EventEmitter()

ngOnInit(): void {
this.customButtonEnabled.emit({ id: 'custom1', enabled: true })
}
}

export const CustomButtons = {
render: (args: any) => ({
props: {
...args,
},
template: `
<ocx-button-dialog-with-portal-dialog-service ${argsToTemplate(args)}>
</ocx-button-dialog-with-portal-dialog-service>
`,
}),
args: {
title: 'Custom title',
messageOrComponent: {
type: ComponentToDisplayCustomButtonsComponent,
},
primaryKey: {
key: 'PRIMARY_KEY',
icon: PrimeIcons.BOOKMARK,
tooltipKey: 'TOOLTIP_KEY',
tooltipPosition: 'right',
},
secondaryKey: {
key: 'SECONDARY_KEY',
icon: PrimeIcons.SEARCH,
tooltipKey: 'TOOLTIP_KEY',
tooltipPosition: 'left',
},
extras: {
customButtons: [
{
id: 'custom1',
alignment: 'right',
key: 'CUSTOM_KEY',
icon: 'pi pi-times',
},
],
},
},
}

export const CustomButtonsWithAutofocus = {
render: (args: any) => ({
props: {
...args,
},
template: `
<ocx-button-dialog-with-portal-dialog-service ${argsToTemplate(args)}>
</ocx-button-dialog-with-portal-dialog-service>
`,
}),
args: {
title: 'Custom title',
messageOrComponent: {
type: ComponentToDisplayCustomButtonsComponent,
},
primaryKey: {
key: 'PRIMARY_KEY',
icon: PrimeIcons.BOOKMARK,
tooltipKey: 'TOOLTIP_KEY',
tooltipPosition: 'right',
},
secondaryKey: {
key: 'SECONDARY_KEY',
icon: PrimeIcons.SEARCH,
tooltipKey: 'TOOLTIP_KEY',
tooltipPosition: 'left',
},
extras: {
customButtons: [
{
id: 'custom1',
alignment: 'right',
key: 'CUSTOM_KEY',
icon: 'pi pi-times',
},
],
autoFocusButton: 'custom',
autoFocusButtonCustomId: 'custom1',
},
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ type Component<T extends unknown> = unknown extends T
inputs?: Record<string, unknown>
}

export type DialogButton = 'primary' | 'secondary' | 'custom'
export type DialogStateButtonClicked = 'primary' | 'secondary' | 'custom'

/**
Expand All @@ -225,6 +226,8 @@ export type DialogState<T> = {
export type PortalDialogConfig = {
showXButton?: boolean
customButtons?: ButtonDialogCustomButtonDetails[]
autoFocusButton?: DialogButton
autoFocusButtonCustomId?: string
ariaLabelledBy?: string
width?: string
height?: string
Expand All @@ -250,7 +253,10 @@ export type PortalDialogConfig = {

@Injectable({ providedIn: 'any' })
export class PortalDialogService {
constructor(private dialogService: DialogService, private translateService: TranslateService) {}
constructor(
private dialogService: DialogService,
private translateService: TranslateService
) {}

/**
* @deprecated
Expand Down Expand Up @@ -443,6 +449,8 @@ export class PortalDialogService {
customButtons: dialogOptions.customButtons?.map(
(button) => this.buttonDetailsOrTranslationKey(button) as ButtonDialogCustomButtonDetails
),
autoFocusButton: dialogOptions.autoFocusButton,
autoFocusButtonCustomId: dialogOptions.autoFocusButtonCustomId,
},
componentData: componentToRender.inputs,
}
Expand All @@ -454,6 +462,7 @@ export class PortalDialogService {
data: dynamicDialogDataConfig,
closable: dialogOptions.showXButton && secondaryButtonTranslationKeyOrDetails !== undefined,
...dialogOptions,
focusOnShow: false,
}).onClose
})
)
Expand Down

0 comments on commit 39230d6

Please sign in to comment.