Skip to content
This repository has been archived by the owner on Nov 9, 2024. It is now read-only.

Commit

Permalink
fix(createSingleton): touch and triggerTarget (#999)
Browse files Browse the repository at this point in the history
  • Loading branch information
atomiks authored Nov 5, 2021
1 parent da04753 commit 9f269fb
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 14 deletions.
21 changes: 16 additions & 5 deletions src/addons/createSingleton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
Instance,
Props,
} from '../types';
import {removeProperties} from '../utils';
import {normalizeToArray, removeProperties} from '../utils';
import {errorWhen} from '../validation';
import {applyStyles, Modifier} from '@popperjs/core';

Expand Down Expand Up @@ -63,11 +63,20 @@ const createSingleton: CreateSingleton = (

let individualInstances = tippyInstances;
let references: Array<ReferenceElement> = [];
let triggerTargets: Array<Element> = [];
let currentTarget: Element | null;
let overrides = optionalProps.overrides;
let interceptSetPropsCleanups: Array<() => void> = [];
let shownOnCreate = false;

function setTriggerTargets(): void {
triggerTargets = individualInstances
.map((instance) =>
normalizeToArray(instance.props.triggerTarget || instance.reference)
)
.reduce((acc, item) => acc.concat(item), []);
}

function setReferences(): void {
references = individualInstances.map((instance) => instance.reference);
}
Expand Down Expand Up @@ -105,7 +114,7 @@ const createSingleton: CreateSingleton = (
singleton: Instance,
target: ReferenceElement
): void {
const index = references.indexOf(target);
const index = triggerTargets.indexOf(target);

// bail-out
if (target === currentTarget) {
Expand All @@ -126,12 +135,13 @@ const createSingleton: CreateSingleton = (
getReferenceClientRect:
typeof overrideProps.getReferenceClientRect === 'function'
? overrideProps.getReferenceClientRect
: (): ClientRect => target.getBoundingClientRect(),
: (): ClientRect => references[index].getBoundingClientRect(),
});
}

enableInstances(false);
setReferences();
setTriggerTargets();

const plugin: Plugin = {
fn() {
Expand Down Expand Up @@ -164,7 +174,7 @@ const createSingleton: CreateSingleton = (
const singleton = tippy(div(), {
...removeProperties(optionalProps, ['overrides']),
plugins: [plugin, ...(optionalProps.plugins || [])],
triggerTarget: references,
triggerTarget: triggerTargets,
popperOptions: {
...optionalProps.popperOptions,
modifiers: [
Expand Down Expand Up @@ -244,9 +254,10 @@ const createSingleton: CreateSingleton = (

enableInstances(false);
setReferences();
setTriggerTargets();
interceptSetPropsCleanups = interceptSetProps(singleton);

singleton.setProps({triggerTarget: references});
singleton.setProps({triggerTarget: triggerTargets});
};

interceptSetPropsCleanups = interceptSetProps(singleton);
Expand Down
6 changes: 5 additions & 1 deletion src/createTippy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,11 @@ export default function createTippy(
}

// Clicked on the event listeners target
if (actualContains(getCurrentTarget(), actualTarget as Element)) {
if (
normalizeToArray(instance.props.triggerTarget || reference).indexOf(
actualTarget as Element
) !== -1
) {
if (currentInput.isTouch) {
return;
}
Expand Down
71 changes: 63 additions & 8 deletions test/integration/addons/createSingleton.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {h} from '../../utils';
import createSingleton from '../../../src/addons/createSingleton';
import tippy from '../../../src';
import {getFormattedMessage} from '../../../src/validation';
import {currentInput} from '../../../src/bindGlobalEventListeners';

describe('createSingleton', () => {
it('shows when a tippy instance reference is triggered', () => {
Expand Down Expand Up @@ -416,10 +417,9 @@ describe('.showPrevious() method', () => {

describe('showOnCreate prop', () => {
it('shows the first tippy instance on creation', () => {
const tippyInstances = [
{content: 'first'},
{content: 'second'},
].map((props) => tippy(h(), props));
const tippyInstances = [{content: 'first'}, {content: 'second'}].map(
(props) => tippy(h(), props)
);

const singletonInstance = createSingleton(tippyInstances, {
showOnCreate: true,
Expand All @@ -430,10 +430,9 @@ describe('showOnCreate prop', () => {
});

it('resets correctly if showOnCreate is cancelled by a click outside', () => {
const tippyInstances = [
{content: 'first'},
{content: 'second'},
].map((props) => tippy(h(), props));
const tippyInstances = [{content: 'first'}, {content: 'second'}].map(
(props) => tippy(h(), props)
);

const singletonInstance = createSingleton(tippyInstances, {
showOnCreate: true,
Expand All @@ -448,4 +447,60 @@ describe('showOnCreate prop', () => {
expect(singletonInstance.state.isVisible).toBe(true);
expect(singletonInstance.props.content).toBe('second');
});

it('does not hide tippy upon clicking next target', () => {
currentInput.isTouch = true;

const tippyInstances = [{content: 'first'}, {content: 'second'}].map(
(props) => tippy(h(), props)
);

const singletonInstance = createSingleton(tippyInstances);

fireEvent.mouseEnter(tippyInstances[0].reference);
fireEvent.mouseDown(document.body);
fireEvent.mouseEnter(tippyInstances[1].reference);

jest.runAllTimers();

expect(singletonInstance.state.isVisible).toBe(true);
expect(singletonInstance.props.content).toBe('second');

currentInput.isTouch = false;
});

it('accepts custom `triggerTarget` in individual instances', () => {
const ref1 = h();
const ref2 = h();
const triggerTarget1 = h();
const triggerTarget2 = h();

const tippyInstances = [
{ref: ref1, content: 'first', triggerTarget: triggerTarget1},
{ref: ref2, content: 'second', triggerTarget: triggerTarget2},
].map(({ref, ...props}) => tippy(ref, props));

const singletonInstance = createSingleton(tippyInstances);

fireEvent.mouseEnter(ref1);
jest.runAllTimers();
expect(singletonInstance.state.isVisible).toBe(false);

fireEvent.mouseEnter(triggerTarget1);
jest.runAllTimers();

expect(singletonInstance.state.isVisible).toBe(true);
expect(singletonInstance.props.content).toBe('first');

fireEvent.mouseEnter(triggerTarget2);
jest.runAllTimers();

expect(singletonInstance.state.isVisible).toBe(true);
expect(singletonInstance.props.content).toBe('second');

fireEvent.mouseEnter(ref1);
jest.runAllTimers();

expect(singletonInstance.props.content).toBe('second');
});
});

0 comments on commit 9f269fb

Please sign in to comment.