homeflowjs 0.9.37 → 0.9.39

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "homeflowjs",
3
- "version": "0.9.37",
3
+ "version": "0.9.39",
4
4
  "description": "JavaScript toolkit for Homeflow themes",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/search/index.js CHANGED
@@ -30,4 +30,4 @@ export {
30
30
  SaveSearchButton,
31
31
  SavedSearch,
32
32
  propertySearch,
33
- }
33
+ };
@@ -0,0 +1,11 @@
1
+ import { numberWithCommas } from '../../utils';
2
+ const defaultPriceLabel = (price, channel, lettingsAs) => {
3
+ if (channel === 'lettings' && lettingsAs === 'pw') {
4
+ const pwPrice = Math.ceil(price / (52 / 12));
5
+ return `£${numberWithCommas(pwPrice)} pw (£${numberWithCommas(price)} pcm)`;
6
+ }
7
+
8
+ return `£${numberWithCommas(price)} ${channel === 'lettings' ? 'pcm' : ''}`;
9
+ };
10
+
11
+ export default defaultPriceLabel;
@@ -1,22 +1,14 @@
1
- import React from 'react';
1
+ import React, { useState, useEffect } from 'react';
2
2
  import { connect } from 'react-redux';
3
3
  import PropTypes from 'prop-types';
4
4
  import Select from 'react-dropdown';
5
5
 
6
6
  import 'react-dropdown/style.css';
7
7
 
8
- import { capitalizeFirstLetter, numberWithCommas } from '../../utils';
8
+ import { capitalizeFirstLetter } from '../../utils';
9
9
  import { defaultSalesPrices, defaultLettingsPrices } from './default-prices';
10
10
  import { setSearchField } from '../../actions/search.actions';
11
-
12
- const priceLabel = (price, channel, lettingsAs) => {
13
- if (channel === 'lettings' && lettingsAs === 'pw') {
14
- const pwPrice = Math.ceil(price / (52 / 12));
15
- return `£${numberWithCommas(pwPrice)} pw (£${numberWithCommas(price)} pcm)`;
16
- }
17
-
18
- return `£${numberWithCommas(price)} ${channel === 'lettings' ? 'pcm' : ''}`
19
- };
11
+ import defaultPriceLabel from './default-price-label';
20
12
 
21
13
  const NormalSelect = ({
22
14
  type,
@@ -28,28 +20,34 @@ const NormalSelect = ({
28
20
  minPrice,
29
21
  maxPrice,
30
22
  ...otherProps
31
- }) => (
32
- <select
33
- name={`${type}Price`}
34
- aria-label={`${type}-price`}
35
- value={selectedPrice || ''}
36
- onChange={e => setSearchField({
37
- [e.target.name]: e.target.value ? parseInt(e.target.value) : null
38
- })}
39
- {...otherProps}
40
- >
41
- <option value="" title={`${type} price`}>{capitalizeFirstLetter(type)}. Price</option>
42
- {prices.map(price => (
43
- <option
44
- key={price}
45
- value={price}
46
- title={price}
47
- >
48
- {priceLabel(price, channel, priceConfig.lettingsAs)}
23
+ }) => {
24
+ const { priceLabel } = priceConfig;
25
+ return (
26
+ <select
27
+ name={`${type}Price`}
28
+ aria-label={`${type}-price`}
29
+ value={selectedPrice || ''}
30
+ onChange={e => setSearchField({
31
+ [e.target.name]: e.target.value ? parseInt(e.target.value) : null,
32
+ })}
33
+ {...otherProps}
34
+ >
35
+ <option value="" title={`${type} price`}>
36
+ {capitalizeFirstLetter(type)}
37
+ . Price
49
38
  </option>
50
- ))}
51
- </select>
52
- );
39
+ {prices.map(price => (
40
+ <option
41
+ key={price}
42
+ value={price}
43
+ title={price}
44
+ >
45
+ {priceLabel(price, channel, priceConfig.lettingsAs)}
46
+ </option>
47
+ ))}
48
+ </select>
49
+ );
50
+ };
53
51
 
