From c348ad4a941cf309de9073d5db02814453469369 Mon Sep 17 00:00:00 2001 From: Rohit Sharma Date: Wed, 25 Nov 2020 12:46:22 +0530 Subject: [PATCH] Ability to add custom class in tooltip/popover (#32217) Porting of #31834 to main. Co-authored-by: XhmikosR --- .bundlewatch.config.json | 2 +- js/src/tooltip.js | 7 +++ js/tests/unit/popover.spec.js | 16 ++++++ js/tests/unit/tooltip.spec.js | 55 ++++++++++++++++++++ site/content/docs/5.0/components/popovers.md | 9 ++++ site/content/docs/5.0/components/tooltips.md | 9 ++++ 6 files changed, 97 insertions(+), 1 deletion(-) diff --git a/.bundlewatch.config.json b/.bundlewatch.config.json index 4bfffbab44..525d29f6a0 100644 --- a/.bundlewatch.config.json +++ b/.bundlewatch.config.json @@ -38,7 +38,7 @@ }, { "path": "./dist/js/bootstrap.bundle.min.js", - "maxSize": "22 kB" + "maxSize": "22.25 kB" }, { "path": "./dist/js/bootstrap.esm.js", diff --git a/js/src/tooltip.js b/js/src/tooltip.js index b2495a3e11..368e04b30c 100644 --- a/js/src/tooltip.js +++ b/js/src/tooltip.js @@ -54,6 +54,7 @@ const DefaultType = { container: '(string|element|boolean)', fallbackPlacement: '(string|array)', boundary: '(string|element)', + customClass: '(string|function)', sanitize: 'boolean', sanitizeFn: '(null|function)', allowList: 'object', @@ -83,6 +84,7 @@ const Default = { container: false, fallbackPlacement: 'flip', boundary: 'scrollParent', + customClass: '', sanitize: true, sanitizeFn: null, allowList: DefaultAllowlist, @@ -296,6 +298,11 @@ class Tooltip { tip.classList.add(CLASS_NAME_SHOW) + const customClass = typeof this.config.customClass === 'function' ? this.config.customClass() : this.config.customClass + if (customClass) { + tip.classList.add(...customClass.split(' ')) + } + // If this is a touch-enabled device we add extra // empty mouseover listeners to the body's immediate children; // only needed because of broken event delegation on iOS diff --git a/js/tests/unit/popover.spec.js b/js/tests/unit/popover.spec.js index df48305954..e87ed12144 100644 --- a/js/tests/unit/popover.spec.js +++ b/js/tests/unit/popover.spec.js @@ -116,6 +116,22 @@ describe('Popover', () => { popover.show() }) + + it('should show a popover with provided custom class', done => { + fixtureEl.innerHTML = 'BS twitter' + + const popoverEl = fixtureEl.querySelector('a') + const popover = new Popover(popoverEl) + + popoverEl.addEventListener('shown.bs.popover', () => { + const tip = document.querySelector('.popover') + expect(tip).toBeDefined() + expect(tip.classList.contains('custom-class')).toBeTrue() + done() + }) + + popover.show() + }) }) describe('hide', () => { diff --git a/js/tests/unit/tooltip.spec.js b/js/tests/unit/tooltip.spec.js index c781f587a1..da2abba314 100644 --- a/js/tests/unit/tooltip.spec.js +++ b/js/tests/unit/tooltip.spec.js @@ -632,6 +632,61 @@ describe('Tooltip', () => { tooltipEl.dispatchEvent(createEvent('mouseover')) }) + + it('should show a tooltip with custom class provided in data attributes', done => { + fixtureEl.innerHTML = '' + + const tooltipEl = fixtureEl.querySelector('a') + const tooltip = new Tooltip(tooltipEl) + + tooltipEl.addEventListener('shown.bs.tooltip', () => { + const tip = document.querySelector('.tooltip') + expect(tip).toBeDefined() + expect(tip.classList.contains('custom-class')).toBeTrue() + done() + }) + + tooltip.show() + }) + + it('should show a tooltip with custom class provided as a string in config', done => { + fixtureEl.innerHTML = '' + + const tooltipEl = fixtureEl.querySelector('a') + const tooltip = new Tooltip(tooltipEl, { + customClass: 'custom-class custom-class-2' + }) + + tooltipEl.addEventListener('shown.bs.tooltip', () => { + const tip = document.querySelector('.tooltip') + expect(tip).toBeDefined() + expect(tip.classList.contains('custom-class')).toBeTrue() + expect(tip.classList.contains('custom-class-2')).toBeTrue() + done() + }) + + tooltip.show() + }) + + it('should show a tooltip with custom class provided as a function in config', done => { + fixtureEl.innerHTML = '' + + const spy = jasmine.createSpy('customClass').and.returnValue('custom-class') + const tooltipEl = fixtureEl.querySelector('a') + const tooltip = new Tooltip(tooltipEl, { + customClass: spy + }) + + tooltipEl.addEventListener('shown.bs.tooltip', () => { + const tip = document.querySelector('.tooltip') + expect(tip).toBeDefined() + expect(spy).toHaveBeenCalled() + expect(tip.classList.contains('custom-class')).toBeTrue() + done() + }) + + tooltip.show() + }) }) describe('hide', () => { diff --git a/site/content/docs/5.0/components/popovers.md b/site/content/docs/5.0/components/popovers.md index c1e1f90173..86efe909e9 100644 --- a/site/content/docs/5.0/components/popovers.md +++ b/site/content/docs/5.0/components/popovers.md @@ -262,6 +262,15 @@ Note that for security reasons the `sanitize`, `sanitizeFn`, and `allowList` opt 'scrollParent' Overflow constraint boundary of the popover. Accepts the values of 'viewport', 'window', 'scrollParent', or an HTMLElement reference (JavaScript only). For more information refer to Popper's preventOverflow docs. + + customClass + string | function + '' + +

Add classes to the popover when it is shown. Note that these classes will be added in addition to any classes specified in the template. To add multiple classes, separate them with spaces: 'class-1 class-2'.

+

You can also pass a function that should return a single string containing additional class names.

+ + sanitize boolean diff --git a/site/content/docs/5.0/components/tooltips.md b/site/content/docs/5.0/components/tooltips.md index 58c1bf9db0..a4e76bc063 100644 --- a/site/content/docs/5.0/components/tooltips.md +++ b/site/content/docs/5.0/components/tooltips.md @@ -271,6 +271,15 @@ Note that for security reasons the `sanitize`, `sanitizeFn`, and `allowList` opt 'scrollParent' Overflow constraint boundary of the tooltip. Accepts the values of 'viewport', 'window', 'scrollParent', or an HTMLElement reference (JavaScript only). For more information refer to Popper's preventOverflow docs. + + customClass + string | function + '' + +

Add classes to the tooltip when it is shown. Note that these classes will be added in addition to any classes specified in the template. To add multiple classes, separate them with spaces: 'class-1 class-2'.

+

You can also pass a function that should return a single string containing additional class names.

+ + sanitize boolean