Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Partial feature: BSB Input #3105

Merged
merged 10 commits into from
Jan 22, 2025
Merged
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@
},
"devDependencies": {
"@adyen/adyen-web-server": "1.0.0",
"@changesets/cli": "2.27.10",
"@changesets/cli": "2.27.11",
"@changesets/get-github-info": "0.6.0",
"concurrently": "8.2.2",
"prettier": "3.3.3"
"prettier": "3.4.2"
}
}
8 changes: 4 additions & 4 deletions packages/lib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
"@rollup/plugin-commonjs": "26.0.3",
"@rollup/plugin-eslint": "9.0.5",
"@rollup/plugin-json": "6.1.0",
"@rollup/plugin-node-resolve": "15.3.0",
"@rollup/plugin-node-resolve": "15.3.1",
"@rollup/plugin-replace": "5.0.7",
"@rollup/plugin-terser": "0.4.4",
"@size-limit/preset-big-lib": "11.1.6",
Expand All @@ -113,9 +113,9 @@
"eslint-plugin-import": "2.31.0",
"eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-react": "7.37.2",
"eslint-plugin-storybook": "0.8.0",
"eslint-plugin-testing-library": "6.2.2",
"globals": "15.8.0",
"eslint-plugin-storybook": "0.11.1",
"eslint-plugin-testing-library": "6.5.0",
"globals": "15.14.0",
"husky": "9.1.7",
"jest": "29.7.0",
"jest-environment-jsdom": "29.7.0",
Expand Down
32 changes: 20 additions & 12 deletions packages/lib/src/components/PayTo/PayTo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,18 @@ Types (previously in their own file)
*/
import { UIElementProps } from '../internal/UIElement/types';
import { TxVariants } from '../tx-variants';
import PayToInput from './components/PayToInput';
import { PayIdFormData } from './components/PayIDInput';
import { PayToIdentifierEnum } from './components/IdentifierSelector';
import PayToComponent, { PayToComponentData } from './components/PayToComponent';
import { BSBFormData } from './components/BSBInput';

export interface PayToConfiguration extends UIElementProps {
paymentData?: any;
data?: PayToData;
placeholders?: any; //TODO
}

