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

DateField not returning a proper value if it's invalid #927

Open
tyrelchambers opened this issue Aug 3, 2023 · 1 comment
Open

DateField not returning a proper value if it's invalid #927

tyrelchambers opened this issue Aug 3, 2023 · 1 comment
Labels
bug Something isn't working ui-extensions

Comments

@tyrelchambers
Copy link

Describe the bug
When using the DateField component and inputting an invalid date such as 5555-55-55, the onChange handler doesn't fire nor does the input validate the input itself which leads to some strange outcomes. In native HTML, the input is validated as you type so 5555-55-55 would become 5555-05-31.

To Reproduce
Steps to reproduce the behavior:

  1. Add a DateField component
  2. input 5555-55-55
  3. input is neither validated nor does the onChange event fire in order to indicate it's an invalid date

Expected behavior
If an invalid date is inputted, the next valid date should be returned. In this context: 5555-05-31

Desktop (please complete the following information):

  • OS: MacOS
  • Browser: Firefox
  • Stripe CLI: 1.15
  • Stripe UI extension: 8.6.0
  • Stripe: 9.8.0
@bensontrent
Copy link

The DateField is one of the less-developed components. You'll have to add error-checking yourself according to your own rules. Here's how we do in our app. Our error-checking rules were custom to how we need the final date in our app, Parcelcraft Shipping.

import { DateField } from "@stripe/ui-extension-sdk/ui";
import { useCallback, useMemo, useState } from "react";

const DateSelector = () => {


    const [error, setError] = useState<string>();
    const [selectedTimestamp, setSelectedTimestamp] = useState<number>(Date.now());


    const currentDate = useMemo(() => {
        // If there's an initial 'after' value in the view, use it; otherwise, use the current date
        const initialTimestamp = Date.now()
        return formatDateToYYYYMMDD(initialTimestamp);
    }, []);




    const changeDate = useCallback((date: string) => {

        const { timestamp, errorMessage } = getDateErrors(date)

        if (!errorMessage) {
            setError(undefined);
            setSelectedTimestamp(timestamp);
        } else {
            setError(errorMessage);
        }
    }, []);

    return (

        <>
        Selected Timestamp: {selectedTimestamp}

            <DateField

                label="Date"
                onChange={(e) => { changeDate(e.target.value) }}
                defaultValue={currentDate}
                invalid={!!error}
                error={error}
            />
        </>

    )
}

const formatDateToYYYYMMDD = (timestamp: number) => {
    const date = new Date(timestamp * 1000); // Convert seconds to milliseconds
    return date.toISOString().split('T')[0]; // This will give you YYYY-MM-DD
};


const getDateErrors = (date: string): { timestamp: number, errorMessage: string } => {

    // Parse the date string manually to avoid timezone issues
    const [year, month, day] = date.split('-').map(Number);
    const inputDate = new Date(year, month - 1, day); // month is 0-indexed in JS Date

    const timestamp = Math.floor(inputDate.getTime() / 1000);
    const currentDate = new Date();

    let errorMessage = "";

    // Check if the date is valid
    if (isNaN(timestamp)) {
        errorMessage = "Invalid date";
    } else {
        // Check if the date is in the future
        if (inputDate > currentDate) {
            errorMessage = "Date cannot be in the future";
        }
        // Check if the date is today
        else if (
            inputDate.getDate() === currentDate.getDate() &&
            inputDate.getMonth() === currentDate.getMonth() &&
            inputDate.getFullYear() === currentDate.getFullYear()
        ) {
            errorMessage = "Date cannot be today";
        }

        else if (timestamp < 1262322000) {
            errorMessage = "Year must greater than 2010";
        }
        // Check if it's a valid day of the month
        else if (inputDate.getDate() !== day) {
            errorMessage = "Must be a valid day of the month";
        }
        // Check if it's a valid month
        else if (inputDate.getMonth() + 1 !== month) {
            errorMessage = "Must be a valid month";
        }
    }
    return { errorMessage, timestamp };
}

export default DateSelector;

You will need to adjust your getDateErrors according to your own app's needs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working ui-extensions
Projects
None yet
Development

No branches or pull requests

3 participants