Skip to content

Commit

Permalink
Merge pull request #1502 from kaliber5/bs5-tooltips
Browse files Browse the repository at this point in the history
Update Tooltips and Popovers for Bootstrap 5
  • Loading branch information
simonihmig committed May 18, 2021
2 parents ffd9a19 + 81423ad commit 8401113
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 21 deletions.
22 changes: 22 additions & 0 deletions addon/components/bs-contextual-help/element.js
Expand Up @@ -4,6 +4,7 @@ import { assert } from '@ember/debug';
import { scheduleOnce } from '@ember/runloop';
import arg from 'ember-bootstrap/utils/decorators/arg';
import { tracked } from '@glimmer/tracking';
import { getOwnConfig, macroCondition } from '@embroider/macros';

/**
Internal (abstract) component for contextual help markup. Should not be used directly.
Expand Down Expand Up @@ -91,6 +92,10 @@ export default class ContextualHelpElement extends Component {
*/
arrowClass = 'arrow';

placementClassPrefix = '';

offset = [0, 0];

/**
* popper.js modifier config
*
Expand All @@ -105,6 +110,7 @@ export default class ContextualHelpElement extends Component {
element: `.${this.arrowClass}`,
},
offset: {
offset: this.offset.join(','),
fn(data) {
let tip = document.getElementById(id);
assert('Contextual help element needs existing popper element', tip);
Expand Down Expand Up @@ -141,6 +147,22 @@ export default class ContextualHelpElement extends Component {
};
}

get actualPlacementClass() {
let ending = this.actualPlacement;

if (macroCondition(getOwnConfig().isBS5)) {
if (ending === 'right') {
ending = 'end';
}

if (ending === 'left') {
ending = 'start';
}
}

return this.placementClassPrefix + ending;
}

@action
updatePlacement(popperDataObject) {
if (this.actualPlacement === popperDataObject.placement) {
Expand Down
2 changes: 1 addition & 1 deletion addon/components/bs-popover/element.hbs
Expand Up @@ -8,7 +8,7 @@
@onCreate={{this.updatePlacement}}
@onUpdate={{this.updatePlacement}}
@id={{@id}}
@class="popover {{@class}} {{if this.fade "fade"}} {{if (macroCondition (macroGetOwnConfig "isNotBS3")) (concat "bs-popover-" this.actualPlacement)}} {{if (macroCondition (macroGetOwnConfig "isBS3")) this.actualPlacement}} {{if (macroCondition (macroGetOwnConfig "isNotBS3")) (if this.showHelp "show")}} {{if (macroCondition (macroGetOwnConfig "isBS3")) (if this.showHelp "in")}} {{if (macroCondition (macroGetOwnConfig "isBS3")) "ember-bootstrap-popover"}}"
@class="popover {{@class}} {{if this.fade "fade"}} {{if (macroCondition (macroGetOwnConfig "isNotBS3")) this.actualPlacementClass}} {{if (macroCondition (macroGetOwnConfig "isBS3")) this.actualPlacement}} {{if (macroCondition (macroGetOwnConfig "isNotBS3")) (if this.showHelp "show")}} {{if (macroCondition (macroGetOwnConfig "isBS3")) (if this.showHelp "in")}} {{if (macroCondition (macroGetOwnConfig "isBS3")) "ember-bootstrap-popover"}}"
...attributes
>
<div class={{this.arrowClass}}></div>
Expand Down
5 changes: 5 additions & 0 deletions addon/components/bs-popover/element.js
@@ -1,4 +1,5 @@
import ContextualHelpElement from '../bs-contextual-help/element';
import { getOwnConfig, macroCondition } from '@embroider/macros';

/**
Internal component for popover's markup. Should not be used directly.
Expand All @@ -14,4 +15,8 @@ export default class PopoverElement extends ContextualHelpElement {
* @type string
* @public
*/

arrowClass = macroCondition(getOwnConfig().isBS5) ? 'popover-arrow' : 'arrow';
placementClassPrefix = 'bs-popover-';
offset = [0, macroCondition(getOwnConfig().isBS5) ? 8 : 0];
}
2 changes: 1 addition & 1 deletion addon/components/bs-tooltip/element.hbs
Expand Up @@ -8,7 +8,7 @@
@onCreate={{this.updatePlacement}}
@onUpdate={{this.updatePlacement}}
@id={{@id}}
@class="tooltip {{@class}} {{if this.fade "fade"}} {{if (macroCondition (macroGetOwnConfig "isNotBS3")) (concat "bs-tooltip-" this.actualPlacement)}} {{if (macroCondition (macroGetOwnConfig "isBS3")) this.actualPlacement}} {{if (macroCondition (macroGetOwnConfig "isNotBS3")) (if this.showHelp "show")}} {{if (macroCondition (macroGetOwnConfig "isBS3")) (if this.showHelp "in")}} {{if (macroCondition (macroGetOwnConfig "isBS3")) "ember-bootstrap-tooltip"}}"
@class="tooltip {{@class}} {{if this.fade "fade"}} {{if (macroCondition (macroGetOwnConfig "isNotBS3")) this.actualPlacementClass}} {{if (macroCondition (macroGetOwnConfig "isBS3")) this.actualPlacement}} {{if (macroCondition (macroGetOwnConfig "isNotBS3")) (if this.showHelp "show")}} {{if (macroCondition (macroGetOwnConfig "isBS3")) (if this.showHelp "in")}} {{if (macroCondition (macroGetOwnConfig "isBS3")) "ember-bootstrap-tooltip"}}"
...attributes
>
<div class={{this.arrowClass}}></div>
Expand Down
3 changes: 2 additions & 1 deletion addon/components/bs-tooltip/element.js
Expand Up @@ -10,5 +10,6 @@ import { getOwnConfig, macroCondition } from '@embroider/macros';
@private
*/
export default class TooltipElement extends ContextualHelpElement {
arrowClass = macroCondition(getOwnConfig().isBS3) ? 'tooltip-arrow' : 'arrow';
arrowClass = macroCondition(getOwnConfig().isBS4) ? 'arrow' : 'tooltip-arrow';
placementClassPrefix = 'bs-tooltip-';
}
6 changes: 5 additions & 1 deletion tests/helpers/bootstrap.js
Expand Up @@ -125,7 +125,11 @@ export function popoverPositionClass(pos) {
}

export function tooltipArrowClass() {
return versionDependent('tooltip-arrow', 'arrow');
return versionDependent('tooltip-arrow', 'arrow', 'tooltip-arrow');
}

export function popoverArrowClass() {
return versionDependent('arrow', 'arrow', 'popover-arrow');
}

export function isVisible(el) {
Expand Down
4 changes: 2 additions & 2 deletions tests/helpers/contextual-help.js
Expand Up @@ -32,7 +32,7 @@ function offset(el) {
};
}

export function assertPositioning(assert, selector = '.tooltip') {
export function assertPositioning(assert, selector = '.tooltip', additionalOffset = 0) {
assert.dom(selector).exists({ count: 1 }, 'Element exists.');

let rootEl = getContext().element;
Expand All @@ -41,7 +41,7 @@ export function assertPositioning(assert, selector = '.tooltip') {
let margin =
-parseInt(window.getComputedStyle(tooltip).marginTop, 10) +
parseInt(window.getComputedStyle(tooltip).marginBottom, 10);
let tooltipPos = Math.round(offset(tooltip).top + tooltip.offsetHeight + margin);
let tooltipPos = Math.round(offset(tooltip).top + tooltip.offsetHeight + margin + additionalOffset);
let triggerPos = Math.round(offset(trigger).top);
assert.ok(Math.abs(triggerPos - tooltipPos) <= 1, `Expected position: ${triggerPos}, actual: ${tooltipPos}`);
}
23 changes: 14 additions & 9 deletions tests/integration/components/bs-popover-test.js
Expand Up @@ -2,7 +2,13 @@ import { module, skip } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, click, triggerEvent, settled } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import { test, versionDependent, visibilityClass, popoverPositionClass } from '../../helpers/bootstrap';
import {
test,
versionDependent,
visibilityClass,
popoverPositionClass,
popoverArrowClass,
} from '../../helpers/bootstrap';
import { setupForPositioning, assertPositioning } from '../../helpers/contextual-help';
import setupStylesheetSupport from '../../helpers/setup-stylesheet-support';
import setupNoDeprecations from '../../helpers/setup-no-deprecations';
Expand All @@ -28,7 +34,7 @@ module('Integration | Component | bs-popover', function (hooks) {
assert.dom('.popover').hasClass('fade', 'has fade class');
assert.dom('.popover').hasClass(visibilityClass(), 'has visibility class');
assert.equal(this.element.querySelector('.popover').getAttribute('role'), 'tooltip', 'has ARIA role');
assert.dom('.arrow').exists({ count: 1 }, 'has arrow');
assert.dom(`.${popoverArrowClass()}`).exists({ count: 1 }, 'has arrow');
assert.dom(versionDependent('.popover-title', '.popover-header')).hasText('dummy title', 'shows title');
assert.dom(versionDependent('.popover-content', '.popover-body')).hasText('template block text', 'shows content');
});
Expand Down Expand Up @@ -69,21 +75,20 @@ module('Integration | Component | bs-popover', function (hooks) {
setupForPositioning();

await click('#target');

assertPositioning(assert, '.popover');
assertPositioning(assert, '.popover', versionDependent(0, 0, 8));
});

test('should adjust popover arrow', async function (assert) {
this.insertCSSRule('#wrapper p { margin-top: 200px }');
this.insertCSSRule('#target { width: 100px; padding: 0; border: none; }');

// @todo v5 needs changes in markup
let expectedArrowPosition = versionDependent(225, 219, 0);
let expectedArrowPosition = versionDependent(214, 213, 217);

await render(hbs`
<div id="ember-bootstrap-wormhole"></div>
<div id="wrapper">
<p>
<button type="button" class="btn" id="target">
<button type="button" id="target">
Click me<BsPopover @placement="top" @autoPlacement={{true}} @viewportSelector="#wrapper" @title="very very very very very very very long popover" @fade={{false}}>very very very very very very very long popover</BsPopover>
</button>
</p>
Expand All @@ -92,7 +97,7 @@ module('Integration | Component | bs-popover', function (hooks) {
setupForPositioning('right');

await click('#target');
let arrowPosition = parseInt(this.element.querySelector('.arrow').style.left, 10);
let arrowPosition = parseInt(this.element.querySelector(`.${popoverArrowClass()}`).style.left, 10);
assert.ok(
Math.abs(arrowPosition - expectedArrowPosition) <= 2,
`Expected position: ${expectedArrowPosition}, actual: ${arrowPosition}`
Expand All @@ -101,7 +106,7 @@ module('Integration | Component | bs-popover', function (hooks) {
// check again to prevent regression of https://github.com/kaliber5/ember-bootstrap/issues/361
await click('#target');
await click('#target');
arrowPosition = parseInt(this.element.querySelector('.arrow').style.left, 10);
arrowPosition = parseInt(this.element.querySelector(`.${popoverArrowClass()}`).style.left, 10);
assert.ok(
Math.abs(arrowPosition - expectedArrowPosition) <= 2,
`Expected position: ${expectedArrowPosition}, actual: ${arrowPosition}`
Expand Down
11 changes: 5 additions & 6 deletions tests/integration/components/bs-tooltip-test.js
Expand Up @@ -46,7 +46,7 @@ module('Integration | Component | bs-tooltip', function (hooks) {
assert.dom('.tooltip').hasClass('fade', 'has fade class');
assert.dom('.tooltip').hasClass(visibilityClass(), 'has visibility class');
assert.equal(this.element.querySelector('.tooltip').getAttribute('role'), 'tooltip', 'has ARIA role');
assert.dom(versionDependent('.tooltip-arrow', '.arrow')).exists({ count: 1 }, 'has arrow');
assert.dom(`.${tooltipArrowClass()}`).exists({ count: 1 }, 'has arrow');
assert.dom('.tooltip-inner').hasText('template block text', 'shows title');
});

Expand Down Expand Up @@ -385,8 +385,7 @@ module('Integration | Component | bs-tooltip', function (hooks) {
test('should position tooltip arrow centered', async function (assert) {
this.insertCSSRule('.margin-top { margin-top: 200px; }');

// @todo v5 needs changes in markup
let expectedArrowPosition = versionDependent(95, 94, 0);
let expectedArrowPosition = versionDependent(95, 94, 94);
await render(hbs`
<div id="ember-bootstrap-wormhole"></div>
<div id="wrapper">
Expand All @@ -409,15 +408,15 @@ module('Integration | Component | bs-tooltip', function (hooks) {

test('should adjust tooltip arrow', async function (assert) {
this.insertCSSRule('.margin-top { margin-top: 200px; }');
this.insertCSSRule('#target { width: 100px; padding: 0; border: none; }');

// @todo v5 needs changes in markup
let expectedArrowPosition = versionDependent(155, 150, 0);
let expectedArrowPosition = versionDependent(145, 144, 144);

await render(hbs`
<div id="ember-bootstrap-wormhole"></div>
<div id="wrapper">
<p class="margin-top">
<button type="button" class="btn" id="target">
<button type="button" id="target">
Click me<BsTooltip @autoPlacement={{true}} @viewportSelector="#wrapper" @placement="top" @title="very very very very very very very long tooltip" @fade={{false}} />
</button>
</p>
Expand Down

0 comments on commit 8401113

Please sign in to comment.