@bytebrand/fe-ui-core 4.2.141 → 4.2.143

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.
@@ -10,7 +10,8 @@ const expectedResultCDP = [
10
10
  max: 21666,
11
11
  value: 16100,
12
12
  unitName: 'modals:financing.euro',
13
- caption: 'modals:financing.firstInstallment'
13
+ caption: 'modals:financing.firstInstallment',
14
+ withRangeLimit: true
14
15
  },
15
16
  {
16
17
  name: 'annualMileage',
@@ -33,7 +34,8 @@ const expectedResultCheckout = [
33
34
  max: 21666,
34
35
  value: 16100,
35
36
  unitName: 'modals:financing.euro',
36
- caption: 'modals:financing.firstInstallment'
37
+ caption: 'modals:financing.firstInstallment',
38
+ withRangeLimit: true
37
39
  }
38
40
  ]
39
41
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytebrand/fe-ui-core",
3
- "version": "4.2.141",
3
+ "version": "4.2.143",
4
4
  "description": "UI components for the auto.de project",
5
5
  "main": "index.ts",
6
6
  "module": "dist/common.js",
@@ -82,7 +82,6 @@
82
82
  "@types/react": "^17.0.2",
83
83
  "@types/react-dom": "^17.0.2",
84
84
  "@types/react-lazyload": "^2.5.0",
85
- "@types/react-scroll": "^1.8.7",
86
85
  "@types/react-select": "^3.0.2",
87
86
  "@types/react-slick": "^0.23.4 ",
88
87
  "autoprefixer": "^9.6.0",
@@ -9,16 +9,18 @@ export interface IRangeGroup {
9
9
  onChange: (event: Event, value: number | number[]) => void;
10
10
  className?: string;
11
11
  isAlternative?: boolean;
12
+ maxFinancingFirstInstallment?: number;
12
13
  }
13
14
 