export interface PayToData extends PayIdFormData {
export interface PayToData extends PayIdFormData, BSBFormData, PayToComponentData {
shopperAccountIdentifier: string;
}

Expand All @@ -38,15 +39,22 @@ const config = {
};

const getAccountIdentifier = (state: PayToData) => {
switch (state.selectedIdentifier) {
case PayToIdentifierEnum.email:
return state.email;
case PayToIdentifierEnum.abn:
return state.abn;
case PayToIdentifierEnum.orgid:
return state.orgid;
case PayToIdentifierEnum.phone:
return `${state.phonePrefix}-${state.phoneNumber}`;
// if it's BSB Input type merge bankAccount with BSB
if (state.selectedInput === 'bsb-option') {
return `${state.bsb}-${state.bankAccountNumber}`;
} else if (state.selectedInput === 'payid-option') {
// otherwise use the option in the dropdown
switch (state.selectedIdentifier) {
case PayToIdentifierEnum.email:
return state.email;
case PayToIdentifierEnum.abn:
return state.abn;
case PayToIdentifierEnum.orgid:
return state.orgid;
case PayToIdentifierEnum.phone:
// merge the phone prefix and number - see comment in ticket
return `${state.phonePrefix}-${state.phoneNumber}`;
}
}
};
/**
Expand Down Expand Up @@ -118,7 +126,7 @@ export class PayToElement extends UIElement<PayToConfiguration> {

return (
<CoreProvider i18n={this.props.i18n} loadingContext={this.props.loadingContext} resources={this.resources}>
<PayToInput
<PayToComponent
data={this.props.data}
placeholders={this.props.placeholders}
setComponentRef={this.setComponentRef}
Expand Down
134 changes: 123 additions & 11 deletions packages/lib/src/components/PayTo/components/BSBInput.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,129 @@
import { h } from 'preact';
import Fieldset from '../../internal/FormFields/Fieldset';
import { useEffect, useRef } from 'preact/hooks';
import useForm from '../../../utils/useForm';
import { getErrorMessage } from '../../../utils/getErrorMessage';
import Field from '../../internal/FormFields/Field';
import { useCoreContext } from '../../../core/Context/CoreProvider';
import InputText from '../../internal/FormFields/InputText';
import { bsbValidationRules } from './validate';
import './PayIDInput.scss';
import { phoneFormatters } from '../../internal/PhoneInput/validate';
import { ComponentMethodsRef } from '../../internal/UIElement/types';

export default function BSBInput() {
// const { i18n } = useCoreContext();
export interface BSBFormData {
bsb: string;
bankAccountNumber: string;
firstName: string;
lastName: string;
}

export interface BSBInputProps {
defaultData: BSBFormData;
placeholders: any; //TODO
onChange: (e) => void;
setComponentRef: (ref: ComponentMethodsRef) => void;
}

const BASE_SCHEMA = ['bankAccountNumber', 'bsb', 'firstName', 'lastName'];

export default function BSBInput({ setComponentRef, defaultData, placeholders, onChange }: BSBInputProps) {
const { i18n } = useCoreContext();

const form = useForm<BSBFormData>({
schema: BASE_SCHEMA,
defaultData: defaultData,
rules: bsbValidationRules,
formatters: phoneFormatters
});
const { handleChangeFor, triggerValidation, data, errors, valid, isValid } = form;

// standard onChange propagate to parent state
useEffect(() => {
onChange({ data, valid, errors, isValid });
}, [data, valid, errors, isValid]);

const payToRef = useRef<ComponentMethodsRef>({
showValidation: triggerValidation
});

useEffect(() => {
setComponentRef(payToRef.current);
}, [setComponentRef]);

return (
<Fieldset classNameModifiers={['payto__bsb_input']} label={'payto.bsb.header'} description={'payto.bsb.description'}>
<Field
label={i18n.get('payto.bsb.label.bankAccountNumber')}
classNameModifiers={['col-60', 'bankAccountNumber']}
errorMessage={getErrorMessage(i18n, errors.bankAccountNumber, i18n.get('payto.bsb.label.bankAccountNumber'))}
name={'bankAccountNumber'}
i18n={i18n}
>
<InputText
name={'bankAccountNumber'}
value={data.bankAccountNumber}
onInput={handleChangeFor('bankAccountNumber', 'input')}
onBlur={handleChangeFor('bankAccountNumber', 'blur')}
placeholder={placeholders?.bankAccountNumber}
required={true}
/>
</Field>

// TODO type this
// const { handleChangeFor, triggerValidation, data, valid, errors } = useForm<any>({
// schema: ['beneficiaryId']
// });
//
// const [status, setStatus] = useState<string>('ready');
<Field
label={i18n.get('payto.bsb.label.bsb')}
classNameModifiers={['col-40', 'bsb']}
errorMessage={getErrorMessage(i18n, errors.bsb, i18n.get('payto.bsb.label.bsb'))}
name={'bsb'}
i18n={i18n}
>
<InputText
name={'bsb'}
value={data.bsb}
onInput={handleChangeFor('bsb', 'input')}
onBlur={handleChangeFor('bsb', 'blur')}
placeholder={placeholders?.bsb}
required={true}
/>
</Field>

// this.setStatus = setStatus;
// this.showValidation = triggerValidation;
<Field
label={i18n.get('firstName')}
classNameModifiers={['col-50', 'firstName']}
errorMessage={getErrorMessage(i18n, errors.firstName, i18n.get('firstName'))}
name={'firstName'}
i18n={i18n}
>
<InputText
name={'firstName'}
value={data.firstName}
classNameModifiers={['firstName']}
onInput={handleChangeFor('firstName', 'input')}
onBlur={handleChangeFor('firstName', 'input')}
placeholder={placeholders?.firstName}
spellCheck={false}
required={true}
/>
</Field>

return <p>BSBInput.tsx</p>;
<Field
label={i18n.get('lastName')}
classNameModifiers={['col-50', 'lastName']}
errorMessage={getErrorMessage(i18n, errors.lastName, i18n.get('lastName'))}
name={'lastName'}
i18n={i18n}
>
<InputText
name={'lastName'}
value={data.lastName}
classNameModifiers={['lastName']}
onInput={handleChangeFor('lastName', 'input')}
onBlur={handleChangeFor('lastName', 'blur')}
placeholder={placeholders?.lastName}
spellCheck={false}
required={true}
/>
</Field>
</Fieldset>
);
}
12 changes: 7 additions & 5 deletions packages/lib/src/components/PayTo/components/PayIDInput.scss
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
@import 'styles/variable-generator';

.adyen-checkout__fieldset--payto__payid_input {
margin-top: token(spacer-070);

.adyen-checkout__fieldset__fields {
.adyen-checkout__payto-component {
.adyen-checkout__fieldset {
margin-top: token(spacer-070);
gap: 0 token(spacer-060);

.adyen-checkout__fieldset__fields {
margin-top: token(spacer-070);
gap: 0 token(spacer-060);
}
}
}
76 changes: 76 additions & 0 deletions packages/lib/src/components/PayTo/components/PayToComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { h } from 'preact';
import LoadingWrapper from '../../internal/LoadingWrapper';
import SegmentedControl from '../../internal/SegmentedControl';
import { useState } from 'preact/hooks';
import { SegmentedControlOptions } from '../../internal/SegmentedControl/SegmentedControl';
import PayIDInput from './PayIDInput';
import BSBInput from './BSBInput';
import { useCoreContext } from '../../../core/Context/CoreProvider';

export type PayToInputOption = 'payid-option' | 'bsb-option';

export type PayToComponentData = { selectedInput: PayToInputOption };

const inputOptions: SegmentedControlOptions<PayToInputOption> = [
{
value: 'payid-option',
label: 'PayID',
htmlProps: {
id: 'payid-option', // TODO move this to i18n
'aria-controls': 'payid-input',
'aria-expanded': true // TODO move this logic to segmented controller
}
},
{
value: 'bsb-option',
label: 'BSB and account number', // TODO move this to i18n
htmlProps: {
id: 'bsb-option',
'aria-controls': 'bsb-input',
'aria-expanded': false // TODO move this logic to segmented controller
}
}
];

export default function PayToComponent(props) {
const { i18n } = useCoreContext();

const [status, setStatus] = useState<string>('ready');

this.setStatus = setStatus;

const defaultOption = inputOptions[0].value;
const [selectedInput, setSelectedInput] = useState<PayToInputOption>(defaultOption);

const onChange = ({ data, valid, errors, isValid }) => {
// merge selected input to as data, this keep the input layers untouched
props.onChange({ data: { selectedInput: selectedInput, ...data }, valid, errors, isValid });
};

return (
<LoadingWrapper>
<div className="adyen-checkout__payto-component">
<SegmentedControl selectedValue={selectedInput} options={inputOptions} onChange={setSelectedInput} />
{selectedInput === 'payid-option' && (
<PayIDInput
setComponentRef={props.setComponentRef}
onChange={onChange}
defaultData={props.data}
onError={props.onError}
placeholders={props.placeholders}
/>
)}
{selectedInput === 'bsb-option' && (
<BSBInput
setComponentRef={props.setComponentRef}
onChange={onChange}
defaultData={props.data}
placeholders={props.placeholders}
/>
)}

{props.showPayButton && props.payButton({ status, label: i18n.get('confirmPurchase') })}
</div>
</LoadingWrapper>
);
}
62 changes: 0 additions & 62 deletions packages/lib/src/components/PayTo/components/PayToInput.tsx

This file was deleted.

Loading
Loading