Skip to content

Commit

Permalink
fix(Bonus Pagamenti Digitali): [#176129361] Cashback payment method. …
Browse files Browse the repository at this point in the history
…Change footer buttons logic (#2592)

* [#176129361] change label
review footer button enabling login

* [#176129361] update comment

* [#176129361] update

* [#176129361] update comments

* [#176129361] add tests
  • Loading branch information
Undermaken authored Dec 15, 2020
1 parent cd57293 commit 187fa70
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 23 deletions.
2 changes: 1 addition & 1 deletion locales/it/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,7 @@ wallet:
title: "Vuoi attivare il Cashback?"
body1: "Attiva il Cashback sui metodi salvati nel tuo portafoglio per iniziare a collezionare le transazioni valide."
body2: "Se preferisci, puoi attivare il Cashback su questi metodi anche in un secondo momento."
skip: "Non ora, grazie"
skip: "Non ora"
satispay:
headerTitle: "Aggiungi Satispay"
loadingSearch:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ import { navigationHistoryPop } from "../../../../../store/actions/navigationHis
import { GlobalState } from "../../../../../store/reducers/types";
import { PaymentMethod } from "../../../../../types/pagopa";
import { emptyContextualHelp } from "../../../../../utils/emptyContextualHelp";
import { FooterTwoButtons } from "../../../bonusVacanze/components/markdown/FooterTwoButtons";
import { PaymentMethodGroupedList } from "../../components/paymentMethodActivationToggle/list/PaymentMethodGroupedList";
import { paymentMethodsWithActivationStatusSelector } from "../../store/reducers/details/combiner";
import { areAnyPaymentMethodsActiveSelector } from "../../store/reducers/details/paymentMethods";
import FooterWithButtons from "../../../../../components/ui/FooterWithButtons";

const loadLocales = () => ({
headerTitle: I18n.t("bonus.bpd.title"),
Expand Down Expand Up @@ -46,11 +47,42 @@ const renderPaymentMethod = (
value => <PaymentMethodGroupedList paymentList={value} />
);

/**
* return a two button footer
* left button is enabled when no payment methods are BPD active
* right button button is enabled when at least one payment method is BPD active
* @param props
*/
const getFooter = (props: Props) => {
const { continueStr, skip } = loadLocales();
const notNowButtonProps = {
primary: false,
bordered: true,
disabled: props.areAnyPaymentMethodsActive,
onPress: props.skip,
title: skip
};
const continueButtonProps = {
block: true,
primary: true,
disabled: !props.areAnyPaymentMethodsActive,
onPress: props.skip,
title: continueStr
};
return (
<FooterWithButtons
type={"TwoButtonsInlineHalf"}
leftButton={notNowButtonProps}
rightButton={continueButtonProps}
/>
);
};

/**
* This screen allows the user to activate bpd on the payment methods already in the wallet
*/
const EnrollPaymentMethodsScreen: React.FunctionComponent<Props> = props => {
const { headerTitle, continueStr, skip, title, body1, body2 } = loadLocales();
const { headerTitle, title, body1, body2 } = loadLocales();
return (
<BaseScreenComponent
goBack={false}
Expand All @@ -70,12 +102,7 @@ const EnrollPaymentMethodsScreen: React.FunctionComponent<Props> = props => {
<Body>{body2}</Body>
</View>
</ScrollView>
<FooterTwoButtons
onRight={props.skip}
onCancel={props.skip}
rightText={continueStr}
leftText={skip}
/>
{getFooter(props)}
</SafeAreaView>
</BaseScreenComponent>
);
Expand All @@ -88,9 +115,15 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({
}
});

const mapStateToProps = (state: GlobalState) => ({
potWallets: paymentMethodsWithActivationStatusSelector(state)
});
const mapStateToProps = (state: GlobalState) => {
const potWallets = paymentMethodsWithActivationStatusSelector(state);
return {
potWallets,
areAnyPaymentMethodsActive: areAnyPaymentMethodsActiveSelector(
pot.getOrElse(potWallets, [])
)(state)
};
};

export default connect(
mapStateToProps,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import * as pot from "italia-ts-commons/lib/pot";
import {
areAnyPaymentMethodsActiveSelector,
BpdPotPaymentMethodActivation
} from "../paymentMethods";
import { IndexedById } from "../../../../../../../store/helpers/indexer";
import { HPan } from "../../../actions/paymentMethods";
import { fromPatchedWalletV2ToRawPaymentMethod } from "../../../../../../../utils/walletv2";
import { bancomat } from "../../__mock__/bancomat";
import { BancomatPaymentMethod } from "../../../../../../../types/pagopa";

const indexedPaymentMethods: IndexedById<BpdPotPaymentMethodActivation> = {
id1: pot.some({
hPan: "hpan1" as HPan,
activationStatus: "active"
}),
id2: pot.some({
hPan: "hpan2" as HPan,
activationStatus: "active"
}),
id3: pot.some({
hPan: "hpan2" as HPan,
activationStatus: "active"
})
};

const paymentMethod = fromPatchedWalletV2ToRawPaymentMethod(
bancomat
) as BancomatPaymentMethod;
const paymentMethodBancomat = {
...paymentMethod,
info: { ...paymentMethod.info, hashPan: "id1" }
};

describe("payment methods reducer tests", () => {
it("should return false since no methods are provided", () => {
expect(
areAnyPaymentMethodsActiveSelector([]).resultFunc(indexedPaymentMethods)
).toBeFalsy();
});

it("should return true (id1 - active)", () => {
expect(
areAnyPaymentMethodsActiveSelector([paymentMethodBancomat]).resultFunc(
indexedPaymentMethods
)
).toBeTruthy();
});

it("should return false (id1 - not active)", () => {
expect(
areAnyPaymentMethodsActiveSelector([paymentMethodBancomat]).resultFunc({
...indexedPaymentMethods,
id1: pot.some({
hPan: "hpan1" as HPan,
activationStatus: "inactive"
})
})
).toBeFalsy();
});

it("should return true, even if in error (some error)", () => {
expect(
areAnyPaymentMethodsActiveSelector([paymentMethodBancomat]).resultFunc({
...indexedPaymentMethods,
id1: pot.toError(
pot.some({
hPan: "hpan1" as HPan,
activationStatus: "active"
}),
new Error("some error")
)
})
).toBeTruthy();
});

it("should return false (none error)", () => {
expect(
areAnyPaymentMethodsActiveSelector([paymentMethodBancomat]).resultFunc({
...indexedPaymentMethods,
id1: pot.toError(pot.none, new Error("some error"))
})
).toBeFalsy();
});

it("should return true (at least one active)", () => {
const indexedPaymentMethodsOneActive: IndexedById<BpdPotPaymentMethodActivation> = {
id1: pot.some({
hPan: "hpan1" as HPan,
activationStatus: "active"
}),
id2: pot.some({
hPan: "hpan2" as HPan,
activationStatus: "inactive"
}),
id3: pot.some({
hPan: "hpan2" as HPan,
activationStatus: "notActivable"
}),
id4: pot.none
};
expect(
areAnyPaymentMethodsActiveSelector([paymentMethodBancomat]).resultFunc(
indexedPaymentMethodsOneActive
)
).toBeTruthy();
});
});
27 changes: 27 additions & 0 deletions ts/features/bonus/bpd/store/reducers/details/paymentMethods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
bpdUpdatePaymentMethodActivation,
HPan
} from "../../actions/paymentMethods";
import { PaymentMethod } from "../../../../../../types/pagopa";
import { getPaymentMethodHash } from "../../../../../../utils/paymentMethod";

export type BpdPotPaymentMethodActivation = pot.Pot<
BpdPaymentMethodActivation,
Expand Down Expand Up @@ -118,3 +120,28 @@ export const bpdPaymentMethodValueSelector = createSelector(
[bpdPaymentMethodActivationByHPanValue],
potValue => potValue ?? pot.none
);

/**
* Return true if at least one method from the given ones is BPD active
* @param paymentMethods
*/
export const areAnyPaymentMethodsActiveSelector = (
paymentMethods: ReadonlyArray<PaymentMethod>
) =>
createSelector(
[bpdPaymentMethodActivationSelector],
(bpdPaymentMethodsActivation): boolean => {
const paymentMethodsHash = paymentMethods.map(getPaymentMethodHash);
return paymentMethodsHash.some(pmh =>
fromNullable(pmh)
.mapNullable(h => bpdPaymentMethodsActivation[h])
.map(potActivation =>
pot.getOrElse(
pot.map(potActivation, p => p.activationStatus === "active"),
false
)
)
.getOrElse(false)
);
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,18 @@ import { IOStyles } from "../../../../../../components/core/variables/IOStyles";
import BaseScreenComponent from "../../../../../../components/screens/BaseScreenComponent";
import I18n from "../../../../../../i18n";
import { PaymentMethod } from "../../../../../../types/pagopa";
import { FooterTwoButtons } from "../../../../../bonus/bonusVacanze/components/markdown/FooterTwoButtons";
import { PaymentMethodRawList } from "../../../../../bonus/bpd/components/paymentMethodActivationToggle/list/PaymentMethodRawList";
import { GlobalState } from "../../../../../../store/reducers/types";
import { areAnyPaymentMethodsActiveSelector } from "../../../../../bonus/bpd/store/reducers/details/paymentMethods";
import FooterWithButtons from "../../../../../../components/ui/FooterWithButtons";

type OwnProps = {
paymentMethods: ReadonlyArray<PaymentMethod>;
title: string;
};

export type Props = ReturnType<typeof mapDispatchToProps> &
ReturnType<typeof mapStateToProps> &
OwnProps &
Pick<React.ComponentProps<typeof BaseScreenComponent>, "contextualHelp">;

Expand All @@ -30,8 +33,40 @@ const loadLocales = () => ({
continueStr: I18n.t("global.buttons.continue")
});

/**
* return a two button footer
* left button is enabled when no payment methods are BPD active
* right button button is enabled when at least one payment method is BPD active
* @param props
*/
const getFooter = (props: Props) => {
const { continueStr, skip } = loadLocales();
const notNowButtonProps = {
primary: false,
bordered: true,
disabled: props.areAnyPaymentMethodsActive,
onPress: props.skip,
title: skip
};
const continueButtonProps = {
block: true,
primary: true,
disabled: !props.areAnyPaymentMethodsActive,
onPress: props.skip,
title: continueStr
};
return (
<FooterWithButtons
type={"TwoButtonsInlineHalf"}
leftButton={notNowButtonProps}
rightButton={continueButtonProps}
/>
);
};

const ActivateBpdOnNewPaymentMethodScreen: React.FunctionComponent<Props> = props => {
const { title, body1, body2, skip, continueStr } = loadLocales();
const { title, body1, body2 } = loadLocales();

return (
<BaseScreenComponent
headerTitle={props.title}
Expand All @@ -50,14 +85,7 @@ const ActivateBpdOnNewPaymentMethodScreen: React.FunctionComponent<Props> = prop
<Body>{body2}</Body>
</View>
</ScrollView>

<FooterTwoButtons
type={"TwoButtonsInlineHalf"}
onCancel={props.skip}
onRight={props.skip}
rightText={continueStr}
leftText={skip}
/>
{getFooter(props)}
</SafeAreaView>
</BaseScreenComponent>
);
Expand All @@ -67,7 +95,13 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({
skip: () => dispatch(NavigationActions.back())
});

const mapStateToProps = (state: GlobalState, props: OwnProps) => ({
areAnyPaymentMethodsActive: areAnyPaymentMethodsActiveSelector(
props.paymentMethods
)(state)
});

export default connect(
undefined,
mapStateToProps,
mapDispatchToProps
)(ActivateBpdOnNewPaymentMethodScreen);

0 comments on commit 187fa70

Please sign in to comment.