54
52
  const ReactSelect = (props) => {
55
53
  const {
@@ -63,6 +61,8 @@ const ReactSelect = (props) => {
63
61
  priceConfig,
64
62
  } = props;
65
63
 
64
+ const { priceLabel } = priceConfig;
65
+
66
66
  const priceOptions = prices.map(price => (
67
67
  { value: price, label: priceLabel(price, channel, priceConfig.lettingsAs) }
68
68
  ));
@@ -103,6 +103,25 @@ const PriceSelect = (props) => {
103
103
  lettingsPrices = defaultLettingsPrices,
104
104
  } = priceConfig;
105
105
 
106
+ /**
107
+ * If not on first render and the channel updates clear the min and
108
+ * and max prices in the redux store. This is to stop bugs when the
109
+ * uses changes between the sales and letting channels, as the minPrice
110
+ * and maxPrice in redux may be set to the wrong channel values. For
111
+ * example the user selects min and max prices the sales channel
112
+ * but then user updates the channel to lettings, the inputs look to
113
+ * have changed but the sales prices will still saved in the store.
114
+ * So thats why we need to clear them is the channel updates.
115
+ */
116
+ const [firstRender, setFirstRender] = useState(true);
117
+ useEffect(() => {
118
+ if (!firstRender) {
119
+ setSearchField({ minPrice: null });
120
+ setSearchField({ maxPrice: null });
121
+ }
122
+ setFirstRender(false);
123
+ }, [channel]);
124
+
106
125
  let prices = channel === 'sales' ? salesPrices : lettingsPrices;
107
126
 
108
127
  const selectedPrice = type === 'min' ? minPrice : maxPrice;
@@ -122,7 +141,7 @@ const PriceSelect = (props) => {
122
141
  selectedPrice={selectedPrice}
123
142
  {...props}
124
143
  />
125
- )
144
+ );
126
145
  }
127
146
 
128
147
  return (
@@ -131,15 +150,9 @@ const PriceSelect = (props) => {
131
150
  selectedPrice={selectedPrice}
132
151
  {...props}
133
152
  />
134
- )
153
+ );
135
154
  };
136
155
 
137
- // const priceConfig = {
138
- // salesPrices: Homeflow.get('sales_prices'),
139
- // salesPrices: Homeflow.get('lettings_prices'),
140
- // lettingsAs: 'pw',
141
- // }
142
-
143
156
  PriceSelect.propTypes = {
144
157
  type: PropTypes.string.isRequired,
145
158
  channel: PropTypes.oneOf(['sales', 'lettings']).isRequired,
@@ -150,6 +163,7 @@ PriceSelect.propTypes = {
150
163
  salesPrices: PropTypes.array,
151
164
  lettingsPrices: PropTypes.array,
152
165
  lettingsAs: PropTypes.string,
166
+ priceLabel: PropTypes.func,
153
167
  }),
154
168
  lettingsAs: PropTypes.string,
155
169
  reactSelect: PropTypes.bool,
@@ -160,6 +174,7 @@ PriceSelect.defaultProps = {
160
174
  salesPrices: defaultSalesPrices,
161
175
  lettingsPrices: defaultLettingsPrices,
162
176
  lettingsAs: 'pcm',
177
+ priceLabel: defaultPriceLabel,
163
178
  },
164
179
  };
165
180
 
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import { shallow, mount } from 'enzyme';
3
+ import defaultPriceLabel from './default-price-label';
3
4
 
