homeflowjs 0.10.25 → 0.11.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/.eslintrc.js CHANGED
@@ -15,6 +15,7 @@ module.exports = {
15
15
  btoa: 'readonly',
16
16
  localStorage: 'readonly',
17
17
  google: 'readonly',
18
+ IntersectionObserver: 'readonly',
18
19
  },
19
20
  parser: 'babel-eslint',
20
21
  parserOptions: {
@@ -9,6 +9,17 @@ import { Router } from 'react-router-dom';
9
9
  import PropertyResults from 'properties/property-results/property-results.component';
10
10
  import { properties } from './__mocks__/property-results.mock';
11
11
 
12
+ beforeEach(() => {
13
+ // IntersectionObserver isn't available in test environment
14
+ const mockIntersectionObserver = jest.fn();
15
+ mockIntersectionObserver.mockReturnValue({
16
+ observe: () => null,
17
+ unobserve: () => null,
18
+ disconnect: () => null,
19
+ });
20
+ window.IntersectionObserver = mockIntersectionObserver;
21
+ });
22
+
12
23
  describe('Property Results', () => {
13
24
  const initialState = {
14
25
  properties: {
@@ -10,7 +10,7 @@ const LoadMoreButton = ({
10
10
  const [loading, setLoading] = useState(false);
11
11
  const [error, setError] = useState(false);
12
12
  const hasNextPage = useSelector((state) => state.articles?.pagination?.has_next_page);
13
- const storeArticles = useSelector((state) => state.articles.articles) || [];
13
+ const storeArticles = useSelector((state) => state.articles.articles || []);
14
14
  const dispatch = useDispatch();
15
15
  const loadNextArticlesPage = () => dispatch(loadNextPage());
16
16
 
package/hooks/index.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import useDefaultSort from './use-default-sort.hook';
2
2
  import useGeolocate from './use-geolocate';
3
3
  import useOutsideClick from './use-outside-click';
4
+ import { useOnScreen } from './use-on-screen';
4
5
 
5
6
  export {
6
7
  useDefaultSort,
7
8
  useGeolocate,
8
9
  useOutsideClick,
10
+ useOnScreen,
9
11
  };
@@ -0,0 +1,22 @@
1
+ import { useEffect, useState, useRef } from 'react';
2
+
3
+ export function useOnScreen(ref) {
4
+ const [isOnScreen, setIsOnScreen] = useState(false);
5
+ const observerRef = useRef(null);
6
+
7
+ useEffect(() => {
8
+ observerRef.current = new IntersectionObserver(([entry]) => {
9
+ setIsOnScreen(entry.isIntersecting);
10
+ });
11
+ }, []);
12
+
13
+ useEffect(() => {
14
+ observerRef.current.observe(ref.current);
15
+
16
+ return () => {
17
+ observerRef.current.disconnect();
18
+ };
19
+ }, [ref]);
20
+
21
+ return isOnScreen;
22
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "homeflowjs",
3
- "version": "0.10.25",
3
+ "version": "0.11.0",
4
4
  "description": "JavaScript toolkit for Homeflow themes",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -2,8 +2,7 @@ import React, { useEffect } from 'react';
2
2
  import { connect } from 'react-redux';
3
3
  import PropTypes from 'prop-types';
4
4
 
5
- import { handleScroll, propertiesByPage } from '../property-utils/property-utils';
6
- import { uniqueKey } from '../../utils';
5
+ import { handleScroll, propertiesByPage, updatePageInURL } from '../property-utils/property-utils';
7
6
 
8
7
  const ConditionalWrapper = ({ condition, wrapper, children }) => (
9
8
  condition ? wrapper(children) : children
@@ -27,7 +26,27 @@ const PropertiesDisplay = ({
27
26
  return function cleanUp() {
28
27
  window.removeEventListener('scroll', onScroll);
29
28
  };
30
- });
29
+ }, []);
30
+
31
+ useEffect(() => {
32
+ const pageMarkers = document.querySelectorAll('[data-page-marker]');
33
+
34
+ const observer = new IntersectionObserver((entries) => {
35
+ entries.forEach((entry) => {
36
+ if (entry.isIntersecting && entry.target?.dataset?.pageMarker) {
37
+ updatePageInURL(parseInt(entry.target.dataset.pageMarker, 10));
38
+ }
39
+ });
40
+ });
41
+
42
+ pageMarkers.forEach((marker) => {
43
+ observer.observe(marker);
44
+ });
45
+
46
+ return function cleanUp() {
47
+ observer.disconnect();
48
+ };
49
+ }, [properties]);
31
50
 
32
51
  if (!properties.length) {
33
52
  return (
@@ -44,8 +63,7 @@ const PropertiesDisplay = ({
44
63
  wrapper={(children) => (
45
64
  <div
46
65
  data-result-page={page[0].resultPage}
47
- key={uniqueKey()}
48
- className={`clearfix results-page--${displayType}`}
66
+ className={`results-page results-page--${displayType}`}
49
67
  >
50
68
  {children}
51
69
  </div>
@@ -65,7 +83,12 @@ const PropertiesDisplay = ({
65
83
  );
66
84
  }
67
85
  return (
68
- <Item key={property.property_id} property={property} {...other} />
86
+ <Item
87
+ key={property.property_id}
88
+ property={property}
89
+ data-page-marker={!addWrapper && index === 0 ? page[0].resultPage : null}
90
+ {...other}
91
+ />
69
92
  );
70
93
  })}
71
94
  </ConditionalWrapper>
@@ -22,10 +22,29 @@ const infiniteScroll = () => {
22
22
  return true;
23
23
  };
24
24
 
25
+ export const updatePageInURL = (currentPage) => {
26
+ const { pathname, hash } = window.location;
27
+
28
+ let newPath;
29
+
30
+ if (currentPage === 1 && !pathname.includes('page-')) return null;
31
+
32
+ if (currentPage === 1) {
33
+ newPath = pathname.replace(/page-./, '');
34
+ } else if (pathname.includes('page-')) {
35
+ newPath = pathname.replace(/page-./, `page-${currentPage}`);
36
+ } else {
37
+ newPath = pathname[pathname.length - 1] === '/' ? pathname : `${pathname}/`;
38
+ newPath = `${newPath}page-${currentPage}`;
39
+ }
40
+
41
+ return history.replaceState(null, '', `${newPath}${hash}`);
42
+ };
43
+
25
44
  // calculates which page we've scrolled to and updates the URL
26
45
  export const handleScroll = (infinite) => {
27
- // TODO: Only do this if the theme has infinite scroll enabled
28
46
  if (infinite) infiniteScroll();
47
+
29
48
  // get the current top of view scroll position
30
49
  const currentPosition = document.documentElement.scrollTop;
31
50
  document.querySelectorAll('[data-result-page]').forEach((resultPage) => {
@@ -38,22 +57,8 @@ export const handleScroll = (infinite) => {
38
57
  // if top of current view is between the top and bottom of the page div, we're on that page
39
58
  if (pageTop <= currentPosition && pageTop + (rect.bottom - rect.top) > currentPosition) {
40
59
  const currentPage = parseInt(resultPage.dataset.resultPage, 10);
41
- const { pathname, hash } = window.location;
42
-
43
- let newPath;
44
60
 
45
- if (currentPage === 1 && !pathname.includes('page-')) return null;
46
-
47
- if (currentPage === 1) {
48
- newPath = pathname.replace(/page-./, '');
49
- } else if (pathname.includes('page-')) {
50
- newPath = pathname.replace(/page-./, `page-${currentPage}`);
51
- } else {
52
- newPath = pathname[pathname.length - 1] === '/' ? pathname : `${pathname}/`;
53
- newPath = `${newPath}page-${currentPage}`;
54
- }
55
-
56
- history.replaceState(null, '', `${newPath}${hash}`);
61
+ updatePageInURL(currentPage);
57
62
  }
58
63
  });
59
64
  };
@@ -75,4 +80,4 @@ export const propertiesByPage = (properties) => {
75
80
  }
76
81
 
77
82
  return properties2d;
78
- }
83
+ };