14
- const RangeGroup: FC<IRangeGroup> = ({ sliders, onChange, isAlternative }) => <>
15
- {sliders.length && sliders.map(({ name, min, max, step, caption, value, unitName }) => {
15
+ const RangeGroup: FC<IRangeGroup> = ({ sliders, onChange, isAlternative, maxFinancingFirstInstallment }) => <>
16
+ {sliders.length && sliders.map(({ name, min, max, step, caption, value, unitName, withRangeLimit }) => {
16
17
  const rangeProps = {
17
18
  min, max, step, value,
18
- isAlternative,
19
+ isAlternative, withRangeLimit,
19
20
  units: value ? ` ${unitName}` : '',
20
21
  onAfterChange: () => {},
21
- onChange: (_: Event, value: number | number[]) => onChange(name, value)
22
+ onChange: (_: Event, value: number | number[]) => onChange(name, value),
23
+ maxFinancingFirstInstallment
22
24
  };
23
25
  return (
24
26
  <Fragment key={name}>
@@ -36,7 +36,7 @@ const RequestedCarsSection = ({ t, getSupportedImageFormat, requestedCars, redir
36
36
  const renderItems = () => {
37
37
  if (requestedCars && requestedCars.length > 0) {
38
38
  const carsCard = requestedCars.map((car) => {
39
- if (car && car.car.mainData && car.car.metaData) {
39
+ if (car && car.car) {
40
40
  const { car: { mainData: { make, model, subModel } } } = car;
41
41
  const { car: { _id, metaData: { mainImageId } } } = car;
42
42
  const { buyingType, paybackPeriod, monthlyInstallment, request, currentSalesPrice } = car;
@@ -120,5 +120,4 @@ const RequestedCarsSection = ({ t, getSupportedImageFormat, requestedCars, redir
120
120
  </>
121
121
  );
122
122
  };
123
-
124
123
  export default RequestedCarsSection;
@@ -183,7 +183,7 @@ const VehiclePrice: React.FC<IVehiclePriceSectionProps> = (props: IVehiclePriceS
183
183
  }
184
184
  ] : '';
185
185
 
186
- const priceData:any = combineRefAlternative ? [
186
+ const priceData = combineRefAlternative ? [
187
187
  ...priceItemsGlobal,
188
188
  {
189
189
  isTotal: true,
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useState, useEffect } from 'react';
2
2
  import FormattedNumber from '../../FormattedNumber/FormattedNumber';
3
3
  import { Slider } from '@mui/material';
4
4
  import { isMobileOnly } from 'react-device-detect';
@@ -11,7 +11,9 @@ interface IRangeProps {
11
11
  step: number;
12
12
  units: string;
13
13
  value: number;
14
+ withRangeLimit?: boolean;
14
15
  onChange: (event: Event, value: number | number[], activeThumb?: number) => void;
16
+ maxFinancingFirstInstallment?: number;
15
17
  }
16
18
 
17
19
  const Theme = createTheme({
@@ -33,8 +35,28 @@ const Range: React.FC<IRangeProps> = ({
33
35
  step,
34
36
  value,
35
37
  units,
36
- onChange
38
+ withRangeLimit,
39
+ onChange,
40
+ maxFinancingFirstInstallment
37
41
  }: IRangeProps) => {
42
+ const [rangeValue, setRangeValue] = useState<number | number[]>(0);
43
+
44
+ useEffect(
45
+ () => {
46
+ setRangeValue(value);
47
+ },
48
+ [value]
49
+ )
50
+
51
+ const onChangeRange = (event: Event, value: number | number[]) => {
52
+ if (value > maxFinancingFirstInstallment && withRangeLimit) {
53
+ return;
54
+ } else {
55
+ setRangeValue(value);
56
+ onChange(event, value)
57
+ }
58
+ }
59
+
38
60
  const formttedValue = (value: number) => {
39
61
  return (
40
62
  <>
@@ -52,8 +74,8 @@ const Range: React.FC<IRangeProps> = ({
52
74
  disableSwap={isMobileOnly ? true : false}
53
75
  valueLabelDisplay='auto'
54
76
  valueLabelFormat={formttedValue}
55
- value={value}
56
- onChange={(event: Event, value: number | number[]) => onChange(event, value)}
77
+ value={rangeValue}
78
+ onChange={onChangeRange}
57
79
  sx={{
58
80
  color: '#005CCB'
59
81
  }}
@@ -3,7 +3,7 @@ import _get from 'lodash/get';
3
3
  import _startsWith from 'lodash/startsWith';
4
4
  import _debounce from 'lodash/debounce';
5
5
  import qs from 'qs';
6
- import { isArray, isEqual } from 'lodash';
6
+ import { isEqual } from 'lodash';
7
7
  import { checkRangeValuesOnEqual, getGroupValuesForQuery } from '../../../../framework/utils/CommonUtils';
8
8
  import { FilterBlockFactory } from '../../../SearchFilters/common/FilterBlock/FilterBlockFactory';
9
9
  import { FilterGroups, IFilters } from '../../../SearchFilters/FiltersFactory';
@@ -83,11 +83,11 @@ class FiltersContainer extends React.Component<IFiltersContainerProps, {}> {
83
83
 
84
84
  if (activeSort !== activeSorting) {
85
85
  // activeSorting = activeSort; // TODO make a function to change at searchStore
86
- setActiveSorting(activeSort as string);
86
+ setActiveSorting(activeSort);
87
87
  }
88
88
 
89
89
  clearSearchState(!activeSort);
90
- search(filters, Number(pageNumber) || 1, true);
90
+ search(filters, pageNumber || 1, true);
91
91
  }
92
92
 
93
93
  this.updateFinancingSearchConfig();
@@ -194,46 +194,41 @@ class FiltersContainer extends React.Component<IFiltersContainerProps, {}> {
194
194
  if (paramsKeys.length) {
195
195
  paramsKeys.forEach((filter: string) => {
196
196
  const paramValue = paramsFromQuery[filter];
197
+
197
198
  if (filter === MMS_GROUPS_KEY) { //tslint:disable-line
198
- if (isArray(paramValue)) {
199
- const stringParamValue: string[] = paramValue as string[];
200
- stringParamValue.map((group: any, index: number) => {
201
- if (index >= 1) {
202
- if (filter === MMS_GROUPS_KEY) addMmsGroup();
199
+ paramValue.map((group: any, index: number) => {
200
+ if (index >= 1) {
201
+ if (filter === MMS_GROUPS_KEY) addMmsGroup();
202
+ }
203
+
204
+ Object.keys(group).forEach((key: string) => {
205
+ changeFilterValue(key, group[key], index);
206
+ if (key === MANUFACTURER_KEY) {
207
+ getModelsInfoByMaker(group[key], true).then((response: any) => {
208
+ const models = response || [];
209
+ addModelsToFilters(models, index);
210
+ });
203
211
  }
204
-
205
- Object.keys(group).forEach((key: string) => {
206
- changeFilterValue(key, group[key], index);
207
- if (key === MANUFACTURER_KEY) {
208
- getModelsInfoByMaker(group[key], true).then((response: any) => {
209
- const models = response || [];
210
- addModelsToFilters(models, index);
211
- });
212
- }
213
- });
214
212
  });
215
- }
213
+ });
216
214
  } else if (filter === MM_GROUPS_EXCLUDE_KEY) {
217
- if (isArray(paramValue)) {
218
- const stringParamValue: string[] = paramValue as string[];
219
- stringParamValue.map((group: any, index: number) => {
220
- if (index >= 1) {
221
- addMmExcludeGroup();
222
- }
215
+ paramValue.map((group: any, index: number) => {
216
+ if (index >= 1) {
217
+ addMmExcludeGroup();
218
+ }
223
219
 
224
- Object.keys(group).forEach((key: string) => {
225
- changeFilterValue(key, group[key], index);
226
- });
220
+ Object.keys(group).forEach((key: string) => {
221
+ changeFilterValue(key, group[key], index);
227
222
  });
228
- }
223
+ });
229
224
  } else {
230
225
  // Defines whether filter is range or not.
231
226
  const isRange = rangeFiltersStrings.indexOf(filter) !== -1;
232
227
  const isObject = objectFiltersStrings.indexOf(filter) !== -1;
233
228
 
234
229
  // Transform url encoded param into range filter object.
235
- const valueToSend = isRange ? qs.parse(paramValue as string, { ignoreQueryPrefix: true }) : paramValue;
236
- changeFilterValue(filter, valueToSend as string);
230
+ const valueToSend = isRange ? qs.parse(paramValue, { ignoreQueryPrefix: true }) : paramValue;
231
+ changeFilterValue(filter, valueToSend);
237
232
  if (isRange || isObject) { changeRangeFilterControls(filter, valueToSend); }
238
233
  }
239
234
  });
@@ -388,7 +383,7 @@ class FiltersContainer extends React.Component<IFiltersContainerProps, {}> {
388
383
  const alternativeIDProps = {
389
384
  t,
390
385
  successTransition,
391
- autoDeId: paramsFromQuery.autoDeId as string,
386
+ autoDeId: paramsFromQuery.autoDeId,
392
387
  error: isErrorAlternativeField,
393
388
  onChange: onAlternativeRedirect
394
389
  };
@@ -399,7 +394,7 @@ class FiltersContainer extends React.Component<IFiltersContainerProps, {}> {
399
394
  <div className={styles.titleAlternative}>
400
395
  <div className={styles.titleWrap}>
401
396
  <h3 className={styles.titleTextAlternative}>auto.de-ID</h3>
402
- <Link to={`/alternative/detailed${locationSearch}`} className={styles.titleLink}>
397
+ <Link to={ `/alternative/detailed${locationSearch}` } className={styles.titleLink}>
403
398
  {this.props.t('SearchPage:detailedSearch')}
404
399
  </Link>
405
400
  </div>
@@ -410,7 +405,7 @@ class FiltersContainer extends React.Component<IFiltersContainerProps, {}> {
410
405
  <h3 className={styles.titleText}>{this.props.t('SearchPage:filterBy')}</h3>
411
406
  </div>
412
407
  {this.renderFilters()}
413
- <Link className={styles.linkToDetailed} to={`/${isAlternative ? 'alternative' : 'search'}/detailed${locationSearch}`}>{t('SearchPage:detailedSearch')}</Link>
408
+ <Link className={styles.linkToDetailed} to={ `/${isAlternative ? 'alternative' : 'search'}/detailed${locationSearch}` }>{t('SearchPage:detailedSearch')}</Link>
414
409
  </div>
415
410
  );
416
411
  }
@@ -425,8 +420,8 @@ const removeUnknownOptions = (options: any) => {
425
420
 
426
421
  export default React.memo(FiltersContainer, (props, nextProps) => {
427
422
  const { t, location, filters, aggregation } = props;
428
- return t === nextProps.t &&
429
- location.search === nextProps.location.search &&
430
- isEqual(aggregation, nextProps.aggregation) &&
431
- isEqual(filters.MMS_GROUPS, nextProps.filters.MMS_GROUPS);
423
+ return t === nextProps.t &&
424
+ location.search === nextProps.location.search &&
425
+ isEqual(aggregation, nextProps.aggregation) &&
426
+ isEqual(filters.MMS_GROUPS, nextProps.filters.MMS_GROUPS);
432
427
  });
@@ -59,7 +59,7 @@ export const offers = [
59
59
  name: 'financing',
60
60
  periods: [12, 24, 36, 48, 60, 72, 84, 96], // tslint:disable-line
61
61
  sliders: [
62
- { name: 'firstInstallment', min: 0, step: 100, unit: 'euro' }, // tslint:disable-line
62
+ { name: 'firstInstallment', min: 0, step: 100, unit: 'euro', withRangeLimit: true }, // tslint:disable-line
63
63
  { name: 'annualMileage', min: 5000, max: 60000, step: 5000, unit: 'km' } // tslint:disable-line
64
64
  ]
65
65
  },
@@ -1,373 +0,0 @@
1
- import React from 'react';
2
- import { render } from '@testing-library/react';
3
- import FavoriteSection from '../../../../../source/components/UserDashboardPage/sections/FavoriteSection/FavoriteSection';
4
- import { observable } from 'mobx';
5
- import qs from 'qs';
6
-
7
- const t = (phrase: string | string[], options: object) => {
8
- if (options) {
9
- const value = Object.values(options).map((option) => option);
10
- return `${phrase} ${value}`;
11
- }
12
- return phrase;
13
- };
14
-
15
- interface IWithRouter {
16
- children?: JSX.Element;
17
- link?: string;
18
- }
19
- const LinkRouter = ({ children, link }: IWithRouter) => {
20
- return (
21
- <a href={link} >
22
- {children}
23
- </a>
24
- );
25
- };
26
- const favoriteCars = [
27
- {
28
- parkedFor: 'N/A',
29
- id: 'c4c9141a-5e4c-4d75-9d0f-c2f1b8f31241',
30
- mainImageId: ' ',
31
- imagesCount: ' ',
32
- hasInteriorExteriorPhoto: ' ',
33
- title: {
34
- make: 'Volkswagen',
35
- model: 'Polo',
36
- subModel: '1.0 TSI Style',
37
- option: '',
38
- to: ''
39
- },
40
- showNewLabel: false,
41
- info: {
42
- regDate: ' ',
43
- mileage: 0,
44
- gearbox: 'selector_gearbox_manualShift',
45
- numberOfPreviousOwners: 0,
46
- usageType: 'selector_unknown',
47
- condition: 'selector_condition_new',
48
- damaged: false,
49
- driveType: 'selector_driveType_frontWheelDrive'
50
- },
51
- location: {
52
- city: 't',
53
- street: 't',
54
- zipCode: 12345
55
- },
56
- consumption: {
57
- fuel: 'selector_fuel_petrol',
58
- consumptionCombined: 5.2,
59
- consumptionPowerCombined: ' '
60
- },
61
- engineData: {
62
- powerKW: 70,
63
- powerPS: 70
64
- },
65
- environmentEmissions: {
66
- co2: 118
67
- },
68
- offer: {
69
- offerId: ' ',
70
- autoDeOfferId: ' ',
71
- offerFrom: 'selector_unknown',
72
- offerSource: ' ',
73
- offerAvailability: 'selector_unknown',
74
- offerDescription: '',
75
- deliveryDate: 1698969600,
76
- deliveryPeriod: 'selector_deliveryPeriod_days270',
77
- dealerHomepage: ' ',
78
- closedDomain: ' ',
79
- export: ' ',
80
- commercial: ' ',
81
- warranty: false,
82
- rentingPossible: ' ',
83
- newHuAu: true,
84
- internalNumber: '118_92436S/3243',
85
- availabilityMode: 'selector_availabilityMode_fromDate',
86
- mobileDeOfferId: ' ',
87
- as24DeOfferId: ' ',
88
- elnOfferId: 'bd11b00a8d643e16b69c308360e5236b'
89
- },
90
- price: {
91
- offerIndex: 0,
92
- isStrike: false,
93
- comparable: {
94
- annualMileage: 10000,
95
- firstInstallment: ' ',
96
- paybackPeriod: 48,
97
- withAddServices: ' ',
98
- withTransportation: ' '
99
- },
100
- checkout: {
101
- warrantyRates: [
102
- 0,
103
- 0,
104
- 0
105
- ],
106
- warrantyPackage: 0,
107
- warrantyPeriod: 1,
108
- admissionService: 0,
109
- licensePlateFee: 0
110
- },
111
- common: {
112
- firstRegistration: ' ',
113
- mileage: 0,
114
- currentSalesPrice: 30666,
115
- highestPrice: 34232,
116
- isStrikeShown: true,
117
- isHybridOrElectric: false
118
- },
119
- financing: {
120
- grossLoanAmountWithoutFinalInstallment: 0,
121
- purchasePrice: 0,
122
- hasPaymentProtection: false,
123
- paymentProtectionInsurance: 0,
124
- monthlyInstallment: 142,
125
- oldMonthlyInstallment: 180,
126
- annualMileage: 25000,
127
- firstInstallment: 11900,
128
- withTransportation: false,
129
- withAddServices: true,
130
- addServicesCostRaw: 799,
131
- addServicesCost: 799,
132
- transportationCostRaw: 499,
133
- transportationCost: 499,
134
- withFinalInstallment: true,
135
- paybackPeriod: 72,
136
- rawPaybackPeriod: 72,
137
- nominalInterestRate: 3.92,
138
- annualPercentageRate: 6.99,
139
- financialInstitution: 'Santander Consumer Bank AG, Santander-Platz 1, 41061 Mönchengladbach',
140
- periodInterestRate: [
141
- {
142
- duration: 12,
143
- recommended: 70,
144
- current: 88,
145
- inEUR: ' ',
146
- value: 18766,
147
- highestValue: 30124.16
148
- },
149
- {
150
- duration: 24,
151
- recommended: 66,
152
- current: 77,
153
- inEUR: ' ',
154
- value: 18766,
155
- highestValue: 26358.64
156
- },
157
- {
158
- duration: 36,
159
- recommended: 60,
160
- current: 66,
161
- inEUR: ' ',
162
- value: 18766,
163
- highestValue: 22593.12
164
- },
165
- {
166
- duration: 48,
167
- recommended: 50,
168
- current: 60,
169
- inEUR: ' ',
170
- value: 17920.2,
171
- highestValue: 20539.2
172
- },
173
- {
174
- duration: 60,
175
- recommended: 40,
176
- current: 50,
177
- inEUR: ' ',
178
- value: 14933.5,
179
- highestValue: 17116
180
- },
181
- {
182
- duration: 72,
183
- recommended: 30,
184
- current: 40,
185
- inEUR: ' ',
186
- value: 11946.8,
187
- highestValue: 13692.8
188
- },
189
- {
190
- duration: 84,
191
- recommended: 20,
192
- current: 30,
193
- inEUR: ' ',
194
- value: 8960.1,
195
- highestValue: 10269.6
196
- },
197
- {
198
- duration: 96,
199
- recommended: 10,
200
- current: 20,
201
- inEUR: ' ',
202
- value: 5973.4,
203
- highestValue: 6846.4
204
- }
205
- ],
206
- closingCosts: 0,
207
- offersFinancing: true,
208
- rateMode: 'dealer'
209
- },
210
- buy: {
211
- withTransportation: false,
212
- withAddServices: true,
213
- withoutAddServices: true,
214
- transportationCost: 499,
215
- addServicesCost: 799,
216
- currentSalesPriceExtra: 30666,
217
- highestPriceExtra: 34232,
218
- extraCost: 0
219
- },
220
- leasing: {
221
- RWG: 7,
222
- maxAge: 24,
223
- maxMileage: 1000000,
224
- isActive: true,
225
- withTransportation: false,
226
- withAddServices: false,
227
- addServicesCostRaw: 799,
228
- transportationCostRaw: 499,
229
- addServicesCost: 799,
230
- transportationCost: 499,
231
- monthlyAddServices: 0,
232
- monthlyTransportation: 0,
233
- monthlyInstallment: 441,
234
- oldMonthlyInstallment: 526,
235
- firstInstallment: 20,
236
- firstInstallmentMax: 2000,
237
- annualMileage: 10000,
238
- paybackPeriodRange: [
239
- 30,
240
- 36,
241
- 42,
242
- 48,
243
- 54,
244
- 60
245
- ],
246
- paybackPeriod: 48,
247
- santanderPayBackMin: 250,
248
- nominalInterestRate: 6.78,
249
- financialInstitution: 'Santander Consumer Bank AG, Santander-Platz 1, 41061 Mönchengladbach',
250
- grossLoanAmountWithoutFinalInstallment: 0,
251
- hasPaymentProtection: false,
252
- paymentProtectionInsurance: 1100,
253
- leasingMode: 'default',
254
- currentSalesPrice: 30666
255
- },
256
- abo: {
257
- paybackPeriod: 6,
258
- annualMileage: 10000
259
- },
260
- firstRegistration: ' ',
261
- mileage: 0,
262
- currentSalesPrice: 30666,
263
- highestPrice: 34232,
264
- isStrikeShown: true,
265
- carId: 'c4c9141a-5e4c-4d75-9d0f-c2f1b8f31241',
266
- offerName: 'financing',
267
- vehicleId: 'c4c9141a-5e4c-4d75-9d0f-c2f1b8f31241',
268
- vatRate: 19,
269
- mlCurrentSalesPricePredicted: ' ',
270
- historyPriceDifference: 3566,
271
- historyPriceDifferencePerCent: 10,
272
- margin: 30666,
273
- toRound: false
274
- },
275
- internalNumber: '118_92436S/3243',
276
- vin: '',
277
- isOnline: true,
278
- firstIsOnline: 1675668230,
279
- url: '/search/vehicle/c4c9141a-5e4c-4d75-9d0f-c2f1b8f31241',
280
- relativeScore: 0,
281
- baseUrl: '',
282
- offerSource: '',
283
- crawledAt: '',
284
- ownerName: 'Guardian of Auto.de',
285
- modificationDate: '18.05.2023',
286
- seoText: ''
287
- }
288
- ];
289
- const favoritesIDs = [
290
- 'b359d74f-bb96-4fca-9539-579792fe7c36'
291
- ];
292
- const getFavoriteCarsMock = jest.fn();
293
-
294
- const aggStatsData = observable({
295
- 'b359d74f-bb96-4fca-9539-579792fe7c36': {
296
- carId: 'b359d74f-bb96-4fca-9539-579792fe7c36',
297
- totalFavCount: 3,
298
- totalCarImpCount: 7399,
299
- slidingCarImpCount: 10
300
- },
301
- get() {
302
- return {
303
- 'b359d74f-bb96-4fca-9539-579792fe7c36': {
304
- carId: 'b359d74f-bb96-4fca-9539-579792fe7c36',
305
- totalFavCount: 3,
306
- totalCarImpCount: 7399,
307
- slidingCarImpCount: 10
308
- }
309
- };
310
- }
311
- });
312
- const mockProps = {
313
- t,
314
- LinkRouter,
315
- PLACEHOLDER_IMAGE_SMALL_URL: 'PLACEHOLDER_IMAGE_SMALL_URL',
316
- pageNumberKey: 'pageNumber',
317
- favoriteStore: { favoriteCars, favoritesIDs, showList: true, getFavouriteCars: getFavoriteCarsMock, getFavoritesIDs: jest.fn() },
318
- appStore: {},
319
- carsStore: { aggStatsData, getAggStatsData: jest.fn() },
320
- imagesStore: { getSupportedImageFormat: jest.fn() }
321
- };
322
-
323
- describe('FavoriteSection', () => {
324
- test('calls getFavouriteCars when component is mounted', () => {
325
- render(<FavoriteSection {...mockProps} />);
326
- expect(mockProps.favoriteStore.getFavouriteCars).toHaveBeenCalledTimes(1);
327
- });
328
- test('calls getFavoritesIDs and getAggStatsData when favoritesIDs is empty', async () => {
329
- const favoritesIDs: string[] = [];
330
- const props = {
331
- t,
332
- LinkRouter,
333
- PLACEHOLDER_IMAGE_SMALL_URL: 'PLACEHOLDER_IMAGE_SMALL_URL',
334
- pageNumberKey: 4,
335
- favoriteStore: { favoriteCars, favoritesIDs, showList: true, getFavouriteCars: getFavoriteCarsMock, getFavoritesIDs: jest.fn() },
336
- appStore: {},
337
- carsStore: { aggStatsData, getAggStatsData: jest.fn() },
338
- imagesStore: { getSupportedImageFormat: jest.fn() }
339
- };
340
- props.favoriteStore.getFavoritesIDs.mockResolvedValueOnce(['b359d74f-bb96-4fca-9539-579792fe7c36']);
341
-
342
- render(<FavoriteSection {...props} />);
343
- expect(props.favoriteStore.getFavoritesIDs).toHaveBeenCalledTimes(1);
344
- expect(mockProps.carsStore.getAggStatsData).toHaveBeenCalledTimes(1);
345
- expect(mockProps.carsStore.getAggStatsData).toHaveBeenCalledWith(['b359d74f-bb96-4fca-9539-579792fe7c36']);
346
- });
347
- test('does not call getFavoritesIDs when favoritesIDs is not empty', async () => {
348
- render(<FavoriteSection {...mockProps} />);
349
- expect(mockProps.favoriteStore.getFavoritesIDs).not.toHaveBeenCalled();
350
- });
351
- test('returns the correct current page', () => {
352
- const mockProps = {
353
- t: jest.fn(),
354
- LinkRouter: jest.fn(),
355
- PLACEHOLDER_IMAGE_SMALL_URL: 'PLACEHOLDER_IMAGE_SMALL_URL',
356
- pageNumberKey: 'pageNumber',
357
- favoriteStore: { favoriteCars, favoritesIDs, showList: true, getFavouriteCars: getFavoriteCarsMock, getFavoritesIDs: jest.fn() },
358
- appStore: {},
359
- carsStore: {
360
- aggStatsData: {},
361
- getAggStatsData: jest.fn()
362
- },
363
- imagesStore: {
364
- getSupportedImageFormat: jest.fn()
365
- }
366
- };
367
- jest.spyOn(qs, 'parse').mockReturnValueOnce({ pageNumber: '3' });
368
- const favoriteSectionInstance = new FavoriteSection(mockProps);
369
- const currentPage = favoriteSectionInstance.getCurrentPage();
370
- expect(currentPage).toBe(3);
371
- });
372
-
373
- });
@@ -1,22 +0,0 @@
1
- @import '../../../../theme/mixins.styl'
2
-
3
- .wrapFavorites
4
- border: none !important;
5
-
6
- .favoriteBtn
7
- height: 30px;
8
- font-size: 13px;
9
- text-transform: none !important
10
- font-weight: 400 !important;
11
-
12
- +media-tablet-landscape-up()
13
- height: 38px;
14
- font-size: 14px;
15
-
16
- .btnCarToFavorites
17
- height: 38px;
18
- min-width: 160px !important;
19
- font-size: 13px !important;
20
- +media-tablet-landscape-up()
21
- min-width: 224px !important;
22
- font-size: 14px !important;
@@ -1,112 +0,0 @@
1
- import * as React from 'react';
2
- import { toJS } from 'mobx';
3
- import qs from 'qs';
4
- import _get from 'lodash/get';
5
- import DashboardSection from '../../../containers/DasboardSection/DashboardSection';
6
- import styles from './FavoriteSection.styl';
7
- import { isMobileOnly } from 'react-device-detect';
8
- import { VehicleSmallCard, Button } from '../../../../../common';
9
-
10
- interface ITFunction {
11
- <T = string>(key: string, options?: object): T;
12
- <T = string>(keys: string[], options?: object): T;
13
- }
14
-
15
- interface IFavoriteSectionProps {
16
- favoriteStore: any;
17
- carsStore: any;
18
- appStore: any;
19
- imagesStore: any;
20
- t?: ITFunction;
21
- PLACEHOLDER_IMAGE_SMALL_URL: string;
22
- pageNumberKey: any;
23
- LinkRouter: any;
24
- }
25
-
26
- class FavoriteSection extends React.Component<IFavoriteSectionProps, {}> {
27
- componentDidMount() {
28
- const currentPage = this.getCurrentPage();
29
- this.getCarsForPage(currentPage);
30
- }
31
-
32
- getCarsForPage = (page: number): void => {
33
- const { favoriteStore, carsStore } = this.props;
34
- if (page) {
35
- favoriteStore.getFavouriteCars(page);
36
-
37
- if (!favoriteStore.favoritesIDs.length) {
38
- favoriteStore.getFavoritesIDs().then((favoritesIDs: any) => {
39
- carsStore.getAggStatsData(favoritesIDs);
40
- });
41
- } else carsStore.getAggStatsData(favoriteStore.favoritesIDs);
42
- }
43
- };
44
-
45
- getCurrentPage = () => {
46
- const { pageNumberKey } = this.props;
47
- const pageFromQuery = qs.parse(location.search, { ignoreQueryPrefix: true })[pageNumberKey];
48
- if (!pageFromQuery) return 1;
49
- const page = Number(pageFromQuery);
50
- return page > 0 ? page : 1;
51
- };
52
-
53
- onCarSelectFavorite = (url: string) => window.open(`/search/vehicle/${url}`, '_blank');
54
-
55
- private renderFavorites = () => {
56
- const { t, appStore: { language }, carsStore, imagesStore, favoriteStore, PLACEHOLDER_IMAGE_SMALL_URL, LinkRouter } = this.props;
57
- const favoriteCars = toJS(favoriteStore.favoriteCars);
58
- return favoriteCars.map((car: any, key: any) => {
59
- const carId = _get(car, 'id');
60
- const mainImageId = _get(car, 'mainImageId');
61
- const src = !!mainImageId && !!car.imagesCount
62
- ? imagesStore.getSupportedImageFormat(carId, mainImageId, 'small')
63
- : PLACEHOLDER_IMAGE_SMALL_URL;
64
- const vehicleProps = {
65
- ...car,
66
- t,
67
- language,
68
- src,
69
- linkTag: LinkRouter,
70
- showSlider: false,
71
- showOfferBtn: true,
72
- showFavoriteStar: false,
73
- priceSubMtl: !isMobileOnly ? t('vehicleProps:value.priceSub') : null,
74
- stats: {
75
- imagesCount: _get(car, 'imagesCount'),
76
- statsData: carsStore.aggStatsData.get(car.id)
77
- },
78
- i18nPrefixForPriceRating: 'common:',
79
- className: styles.wrapFavorites,
80
- classButton: styles.favoriteBtn,
81
- onDetailsClick: this.onCarSelectFavorite,
82
- vehicleComponentName: 'favorite'
83
- };
84
-
85
- return (key === 0) && <VehicleSmallCard key={`favoriteCar${carId}`} {...vehicleProps} />;
86
- });
87
- };
88
-
89
- render(): JSX.Element {
90
- const { t, favoriteStore, LinkRouter } = this.props;
91
-
92
- return (
93
- <>
94
- {favoriteStore.showList ? (
95
- <DashboardSection
96
- title={t('DashboardPage:favoriteTitle')}
97
- link={
98
- <LinkRouter to='/favourites'>
99
- <Button className={styles.btnCarToFavorites} variant='outlined'>
100
- {t('vehicleProps:title.toFavoritesCar')}
101
- </Button>
102
- </LinkRouter>}
103
- >
104
- {this.renderFavorites()}
105
- </DashboardSection>
106
- ) : false}
107
- </>
108
- );
109
- }
110
- }
111
-
112
- export default FavoriteSection;