diff --git a/src-ui/src/app/app.module.ts b/src-ui/src/app/app.module.ts index a20a69eb8..395ddc04f 100644 --- a/src-ui/src/app/app.module.ts +++ b/src-ui/src/app/app.module.ts @@ -112,6 +112,7 @@ import { SwitchComponent } from './components/common/input/switch/switch.compone import { ConfigComponent } from './components/admin/config/config.component' import { FileComponent } from './components/common/input/file/file.component' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' +import { ConfirmButtonComponent } from './components/common/confirm-button/confirm-button.component' import { archive, arrowCounterclockwise, @@ -439,6 +440,7 @@ function initializeApp(settings: SettingsService) { SwitchComponent, ConfigComponent, FileComponent, + ConfirmButtonComponent, ], imports: [ BrowserModule, diff --git a/src-ui/src/app/components/common/confirm-button/confirm-button.component.html b/src-ui/src/app/components/common/confirm-button/confirm-button.component.html new file mode 100644 index 000000000..a82a06c4b --- /dev/null +++ b/src-ui/src/app/components/common/confirm-button/confirm-button.component.html @@ -0,0 +1,22 @@ + + + +
+ {{confirmMessage}}  +
+
diff --git a/src-ui/src/app/components/common/confirm-button/confirm-button.component.scss b/src-ui/src/app/components/common/confirm-button/confirm-button.component.scss new file mode 100644 index 000000000..14d19be51 --- /dev/null +++ b/src-ui/src/app/components/common/confirm-button/confirm-button.component.scss @@ -0,0 +1,12 @@ +// Taken from bootstrap rules, obv +::ng-deep .input-group > pngx-confirm-button:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) > button, +::ng-deep .btn-group > pngx-confirm-button:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) > button { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + + ::ng-deep .input-group:not(.has-validation) > pngx-confirm-button:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating) > button, + ::ng-deep .btn-group:not(.has-validation) > pngx-confirm-button:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating) > button { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } diff --git a/src-ui/src/app/components/common/confirm-button/confirm-button.component.spec.ts b/src-ui/src/app/components/common/confirm-button/confirm-button.component.spec.ts new file mode 100644 index 000000000..d67777d45 --- /dev/null +++ b/src-ui/src/app/components/common/confirm-button/confirm-button.component.spec.ts @@ -0,0 +1,37 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing' + +import { ConfirmButtonComponent } from './confirm-button.component' +import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap' +import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' + +describe('ConfirmButtonComponent', () => { + let component: ConfirmButtonComponent + let fixture: ComponentFixture + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ConfirmButtonComponent], + imports: [NgbPopoverModule, NgxBootstrapIconsModule.pick(allIcons)], + }).compileComponents() + + fixture = TestBed.createComponent(ConfirmButtonComponent) + component = fixture.componentInstance + fixture.detectChanges() + }) + + it('should show confirm on click', () => { + expect(component.popover.isOpen()).toBeFalsy() + expect(component.confirming).toBeFalsy() + component.onClick(new MouseEvent('click')) + expect(component.popover.isOpen()).toBeTruthy() + expect(component.confirming).toBeTruthy() + }) + + it('should emit confirm on confirm', () => { + const confirmSpy = jest.spyOn(component.confirm, 'emit') + component.onConfirm(new MouseEvent('click')) + expect(confirmSpy).toHaveBeenCalled() + expect(component.popover.isOpen()).toBeFalsy() + expect(component.confirming).toBeFalsy() + }) +}) diff --git a/src-ui/src/app/components/common/confirm-button/confirm-button.component.ts b/src-ui/src/app/components/common/confirm-button/confirm-button.component.ts new file mode 100644 index 000000000..f6746f99d --- /dev/null +++ b/src-ui/src/app/components/common/confirm-button/confirm-button.component.ts @@ -0,0 +1,55 @@ +import { + Component, + EventEmitter, + Input, + Output, + ViewChild, +} from '@angular/core' +import { NgbPopover } from '@ng-bootstrap/ng-bootstrap' + +@Component({ + selector: 'pngx-confirm-button', + templateUrl: './confirm-button.component.html', + styleUrl: './confirm-button.component.scss', +}) +export class ConfirmButtonComponent { + @Input() + label: string + + @Input() + confirmMessage: string = $localize`Are you sure?` + + @Input() + buttonClasses: string = 'btn-primary' + + @Input() + iconName: string + + @Input() + disabled: boolean = false + + @Output() + confirm: EventEmitter = new EventEmitter() + + @ViewChild('popover') popover: NgbPopover + + public confirming: boolean = false + + public onClick(event: MouseEvent) { + if (!this.confirming) { + this.confirming = true + this.popover.open() + } + + event.preventDefault() + event.stopImmediatePropagation() + } + + public onConfirm(event: MouseEvent) { + this.confirm.emit() + this.confirming = false + + event.preventDefault() + event.stopImmediatePropagation() + } +}