@wix/interact 1.91.0 → 1.93.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.
Files changed (60) hide show
  1. package/dist/cjs/InteractElement.js +1 -0
  2. package/dist/cjs/InteractElement.js.map +1 -1
  3. package/dist/cjs/__tests__/interact.spec.js +294 -1
  4. package/dist/cjs/__tests__/interact.spec.js.map +1 -1
  5. package/dist/cjs/core/Interact.js +4 -0
  6. package/dist/cjs/core/Interact.js.map +1 -1
  7. package/dist/cjs/core/add.js +15 -8
  8. package/dist/cjs/core/add.js.map +1 -1
  9. package/dist/cjs/handlers/animationEnd.js +6 -2
  10. package/dist/cjs/handlers/animationEnd.js.map +1 -1
  11. package/dist/cjs/handlers/click.js +37 -9
  12. package/dist/cjs/handlers/click.js.map +1 -1
  13. package/dist/cjs/handlers/hover.js +41 -10
  14. package/dist/cjs/handlers/hover.js.map +1 -1
  15. package/dist/cjs/handlers/index.js +12 -1
  16. package/dist/cjs/handlers/index.js.map +1 -1
  17. package/dist/cjs/handlers/pointerMove.js +2 -2
  18. package/dist/cjs/handlers/pointerMove.js.map +1 -1
  19. package/dist/cjs/handlers/viewEnter.js +6 -2
  20. package/dist/cjs/handlers/viewEnter.js.map +1 -1
  21. package/dist/cjs/handlers/viewProgress.js +24 -10
  22. package/dist/cjs/handlers/viewProgress.js.map +1 -1
  23. package/dist/cjs/types.js.map +1 -1
  24. package/dist/cjs/utils.js +34 -4
  25. package/dist/cjs/utils.js.map +1 -1
  26. package/dist/esm/InteractElement.js +1 -0
  27. package/dist/esm/InteractElement.js.map +1 -1
  28. package/dist/esm/__tests__/interact.spec.js +295 -1
  29. package/dist/esm/__tests__/interact.spec.js.map +1 -1
  30. package/dist/esm/core/Interact.js +4 -0
  31. package/dist/esm/core/Interact.js.map +1 -1
  32. package/dist/esm/core/add.js +16 -9
  33. package/dist/esm/core/add.js.map +1 -1
  34. package/dist/esm/handlers/animationEnd.js +6 -5
  35. package/dist/esm/handlers/animationEnd.js.map +1 -1
  36. package/dist/esm/handlers/click.js +37 -15
  37. package/dist/esm/handlers/click.js.map +1 -1
  38. package/dist/esm/handlers/hover.js +41 -16
  39. package/dist/esm/handlers/hover.js.map +1 -1
  40. package/dist/esm/handlers/index.js +12 -1
  41. package/dist/esm/handlers/index.js.map +1 -1
  42. package/dist/esm/handlers/pointerMove.js +2 -5
  43. package/dist/esm/handlers/pointerMove.js.map +1 -1
  44. package/dist/esm/handlers/viewEnter.js +6 -5
  45. package/dist/esm/handlers/viewEnter.js.map +1 -1
  46. package/dist/esm/handlers/viewProgress.js +24 -13
  47. package/dist/esm/handlers/viewProgress.js.map +1 -1
  48. package/dist/esm/types.js.map +1 -1
  49. package/dist/esm/utils.js +33 -4
  50. package/dist/esm/utils.js.map +1 -1
  51. package/dist/types/core/Interact.d.ts +2 -0
  52. package/dist/types/handlers/animationEnd.d.ts +2 -2
  53. package/dist/types/handlers/click.d.ts +2 -2
  54. package/dist/types/handlers/hover.d.ts +2 -2
  55. package/dist/types/handlers/pointerMove.d.ts +2 -2
  56. package/dist/types/handlers/viewEnter.d.ts +2 -2
  57. package/dist/types/handlers/viewProgress.d.ts +2 -2
  58. package/dist/types/types.d.ts +11 -3
  59. package/dist/types/utils.d.ts +2 -1
  60. package/package.json +2 -2
@@ -1,3 +1,4 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
1
2
  import { Interact } from '../core/Interact';
2
3
  import { add, addListItems } from '../core/add';
3
4
  import { remove } from '../core/remove';
@@ -308,6 +309,18 @@ describe('interact', () => {
308
309
  }
309
310
  };
310
311
 