4
5
  import {
5
6
  TESTPriceSelect as PriceSelect,
@@ -39,6 +40,7 @@ describe('PriceSelect', () => {
39
40
  minPrice: 150000,
40
41
  priceConfig: {
41
42
  salesPrices: [50000, 100000, 150000, 200000],
43
+ priceLabel: defaultPriceLabel,
42
44
  }
43
45
  };
44
46
  const wrapper = mount(<PriceSelect {...testProps} />);
@@ -55,6 +57,7 @@ describe('PriceSelect', () => {
55
57
  maxPrice: 150000,
56
58
  priceConfig: {
57
59
  salesPrices: [50000, 100000, 150000, 200000],
60
+ priceLabel: defaultPriceLabel,
58
61
  },
59
62
  };
60
63
  const wrapper = mount(<PriceSelect {...testProps} />);
@@ -71,6 +74,7 @@ describe('PriceSelect', () => {
71
74
  maxPrice: 150000,
72
75
  priceConfig: {
73
76
  salesPrices: [50000, 100000, 150000, 200000],
77
+ priceLabel: defaultPriceLabel,
74
78
  },
75
79
  };
76
80
  const wrapper = mount(<PriceSelect {...testProps} />);
@@ -1,7 +1,8 @@
1
1
  import React from 'react';
2
2
  import { connect } from 'react-redux';
3
3
  import PropTypes from 'prop-types';
4
-
4
+ import Select from 'react-dropdown';
5
+ import 'react-dropdown/style.css';
5
6
  import { removeSavedSearchAsync, updateSavedSearchAsync } from '../../actions/search.actions';
6
7
  import propertySearch from '../property-search/property-search';
7
8
  import notify from '../../app/notify';
@@ -20,15 +21,23 @@ const SavedSearch = (props) => {
20
21
  frequencyLabel,
21
22
  visitSearchLabel,
22
23
  removeSearchLabel,
24
+ reactSelect,
23
25
  } = props;
24
26
 
27
+ const options = [
28
+ { value: 'I', label: 'Immediately' },
29
+ { value: 'W', label: 'Weekly' },
30
+ { value: 'M', label: 'Monthly' },
31
+ { value: 'N', label: 'Never' },
32
+ ];
33
+
25
34
  const visitSearch = (e) => {
26
35
  e.preventDefault();
27
36
  propertySearch(search);
28
37
  };
29
38
 
30
- const handleFrequencyChange = (e) => {
31
- const newSearch = { ...search, alert_frequency: e.target.value };
39
+ const handleFrequencyChange = (value) => {
40
+ const newSearch = { ...search, alert_frequency: value };
32
41
  updateSavedSearchAsync(newSearch);
33
42
  };
34
43
 
@@ -45,16 +54,23 @@ const SavedSearch = (props) => {
45
54
  {userLoggedIn && (
46
55
  <>
47
56
  {frequencyLabel}
48
- <select
49
- className={`saved-search__frequency ${selectClass}`}
50
- onChange={handleFrequencyChange}
51
- value={search.alert_frequency}
52
- >
53
- <option value="I">Immediately</option>
54
- <option value="W">Weekly</option>
55
- <option value="M">Monthly</option>
56
- <option value="N">Never</option>
57
- </select>
57
+ {reactSelect ? (
58
+ <Select
59
+ className={selectClass}
60
+ options={options}
61
+ value={search.alert_frequency}
62
+ isSearchable={false}
63
+ onChange={({ value }) => handleFrequencyChange(value)}
64
+ />
65
+ ) : (
66
+ <select
67
+ className={`saved-search__frequency ${selectClass}`}
68
+ onChange={(e) => handleFrequencyChange(e.target.value)}
69
+ value={search.alert_frequency}
70
+ >
71
+ {options.map((option) => <option value={option.value}>{option.label}</option>)}
72
+ </select>
73
+ )}
58
74
  </>
59
75
  )}
60
76
 
@@ -90,6 +106,7 @@ SavedSearch.propTypes = {
90
106
  frequencyLabel: PropTypes.string,
91
107
  visitSearchLabel: PropTypes.string,
92
108
  removeSearchLabel: PropTypes.string,
109
+ reactSelect: PropTypes.bool,
93
110
  };
94
111
 
95
112
  SavedSearch.defaultProps = {
@@ -100,6 +117,7 @@ SavedSearch.defaultProps = {
100
117
  frequencyLabel: 'Email me matching properties:',
101
118
  visitSearchLabel: 'Visit',
102
119
  removeSearchLabel: 'Remove',
120
+ reactSelect: false,
103
121
  };
104
122
 
105
123
  const mapStateToProps = (state) => ({