Skip to content

Commit

Permalink
bsb input component
Browse files Browse the repository at this point in the history
  • Loading branch information
m1aw committed Jan 14, 2025
1 parent 2ade781 commit 1c63802
Show file tree
Hide file tree
Showing 7 changed files with 262 additions and 91 deletions.
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.

31 changes: 31 additions & 0 deletions packages/lib/src/components/PayTo/components/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,34 @@ export const payIdValidationRules: ValidatorRules = {
errorMessage: 'mobileNumber.invalid'
}
};

//original regex /^\d{6}-[ -~]{1,28}$/
const bsbRegex = /^\d{6}$/;
const bankAccountNumberRegex = /^[ -~]{1,28}$/;

const bsbValidatorRule: ValidatorRule = {
validate: value => validationFromRegex(value, bsbRegex, bsbValidatorRule),
errorMessage: 'bsb.invalid',
modes: ['blur']
};

const bankAccountNumberValidatorRule: ValidatorRule = {
validate: value => validationFromRegex(value, bankAccountNumberRegex, bankAccountNumberValidatorRule),
errorMessage: 'bankAccountNumber.invalid',
modes: ['blur']
};

export const bsbValidationRules: ValidatorRules = {
bsb: bsbValidatorRule,
bankAccountNumber: bankAccountNumberValidatorRule,
firstName: {
validate: value => (isEmpty(value) ? null : true), // valid, if there are chars other than spaces,
errorMessage: 'firstName.invalid',
modes: ['blur']
},
lastName: {
validate: value => (isEmpty(value) ? null : true),
errorMessage: 'lastName.invalid',
modes: ['blur']
}
};
Loading

0 comments on commit 1c63802

Please sign in to comment.