Coder Social home page Coder Social logo

dan503 / react-time-input-polyfill Goto Github PK

View Code? Open in Web Editor NEW
3.0 1.0 10.0 9.26 MB

A simple React component that produces an input[type=time] element with a built in Polyfill for Safari and IE support

JavaScript 14.14% HTML 3.84% CSS 3.20% TypeScript 78.82%

react-time-input-polyfill's Introduction

@time-input-polyfill/react

This is a pre-built, plug-and-play, fully accessible React component that will produce an <input type="time"> element with a built in polyfill for IE and Safari support.

  • ✔️ Modeled after the Chrome 78 and Firefox 70 desktop implementations.
  • ✔️ Fully keyboard and screen reader accessible.
  • ✔️ Sends back the same values as real time inputs (24 hour time).
  • ✔️ Only downloads the full polyfill code in the browsers that need it
  • ✔️ Quality assured with Cypress tests

You may have already come across the plain JavaScript version. This is not just a wrapper component though. This package was built from the ground up in React, for React.

You can view a demo of the time input polyfill in action here: https://dan503.github.io/react-time-input-polyfill/

You can view a demo of the original plain javascript version here: https://dan503.github.io/time-input-polyfill/

Install

The component was built to work in create-react-app projects. It should work ok in other React based frameworks though as well.

react-scripts v5 is currently not supported.

This is related to React upgrading to Webpack v5. This linked issue is blocking my ability to support react-scripts v5.

Install the polyfill component with npm:

npm i @time-input-polyfill/react

or install via Yarn:

yarn add @time-input-polyfill/react

Usage

/* TimeInput.js */

import React from 'react'

// Import the component into your project
import { TimeInputPolyfill } from '@time-input-polyfill/react'
// Note: default import is also supported

export function TimeInput({ label, value, setValue }) {
    return (
        <label>
            <span>{label}</span>
            <TimeInputPolyfill
                // Set the value through props
                value={value}
                // Pass in the state setter
                setValue={setValue}
            />
        </label>
    )
}
/* ExampleForm.js */

import React, { useState, useEffect } from 'react'

// import your local time input component into your form component
import { TimeInput } from './TimeInput'

export function ExampleForm() {
    // Use state to keep track of the value
    const [inputValue, setInputValue] = useState('20:30') // default to 8:30 PM

    // Use useEffect to trigger functionality when the value changes
    useEffect(() => {
        console.log({ inputValue })
    }, [inputValue])

    return (
        <form>
            <TimeInput
                label="Label text"
                // Use the state value to set the time
                value={inputValue}
                // Pass the state setter function into the component
                setValue={setInputValue}
            />
            <button type="submit">Submit</button>
        </form>
    )
}

You can also force-enable the polyfill so that it is active in modern browsers that support <input type="time"> natively. This is helpful when it comes to debugging since it gives you access to modern dev tools (just make sure to disable it again when you are done).

/* TimeInput.js */

import React from 'react'
import { TimeInputPolyfill } from '@time-input-polyfill/react'

export function TimeInput({ label, value, setValue }) {
    return (
        <label>
            <span>{label}</span>
            <TimeInputPolyfill
                value={value}
                setValue={setValue}
                /* Force browsers that support input[type=time]
                   to use the polyfill.
                   (useful for testing and debugging) */
                forcePolyfill={true}
            />
        </label>
    )
}

Content Security Policy (CSP) work around

The way that the polyfill avoids downloading the full polyfill code in modern browsers is by injecting the following script tag onto the page:

<script src="https://cdn.jsdelivr.net/npm/@time-input-polyfill/utils@1"></script>

That downloads the extra helper functions that the polyfill needs to function.

Your CSP might not allow for this.

To work around the issue, first create a timePolyfillUtils.js file and ensure that whatever you are using to compile your JS also compiles this file as it's own separate thing. Don't import it into your main js file.

// timePolyfillUtils.js

// ES5
require('@time-input-polyfill/utils/npm/time-input-polyfill-utils.min.js')

// ES6
import '@time-input-polyfill/utils/npm/time-input-polyfill-utils.min.js'

Then when using the component, add a polyfillSource prop that points to the compiled helpers file on your server.

<TimeInput
    value={currentValue}
    setValue={setCurrentValue}
    polyfillSource="/path/to/timePolyfillUtils.js"
/>

Breaking changes in v2

onChange replaced with setValue

In v1 you updated the value using an onChange event. This was really clunky though.

// v1 syntax

const [value, setValue] = useState()

// ...

<TimeInput value={value} onChange={({ value }) => {
    doStuff(value)
    setValue(value)
}} />

In v2, the syntax has been simplified down to this:

// v2 syntax

const [value, setValue] = useState()

useEffect(()=>{
    doStuff(value)
}, [value])

// ...

<TimeInput value={value} setValue={setValue} />

Note: It is still possible to use onChange, however this is just an extension of the native <input type="time"> onChange event now. It is not compatible with v1 and it does not provide a consistent value between polyfilled and non-polyfilled browsers.

Warning: events like onChange and onKeyUp fire before the state in the polyfill has settled. This means that event.target.currentValue will not return the expected value in the polyfill version. It was out of scope to adjust the timing on every possible event to fire after the state has settled.

polyfillSource value has changed location

In version 1, you would import the polyfill utils from here:

react-time-input-polyfill/dist/timePolyfillUtils.js.

In version 2, you will need to import from here instead now:

@time-input-polyfill/utils/npm/time-input-polyfill-utils.min.js

react-time-input-polyfill's People

Contributors

dan503 avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar

react-time-input-polyfill's Issues

Older versions of Edge showing error

Seeing this error when importing the package and running on pre-Chromium Edge.

SCRIPT: 1028: Expected identifier, string, or number

I see this error simply by importing, import TimeInputPolyfill from 'react-time-input-polyfill', even if I do not actually add the component to my layout.

It sounds like that error is due to inability of legacy Edge to read spread syntax. This doesn't make sense to me as I'd think your babel config would take care of that.

I noticed that I'm able to load your demo site -- dan503.github.io/react-time-input-polyfill/ -- on this same legacy version of Edge, without issue. Are you doing any extra compilation for that demo site?

Microsoft Edge 44.17763.1.0

React v. 16.13.1
React-time-input-polyfill v. 1.0.5

Cannot use import statement outside a module

I'm trying to use this in a Next JS project, but when I import I get the following error:

