Skip to content

Instantly share code, notes, and snippets.

@pachun
Created July 25, 2024 08:43
Show Gist options
  • Save pachun/e9fda4b224d672b0fcc0b2e52cbbc3b1 to your computer and use it in GitHub Desktop.
Save pachun/e9fda4b224d672b0fcc0b2e52cbbc3b1 to your computer and use it in GitHub Desktop.
import React from "react"
import * as ReactNative from "react-native"
import * as RNTL from "@testing-library/react-native"
const formatCompleteOrPartialPhoneNumber = (
phoneNumberDigits: string,
): string => {
if (phoneNumberDigits.length < 4) {
return phoneNumberDigits
} else if (phoneNumberDigits.length < 7) {
return `(${phoneNumberDigits.slice(0, 3)}) ${phoneNumberDigits.slice(3)}`
}
return `(${phoneNumberDigits.slice(0, 3)}) ${phoneNumberDigits.slice(3, 6)}-${phoneNumberDigits.slice(6)}`
}
type PhoneNumberFieldType = (
props: ReactNative.TextInputProps & {
phoneNumber: string
onChangePhoneNumber: (phoneNumber: string) => void
},
) => React.ReactElement
const numberOfDigitsInFullPhoneNumber = 10
const fullPhoneNumber = "0".repeat(numberOfDigitsInFullPhoneNumber)
const PhoneNumberField: PhoneNumberFieldType = props => {
const [displayedPhoneNumber, setDisplayedPhoneNumber] =
React.useState<string>(
formatCompleteOrPartialPhoneNumber(props.phoneNumber),
)
const setDigits = React.useMemo(
() => props.onChangePhoneNumber,
[props.onChangePhoneNumber],
)
return (
<ReactNative.TextInput
keyboardType="number-pad"
{...props}
value={displayedPhoneNumber}
onChangeText={editedPhoneNumber => {
const digits = editedPhoneNumber.replace(/\D/g, "")
setDigits(digits)
setDisplayedPhoneNumber(formatCompleteOrPartialPhoneNumber(digits))
}}
maxLength={formatCompleteOrPartialPhoneNumber(fullPhoneNumber).length}
/>
)
}
const typeIntoTestId = (testId: string, text: string): void => {
RNTL.userEvent.setup().type(RNTL.screen.getByTestId(testId), text)
}
const PhoneNumberFieldTestHelper = (): React.ReactElement => {
const [phoneNumber, setPhoneNumber] = React.useState("")
return (
<>
<ReactNative.Text>{phoneNumber}</ReactNative.Text>
<PhoneNumberField
testID="Phone Number Field"
phoneNumber={phoneNumber}
onChangePhoneNumber={setPhoneNumber}
/>
</>
)
}
describe("The Phone Number Field Component", () => {
it("accepts and applies any ReactNative.TextField props", () => {
RNTL.render(
<PhoneNumberField
placeholder="Phone Number"
phoneNumber=""
onChangePhoneNumber={() => {}}
/>,
)
expect(RNTL.screen.getByPlaceholderText("Phone Number"))
})
it("defaults its keyboardType to number-pad", () => {
RNTL.render(
<PhoneNumberField
testID="Phone Number Field"
phoneNumber=""
onChangePhoneNumber={() => {}}
/>,
)
const phoneNumberField = RNTL.screen.getByTestId("Phone Number Field")
expect(phoneNumberField.props.keyboardType).toEqual("number-pad")
})
describe("given a digit-only initial phone number", () => {
it("formats the initially displayed phone number", () => {
RNTL.render(
<PhoneNumberField
testID="Phone Number Field"
phoneNumber="0123456789"
onChangePhoneNumber={() => {}}
/>,
)
const phoneNumberField = RNTL.screen.getByTestId("Phone Number Field")
expect(phoneNumberField.props.value).toEqual("(012) 345-6789")
})
})
describe("when digits are entered", () => {
it("does not permit entering more characters than are in a single full phone number", async () => {
RNTL.render(<PhoneNumberFieldTestHelper />)
const phoneNumberField = RNTL.screen.getByTestId("Phone Number Field")
await RNTL.waitFor(() => {
expect(phoneNumberField.props.maxLength).toEqual(
"(000) 000-0000".length,
)
})
})
it("formats the phone number in the text field and returns the unformatted phone number to the controlling component with onChangePhoneNumber", async () => {
RNTL.render(<PhoneNumberFieldTestHelper />)
const phoneNumberField = RNTL.screen.getByTestId("Phone Number Field")
typeIntoTestId("Phone Number Field", "0")
await RNTL.waitFor(() => {
expect(phoneNumberField.props.value).toEqual("0")
expect(RNTL.screen.getByText("0"))
})
typeIntoTestId("Phone Number Field", "1")
await RNTL.waitFor(() => {
expect(phoneNumberField.props.value).toEqual("01")
expect(RNTL.screen.getByText("01"))
})
typeIntoTestId("Phone Number Field", "2")
await RNTL.waitFor(() => {
expect(phoneNumberField.props.value).toEqual("012")
expect(RNTL.screen.getByText("012"))
})
typeIntoTestId("Phone Number Field", "3")
await RNTL.waitFor(() => {
expect(phoneNumberField.props.value).toEqual("(012) 3")
expect(RNTL.screen.getByText("0123"))
})
typeIntoTestId("Phone Number Field", "4")
await RNTL.waitFor(() => {
expect(phoneNumberField.props.value).toEqual("(012) 34")
expect(RNTL.screen.getByText("01234"))
})
typeIntoTestId("Phone Number Field", "5")
await RNTL.waitFor(() => {
expect(phoneNumberField.props.value).toEqual("(012) 345")
expect(RNTL.screen.getByText("012345"))
})
typeIntoTestId("Phone Number Field", "6")
await RNTL.waitFor(() => {
expect(phoneNumberField.props.value).toEqual("(012) 345-6")
expect(RNTL.screen.getByText("0123456"))
})
typeIntoTestId("Phone Number Field", "7")
await RNTL.waitFor(() => {
expect(phoneNumberField.props.value).toEqual("(012) 345-67")
expect(RNTL.screen.getByText("01234567"))
})
typeIntoTestId("Phone Number Field", "8")
await RNTL.waitFor(() => {
expect(phoneNumberField.props.value).toEqual("(012) 345-678")
expect(RNTL.screen.getByText("012345678"))
})
typeIntoTestId("Phone Number Field", "9")
await RNTL.waitFor(() => {
expect(phoneNumberField.props.value).toEqual("(012) 345-6789")
expect(RNTL.screen.getByText("0123456789"))
})
})
})
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment