Repository: kiwicom/mobile
Pull request #1208 (2018-10-10 08:55:57)

Refactor: Price component

RobinCsl: @tbergq  🚀 

Patch:
diff --git a/app/MMB/src/scenes/tripServices/insurance/insuranceOverviewScene/orderSummary/InsuranceRow.js b/app/MMB/src/scenes/tripServices/insurance/insuranceOverviewScene/orderSummary/InsuranceRow.js
index 03ccc21e..4228d5e5 100644
--- a/app/MMB/src/scenes/tripServices/insurance/insuranceOverviewScene/orderSummary/InsuranceRow.js
+++ b/app/MMB/src/scenes/tripServices/insurance/insuranceOverviewScene/orderSummary/InsuranceRow.js
@@ -59,8 +59,8 @@ export function InsuranceRow(props: Props) {
     return null;
   }
 
-  let insuranceCurrency = '';
-  const insuranceAmount = changes
+  let currency = '';
+  const amount = changes
     .filter(
       change => change.to === props.insuranceType && change.from !== change.to,
     )
@@ -78,13 +78,13 @@ export function InsuranceRow(props: Props) {
         _ => _.price,
       );
       if (priceFrom && priceTo && priceFrom.currency === priceTo.currency) {
-        insuranceCurrency = priceFrom.currency;
+        currency = priceFrom.currency;
         return priceTo.amount - priceFrom.amount;
       } else if (priceFrom && priceTo === null) {
-        insuranceCurrency = priceFrom.currency;
+        currency = priceFrom.currency;
         return -priceFrom.amount;
       } else if (priceFrom === null && priceTo) {
-        insuranceCurrency = priceTo.currency;
+        currency = priceTo.currency;
         return priceTo.amount;
       }
       return 0;
@@ -101,11 +101,7 @@ export function InsuranceRow(props: Props) {
           />
         </Text>
       </View>
-      <Price
-        amount={insuranceAmount}
-        currency={insuranceCurrency}
-        style={styleSheet.price}
-      />
+      <Price price={{ amount, currency }} style={styleSheet.price} />
     </View>
   );
 }
diff --git a/app/MMB/src/scenes/tripServices/insurance/insuranceOverviewScene/orderSummary/OrderSummary.js b/app/MMB/src/scenes/tripServices/insurance/insuranceOverviewScene/orderSummary/OrderSummary.js
index cb463837..047a4cbf 100644
--- a/app/MMB/src/scenes/tripServices/insurance/insuranceOverviewScene/orderSummary/OrderSummary.js
+++ b/app/MMB/src/scenes/tripServices/insurance/insuranceOverviewScene/orderSummary/OrderSummary.js
@@ -16,11 +16,15 @@ import { defaultTokens } from '@kiwicom/mobile-orbit';
 import { withInsuranceContext } from '../InsuranceOverviewSceneContext';
 import InsuranceRow from './InsuranceRow';
 
-type Props = {|
+type PriceType = {|
   +amount: number,
   +currency: string,
 |};
 
+type Props = {|
+  +price: PriceType,
+|};
+
 type State = {|
   isExpanded: boolean,
 |};
@@ -62,11 +66,7 @@ class OrderSummary extends React.Component<Props, State> {
                 <Translation id="mmb.trip_services.order.total" />
               </Text>
             </View>
-            <Price
-              amount={this.props.amount}
-              currency={this.props.currency}
-              style={styleSheet.price}
-            />
+            <Price price={this.props.price} style={styleSheet.price} />
           </View>
         </View>
       </TouchableWithoutFeedback>
diff --git a/app/MMB/src/scenes/tripServices/insurance/insuranceSelectionScene/variantButtons/Price.js b/app/MMB/src/scenes/tripServices/insurance/insuranceSelectionScene/variantButtons/Price.js
index cd672404..95a8b86a 100644
--- a/app/MMB/src/scenes/tripServices/insurance/insuranceSelectionScene/variantButtons/Price.js
+++ b/app/MMB/src/scenes/tripServices/insurance/insuranceSelectionScene/variantButtons/Price.js
@@ -31,13 +31,7 @@ const Price = (props: Props) => {
       </Text>
     );
   } else if (price && price.amount != null && price.currency != null) {
-    return (
-      <OriginalPrice
-        amount={price.amount}
-        currency={price.currency}
-        style={style}
-      />
-    );
+    return <OriginalPrice price={price} style={style} />;
   }
   return (
     <Text style={style}>
diff --git a/app/hotels/src/allHotels/HotelTitle.js b/app/hotels/src/allHotels/HotelTitle.js
index 014b5cc3..73df0e26 100644
--- a/app/hotels/src/allHotels/HotelTitle.js
+++ b/app/hotels/src/allHotels/HotelTitle.js
@@ -19,14 +19,7 @@ function HotelTitle({ data }: Props) {
   const hotelName = idx(data, _ => _.name);
   const hotelStars = idx(data, _ => _.rating.stars);
 
-  const price = {
-    ...{
-      // default null object
-      amount: null,
-      currency: null,
-    },
-    ...data.price,
-  };
+  const price = data.price || null;
 
   return (
     <React.Fragment>
@@ -40,11 +33,7 @@ function HotelTitle({ data }: Props) {
       <View style={style.distance}>
         <Distance hotel={data} />
       </View>
-      <Price
-        amount={price.amount}
-        currency={price.currency}
-        style={style.price}
-      />
+      <Price price={price} style={style.price} />
     </React.Fragment>
   );
 }
diff --git a/app/hotels/src/map/allHotels/PriceMarker.js b/app/hotels/src/map/allHotels/PriceMarker.js
index 9541647a..5dd06963 100644
--- a/app/hotels/src/map/allHotels/PriceMarker.js
+++ b/app/hotels/src/map/allHotels/PriceMarker.js
@@ -27,15 +27,7 @@ const PriceMarker = (props: Props) => {
   return (
     <View>
       <View style={bubbleStyles}>
-        {price &&
-          price.amount &&
-          price.currency && (
-            <Price
-              amount={price.amount}
-              currency={price.currency}
-              style={priceStyles}
-            />
-          )}
+        {price != null && <Price price={price} style={priceStyles} />}
       </View>
       <View style={[styles.arrowBorder]} />
       <View style={[styles.arrow, isSelected && selectedStyles.arrow]} />
diff --git a/app/hotels/src/map/hotelDetailPreview/HotelDetailPreview.js b/app/hotels/src/map/hotelDetailPreview/HotelDetailPreview.js
index f8d00f9a..1878dc1d 100644
--- a/app/hotels/src/map/hotelDetailPreview/HotelDetailPreview.js
+++ b/app/hotels/src/map/hotelDetailPreview/HotelDetailPreview.js
@@ -53,13 +53,7 @@ export default function HotelDetailPreview(props: Props) {
             <View style={styles.row}>
               <View>
                 {stars > 0 && <Stars rating={stars} style={styles.stars} />}
-                {price &&
-                  price.currency != null &&
-                  price.amount != null && (
-                    <Text style={styles.price}>
-                      <Price currency={price.currency} amount={price.amount} />
-                    </Text>
-                  )}
+                {price != null && <Price price={price} style={styles.price} />}
               </View>
               <HotelReviewScore score={score} />
             </View>
diff --git a/app/hotels/src/singleHotel/RoomSummary.js b/app/hotels/src/singleHotel/RoomSummary.js
index edc0c161..339067c1 100644
--- a/app/hotels/src/singleHotel/RoomSummary.js
+++ b/app/hotels/src/singleHotel/RoomSummary.js
@@ -35,11 +35,7 @@ export default function RoomSummary(props: Props) {
               numberOfRooms: props.roomCount,
             }}
           />
-          <Price
-            amount={props.price.amount}
-            currency={props.price.currency}
-            style={styles.currency}
-          />
+          <Price price={props.price} style={styles.currency} />
         </View>
       </View>
     </View>
diff --git a/app/hotels/src/singleHotel/roomList/RoomRow.js b/app/hotels/src/singleHotel/roomList/RoomRow.js
index 93bcc002..450ad2a5 100644
--- a/app/hotels/src/singleHotel/roomList/RoomRow.js
+++ b/app/hotels/src/singleHotel/roomList/RoomRow.js
@@ -78,7 +78,7 @@ export class RoomRow extends React.Component<Props> {
       availableRoom,
       _ => _.room.photos.edges[0].node.lowResUrl,
     );
-    const price = idx(availableRoom, _ => _.minimalPrice.amount) || null;
+    const amount = idx(availableRoom, _ => _.minimalPrice.amount) || null;
     const currency = idx(availableRoom, _ => _.minimalPrice.currency) || null;
     const selectableCount =
       idx(availableRoom, _ => _.incrementalPrice.length) || 0;
@@ -103,8 +103,7 @@ export class RoomRow extends React.Component<Props> {
             <BeddingInfo room={room} />
           </View>
           <RoomPicker
-            price={price}
-            currency={currency}
+            price={{ amount, currency }}
             selectedCount={selectedCount}
             selectableCount={selectableCount}
             increment={this.select}
diff --git a/app/hotels/src/singleHotel/roomPicker/IncrementDecrement.js b/app/hotels/src/singleHotel/roomPicker/IncrementDecrement.js
index ccd27c3d..6d181358 100644
--- a/app/hotels/src/singleHotel/roomPicker/IncrementDecrement.js
+++ b/app/hotels/src/singleHotel/roomPicker/IncrementDecrement.js
@@ -12,9 +12,13 @@ import {
 import { Translation } from '@kiwicom/mobile-localization';
 import { defaultTokens } from '@kiwicom/mobile-orbit';
 
-type Props = {|
-  +price: number | null,
+type PriceType = {|
+  +amount: number | null,
   +currency: string | null,
+|};
+
+type Props = {|
+  +price: PriceType | null,
   +selectedCount: number,
   +selectableCount: number,
   +increment: () => void,
@@ -25,7 +29,6 @@ export default function IncrementDecrement({
   decrement,
   selectedCount,
   price,
-  currency,
   increment,
   selectableCount,
 }: Props) {
@@ -43,7 +46,7 @@ export default function IncrementDecrement({
               {...androidProps}
             />
           </Text>
-          <Price amount={price} currency={currency} style={styles.price} />
+          <Price price={price} style={styles.price} />
         </View>
 
         <View style={styles.countContainer}>
diff --git a/app/hotels/src/singleHotel/roomPicker/RoomPicker.js b/app/hotels/src/singleHotel/roomPicker/RoomPicker.js
index c782813b..93e2e84e 100644
--- a/app/hotels/src/singleHotel/roomPicker/RoomPicker.js
+++ b/app/hotels/src/singleHotel/roomPicker/RoomPicker.js
@@ -5,9 +5,13 @@ import * as React from 'react';
 import SelectButton from './SelectButton';
 import IncrementDecrement from './IncrementDecrement';
 
-type Props = {|
-  +price: number | null,
+type Price = {|
+  +amount: number | null,
   +currency: string | null,
+|};
+
+type Props = {|
+  +price: Price | null,
   +selectedCount: number,
   +selectableCount: number,
   +increment: () => void,
@@ -15,18 +19,12 @@ type Props = {|
 |};
 
 export default function RoomPicker(props: Props) {
-  if (props.price == null && props.currency == null) {
+  if (props.price == null) {
     return null;
   }
 
   if (props.selectedCount === 0) {
-    return (
-      <SelectButton
-        price={props.price}
-        currency={props.currency}
-        increment={props.increment}
-      />
-    );
+    return <SelectButton price={props.price} increment={props.increment} />;
   }
 
   return <IncrementDecrement {...props} />;
diff --git a/app/hotels/src/singleHotel/roomPicker/SelectButton.js b/app/hotels/src/singleHotel/roomPicker/SelectButton.js
index 8b36b0f0..4955733c 100644
--- a/app/hotels/src/singleHotel/roomPicker/SelectButton.js
+++ b/app/hotels/src/singleHotel/roomPicker/SelectButton.js
@@ -11,13 +11,17 @@ import {
 import { Translation } from '@kiwicom/mobile-localization';
 import { defaultTokens } from '@kiwicom/mobile-orbit';
 
-type Props = {|
-  +price: number | null,
+type PriceType = {|
+  +amount: number | null,
   +currency: string | null,
+|};
+
+type Props = {|
+  +price: PriceType,
   +increment: () => void,
 |};
 
-export default function SelectButton({ increment, price, currency }: Props) {
+export default function SelectButton({ increment, price }: Props) {
   return (
     <Touchable style={styles.container} onPress={increment} delayPressIn={40}>
       <View style={styles.row}>
@@ -26,7 +30,7 @@ export default function SelectButton({ increment, price, currency }: Props) {
           text={<Translation id="single_hotel.room_picker.select" />}
         />
         <View style={styles.row} />
-        <Price amount={price} currency={currency} style={styles.price} />
+        <Price price={price} style={styles.price} />
       </View>
     </Touchable>
   );
diff --git a/packages/shared/src/Price.js b/packages/shared/src/Price.js
index 5c994875..90445ed1 100644
--- a/packages/shared/src/Price.js
+++ b/packages/shared/src/Price.js
@@ -10,9 +10,13 @@ import CancellablePromise, {
   type CancellablePromiseType,
 } from './CancellablePromise';
 
+type PriceType = {
+  +amount?: ?number,
+  +currency?: ?string,
+};
+
 type Props = {|
-  +amount: number | null,
-  +currency: string | null,
+  +price?: ?PriceType,
   +style?: StylePropType,
 |};
 
@@ -36,7 +40,10 @@ export default class Price extends React.Component<Props, State> {
   };
 
   componentDidUpdate = (prevProps: Props) => {
-    if (prevProps.amount !== this.props.amount) {
+    if (
+      (prevProps.price && prevProps.price.amount) !==
+      (this.props.price && this.props.price.amount)
+    ) {
       this.formatCurrency();
     }
   };
@@ -48,10 +55,14 @@ export default class Price extends React.Component<Props, State> {
   };
 
   formatCurrency = async () => {
-    if (this.props.amount != null && this.props.currency != null) {
+    if (
+      this.props.price != null &&
+      this.props.price.amount != null &&
+      this.props.price.currency != null
+    ) {
       try {
         this.cancellablePromise = CancellablePromise(
-          CurrencyFormatter(this.props.amount, this.props.currency),
+          CurrencyFormatter(this.props.price.amount, this.props.price.currency),
         );
         const formattedCurrency = await this.cancellablePromise.promise;
         this.setState({ formattedCurrency });


Test patch:
diff --git a/app/MMB/src/scenes/tripServices/insurance/insuranceOverviewScene/orderSummary/__tests__/InsuranceRow.test.js b/app/MMB/src/scenes/tripServices/insurance/insuranceOverviewScene/orderSummary/__tests__/InsuranceRow.test.js
index 6dc1739d..6dd75ea4 100644
--- a/app/MMB/src/scenes/tripServices/insurance/insuranceOverviewScene/orderSummary/__tests__/InsuranceRow.test.js
+++ b/app/MMB/src/scenes/tripServices/insurance/insuranceOverviewScene/orderSummary/__tests__/InsuranceRow.test.js
@@ -51,8 +51,8 @@ describe('InsuranceRow', () => {
       />,
     );
     const price = travelPlus.root.findByType(Price);
-    expect(price.props.amount).toBe(48.96);
-    expect(price.props.currency).toBe('EUR');
+    expect(price.props.price.amount).toBe(48.96);
+    expect(price.props.price.currency).toBe('EUR');
 
     const translations = travelPlus.root.findAllByType(Translation);
     expect(translations[0].props.values.quantity).toBe(2);
@@ -80,8 +80,8 @@ describe('InsuranceRow', () => {
       />,
     );
     const price = travelBasic.root.findByType(Price);
-    expect(price.props.amount).toBe(-18.32);
-    expect(price.props.currency).toBe('EUR');
+    expect(price.props.price.amount).toBe(-18.32);
+    expect(price.props.price.currency).toBe('EUR');
 
     const translations = travelBasic.root.findAllByType(Translation);
     expect(translations[0].props.values.quantity).toBe(1);
@@ -114,8 +114,8 @@ describe('InsuranceRow', () => {
       />,
     );
     const price = none.root.findByType(Price);
-    expect(price.props.amount).toBe(-61.28);
-    expect(price.props.currency).toBe('EUR');
+    expect(price.props.price.amount).toBe(-61.28);
+    expect(price.props.price.currency).toBe('EUR');
 
     const translations = none.root.findAllByType(Translation);
     expect(translations[0].props.values.quantity).toBe(2);
diff --git a/app/hotels/src/singleHotel/roomPicker/__tests__/RoomPicker.test.js b/app/hotels/src/singleHotel/roomPicker/__tests__/RoomPicker.test.js
index f03cd0d4..b09da499 100644
--- a/app/hotels/src/singleHotel/roomPicker/__tests__/RoomPicker.test.js
+++ b/app/hotels/src/singleHotel/roomPicker/__tests__/RoomPicker.test.js
@@ -10,8 +10,7 @@ const VoidAction = () => {};
 it('renders as expected', () => {
   PlaygroundRenderer.render(
     <RoomPicker
-      price={100}
-      currency="CZK"
+      price={{ amount: 100, currency: 'CZK' }}
       selectedCount={0}
       selectableCount={1}
       increment={VoidAction}
@@ -21,8 +20,7 @@ it('renders as expected', () => {
 
   PlaygroundRenderer.render(
     <RoomPicker
-      price={100}
-      currency="CZK"
+      price={{ amount: 100, currency: 'CZK' }}
       selectedCount={1}
       selectableCount={2}
       increment={VoidAction}
@@ -32,8 +30,7 @@ it('renders as expected', () => {
 
   PlaygroundRenderer.render(
     <RoomPicker
-      price={100}
-      currency="CZK"
+      price={{ amount: 100, currency: 'CZK' }}
       selectedCount={2}
       selectableCount={2}
       increment={VoidAction}
diff --git a/app/hotels/src/singleHotel/roomPicker/__tests__/__snapshots__/RoomPicker.test.js.snap b/app/hotels/src/singleHotel/roomPicker/__tests__/__snapshots__/RoomPicker.test.js.snap
index 21adec91..53c39b54 100644
--- a/app/hotels/src/singleHotel/roomPicker/__tests__/__snapshots__/RoomPicker.test.js.snap
+++ b/app/hotels/src/singleHotel/roomPicker/__tests__/__snapshots__/RoomPicker.test.js.snap
@@ -2,18 +2,26 @@
 
 exports[`renders as expected 1`] = `
 <SelectButton
-  currency="CZK"
   increment={[Function]}
-  price={100}
+  price={
+    Object {
+      "amount": 100,
+      "currency": "CZK",
+    }
+  }
 />
 `;
 
 exports[`renders as expected 2`] = `
 <IncrementDecrement
-  currency="CZK"
   decrement={[Function]}
   increment={[Function]}
-  price={100}
+  price={
+    Object {
+      "amount": 100,
+      "currency": "CZK",
+    }
+  }
   selectableCount={2}
   selectedCount={1}
 />
@@ -21,10 +29,14 @@ exports[`renders as expected 2`] = `
 
 exports[`renders as expected 3`] = `
 <IncrementDecrement
-  currency="CZK"
   decrement={[Function]}
   increment={[Function]}
-  price={100}
+  price={
+    Object {
+      "amount": 100,
+      "currency": "CZK",
+    }
+  }
   selectableCount={2}
   selectedCount={2}
 />
diff --git a/packages/shared/src/__tests__/Price.test.js b/packages/shared/src/__tests__/Price.test.js
index 5f22e749..f3a1d316 100644
--- a/packages/shared/src/__tests__/Price.test.js
+++ b/packages/shared/src/__tests__/Price.test.js
@@ -19,23 +19,23 @@ const renderer = new ShallowRenderer();
 describe('Price', () => {
   it('renders null values correctly', () => {
     expect(
-      renderer.render(<Price amount={null} currency={null} />),
+      renderer.render(<Price price={{ amount: null, currency: null }} />),
     ).toMatchSnapshot();
     expect(
-      renderer.render(<Price amount={45} currency={null} />),
+      renderer.render(<Price price={{ amount: 45, currency: null }} />),
     ).toMatchSnapshot();
     expect(
-      renderer.render(<Price amount={null} currency="EUR" />),
+      renderer.render(<Price price={{ amount: null, currency: 'EUR' }} />),
     ).toMatchSnapshot();
   });
 
   it('calls Currencyformatter if amount and currency is passed', () => {
-    testRenderer.create(<Price amount={45} currency="EUR" />);
+    testRenderer.create(<Price price={{ amount: 45, currency: 'EUR' }} />);
     expect(CurrencyFormatter).toHaveBeenCalledWith(45, 'EUR');
   });
 
   it('does not call set state if promise is rejected', async () => {
-    const Component = new Price({ amount: 45, currency: 'EUR' });
+    const Component = new Price({ price: { amount: 45, currency: 'EUR' } });
 
     jest.spyOn(Component, 'setState');
     await Component.formatCurrency();