Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow to specify autofocus for buttons #634

Merged
Merged
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
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
Loading