0

I'm using react-datetime module in my form along with formik.

import React, { useImperativeHandle, useReducer, forwardRef } from 'react';
import PropTypes from 'prop-types';
import { Form} from "react-bootstrap";

import "react-datetime/css/react-datetime.css";

import { useFormik} from 'formik';
import * as yup from "yup";
import Datetime from 'react-datetime';
import moment from 'moment';
import { reducer } from '../common/reducer'


// Restrict future dates when entering "Date of Birth"
const validDOB = function( current ){
    return current.isBefore(moment());
};

// Initial states of WorkloadForm
const initialState = {
    doesExist: false
};

const WorkloadForm = forwardRef((props, ref) => {
    // State Handling
    const [state, dispatch] = useReducer(reducer, initialState);

    
    // Initial Values for the WorkfloadForm
    const initialValues = {
        patDOB: '',
    }

    // Validation schema for form fields
    const validationSchema = yup.object().shape({
        patDOB: yup.string()
                        .test('invalid-dob', 'Please enter a valid DOB.', value => {
                            const dob = moment(value, 'DD-MM-YYYY', true);
                            return dob.isValid();
                        }),
    });

    // Form Submission Action
    const handleSubmit = (values, actions) =>{
        
    }

    // Formik initialization of 'useFormik'
    const formik = useFormik({
        initialValues: initialValues,
        onSubmit: handleSubmit,
        validationSchema: validationSchema
    });

    // 'useImperativeHandle' to call formik actions from parent component
    useImperativeHandle(ref, () => ({
        ...formik
    }), []);

    return (
        <>
            <Form noValidate onSubmit={formik.handleSubmit}>
                <Form.Group controlId="patDOB">
                    <Form.Label>Date of Birth</Form.Label>
                    <Datetime
                        inputProps={{
                            placeholder: 'DD-MM-YYYY',
                            id: 'patDOB',
                            name: 'patDOB',
                            readOnly: state.doesExist ? true: false,
                            className: formik.errors.patDOB && formik.touched.patDOB ? "form-control is-invalid": "form-control"
                        }}
                        closeOnSelect={true}
                        dateFormat="DD-MM-YYYY"
                        timeFormat={false}
                        value={formik.values.patDOB}
                        isValidDate={validDOB}
                        onChange={(dateFromValue) => {
                            formik.setFieldValue('patDOB', moment(dateFromValue).format('DD-MM-YYYY'));
                            }
                        }
                        renderInput={(props) => {
                            return <input {...props} value={(formik.values.patDOB) ? props.value : ''} />
                        }}
                    />
                    {formik.errors.patDOB && formik.touched.patDOB ? (
                            <div className="text-danger" style={{fontSize: '0.875em'}}>{formik.errors.patDOB}</div>
                    ) : null}
                </Form.Group>
            </Form>
        </>
    );
})

WorkloadForm.propTypes = {
    api: PropTypes.object.isRequired
};

export default WorkloadForm;

readOnly prop is working fine and it's conditional.

My issue is that I can still open the calendar or picker when I touch the field even though its readOnly.

Issue

How to prevent the field from opening the picker/calendar when it's readOnly?

Thanks

SM1312
  • 516
  • 4
  • 13

1 Answers1

2

You can pass the open prop to false

<Datetime open={false} />

or in your case if the condition is the state.doesExist you could use the state value

<Datetime open={!state.doesExist} />

Or if you want to keep the default behavior if your state is true maybe you could try

<Datetime open={state.doesExist ? false : null} />

Edit:

For some reason, explicitely setting the null default value of the open prop, doesn't work, I found that workaround which is quite ugly but works...

<Datetime 
//...props
{...Object.assign({}, state.doesExist ? {open:false} : {})}
 />
Peterrabbit
  • 2,166
  • 1
  • 3
  • 19
  • Thanks. Could you help on conditioning it based on the 'readOnly' prop? Because `open={ state.doesExist ? false: true}` will work when it's readOnly but it will always open the picker by default if it's not readOnly which is an issue. – SM1312 Mar 28 '22 at 09:28
  • @SM1312 I'm not sure to understand what is the expected behavior. Should the calendar never open itself ? Or open only when `state.doesExist` ? Is there a casee where the input is not read only but the calendar should not be openable ? – Peterrabbit Mar 28 '22 at 09:43
  • Another way is conditionally rendering the element along with different props like `{ state.doesExist ? () : () }` But I'm not sure whether it's the best solution! – SM1312 Mar 28 '22 at 09:44
  • The calendar should open only if `state.doesExist` is `false`. @Peterrabbit – SM1312 Mar 28 '22 at 09:49
  • @SM1312 with both syntax the element is recreated when parent component is rendered so i'd say it'sequivalent in this case. It didn't understand what would be the issue with `open={ state.doesExist ? false: true}`. – Peterrabbit Mar 28 '22 at 09:52
  • `open={ state.doesExist ? false: true}` will work but it will cause another issue which is the calendar will be open even without touching the field if `state.doesExist` is `false`. @Peterrabbit – SM1312 Mar 28 '22 at 09:55
  • @SM1312 I updated my answer with a 3rd suggestion with `null` which is default. – Peterrabbit Mar 28 '22 at 09:59
  • 3rd suggestion isn't working unfortunately. https://codesandbox.io/s/happy-panka-ujk3lw?file=/src/App.js @Peterrabit – SM1312 Mar 28 '22 at 10:08
  • @SM1312 I'm a little confused about that... I would consider that an issue in the lib, but I proposed a workaround which seems to work... – Peterrabbit Mar 28 '22 at 10:37