312
+ // Mock PointerEvent (not available in JSDOM)
313
+ window.PointerEvent = class PointerEvent extends MouseEvent {
314
+ constructor(type, params) {
315
+ if (params === void 0) {
316
+ params = {};
317
+ }
318
+ super(type, params);
319
+ _defineProperty(this, "pointerType", void 0);
320
+ this.pointerType = params.pointerType || '';
321
+ }
322
+ };
323
+
311
324
  // Mock adoptedStyleSheets
312
325
  if (!document.adoptedStyleSheets) {
313
326
  document.adoptedStyleSheets = [];
@@ -403,8 +416,9 @@ describe('interact', () => {
403
416
  jest.clearAllMocks();
404
417
  // Clear Interact instances to ensure test isolation
405
418
  Interact.destroy();
406
- // Reset forceReducedMotion to default
419
+ // Reset static flags to default
407
420
  Interact.forceReducedMotion = false;
421
+ Interact.allowA11yTriggers = false;
408
422
  });
409
423
  describe('init Interact instance', () => {
410
424
  it('should initialize with valid config', () => {
@@ -1804,5 +1818,285 @@ describe('interact', () => {
1804
1818
  expect(spy).toHaveBeenCalledWith(pointerOptionsGetter);
1805
1819
  });
1806
1820
  });
1821
+ describe('a11y - accessible triggers', () => {
1822
+ let a11yElement;
1823
+ function getA11yConfig(trigger, key) {
1824
+ return {
1825
+ interactions: [{
1826
+ trigger,
1827
+ key,
1828
+ effects: [{
1829
+ effectId: 'test-effect'
1830
+ }]
1831
+ }],
1832
+ effects: {
1833
+ 'test-effect': {
1834
+ namedEffect: {
1835
+ type: 'BounceIn',
1836
+ power: 'medium'
1837
+ },
1838
+ duration: 500
1839
+ }
1840
+ }
1841
+ };
1842
+ }
1843
+ afterEach(() => {
1844
+ Interact.destroy();
1845
+ });
1846
+ describe('activate trigger', () => {
1847
+ it('should add both click and keydown listeners', () => {
1848
+ Interact.create(getA11yConfig('activate', 'activate-div'));
1849
+ a11yElement = document.createElement('interact-element');
1850
+ const div = document.createElement('div');
1851
+ a11yElement.append(div);
1852
+ const addEventListenerSpy = jest.spyOn(div, 'addEventListener');
1853
+ add(a11yElement, 'activate-div');
1854
+ expect(addEventListenerSpy).toHaveBeenCalledWith('click', expect.any(Function), expect.any(Object));
1855
+ expect(addEventListenerSpy).toHaveBeenCalledWith('keydown', expect.any(Function), expect.any(Object));
1856
+ });
1857
+ it('should not double-invoke handler when Enter triggers both keydown and click', () => {
1858
+ const {
1859
+ getWebAnimation
1860
+ } = require('@wix/motion');
1861
+ const mockPlay = getWebAnimation().play;
1862
+ mockPlay.mockClear();
1863
+ Interact.create(getA11yConfig('activate', 'activate-handler-test'));
1864
+ a11yElement = document.createElement('interact-element');
1865
+ const button = document.createElement('button');
1866
+ a11yElement.append(button);
1867
+ add(a11yElement, 'activate-handler-test');
1868
+
1869
+ // Simulate browser behavior: Enter key triggers keydown AND synthesized click with no pointerType
1870
+ button.dispatchEvent(new KeyboardEvent('keydown', {
1871
+ code: 'Enter',
1872
+ bubbles: true
1873
+ }));
1874
+ button.dispatchEvent(new MouseEvent('click', {
1875
+ bubbles: true
1876
+ }));
1877
+ expect(mockPlay).toHaveBeenCalledTimes(1);
1878
+ });
1879
+ });
1880
+ describe('interest trigger', () => {
1881
+ it('should add focusin listener alongside mouseenter', () => {
1882
+ Interact.create(getA11yConfig('interest', 'interest-test'));
1883
+ a11yElement = document.createElement('interact-element');
1884
+ const div = document.createElement('div');
1885
+ a11yElement.append(div);
1886
+ const addEventListenerSpy = jest.spyOn(div, 'addEventListener');
1887
+ add(a11yElement, 'interest-test');
1888
+ expect(addEventListenerSpy).toHaveBeenCalledWith('mouseenter', expect.any(Function), expect.any(Object));
1889
+ expect(addEventListenerSpy).toHaveBeenCalledWith('focusin', expect.any(Function), expect.any(Object));
1890
+ });
1891
+ });
1892
+ describe('click trigger with allowA11yTriggers flag', () => {
1893
+ it('should NOT add keydown listener when flag is false', () => {
1894
+ Interact.create(getA11yConfig('click', 'click-no-flag'));
1895
+ Interact.setup({
1896
+ allowA11yTriggers: false
1897
+ });
1898
+ a11yElement = document.createElement('interact-element');
1899
+ const div = document.createElement('div');
1900
+ a11yElement.append(div);
1901
+ const addEventListenerSpy = jest.spyOn(div, 'addEventListener');
1902
+ add(a11yElement, 'click-no-flag');
1903
+ expect(addEventListenerSpy).toHaveBeenCalledWith('click', expect.any(Function), expect.any(Object));
1904
+ expect(addEventListenerSpy).not.toHaveBeenCalledWith('keydown', expect.any(Function), expect.any(Object));
1905
+ });
1906
+ it('should add keydown listener when flag is true', () => {
1907
+ Interact.setup({
1908
+ allowA11yTriggers: true
1909
+ });
1910
+ Interact.create(getA11yConfig('click', 'click-with-flag'));
1911
+ a11yElement = document.createElement('interact-element');
1912
+ const div = document.createElement('div');
1913
+ a11yElement.append(div);
1914
+ const addEventListenerSpy = jest.spyOn(div, 'addEventListener');
1915
+ add(a11yElement, 'click-with-flag');
1916
+ expect(addEventListenerSpy).toHaveBeenCalledWith('click', expect.any(Function), expect.any(Object));
1917
+ expect(addEventListenerSpy).toHaveBeenCalledWith('keydown', expect.any(Function), expect.any(Object));
1918
+ });
1919
+ });
1920
+ describe('hover trigger with allowA11yTriggers flag', () => {
1921
+ it('should NOT add focusin listener when flag is false', () => {
1922
+ Interact.setup({
1923
+ allowA11yTriggers: false
1924
+ });
1925
+ Interact.create(getA11yConfig('hover', 'hover-no-flag'));
1926
+ a11yElement = document.createElement('interact-element');
1927
+ const div = document.createElement('div');
1928
+ a11yElement.append(div);
1929
+ const addEventListenerSpy = jest.spyOn(div, 'addEventListener');
1930
+ add(a11yElement, 'hover-no-flag');
1931
+ expect(addEventListenerSpy).toHaveBeenCalledWith('mouseenter', expect.any(Function), expect.any(Object));
1932
+ expect(addEventListenerSpy).not.toHaveBeenCalledWith('focusin', expect.any(Function), expect.any(Object));
1933
+ });
1934
+ it('should add focusin listener when flag is true', () => {
1935
+ Interact.setup({
1936
+ allowA11yTriggers: true
1937
+ });
1938
+ Interact.create(getA11yConfig('hover', 'hover-with-flag'));
1939
+ a11yElement = document.createElement('interact-element');
1940
+ const div = document.createElement('div');
1941
+ a11yElement.append(div);
1942
+ const addEventListenerSpy = jest.spyOn(div, 'addEventListener');
1943
+ add(a11yElement, 'hover-with-flag');
1944
+ expect(addEventListenerSpy).toHaveBeenCalledWith('mouseenter', expect.any(Function), expect.any(Object));
1945
+ expect(addEventListenerSpy).toHaveBeenCalledWith('focusin', expect.any(Function), expect.any(Object));
1946
+ });
1947
+ });
1948
+ });
1949
+ describe('selector condition type', () => {
1950
+ it('should pass selectorCondition to handler when condition type is selector', async () => {
1951
+ const config = {
1952
+ conditions: {
1953
+ active: {
1954
+ type: 'selector',
1955
+ predicate: '.active'
1956
+ }
1957
+ },
1958
+ interactions: [{
1959
+ trigger: 'click',
1960
+ key: 'selector-test',
1961
+ effects: [{
1962
+ key: 'selector-test',
1963
+ effectId: 'test-effect',
1964
+ conditions: ['active']
1965
+ }]
1966
+ }],
1967
+ effects: {
1968
+ 'test-effect': {
1969
+ namedEffect: {
1970
+ type: 'FadeIn',
1971
+ power: 'medium'
1972
+ },
1973
+ duration: 500
1974
+ }
1975
+ }
1976
+ };
1977
+ Interact.create(config);
1978
+ const testElement = document.createElement('interact-element');
1979
+ const div = document.createElement('div');
1980
+ testElement.append(div);
1981
+ add(testElement, 'selector-test');
1982
+
1983
+ // The handler should have received selectorCondition
1984
+ // We verify by checking the animation is created (condition doesn't block setup)
1985
+ const {
1986
+ getWebAnimation
1987
+ } = await import('@wix/motion');
1988
+ expect(getWebAnimation).toHaveBeenCalled();
1989
+ });
1990
+ it('should skip handler execution when element does not match selector condition', async () => {
1991
+ const {
1992
+ getWebAnimation
1993
+ } = await import('@wix/motion');
1994
+ const mockAnimation = {
1995
+ play: jest.fn(),
1996
+ cancel: jest.fn(),
1997
+ onFinish: jest.fn(),
1998
+ reverse: jest.fn(),
1999
+ progress: jest.fn(),
2000
+ playState: 'idle',
2001
+ isCSS: false
2002
+ };
2003
+ getWebAnimation.mockReturnValue(mockAnimation);
2004
+ const config = {
2005
+ conditions: {
2006
+ active: {
2007
+ type: 'selector',
2008
+ predicate: '.active'
2009
+ }
2010
+ },
2011
+ interactions: [{
2012
+ trigger: 'click',
2013
+ key: 'selector-skip-test',
2014
+ effects: [{
2015
+ key: 'selector-skip-test',
2016
+ effectId: 'skip-effect',
2017
+ conditions: ['active']
2018
+ }]
2019
+ }],
2020
+ effects: {
2021
+ 'skip-effect': {
2022
+ namedEffect: {
2023
+ type: 'FadeIn',
2024
+ power: 'medium'
2025
+ },
2026
+ duration: 500
2027
+ }
2028
+ }
2029
+ };
2030
+ Interact.create(config);
2031
+ const testElement = document.createElement('interact-element');
2032
+ const div = document.createElement('div');
2033
+ // div does NOT have .active class
2034
+ testElement.append(div);
2035
+ add(testElement, 'selector-skip-test');
2036
+
2037
+ // Simulate click - animation should NOT play because element doesn't match .active
2038
+ // Must use PointerEvent with pointerType because click handler filters by pointerType
2039
+ div.dispatchEvent(new PointerEvent('click', {
2040
+ bubbles: true,
2041
+ pointerType: 'mouse'
2042
+ }));
2043
+ expect(mockAnimation.play).not.toHaveBeenCalled();
2044
+ });
2045
+ it('should execute handler when element matches selector condition', async () => {
2046
+ const {
2047
+ getWebAnimation
2048
+ } = await import('@wix/motion');
2049
+ const mockAnimation = {
2050
+ play: jest.fn(),
2051
+ cancel: jest.fn(),
2052
+ onFinish: jest.fn(),
2053
+ reverse: jest.fn(),
2054
+ progress: jest.fn(),
2055
+ playState: 'idle',
2056
+ isCSS: false
2057
+ };
2058
+ getWebAnimation.mockReturnValue(mockAnimation);
2059
+ const config = {
2060
+ conditions: {
2061
+ active: {
2062
+ type: 'selector',
2063
+ predicate: '.active'
2064
+ }
2065
+ },
2066
+ interactions: [{
2067
+ trigger: 'click',
2068
+ key: 'selector-match-test',
2069
+ effects: [{
2070
+ key: 'selector-match-test',
2071
+ effectId: 'match-effect',
2072
+ conditions: ['active']
2073
+ }]
2074
+ }],
2075
+ effects: {
2076
+ 'match-effect': {
2077
+ namedEffect: {
2078
+ type: 'FadeIn',
2079
+ power: 'medium'
2080
+ },
2081
+ duration: 500
2082
+ }
2083
+ }
2084
+ };
2085
+ Interact.create(config);
2086
+ const testElement = document.createElement('interact-element');
2087
+ const div = document.createElement('div');
2088
+ div.classList.add('active'); // div HAS .active class
2089
+ testElement.append(div);
2090
+ add(testElement, 'selector-match-test');
2091
+
2092
+ // Simulate click - animation SHOULD play because element matches .active
2093
+ // Must use PointerEvent with pointerType because click handler filters by pointerType
2094
+ div.dispatchEvent(new PointerEvent('click', {
2095
+ bubbles: true,
2096
+ pointerType: 'mouse'
2097
+ }));
2098
+ expect(mockAnimation.play).toHaveBeenCalled();
2099
+ });
2100
+ });
1807
2101
  });
1808
2102
  //# sourceMappingURL=interact.spec.js.map