Skip to content

Commit

Permalink
feat: add borderClass prop to allow a custom border class [skip-ci]
Browse files Browse the repository at this point in the history
feat: add borderClass prop to allow a custom border class
  • Loading branch information
danielbarion committed Oct 13, 2022
2 parents d99b3e8 + 53792a0 commit 9844a6c
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 44 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ Notes:
| delayUpdate | data-delay-update | Number | | `<p data-tip="tooltip" data-delay-update='1000'></p>` or `<ReactTooltip delayUpdate={1000} />` Sets a delay in calling getContent if the tooltip is already shown and you mouse over another target |
| insecure | null | Bool | true, false | Whether to inject the style header into the page dynamically (violates CSP style-src but is a convenient default) |
| border | data-border | Bool | true, false | Add one pixel white border |
| borderClass | data-border-class | String | e.g. custom-border-class | A custom class name to use for the border - enabled by the `border` prop |
| textColor | data-text-color | String | e.g. red | Popup text color |
| backgroundColor | data-background-color | String | e.g. yellow | Popup background color |
| borderColor | data-border-color | String | e.g. green | Popup border color - enabled by the `border` value |
Expand Down
3 changes: 2 additions & 1 deletion example/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ export default class App extends Component {
<div className="side">
<a
data-for="custom-color"
data-tip="That is one weird arrow (and a border)!"
data-tip="That is one weird arrow (and a border with custom class name)!"
>
V(^-^)V
</a>
Expand All @@ -325,6 +325,7 @@ export default class App extends Component {
className="custom-color"
place="right"
border
borderClass="custom-border-class"
textColor="#5F4B8BFF"
backgroundColor="#E69A8DFF"
borderColor="darkgreen"
Expand Down
16 changes: 9 additions & 7 deletions react-tooltip.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export interface TooltipProps {
multiline?: boolean;
// Add 1px white border
border?: boolean;
// A custom class name to use for the border
borderClass?: string;
// Popup text color
textColor?: string;
// Popup background color
Expand Down Expand Up @@ -74,7 +76,7 @@ export interface TooltipProps {
afterHide?: VoidFunc;
// Callback to override the tooltip position
overridePosition?: (
position: { left: number; top: number; },
position: { left: number; top: number },
currentEvent: Event,
currentTarget: EventTarget,
// node is the ref argument, and the wrapper
Expand All @@ -83,8 +85,8 @@ export interface TooltipProps {
place: Place,
desiredPlace: Place,
effect: Effect,
offset: Offset,
) => ({ left: number; top: number; });
offset: Offset
) => { left: number; top: number };
// Manually disable the tooltip behavior
disable?: boolean;
// Hide the tooltip when scrolling;
Expand All @@ -94,7 +96,7 @@ export interface TooltipProps {
// default = true
resizeHide?: boolean;
// The tooltip parent component;
// default = 'div'
// default = 'div'
wrapper?: 'div' | 'span';
// Listen to body events vs. individual events
bodyMode?: boolean;
Expand All @@ -112,7 +114,7 @@ export interface TooltipProps {
// You can overview demo examples here: https://bddeu.csb.app
export default class ReactTooltip extends React.Component<TooltipProps> {
// static methods
static show: (target: Element) => {};
static hide: (target?: Element) => {};
static rebuild: () => {};
static show: (target: Element) => {};
static hide: (target?: Element) => {};
static rebuild: () => {};
}
30 changes: 13 additions & 17 deletions src/decorators/bodyMode.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/
import { checkStatus } from './customEvent';

const makeProxy = e => {
const makeProxy = (e) => {
const proxy = {};
for (const key in e) {
if (typeof e[key] === 'function') {
Expand All @@ -15,7 +15,7 @@ const makeProxy = e => {
return proxy;
};

const bodyListener = function(callback, options, e) {
const bodyListener = function (callback, options, e) {
const { respectEffect = false, customEvent = false } = options;
const { id } = this.props;

Expand Down Expand Up @@ -54,28 +54,24 @@ const bodyListener = function(callback, options, e) {

const findCustomEvents = (targetArray, dataAttribute) => {
const events = {};
targetArray.forEach(target => {
targetArray.forEach((target) => {
const event = target.getAttribute(dataAttribute);
if (event) event.split(' ').forEach(event => (events[event] = true));
if (event) event.split(' ').forEach((event) => (events[event] = true));
});

return events;
};

const getBody = () => document.getElementsByTagName('body')[0];

export default function(target) {
target.prototype.isBodyMode = function() {
export default function (target) {
target.prototype.isBodyMode = function () {
return !!this.props.bodyMode;
};

target.prototype.bindBodyListener = function(targetArray) {
const {
event,
eventOff,
possibleCustomEvents,
possibleCustomEventsOff
} = this.state;
target.prototype.bindBodyListener = function (targetArray) {
const { event, eventOff, possibleCustomEvents, possibleCustomEventsOff } =
this.state;
const body = getBody();

const customEvents = findCustomEvents(targetArray, 'data-event');
Expand All @@ -85,10 +81,10 @@ export default function(target) {
if (eventOff != null) customEventsOff[eventOff] = true;
possibleCustomEvents
.split(' ')
.forEach(event => (customEvents[event] = true));
.forEach((event) => (customEvents[event] = true));
possibleCustomEventsOff
.split(' ')
.forEach(event => (customEventsOff[event] = true));
.forEach((event) => (customEventsOff[event] = true));

this.unbindBodyListener(body);

Expand All @@ -104,7 +100,7 @@ export default function(target) {
for (const event in customEvents) {
listeners[event] = bodyListener.bind(
this,
e => {
(e) => {
const targetEventOff =
e.currentTarget.getAttribute('data-event-off') || eventOff;
checkStatus.call(this, targetEventOff, e);
Expand All @@ -122,7 +118,7 @@ export default function(target) {
}
};

target.prototype.unbindBodyListener = function(body) {
target.prototype.unbindBodyListener = function (body) {
body = body || getBody();

const listeners = this.bodyModeListeners;
Expand Down
8 changes: 7 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class ReactTooltip extends React.Component {
padding: PropTypes.string,
multiline: PropTypes.bool,
border: PropTypes.bool,
borderClass: PropTypes.string,
textColor: PropTypes.string,
backgroundColor: PropTypes.string,
borderColor: PropTypes.string,
Expand Down Expand Up @@ -97,6 +98,7 @@ class ReactTooltip extends React.Component {
effect: props.effect || 'float', // float or fixed
show: false,
border: false,
borderClass: 'border',
customColors: {},
offset: {},
padding: props.padding,
Expand Down Expand Up @@ -507,6 +509,10 @@ class ReactTooltip extends React.Component {
(target.getAttribute('data-border')
? target.getAttribute('data-border') === 'true'
: self.props.border) || false,
borderClass:
target.getAttribute('data-border-class') ||
self.props.borderClass ||
'border',
extraClass:
target.getAttribute('data-class') ||
self.props.class ||
Expand Down Expand Up @@ -791,7 +797,7 @@ class ReactTooltip extends React.Component {
'__react_component_tooltip' +
` ${this.state.uuid}` +
(this.state.show && !disable && !isEmptyTip ? ' show' : '') +
(this.state.border ? ' border' : '') +
(this.state.border ? ' ' + this.state.borderClass : '') +
` place-${this.state.place}` + // top, bottom, left, right
` type-${this.hasCustomColors() ? 'custom' : this.state.type}` + // dark, success, warning, error, info, light, custom
(this.props.delayUpdate ? ' allow_hover' : '') +
Expand Down
33 changes: 16 additions & 17 deletions src/utils/getPosition.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* - `newState` {Object}
* - `position` {Object} {left: {Number}, top: {Number}}
*/
export default function(e, target, node, place, desiredPlace, effect, offset) {
export default function (e, target, node, place, desiredPlace, effect, offset) {
const { width: tipWidth, height: tipHeight } = getDimensions(node);

const { width: targetWidth, height: targetHeight } = getDimensions(target);
Expand All @@ -35,19 +35,19 @@ export default function(e, target, node, place, desiredPlace, effect, offset) {
const { parentTop, parentLeft } = getParent(node);

// Get the edge offset of the tooltip
const getTipOffsetLeft = place => {
const getTipOffsetLeft = (place) => {
const offsetX = defaultOffset[place].l;
return mouseX + offsetX + extraOffsetX;
};
const getTipOffsetRight = place => {
const getTipOffsetRight = (place) => {
const offsetX = defaultOffset[place].r;
return mouseX + offsetX + extraOffsetX;
};
const getTipOffsetTop = place => {
const getTipOffsetTop = (place) => {
const offsetY = defaultOffset[place].t;
return mouseY + offsetY + extraOffsetY;
};
const getTipOffsetBottom = place => {
const getTipOffsetBottom = (place) => {
const offsetY = defaultOffset[place].b;
return mouseY + offsetY + extraOffsetY;
};
Expand All @@ -66,15 +66,15 @@ export default function(e, target, node, place, desiredPlace, effect, offset) {
// |
// Bottom side
//
const outsideLeft = p => getTipOffsetLeft(p) < 0;
const outsideRight = p => getTipOffsetRight(p) > windowWidth;
const outsideTop = p => getTipOffsetTop(p) < 0;
const outsideBottom = p => getTipOffsetBottom(p) > windowHeight;
const outsideLeft = (p) => getTipOffsetLeft(p) < 0;
const outsideRight = (p) => getTipOffsetRight(p) > windowWidth;
const outsideTop = (p) => getTipOffsetTop(p) < 0;
const outsideBottom = (p) => getTipOffsetBottom(p) > windowHeight;

// Check whether the tooltip with orientation p is completely inside the client window
const outside = p =>
const outside = (p) =>
outsideLeft(p) || outsideRight(p) || outsideTop(p) || outsideBottom(p);
const inside = p => !outside(p);
const inside = (p) => !outside(p);

const placeIsInside = {
top: inside('top'),
Expand Down Expand Up @@ -119,7 +119,7 @@ export default function(e, target, node, place, desiredPlace, effect, offset) {
};
}

const getDimensions = node => {
const getDimensions = (node) => {
const { height, width } = node.getBoundingClientRect();
return {
height: parseInt(height, 10),
Expand All @@ -132,9 +132,8 @@ const getCurrentOffset = (e, currentTarget, effect) => {
const boundingClientRect = currentTarget.getBoundingClientRect();
const targetTop = boundingClientRect.top;
const targetLeft = boundingClientRect.left;
const { width: targetWidth, height: targetHeight } = getDimensions(
currentTarget
);
const { width: targetWidth, height: targetHeight } =
getDimensions(currentTarget);

if (effect === 'float') {
return {
Expand Down Expand Up @@ -221,7 +220,7 @@ const getDefaultPosition = (
};

// Consider additional offset into position calculation
const calculateOffset = offset => {
const calculateOffset = (offset) => {
let extraOffsetX = 0;
let extraOffsetY = 0;

Expand All @@ -244,7 +243,7 @@ const calculateOffset = offset => {
};

// Get the offset of the parent elements
const getParent = currentTarget => {
const getParent = (currentTarget) => {
let currentParent = currentTarget;
while (currentParent) {
const computedStyle = window.getComputedStyle(currentParent);
Expand Down
20 changes: 19 additions & 1 deletion test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,21 @@ describe('Tooltip', () => {
arrowColor: '#222',
borderColor: 'blue'
}
],
[
{
border: true,
borderClass: 'custom-border-class',
borderColor: '#414141'
},
{
borderColor: '#414141',
borderClass: 'custom-border-class',
popupType: 'type-custom',
textColor: '#fff',
background: '#222',
arrowColor: '#222'
}
]
]).it('Popup color generation - show', (props, res) => {
render(
Expand All @@ -184,10 +199,13 @@ describe('Tooltip', () => {
);

const tooltip = document.getElementById('colorSpec');

const expectedBorderClass = res.borderClass || 'border';

expect(tooltip.className).to.match(
new RegExp(
'__react_component_tooltip [a-zA-Z0-9-]+ show' +
(props.border ? ' border ' : ' ') +
(props.border ? ` ${expectedBorderClass} ` : ' ') +
'place-top ' +
res.popupType,
'i'
Expand Down

0 comments on commit 9844a6c

Please sign in to comment.