@wix/interact 1.103.0 → 1.105.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/__tests__/interact.spec.js +215 -1
- package/dist/cjs/__tests__/interact.spec.js.map +1 -1
- package/dist/cjs/core/Interact.js +26 -0
- package/dist/cjs/core/Interact.js.map +1 -1
- package/dist/cjs/core/add.js +28 -10
- package/dist/cjs/core/add.js.map +1 -1
- package/dist/esm/__tests__/interact.spec.js +215 -1
- package/dist/esm/__tests__/interact.spec.js.map +1 -1
- package/dist/esm/core/Interact.js +26 -0
- package/dist/esm/core/Interact.js.map +1 -1
- package/dist/esm/core/add.js +28 -10
- package/dist/esm/core/add.js.map +1 -1
- package/dist/types/core/Interact.d.ts +7 -0
- package/package.json +10 -4
|
@@ -42,6 +42,7 @@ jest.mock('fizban', () => ({
|
|
|
42
42
|
end: jest.fn()
|
|
43
43
|
}))
|
|
44
44
|
}));
|
|
45
|
+
let mockMQLs;
|
|
45
46
|
describe('interact', () => {
|
|
46
47
|
let element;
|
|
47
48
|
let mockConfig;
|
|
@@ -327,12 +328,16 @@ describe('interact', () => {
|
|
|
327
328
|
}
|
|
328
329
|
|
|
329
330
|
// Mock matchMedia for condition testing
|
|
331
|
+
mockMQLs = new Map();
|
|
330
332
|
mockMatchMedia();
|
|
331
333
|
});
|
|
332
334
|
function mockMatchMedia(matchingQueries = []) {
|
|
333
335
|
const queryRule = `(${matchingQueries.join(') and (')})`;
|
|
334
336
|
const mockMQL = query => {
|
|
335
|
-
|
|
337
|
+
if (mockMQLs.has(query)) {
|
|
338
|
+
return mockMQLs.get(query);
|
|
339
|
+
}
|
|
340
|
+
const mql = {
|
|
336
341
|
matches: queryRule === query,
|
|
337
342
|
media: query,
|
|
338
343
|
onchange: null,
|
|
@@ -340,9 +345,12 @@ describe('interact', () => {
|
|
|
340
345
|
removeEventListener: jest.fn(),
|
|
341
346
|
dispatchEvent: jest.fn()
|
|
342
347
|
};
|
|
348
|
+
mockMQLs.set(query, mql);
|
|
349
|
+
return mql;
|
|
343
350
|
};
|
|
344
351
|
Object.defineProperty(window, 'matchMedia', {
|
|
345
352
|
writable: true,
|
|
353
|
+
configurable: true,
|
|
346
354
|
value: jest.fn().mockImplementation(mockMQL)
|
|
347
355
|
});
|
|
348
356
|
}
|
|
@@ -2725,5 +2733,211 @@ describe('interact', () => {
|
|
|
2725
2733
|
});
|
|
2726
2734
|
});
|
|
2727
2735
|
});
|
|
2736
|
+
describe('media query listeners', () => {
|
|
2737
|
+
let instance;
|
|
2738
|
+
beforeEach(() => {
|
|
2739
|
+
mockMQLs.clear();
|
|
2740
|
+
mockMatchMedia();
|
|
2741
|
+
});
|
|
2742
|
+
afterEach(() => {
|
|
2743
|
+
_Interact.Interact.destroy();
|
|
2744
|
+
});
|
|
2745
|
+
function createMockInteractElement(key) {
|
|
2746
|
+
const el = document.createElement('interact-element');
|
|
2747
|
+
const div = document.createElement('div');
|
|
2748
|
+
el.append(div);
|
|
2749
|
+
el.dataset.interactKey = key;
|
|
2750
|
+
el.connected = false;
|
|
2751
|
+
el._internals = null;
|
|
2752
|
+
el._observers = new WeakMap();
|
|
2753
|
+
el.sheet = null;
|
|
2754
|
+
el.disconnect = jest.fn(function () {
|
|
2755
|
+
const k = el.dataset.interactKey;
|
|
2756
|
+
if (k) {
|
|
2757
|
+
(0, _remove.remove)(k);
|
|
2758
|
+
}
|
|
2759
|
+
el.sheet = null;
|
|
2760
|
+
el.connected = false;
|
|
2761
|
+
});
|
|
2762
|
+
el.connect = jest.fn(function () {
|
|
2763
|
+
if (el.connected) {
|
|
2764
|
+
return;
|
|
2765
|
+
}
|
|
2766
|
+
const k = el.dataset.interactKey;
|
|
2767
|
+
if (k) {
|
|
2768
|
+
el.connected = (0, _add.add)(el, k);
|
|
2769
|
+
}
|
|
2770
|
+
});
|
|
2771
|
+
el.renderStyle = jest.fn();
|
|
2772
|
+
el.toggleEffect = jest.fn();
|
|
2773
|
+
el.watchChildList = jest.fn();
|
|
2774
|
+
return el;
|
|
2775
|
+
}
|
|
2776
|
+
const createResponsiveConfig = () => ({
|
|
2777
|
+
conditions: {
|
|
2778
|
+
desktop: {
|
|
2779
|
+
type: 'media',
|
|
2780
|
+
predicate: 'min-width: 768px'
|
|
2781
|
+
}
|
|
2782
|
+
},
|
|
2783
|
+
interactions: [{
|
|
2784
|
+
trigger: 'click',
|
|
2785
|
+
key: 'responsive-button',
|
|
2786
|
+
conditions: ['desktop'],
|
|
2787
|
+
effects: [{
|
|
2788
|
+
key: 'responsive-button',
|
|
2789
|
+
effectId: 'test-effect'
|
|
2790
|
+
}]
|
|
2791
|
+
}],
|
|
2792
|
+
effects: {
|
|
2793
|
+
'test-effect': {
|
|
2794
|
+
namedEffect: {
|
|
2795
|
+
type: 'FadeIn',
|
|
2796
|
+
power: 'medium'
|
|
2797
|
+
},
|
|
2798
|
+
duration: 500
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
});
|
|
2802
|
+
it('should set up a media query listener when interaction has conditions', () => {
|
|
2803
|
+
const config = createResponsiveConfig();
|
|
2804
|
+
instance = _Interact.Interact.create(config);
|
|
2805
|
+
const testElement = createMockInteractElement('responsive-button');
|
|
2806
|
+
(0, _add.add)(testElement, 'responsive-button');
|
|
2807
|
+
expect(window.matchMedia).toHaveBeenCalledWith('(min-width: 768px)');
|
|
2808
|
+
expect(instance.mediaQueryListeners.size).toBe(1);
|
|
2809
|
+
expect(instance.mediaQueryListeners.has('responsive-button::trigger::0')).toBe(true);
|
|
2810
|
+
const mql = mockMQLs.get('(min-width: 768px)');
|
|
2811
|
+
expect(mql == null ? void 0 : mql.addEventListener).toHaveBeenCalledWith('change', expect.any(Function));
|
|
2812
|
+
});
|
|
2813
|
+
it('should reconcile when media query changes', () => {
|
|
2814
|
+
const config = createResponsiveConfig();
|
|
2815
|
+
instance = _Interact.Interact.create(config);
|
|
2816
|
+
const testElement = createMockInteractElement('responsive-button');
|
|
2817
|
+
(0, _add.add)(testElement, 'responsive-button');
|
|
2818
|
+
const listenerEntry = instance.mediaQueryListeners.get('responsive-button::trigger::0');
|
|
2819
|
+
expect(listenerEntry).toBeDefined();
|
|
2820
|
+
const clearStateSpy = jest.spyOn(instance, 'clearInteractionStateForKey');
|
|
2821
|
+
listenerEntry.handler({
|
|
2822
|
+
matches: true,
|
|
2823
|
+
media: '(min-width: 768px)'
|
|
2824
|
+
});
|
|
2825
|
+
|
|
2826
|
+
// disconnect triggers remove which calls clearInteractionStateForKey
|
|
2827
|
+
expect(clearStateSpy).toHaveBeenCalledWith('responsive-button');
|
|
2828
|
+
});
|
|
2829
|
+
it('should properly remove event listeners when instance is destroyed', () => {
|
|
2830
|
+
const config = createResponsiveConfig();
|
|
2831
|
+
instance = _Interact.Interact.create(config);
|
|
2832
|
+
const testElement = createMockInteractElement('responsive-button');
|
|
2833
|
+
(0, _add.add)(testElement, 'responsive-button');
|
|
2834
|
+
const mql = mockMQLs.get('(min-width: 768px)');
|
|
2835
|
+
expect(mql).toBeDefined();
|
|
2836
|
+
expect(mql.addEventListener).toHaveBeenCalled();
|
|
2837
|
+
instance.destroy();
|
|
2838
|
+
expect(mql.removeEventListener).toHaveBeenCalledWith('change', expect.any(Function));
|
|
2839
|
+
expect(instance.mediaQueryListeners.size).toBe(0);
|
|
2840
|
+
});
|
|
2841
|
+
it('should not create duplicate listeners when add() is called twice', () => {
|
|
2842
|
+
const config = createResponsiveConfig();
|
|
2843
|
+
instance = _Interact.Interact.create(config);
|
|
2844
|
+
const testElement = createMockInteractElement('responsive-button');
|
|
2845
|
+
(0, _add.add)(testElement, 'responsive-button');
|
|
2846
|
+
// Reset addedInteractions so add() processes interactions again
|
|
2847
|
+
instance.addedInteractions = {};
|
|
2848
|
+
(0, _add.add)(testElement, 'responsive-button');
|
|
2849
|
+
expect(instance.mediaQueryListeners.size).toBe(1);
|
|
2850
|
+
const mql = mockMQLs.get('(min-width: 768px)');
|
|
2851
|
+
expect(mql == null ? void 0 : mql.addEventListener).toHaveBeenCalledTimes(1);
|
|
2852
|
+
});
|
|
2853
|
+
it('should remove listeners when element is deleted', () => {
|
|
2854
|
+
const config = createResponsiveConfig();
|
|
2855
|
+
instance = _Interact.Interact.create(config);
|
|
2856
|
+
const testElement = createMockInteractElement('responsive-button');
|
|
2857
|
+
(0, _add.add)(testElement, 'responsive-button');
|
|
2858
|
+
const mql = mockMQLs.get('(min-width: 768px)');
|
|
2859
|
+
expect(instance.mediaQueryListeners.size).toBe(1);
|
|
2860
|
+
instance.deleteElement('responsive-button');
|
|
2861
|
+
expect(mql.removeEventListener).toHaveBeenCalledWith('change', expect.any(Function));
|
|
2862
|
+
expect(instance.mediaQueryListeners.size).toBe(0);
|
|
2863
|
+
});
|
|
2864
|
+
it('should switch interactions when media query changes', () => {
|
|
2865
|
+
const config = {
|
|
2866
|
+
conditions: {
|
|
2867
|
+
desktop: {
|
|
2868
|
+
type: 'media',
|
|
2869
|
+
predicate: 'min-width: 1024px'
|
|
2870
|
+
},
|
|
2871
|
+
mobile: {
|
|
2872
|
+
type: 'media',
|
|
2873
|
+
predicate: 'max-width: 767px'
|
|
2874
|
+
}
|
|
2875
|
+
},
|
|
2876
|
+
interactions: [{
|
|
2877
|
+
trigger: 'click',
|
|
2878
|
+
key: 'responsive-element',
|
|
2879
|
+
conditions: ['desktop'],
|
|
2880
|
+
effects: [{
|
|
2881
|
+
key: 'responsive-element',
|
|
2882
|
+
effectId: 'desktop-effect'
|
|
2883
|
+
}]
|
|
2884
|
+
}, {
|
|
2885
|
+
trigger: 'hover',
|
|
2886
|
+
key: 'responsive-element',
|
|
2887
|
+
conditions: ['mobile'],
|
|
2888
|
+
effects: [{
|
|
2889
|
+
key: 'responsive-element',
|
|
2890
|
+
effectId: 'mobile-effect'
|
|
2891
|
+
}]
|
|
2892
|
+
}],
|
|
2893
|
+
effects: {
|
|
2894
|
+
'desktop-effect': {
|
|
2895
|
+
namedEffect: {
|
|
2896
|
+
type: 'FadeIn',
|
|
2897
|
+
power: 'medium'
|
|
2898
|
+
},
|
|
2899
|
+
duration: 500
|
|
2900
|
+
},
|
|
2901
|
+
'mobile-effect': {
|
|
2902
|
+
namedEffect: {
|
|
2903
|
+
type: 'BounceIn',
|
|
2904
|
+
direction: 'center',
|
|
2905
|
+
power: 'hard'
|
|
2906
|
+
},
|
|
2907
|
+
duration: 300
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
};
|
|
2911
|
+
mockMatchMedia(['min-width: 1024px']);
|
|
2912
|
+
instance = _Interact.Interact.create(config);
|
|
2913
|
+
const testElement = createMockInteractElement('responsive-element');
|
|
2914
|
+
// Handlers are added to the child div (firstElementChild), not the root
|
|
2915
|
+
const childDiv = testElement.firstElementChild;
|
|
2916
|
+
const addEventListenerSpy = jest.spyOn(childDiv, 'addEventListener');
|
|
2917
|
+
(0, _add.add)(testElement, 'responsive-element');
|
|
2918
|
+
|
|
2919
|
+
// Desktop interaction (click) should be added
|
|
2920
|
+
expect(addEventListenerSpy).toHaveBeenCalledWith('click', expect.any(Function), expect.any(Object));
|
|
2921
|
+
expect(addEventListenerSpy).not.toHaveBeenCalledWith('mouseenter', expect.any(Function), expect.any(Object));
|
|
2922
|
+
addEventListenerSpy.mockClear();
|
|
2923
|
+
|
|
2924
|
+
// Simulate media query change to mobile
|
|
2925
|
+
const desktopMql = mockMQLs.get('(min-width: 1024px)');
|
|
2926
|
+
const mobileMql = mockMQLs.get('(max-width: 767px)');
|
|
2927
|
+
desktopMql.matches = false;
|
|
2928
|
+
mobileMql.matches = true;
|
|
2929
|
+
|
|
2930
|
+
// Trigger the media query change handler
|
|
2931
|
+
const listenerEntry = instance.mediaQueryListeners.get('responsive-element::trigger::0');
|
|
2932
|
+
expect(listenerEntry).toBeDefined();
|
|
2933
|
+
listenerEntry.handler({
|
|
2934
|
+
matches: false,
|
|
2935
|
+
media: '(min-width: 1024px)'
|
|
2936
|
+
});
|
|
2937
|
+
|
|
2938
|
+
// After reconciliation, the hover (mobile) handler should be added
|
|
2939
|
+
expect(addEventListenerSpy).toHaveBeenCalledWith('mouseenter', expect.any(Function), expect.any(Object));
|
|
2940
|
+
});
|
|
2941
|
+
});
|
|
2728
2942
|
});
|
|
2729
2943
|
//# sourceMappingURL=interact.spec.js.map
|