Update tooltip behavior to support bottom tooltips
Account statuses will have tooltips that are really close to the top of the page. In order to ensure their content doesn't get cut off, we will use a bottom tooltip instead. This change modifies the tooltip element to support an optional positionBottom property. If the property does not exist, the tooltip is positioned to the top, as it was before. Change-Id: Iaa66b9edeebf4eb40cd20e897e59388bc9c0f70c
This commit is contained in:
@@ -26,6 +26,11 @@
|
||||
type: Boolean,
|
||||
observer: '_setupTooltipListeners',
|
||||
},
|
||||
positionBelow: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
reflectToAttribute: true,
|
||||
},
|
||||
|
||||
_isTouchDevice: {
|
||||
type: Boolean,
|
||||
@@ -73,6 +78,7 @@
|
||||
const tooltip = document.createElement('gr-tooltip');
|
||||
tooltip.text = this._titleText;
|
||||
tooltip.maxWidth = this.getAttribute('max-width');
|
||||
tooltip.positionBelow = this.getAttribute('position-below');
|
||||
|
||||
// Set visibility to hidden before appending to the DOM so that
|
||||
// calculations can be made based on the element’s size.
|
||||
@@ -125,9 +131,14 @@
|
||||
});
|
||||
}
|
||||
tooltip.style.left = Math.max(0, left) + 'px';
|
||||
tooltip.style.top = Math.max(0, top) + 'px';
|
||||
tooltip.style.transform = 'translateY(calc(-100% - ' + BOTTOM_OFFSET +
|
||||
'px))';
|
||||
|
||||
if (!this.positionBelow) {
|
||||
tooltip.style.top = Math.max(0, top) + 'px';
|
||||
tooltip.style.transform = 'translateY(calc(-100% - ' + BOTTOM_OFFSET +
|
||||
'px))';
|
||||
} else {
|
||||
tooltip.style.top = top + rect.height + BOTTOM_OFFSET + 'px';
|
||||
}
|
||||
},
|
||||
};
|
||||
})(window);
|
||||
|
||||
@@ -111,6 +111,24 @@ limitations under the License.
|
||||
assert.equal(tooltip.style.top, '100px');
|
||||
});
|
||||
|
||||
test('position to bottom', () => {
|
||||
sandbox.stub(element, 'getBoundingClientRect', () => {
|
||||
return {top: 100, left: 950, width: 50, height: 50};
|
||||
});
|
||||
const tooltip = makeTooltip(
|
||||
{height: 30, width: 120},
|
||||
{top: 0, left: 0, width: 1000});
|
||||
|
||||
element.positionBelow = true;
|
||||
element._positionTooltip(tooltip);
|
||||
assert.isTrue(tooltip.updateStyles.called);
|
||||
const offset = tooltip.updateStyles
|
||||
.lastCall.args[0]['--gr-tooltip-arrow-center-offset'];
|
||||
assert.isAbove(parseFloat(offset.replace(/px$/, '')), 0);
|
||||
assert.equal(tooltip.style.left, '915px');
|
||||
assert.equal(tooltip.style.top, '157.2px');
|
||||
});
|
||||
|
||||
test('hides tooltip when detached', () => {
|
||||
sandbox.stub(element, '_handleHideTooltip');
|
||||
element.remove();
|
||||
|
||||
@@ -53,7 +53,7 @@ limitations under the License.
|
||||
</style>
|
||||
<gr-tooltip-content
|
||||
has-tooltip
|
||||
position-bottom
|
||||
position-below
|
||||
title="[[tooltipText]]"
|
||||
max-width="40em">
|
||||
<div class="chip" aria-label$="Label: [[status]]">
|
||||
|
||||
@@ -26,6 +26,11 @@
|
||||
type: String,
|
||||
reflectToAttribute: true,
|
||||
},
|
||||
positionBelow: {
|
||||
type: Boolean,
|
||||
valye: false,
|
||||
reflectToAttribute: true,
|
||||
},
|
||||
showIcon: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
|
||||
@@ -43,6 +43,12 @@ limitations under the License.
|
||||
.querySelector('.arrow').hidden, true);
|
||||
});
|
||||
|
||||
test('position-below attribute is reflected', () => {
|
||||
assert.isFalse(element.hasAttribute('position-below'));
|
||||
element.positionBelow = true;
|
||||
assert.isTrue(element.hasAttribute('position-below'));
|
||||
});
|
||||
|
||||
test('icon is visible with showIcon property', () => {
|
||||
element.showIcon = true;
|
||||
assert.equal(Polymer.dom(element.root)
|
||||
|
||||
@@ -35,21 +35,35 @@ limitations under the License.
|
||||
:host .tooltip {
|
||||
padding: .5em .85em;
|
||||
}
|
||||
:host .arrowPositionBelow,
|
||||
:host([position-below]) .arrowPositionAbove {
|
||||
display: none;
|
||||
}
|
||||
:host([position-below]) .arrowPositionBelow {
|
||||
display: initial;
|
||||
}
|
||||
.arrow {
|
||||
border-left: var(--gr-tooltip-arrow-size) solid transparent;
|
||||
border-right: var(--gr-tooltip-arrow-size) solid transparent;
|
||||
border-top: var(--gr-tooltip-arrow-size) solid #333;
|
||||
bottom: -var(--gr-tooltip-arrow-size);
|
||||
height: 0;
|
||||
position: absolute;
|
||||
left: calc(50% - var(--gr-tooltip-arrow-size));
|
||||
margin-left: var(--gr-tooltip-arrow-center-offset);
|
||||
width: 0;
|
||||
}
|
||||
.arrowPositionAbove {
|
||||
border-top: var(--gr-tooltip-arrow-size) solid #333;
|
||||
bottom: -var(--gr-tooltip-arrow-size);
|
||||
}
|
||||
.arrowPositionBelow {
|
||||
border-bottom: var(--gr-tooltip-arrow-size) solid #333;
|
||||
top: -var(--gr-tooltip-arrow-size);
|
||||
}
|
||||
</style>
|
||||
<div class="tooltip">
|
||||
<i class="arrowPositionBelow arrow"></i>
|
||||
[[text]]
|
||||
<i class="arrow"></i>
|
||||
<i class="arrowPositionAbove arrow"></i>
|
||||
</div>
|
||||
</template>
|
||||
<script src="gr-tooltip.js"></script>
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
type: String,
|
||||
observer: '_updateWidth',
|
||||
},
|
||||
positionBelow: {
|
||||
type: Boolean,
|
||||
reflectToAttribute: true,
|
||||
},
|
||||
},
|
||||
|
||||
_updateWidth(maxWidth) {
|
||||
|
||||
@@ -44,5 +44,17 @@ limitations under the License.
|
||||
element.maxWidth = '50px';
|
||||
assert.equal(getComputedStyle(element).width, '50px');
|
||||
});
|
||||
|
||||
test('the correct arrow is displayed', () => {
|
||||
assert.equal(getComputedStyle(element.$$('.arrowPositionBelow')).display,
|
||||
'none');
|
||||
assert.notEqual(getComputedStyle(element.$$('.arrowPositionAbove'))
|
||||
.display, 'none');
|
||||
element.positionBelow = true;
|
||||
assert.notEqual(getComputedStyle(element.$$('.arrowPositionBelow'))
|
||||
.display, 'none');
|
||||
assert.equal(getComputedStyle(element.$$('.arrowPositionAbove'))
|
||||
.display, 'none');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user