/web/node_modules/react-time-input-polyfill/index.js:1
import React from 'react'
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at wrapSafe (node:internal/modules/cjs/loader:1024:16)
    at Module._compile (node:internal/modules/cjs/loader:1072:27)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1137:10)
    at Module.load (node:internal/modules/cjs/loader:973:32)
    at Function.Module._load (node:internal/modules/cjs/loader:813:14)
    at Module.require (node:internal/modules/cjs/loader:997:19)
    at require (node:internal/modules/cjs/helpers:92:18)
    at Object.react-time-input-polyfill (/web/.next/server/pages/components.js:625:18)
    at __webpack_require__ (/web/.next/server/webpack-runtime.js:33:42)
    at eval (webpack-internal:///./components/TimeV2/index.js:11:83)
    at Object../components/TimeV2/index.js (/web/.next/server/pages/components.js:121:1)
    at __webpack_require__ (/web/.next/server/webpack-runtime.js:33:42)
    at eval (webpack-internal:///./pages/components.js:11:76)
    at Object../pages/components.js (/web/.next/server/pages/components.js:352:1)
    at __webpack_require__ (/web/.next/server/webpack-runtime.js:33:42)
    at __webpack_exec__ (/web/.next/server/pages/components.js:656:52)

My code is very similar to the example in the docs:

import css from './style.module.scss';
import { useState } from 'react';
import TimeInputPolyfill from 'react-time-input-polyfill';

export default function TimeV2 () {
    const [val, setVal] = useState('');

    const onChange = ({ value }) => setVal(value);

    return (
        <div className={css.wrap}>
            <TimeInputPolyfill
                value={val}
                onChange={onChange}
            />
        </div>
    );
}

this.$input.current is null

Seeing this error in the safari polyfill:

TypeError: null is not an object (evaluating 'this.$input.current.form')
(anonymous function) — index.js:105

It looks like this.$input.current is null in the two instances of this line:

if (this.$input.current.form) {

I'm on a slightly older version of safari (12.1).

It might be that adding a little more defense here is all that's needed. If you don't have access to a Mac, I could probably test and PR this change

Request for timePolyfillHelpers.js conflicts with Content Security Policy on Safari

Using both react-time-input-polyfill and a Content Security Policy causes the following error to occur on attempting to request timePolyfillHelpers.js:

Refused to load https://cdn.jsdelivr.net/npm/react-time-input-polyfill@1/dist/timePolyfillHelpers.js because it appears in neither the script-src directive nor the default-src directive of the Content Security Policy.

It appears there's a non-configurable debug variables that causes the local version of the file is used instead. Would it be possible to expose said variable via the component so we can use the local file instead?

v2 error: "Uncaught ReferenceError: exports is not defined" when using react-scripts v5

I installed this into a simple CRA project that has these dependencies:

"@testing-library/jest-dom": "^5.16.1",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.4.0",
"@types/node": "^17.0.8",
"@types/react": "^17.0.38",
"@types/react-dom": "^17.0.11",
"@typescript-eslint/eslint-plugin": "^5.9.0",
"@typescript-eslint/parser": "^5.9.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "5.0.0",
"typescript": "^4.5.4",
"web-vitals": "^2.1.2"

I think the main issue is the upgrade from react-scripts v4 to v5.

It is giving me this error:

Uncaught ReferenceError: exports is not defined
    node_modules time-input-polyfill/utils/npm/common/supportsTime.js@http://localhost:3001/static/js/bundle.js:42582
    factory http://localhost:3001/static/js/bundle.js:43179
    __webpack_require__ http://localhost:3001/static/js/bundle.js:42629
    fn http://localhost:3001/static/js/bundle.js:42850
    node_modules time-input-polyfill/react/src/core/TimeInputPolyfill.js@http://localhost:3001/static/js/bundle.js:525
    factory http://localhost:3001/static/js/bundle.js:43179
    __webpack_require__ http://localhost:3001/static/js/bundle.js:42629
    fn http://localhost:3001/static/js/bundle.js:42850
    node_modules time-input-polyfill/react/index.js@http://localhost:3001/static/js/bundle.js:420
    factory http://localhost:3001/static/js/bundle.js:43179
    __webpack_require__ http://localhost:3001/static/js/bundle.js:42629
    fn http://localhost:3001/static/js/bundle.js:42850
    tsx http://localhost:3001/static/js/bundle.js:16
    factory http://localhost:3001/static/js/bundle.js:43179
    __webpack_require__ http://localhost:3001/static/js/bundle.js:42629
    fn http://localhost:3001/static/js/bundle.js:42850
    tsx http://localhost:3001/static/js/bundle.js:124
    factory http://localhost:3001/static/js/bundle.js:43179
    __webpack_require__ http://localhost:3001/static/js/bundle.js:42629
    <anonymous> http://localhost:3001/static/js/bundle.js:43818
    <anonymous> http://localhost:3001/static/js/bundle.js:43820
bundle.js:42582:1
    js http://localhost:3001/static/js/bundle.js:42582
    factory http://localhost:3001/static/js/bundle.js:43179
    __webpack_require__ http://localhost:3001/static/js/bundle.js:42629
    fn http://localhost:3001/static/js/bundle.js:42850
    js http://localhost:3001/static/js/bundle.js:525
    factory http://localhost:3001/static/js/bundle.js:43179
    __webpack_require__ http://localhost:3001/static/js/bundle.js:42629
    fn http://localhost:3001/static/js/bundle.js:42850
    js http://localhost:3001/static/js/bundle.js:420
    factory http://localhost:3001/static/js/bundle.js:43179
    __webpack_require__ http://localhost:3001/static/js/bundle.js:42629
    fn http://localhost:3001/static/js/bundle.js:42850
    tsx http://localhost:3001/static/js/bundle.js:16
    factory http://localhost:3001/static/js/bundle.js:43179
    __webpack_require__ http://localhost:3001/static/js/bundle.js:42629
    fn http://localhost:3001/static/js/bundle.js:42850
    tsx http://localhost:3001/static/js/bundle.js:124
    factory http://localhost:3001/static/js/bundle.js:43179
    __webpack_require__ http://localhost:3001/static/js/bundle.js:42629
    <anonymous> http://localhost:3001/static/js/bundle.js:43818
    <anonymous> http://localhost:3001/static/js/bundle.js:43820

Repository demonstrating the error:
https://github.com/Dan503/time-input-polyfill-tests/tree/react-version

I can't figure out what the issue is :(

Add `ref` support

It is written as a functional component so ref support needs to be explicitly added using forwardRef

I would expect the structure to be like this:

const TimeInputPolyfill = (props) => {
  // main component code
}

const _TimeInputPolyfill = forwardRef((props, forwardedRef) => (
    <TimeInputPolyfill {...props} forwardedRef={forwardedRef} />
))

export default _TimeInputPolyfill 

The TypeScript types might be tricky to update.

Polyfill doesn't work with Virtual Keyboards

Due to virtual keyboards not triggering key down events, it isn't triggering the ManualEntryLog code. It instead tryis to run onChange which is not built to handle manual entry.

Any device that has a virtual keyboard most likely also has native time input support.

Since adding virtual keyboard support would require a lot of additional work (and file weight) I don't think supporting virtual keyboards is worth it.

Can't clear/reset input value in Safari

Since in Safari the element reverts back to a text input, there doesn't appear to be a way to manually clear the input to the initial "--:-- --" state.

I've tried manually passing in the following to the value prop:
empty string - no effect
null - error
undefined - error
"--:-- --" - "NaN:-- -- AM"

Any help would be appreciated!

Switching from AM to PM mode doesn't work correctly for 12:00 time

Steps for reproduce:

  1. Set up hours to '12', minutes to '00' and mode to 'AM' in the time input field. So for the start position you should see '12:00 AM'.
  2. Tab or click to mode segment in the time input field.
  3. Press 'p' on the keyboard or press arrow on the keyboard.

Actual behaviour:

Nothing happen at first and you will see AM on the mode segment, but if you do the 3rd step again you will change the mode to PM.

Expected behaviour:

The mode have to be switched from AM to PM after the first attempt of 3rd step.

custom onKeyDown events not working

when I try to add custom onKeyDown effect - I do not see any result. as I see in library code - it just return null if TimeInput state.usePolyfill === false. and even for polyfill logic - no handler inside.

polyfill stop working on second load

I have a time input polyfill in a modal
When modal opens for the first time the polyfill works fine
When I open the same modal again, with different values, it looks like internal components data is missing

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.