From: Volker Theile Date: Tue, 22 Sep 2020 12:09:52 +0000 (+0200) Subject: mgr/dashboard: Copy to clipboard does not work in Firefox X-Git-Tag: v15.2.9~122^2~49^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=refs%2Fpull%2F37493%2Fhead;p=ceph.git mgr/dashboard: Copy to clipboard does not work in Firefox Fixes: https://tracker.ceph.com/issues/47578 Signed-off-by: Volker Theile (cherry picked from commit 8b9d881a63aac8ed3d7d1a7fc80be5e3d3d0b3b1) Conflicts: src/pybind/mgr/dashboard/frontend/src/app/shared/directives/copy2clipboard-button.directive.spec.ts --- diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/directives/copy2clipboard-button.directive.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/directives/copy2clipboard-button.directive.spec.ts index 37fca45264e..28659705a83 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/directives/copy2clipboard-button.directive.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/directives/copy2clipboard-button.directive.spec.ts @@ -1,18 +1,69 @@ import { TestBed } from '@angular/core/testing'; import { I18n } from '@ngx-translate/i18n-polyfill'; +import * as BrowserDetect from 'detect-browser'; +import { ToastrService } from 'ngx-toastr'; import { configureTestBed, i18nProviders } from '../../../testing/unit-test-helper'; import { Copy2ClipboardButtonDirective } from './copy2clipboard-button.directive'; describe('Copy2clipboardButtonDirective', () => { + let directive: Copy2ClipboardButtonDirective; + configureTestBed({ - providers: [i18nProviders] + providers: [ + i18nProviders, + { + provide: ToastrService, + useValue: { + error: () => true, + success: () => true + } + } + ] }); it('should create an instance', () => { const i18n = TestBed.get(I18n); - const directive = new Copy2ClipboardButtonDirective(null, null, null, i18n); + directive = new Copy2ClipboardButtonDirective(null, null, null, i18n); expect(directive).toBeTruthy(); }); + + describe('test onClick behaviours', () => { + let toastrService: ToastrService; + let queryFn: jasmine.Spy; + let writeTextFn: jasmine.Spy; + + beforeEach(() => { + const i18n = TestBed.get(I18n); + toastrService = TestBed.get(ToastrService); + directive = new Copy2ClipboardButtonDirective(null, null, toastrService, i18n); + spyOn(directive, 'getText').and.returnValue('foo'); + Object.assign(navigator, { + permissions: { query: jest.fn() }, + clipboard: { + writeText: jest.fn() + } + }); + queryFn = spyOn(navigator.permissions, 'query'); + }); + + it('should not call permissions API', () => { + spyOn(BrowserDetect, 'detect').and.returnValue({ name: 'firefox' }); + writeTextFn = spyOn(navigator.clipboard, 'writeText').and.returnValue( + new Promise((resolve, _) => { + resolve(); + }) + ); + directive.onClick(); + expect(queryFn).not.toHaveBeenCalled(); + expect(writeTextFn).toHaveBeenCalledWith('foo'); + }); + + it('should call permissions API', () => { + spyOn(BrowserDetect, 'detect').and.returnValue({ name: 'chrome' }); + directive.onClick(); + expect(queryFn).toHaveBeenCalled(); + }); + }); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/directives/copy2clipboard-button.directive.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/directives/copy2clipboard-button.directive.ts index 91d76151950..be84809b4c7 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/directives/copy2clipboard-button.directive.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/directives/copy2clipboard-button.directive.ts @@ -1,6 +1,7 @@ import { Directive, ElementRef, HostListener, Input, OnInit, Renderer2 } from '@angular/core'; import { I18n } from '@ngx-translate/i18n-polyfill'; +import { detect } from 'detect-browser'; import { ToastrService } from 'ngx-toastr'; @Directive({ @@ -25,24 +26,34 @@ export class Copy2ClipboardButtonDirective implements OnInit { this.renderer.appendChild(this.elementRef.nativeElement, iElement); } - private getInputElement() { - return document.getElementById(this.cdCopy2ClipboardButton) as HTMLInputElement; + private getText(): string { + const element = document.getElementById(this.cdCopy2ClipboardButton) as HTMLInputElement; + return element.value; } @HostListener('click') onClick() { try { - // Checking if we have the clipboard-write permission - navigator.permissions - .query({ name: 'clipboard-write' as PermissionName }) - .then((result: any) => { - if (result.state === 'granted' || result.state === 'prompt') { - // Copy text to clipboard. - navigator.clipboard.writeText(this.getInputElement().value); - } - }); - this.toastr.success('Copied text to the clipboard successfully.'); - } catch (err) { + const browser = detect(); + const text = this.getText(); + const toastrFn = () => { + this.toastr.success('Copied text to the clipboard successfully.'); + }; + if (['firefox', 'ie', 'ios', 'safari'].includes(browser.name)) { + // Various browsers do not support the `Permissions API`. + // https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API#Browser_compatibility + navigator.clipboard.writeText(text).then(() => toastrFn()); + } else { + // Checking if we have the clipboard-write permission + navigator.permissions + .query({ name: 'clipboard-write' as PermissionName }) + .then((result: any) => { + if (result.state === 'granted' || result.state === 'prompt') { + navigator.clipboard.writeText(text).then(() => toastrFn()); + } + }); + } + } catch (_) { this.toastr.error('Failed to copy text to the clipboard.'); } }