Skip to content

Conversation

@magrinj
Copy link
Contributor

@magrinj magrinj commented May 28, 2025

This PR is fixing the following issues:

  • paste is not possible on iOS because the opacity is set to 0 (the workaround is to set the opacity to 0.02, the color to transparent and hide the caret)
  • pasteTransformer was not passed to useInput from the props
  • add a defaultPasteTransformer removing non numeric character by default
  • instead of overriding style of the input, merge them
  • bump the example project to expo 53

@yjose
Copy link
Owner

yjose commented May 28, 2025

@magrinj, thank you for submitting the fix! Do you think we can find a way to test this past feature?

@magrinj
Copy link
Contributor Author

magrinj commented May 28, 2025

@yjose With unit testing I don't think so, as the issue is actually that we can't have the tooltip when doing a long-press to paste on the otp-input on iOS. This can only be done by doing E2E test otherwise.

@yjose
Copy link
Owner

yjose commented May 28, 2025

ok sure noted, will check the PR tonight and retest it and add my review 🙏

@magrinj
Copy link
Contributor Author

magrinj commented May 28, 2025

No problem, thanks for your reactivity !

@yjose
Copy link
Owner

yjose commented May 28, 2025

I've noticed something about how OTP codes work while pasting a long text. Let's say you receive a message like "Your OTP code is 234323." If you copy that entire message and then try to paste it, it won't work correctly with the current setup.

This is because we are passing the maxLength to the input field itself. So, when you paste the text, the hidden input only gets "your o" and the transformPast function doesn't receive the whole pasted message to extract the digits.

To fix this, I believe we should remove the maxLength property from the hiddenTextInput and the length validation will be handled by the hook programmatically ?

@magrinj
Copy link
Contributor Author

magrinj commented May 28, 2025

Pretty good catch, I just try to directly copy an OTP code instead of the whole message.
We can indeed do that programatically yes, but we must always strip the value after the transform based on the maxLength to avoid too long value.
I can take a look on that on Friday, I won't be able to do it today or tomorrow

@yjose
Copy link
Owner

yjose commented May 28, 2025

yeah sure take your time

@magrinj
Copy link
Contributor Author

magrinj commented May 30, 2025

@yjose Ok, I've fixed the issue you had.
I remove the maxLength from the TextInput. In the handle change there is already a slice, I just add a comment above it explaining the purpose.
I'm passing the maxLength to the pasteTransformer function, and the default function is not extracting the first digit characters matching the maxLength, so if the code is smaller or longer nothing is gonna be paste.
Let me know if it's good for you ?
If someone actually want to use something else, the prop is editable.

@yjose
Copy link
Owner

yjose commented May 30, 2025

I don't believe it's necessary to include maxLength within the pasteTransformer. If the user provides maxLength and pasteTransformer as a prop, we'll essentially have two versions of the same value, and they should be identical or we will have issues. Also, I'd prefer to keep the API consistent with the input-otp package.

I think you can keep the pasteTransformer as it was from the first version you propose and only delete the maxLength from the TextInput

Also few tests that should be fixed, and i suggest to only delete the checks related to maxLength causing the problem as we do not need them anymore

@magrinj
Copy link
Contributor Author

magrinj commented Jun 2, 2025

I can remove the maxLength from the pasteTransformer but I've included it so the regex is dynamic based on the maxLength from the props.
This way it's working fine out of the box without having to provide a pasteTransformer for "classic" OTPInput, when we change the maxLength.
I can do a regex that just check a suite of number, without comparing with maxLength, but I found this more useless.
I understand your point with matching the API of input-otp package, but I'm not sure it's the right solution to drop it.
For the test I'll remove the one we don't need anymore.

@yjose
Copy link
Owner

yjose commented Jun 2, 2025

I see your point. How about we try this approach instead?

const defaultPasteTransformer = (maxLength: number) => (pasted: string)  => {
  // match exactly maxLength digits, not preceded or followed by another digit
  const otpRegex = new RegExp(`(?<!\\d)(\\d{${maxLength}})(?!\\d)`);
  const match = pasted.match(otpRegex);

  return match?.[1] ?? '';
};

then

pasteTransformer: props.pasteTransformer ?? defaultPasteTransformer(props.maxLength),

This approach means you won't need to update either the user-input implementation or the props types.

@magrinj
Copy link
Contributor Author

magrinj commented Jun 4, 2025

Ok everything is fixed 🙂
I've change the test to emulate user typing and avoid having the test failing because it act like a paste.

@yjose yjose self-requested a review June 4, 2025 20:22
Copy link
Owner

@yjose yjose left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, thank you @magrinj for your efforts

@yjose yjose merged commit a8387a6 into yjose:main Jun 4, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants