homeflowjs 0.10.18 → 0.10.20
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/__tests__/instant-valuation.test.jsx +1 -1
- package/branches/branches-search-form/branches-search-input.component.jsx +2 -1
- package/hooks/index.js +2 -0
- package/hooks/use-outside-click.js +18 -0
- package/instant-valuation/instant-valuation/instant-valuation.component.jsx +2 -1
- package/instant-valuation/result-step/result-step.component.jsx +12 -13
- package/package.json +1 -1
- package/search/location-input/location-input.component.jsx +2 -1
- package/user/default-profile/user-profile/user-profile-modal.component.jsx +12 -2
- package/utils/index.js +11 -13
@@ -71,7 +71,7 @@ describe('Instant Valuation', () => {
|
|
71
71
|
|
72
72
|
describe('Instant Valuation Results', () => {
|
73
73
|
it('renders error message if it can\'t provide an accurate valuation', () => {
|
74
|
-
render(<ResultStep valuation={{ price: null }} />);
|
74
|
+
render(<ResultStep valuation={{ price: null }} getValuation={jest.fn} />);
|
75
75
|
const errorMessage = screen.getByTestId('valuation-error');
|
76
76
|
expect(errorMessage).toBeInTheDocument();
|
77
77
|
});
|
@@ -6,6 +6,7 @@ import PropTypes from 'prop-types';
|
|
6
6
|
import Autosuggest from 'react-autosuggest';
|
7
7
|
import debounce from 'lodash.debounce';
|
8
8
|
import { setBranchesSearch } from '../../actions/branches.actions';
|
9
|
+
import { DEBOUNCE_DELAY } from '../../utils';
|
9
10
|
|
10
11
|
const BranchesSearchInput = ({
|
11
12
|
placeholder,
|
@@ -53,7 +54,7 @@ const BranchesSearchInput = ({
|
|
53
54
|
setSuggestions(suggestions);
|
54
55
|
});
|
55
56
|
|
56
|
-
const debouncedGetSuggestions = useCallback(debounce(getSuggestions,
|
57
|
+
const debouncedGetSuggestions = useCallback(debounce(getSuggestions, DEBOUNCE_DELAY), []);
|
57
58
|
|
58
59
|
const renderSuggestion = (suggestion) => (
|
59
60
|
<span className="react-autosuggest_span">{suggestion}</span>
|
package/hooks/index.js
CHANGED
@@ -0,0 +1,18 @@
|
|
1
|
+
import { useEffect } from 'react';
|
2
|
+
|
3
|
+
const useOutsideClick = (ref, callback) => {
|
4
|
+
useEffect(() => {
|
5
|
+
const handleClick = (e) => {
|
6
|
+
if (ref.current && !ref.current.contains(e.target)) {
|
7
|
+
callback();
|
8
|
+
}
|
9
|
+
};
|
10
|
+
document.addEventListener('mousedown', handleClick);
|
11
|
+
return () => {
|
12
|
+
document.removeEventListener('mousedown', handleClick);
|
13
|
+
};
|
14
|
+
}, [ref]);
|
15
|
+
return ref;
|
16
|
+
};
|
17
|
+
|
18
|
+
export default useOutsideClick;
|
@@ -10,6 +10,7 @@ import IntroStep from '../intro-step/intro-step.component';
|
|
10
10
|
import YourDetailsStep from '../your-details-step/your-details-step.component';
|
11
11
|
import SimilarPropertiesStep from '../similar-properties-step/similar-properties-step.component';
|
12
12
|
import ResultStep from '../result-step/result-step.component';
|
13
|
+
import { DEBOUNCE_DELAY } from '../../utils';
|
13
14
|
|
14
15
|
import './instant-valuation.styles.scss';
|
15
16
|
|
@@ -82,7 +83,7 @@ class InstantValuation extends Component {
|
|
82
83
|
this.getSimilarProperties = this.getSimilarProperties.bind(this);
|
83
84
|
this.toggleSimilarProperty = this.toggleSimilarProperty.bind(this);
|
84
85
|
this.getValuation = this.getValuation.bind(this);
|
85
|
-
this.addressLookup = debounce(this.addressLookup,
|
86
|
+
this.addressLookup = debounce(this.addressLookup, DEBOUNCE_DELAY).bind(this);
|
86
87
|
this.reset = this.reset.bind(this);
|
87
88
|
this.setMessage = this.setMessage.bind(this);
|
88
89
|
}
|
@@ -17,6 +17,18 @@ const ResultStep = ({
|
|
17
17
|
reset,
|
18
18
|
loading,
|
19
19
|
}) => {
|
20
|
+
const [selectedChannel, setSelectedChannel] = useState('sales');
|
21
|
+
|
22
|
+
useEffect(() => {
|
23
|
+
getValuation();
|
24
|
+
}, []);
|
25
|
+
|
26
|
+
useEffect(() => {
|
27
|
+
if (valuation) Homeflow.kickEvent('valuation_successful');
|
28
|
+
}, [valuation])
|
29
|
+
|
30
|
+
if (!valuation || loading) return <Loader className="hfjs-instant-val__loader" />;
|
31
|
+
|
20
32
|
if (!valuation.price) {
|
21
33
|
return (
|
22
34
|
<div data-testid="valuation-error" id="no_results">
|
@@ -32,16 +44,6 @@ const ResultStep = ({
|
|
32
44
|
);
|
33
45
|
}
|
34
46
|
|
35
|
-
const [selectedChannel, setSelectedChannel] = useState('sales');
|
36
|
-
|
37
|
-
useEffect(() => {
|
38
|
-
getValuation();
|
39
|
-
}, []);
|
40
|
-
|
41
|
-
useEffect(() => {
|
42
|
-
if (valuation) Homeflow.kickEvent('valuation_successful');
|
43
|
-
}, [valuation])
|
44
|
-
|
45
47
|
const formatPrice = (price) => (
|
46
48
|
`£${Math.round(price)}${selectedChannel === 'lettings' ? '/month' : ''}`
|
47
49
|
.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,')
|
@@ -82,11 +84,8 @@ const ResultStep = ({
|
|
82
84
|
const companyName = Homeflow.get('company_name');
|
83
85
|
const gmapsKey = Homeflow.get('theme_preferences').google_maps_api_key;
|
84
86
|
|
85
|
-
if (!valuation || loading) return <Loader className="hfjs-instant-val__loader" />;
|
86
|
-
|
87
87
|
const streetviewQuery = `size=900x900&location=${lead.full_address},${search.postcode}&key=${gmapsKey}`;
|
88
88
|
|
89
|
-
|
90
89
|
return (
|
91
90
|
<div className="result-container">
|
92
91
|
<div className="inner-wrapper">
|
package/package.json
CHANGED
@@ -6,6 +6,7 @@ import Autosuggest from 'react-autosuggest';
|
|
6
6
|
|
7
7
|
import { setSuggestions, setSearchField } from '../../actions/search.actions';
|
8
8
|
import propertySearch from '../property-search/property-search';
|
9
|
+
import { DEBOUNCE_DELAY } from '../../utils';
|
9
10
|
|
10
11
|
import { autosuggestTheme } from './location-input.styles';
|
11
12
|
|
@@ -15,7 +16,7 @@ class LocationInput extends Component {
|
|
15
16
|
|
16
17
|
this.onLocationChange = this.onLocationChange.bind(this);
|
17
18
|
// wait half a second after typing stops before fetching results
|
18
|
-
this.debouncedLoadSuggestions = debounce(this.loadSuggestions,
|
19
|
+
this.debouncedLoadSuggestions = debounce(this.loadSuggestions, DEBOUNCE_DELAY);
|
19
20
|
}
|
20
21
|
|
21
22
|
onLocationChange(event, { newValue }) {
|
@@ -1,10 +1,11 @@
|
|
1
|
-
import React from 'react';
|
1
|
+
import React, { useRef } from 'react';
|
2
2
|
import { connect } from 'react-redux';
|
3
3
|
import {
|
4
4
|
HashRouter as Router,
|
5
5
|
Route,
|
6
6
|
Switch,
|
7
7
|
NavLink,
|
8
|
+
useHistory
|
8
9
|
} from 'react-router-dom';
|
9
10
|
import PropTypes from 'prop-types';
|
10
11
|
|
@@ -17,6 +18,7 @@ import ForgottenPassword from '../forgotten-password/forgotten-password.componen
|
|
17
18
|
import SignOutButton from '../../sign-out-button/sign-out-button.component';
|
18
19
|
import Loader from '../../../shared/loader.component';
|
19
20
|
import { loadingStyles } from '../../../modal/modal.component';
|
21
|
+
import useOutsideClick from '../../../hooks/use-outside-click';
|
20
22
|
|
21
23
|
import './user-profile.styles.scss';
|
22
24
|
|
@@ -32,9 +34,17 @@ const UserProfileModal = ({ currentUser, loading }) => {
|
|
32
34
|
}
|
33
35
|
|
34
36
|
const logo = Homeflow.get('agency_logo');
|
37
|
+
const history = useHistory();
|
38
|
+
const ref = useRef();
|
39
|
+
|
40
|
+
const handleClickOutside = () => {
|
41
|
+
history.push('/')
|
42
|
+
};
|
43
|
+
|
44
|
+
useOutsideClick(ref, handleClickOutside);
|
35
45
|
|
36
46
|
return (
|
37
|
-
<div className="user-profile hf-modal">
|
47
|
+
<div className="user-profile hf-modal" ref={ref}>
|
38
48
|
<div className="user-profile__side">
|
39
49
|
<img src={logo} alt="Logo" className="user-profile__agency-logo" />
|
40
50
|
|
package/utils/index.js
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
export { default as stampDutyCalculator } from './stamp-duty-calculator/stamp-duty-calculator';
|
2
2
|
|
3
|
-
export const capitalizeFirstLetter = str => (
|
3
|
+
export const capitalizeFirstLetter = (str) => (
|
4
4
|
str.charAt(0).toUpperCase() + str.slice(1)
|
5
|
-
)
|
5
|
+
);
|
6
6
|
|
7
|
-
export const camelToSnakeCase = str => (
|
8
|
-
str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`)
|
9
|
-
)
|
7
|
+
export const camelToSnakeCase = (str) => (
|
8
|
+
str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)
|
9
|
+
);
|
10
10
|
|
11
11
|
export const isEmpty = (obj) => {
|
12
12
|
for (const key in obj) {
|
@@ -16,9 +16,7 @@ export const isEmpty = (obj) => {
|
|
16
16
|
return true;
|
17
17
|
};
|
18
18
|
|
19
|
-
export const numberWithCommas = (num) => {
|
20
|
-
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
21
|
-
}
|
19
|
+
export const numberWithCommas = (num) => num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
22
20
|
|
23
21
|
// TODO: DRY these two functions up
|
24
22
|
export const findSavedSearchIndex = (savedSearches, search) => {
|
@@ -64,9 +62,7 @@ export const formatMoney = (num) => {
|
|
64
62
|
return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
|
65
63
|
};
|
66
64
|
|
67
|
-
export const uniqueKey = () =>
|
68
|
-
return Math.random().toString(36).substring(7);
|
69
|
-
};
|
65
|
+
export const uniqueKey = () => Math.random().toString(36).substring(7);
|
70
66
|
|
71
67
|
export const objectDiff = (oldObject, newObject) => {
|
72
68
|
const changed = {};
|
@@ -78,8 +74,10 @@ export const objectDiff = (oldObject, newObject) => {
|
|
78
74
|
}
|
79
75
|
|
80
76
|
return changed;
|
81
|
-
}
|
77
|
+
};
|
82
78
|
|
83
|
-
export const compact = (array) => array.filter(item => typeof item !== 'undefined' && item !== null);
|
79
|
+
export const compact = (array) => array.filter((item) => typeof item !== 'undefined' && item !== null);
|
84
80
|
|
85
81
|
export const sanitizeText = (string) => new Option(string).innerHTML;
|
82
|
+
|
83
|
+
export const DEBOUNCE_DELAY = 200;
|