Skip to content

Instantly share code, notes, and snippets.

@lfalke
Last active August 8, 2023 05:10
Show Gist options
  • Star 45 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save lfalke/1c5e7168424c8b2a65dcfba425fcc310 to your computer and use it in GitHub Desktop.
Save lfalke/1c5e7168424c8b2a65dcfba425fcc310 to your computer and use it in GitHub Desktop.
Usage of react-stripe-elements with Material UI (v1.0) styling
import React, { PureComponent } from 'react'
import Grid from 'material-ui/Grid'
import { CardNumberElement, CardExpiryElement, CardCVCElement } from 'react-stripe-elements'
import StripeElementWrapper from './StripeElementWrapper'
export default class extends PureComponent {
static displayName = 'StripeCardsSection'
render() {
return (
<Grid container>
<Grid item xs={12}>
<StripeElementWrapper label="Card Number" component={CardNumberElement} />
</Grid>
<Grid item xs={7}>
<StripeElementWrapper label="Expiry (MM / YY)" component={CardExpiryElement} />
</Grid>
<Grid item xs={5}>
<StripeElementWrapper label="CVC" component={CardCVCElement} />
</Grid>
</Grid>
)
}
}
// A Wrapper for the <FormControl>, <InputLabel>, <Error> and the Stripe <*Element>.
// Similar to Material UI's <TextField>. Handles focused, empty and error state
// to correctly show the floating label and error messages etc.
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import { FormControl } from 'material-ui/Form'
import Input, { InputLabel } from 'material-ui/Input'
import StripeInput from './StripeInput'
import Errors from './Errors'
export default class extends PureComponent {
static displayName = 'StripeElementWrapper'
static propTypes = {
component: PropTypes.func.isRequired,
label: PropTypes.string.isRequired,
}
state = {
focused: false,
empty: true,
error: false,
}
handleBlur = () => {
this.setState({ focused: false })
}
handleFocus = () => {
this.setState({ focused: true })
}
handleChange = changeObj => {
this.setState({
error: changeObj.error,
empty: changeObj.empty,
})
}
render() {
const { component, label } = this.props
const { focused, empty, error } = this.state
return (
<div>
<FormControl fullWidth margin="normal">
<InputLabel
focused={focused}
shrink={focused || !empty}
error={!!error}>
{label}
</InputLabel>
<Input
fullWidth
inputComponent={StripeInput}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
onChange={this.handleChange}
inputProps={{ component }}
/>
</FormControl>
{error && <Errors>{error.message}</Errors>}
</div>
)
}
}
// Wrapper around the actual Stripe <*Element>, so that it can be used as `inputComponent`
// for Material UI's <Input>. Also adds some styling.
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import { withStyles } from 'material-ui/styles'
const styles = () => ({
root: {
width: '100%',
padding: '6px 0 7px',
cursor: 'text',
},
})
@withStyles(styles, { withTheme: true })
export default class extends PureComponent {
static displayName = 'StripeInput'
static propTypes = {
classes: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
component: PropTypes.func.isRequired,
onBlur: PropTypes.func,
onFocus: PropTypes.func,
onChange: PropTypes.func,
}
static defaultProps = {
onFocus: () => {},
onBlur: () => {},
onChange: () => {},
}
render() {
const {
classes: c,
theme,
component: Component,
onFocus,
onBlur,
onChange,
} = this.props
return (
<Component
className={c.root}
onFocus={onFocus}
onBlur={onBlur}
onChange={onChange}
placeholder=""
style={{
base: {
fontSize: `${theme.typography.fontSize}px`,
fontFamily: theme.typography.fontFamily,
color: '#000000de',
},
}}
/>
)
}
}
@rickymarcon
Copy link

Exactly what I was developing; except more elegant :)

You may want to update the package paths to the new format @material-ui/core/..

Thanks @lfalke.

@davidnathanael
Copy link

davidnathanael commented Sep 19, 2018

Thanks for this. I've been trying to use this to make react-final-form with material-ui and react-stripe-elements. It's quite overwhelming because of the contexts. Have you guys tried to do this? or do you have any tip on how to achieve it? thanks

@tobytraylor
Copy link

Hey. I just stumbled across this. I don't suppose you could post your Errors.js file?

@tupton
Copy link

tupton commented May 24, 2019

@tobytraylor I just used FormHelperText like the material-ui TextField on which this input wrapper is based does:

{error &&
  <FormHelperText error>
    {error.message}
  </FormHelperText>}

You could possibly embellish this or pass other props specific to the error message, but this displays error messages underneath the input like TextField.

@oliviertassinari
Copy link

You can find a dedicated thread to this problem in mui/material-ui#16037.

@jermsam
Copy link

jermsam commented Jun 14, 2019

Am afraid this is not working for me. I am getting this error: Warning: Failed context type: The context `addElementsLoadListener` is marked as required in `CardNum berElement`, but its value is `undefined`.

@Stassi
Copy link

Stassi commented Nov 26, 2019

Am afraid this is not working for me. I am getting this error: Warning: Failed context type: The context `addElementsLoadListener` is marked as required in `CardNum berElement`, but its value is `undefined`.

@jermsam those undefined values are usually provided by a React Stripe Elements implementation. The failed context issue is likely caused by missing required parents described in the setup procedure for stripe-react-elements@6.0.1 that would embed the content of this gist as a child:

<StripeProvider>
  <Elements>
    <InjectedForm>
      {contentOfThisGist}
    </InjectedForm>
  </Elements>
</StripeProvider>

@RaedElNABOULSI
Copy link

It is showing an error that 'c' is undefined in StripeInput.js

@oliviertassinari
Copy link

Please refer to mui/material-ui#16037 for a working solution.

@RaedElNABOULSI
Copy link

when pressing submit, the 'required' attribute is not triggered when the field is empty. The Component in stripeinput is disabling this option, any suggestions?

@agicent0rishabh
Copy link

Am afraid this is not working for me. I am getting this error: Warning: Failed context type: The context `addElementsLoadListener` is marked as required in `CardNum berElement`, but its value is `undefined`.

@jermsam those undefined values are usually provided by a React Stripe Elements implementation. The failed context issue is likely caused by missing required parents described in the setup procedure for stripe-react-elements@6.0.1 that would embed the content of this gist as a child:

<StripeProvider>
  <Elements>
    <InjectedForm>
      {contentOfThisGist}
    </InjectedForm>
  </Elements>
</StripeProvider>

Where is InjectedForm Component?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment