UNPKG

@bigfishtv/cockpit

Version:

224 lines (201 loc) 5.69 kB
import PropTypes from 'prop-types' import React, { Component } from 'react' import { createValue, Fieldset } from '@bigfishtv/react-forms' import { post, get } from '../../api/xhrUtils' import { renderComponent } from '../../utils/componentUtils' import { validationErrors } from 'react-forms-cakephp' export default class Form extends Component { static propTypes = { dataUrl: PropTypes.string, defaultValue: PropTypes.object, value: PropTypes.object, schema: PropTypes.object, submitUrl: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), onLoad: PropTypes.func, onBeforeChange: PropTypes.func, onChange: PropTypes.func, onBeforeSubmit: PropTypes.func, onSubmitSuccess: PropTypes.func, onSubmitError: PropTypes.func, confirmDirty: PropTypes.bool, confirmMessage: PropTypes.string, valueMapper: PropTypes.func, render: PropTypes.func, } static defaultProps = { confirmDirty: true, valueMapper: data => data.data || data, confirmMessage: 'You have unsaved changes! If you leave this page, your changes will be lost!', } static contextTypes = { router: PropTypes.shape({ history: PropTypes.shape({ block: PropTypes.func, }), }), } constructor(props) { super(props) this.state = { data: {}, loading: !!props.dataUrl, formValue: createValue({ schema: props.schema, value: props.value || props.defaultValue, onChange: this.onChange, }), dirty: false, submitStatus: 'ready', } if (props.dataUrl) { this.loadData(props.dataUrl) } } loadData(url) { get({ url, callback: (data, responseData) => { this.setState({ data: responseData, loading: false, formValue: createValue({ schema: this.props.schema, value: this.props.valueMapper(responseData), onChange: this.onChange, }), }) this.props.onLoad && this.props.onLoad(this.props.valueMapper(responseData), responseData) }, }) } componentDidMount() { window.addEventListener('beforeunload', this.confirmUnload) } componentWillUnmount() { window.removeEventListener('beforeunload', this.confirmUnload) this.unblock && this.unblock() } componentWillReceiveProps(nextProps) { if (nextProps.value !== this.props.value) { this.setState({ dirty: false, formValue: createValue({ schema: nextProps.schema, value: nextProps.value, onChange: this.onChange, }), }) } if (nextProps.dataUrl && nextProps.dataUrl !== this.props.dataUrl) { this.loadData(nextProps.dataUrl) } } componentDidUpdate() { this.reactRouterConfirmUnload() } reactRouterConfirmUnload() { // compatibility with react router v4 if (this.context && this.context.router) { const shouldBlock = this.shouldBlockNavigation() if (shouldBlock && !this.unblock) { this.unblock = this.context.router.history.block(this.props.confirmMessage) } else if (!shouldBlock && this.unblock) { this.unblock() this.unblock = null } } } shouldBlockNavigation() { return this.props.confirmDirty && this.state.dirty && !window.dontPromptChanges } confirmUnload = e => { if (!this.shouldBlockNavigation()) return ;(e || window.event).returnValue = this.props.confirmMessage //Gecko + IE return this.props.confirmMessage } onChange = nextFormValue => { if (this.props.onBeforeChange) { nextFormValue = this.props.onBeforeChange(nextFormValue) if (nextFormValue === false) { return } } this.setState({ dirty: true, formValue: nextFormValue, submitStatus: this.state.submitStatus != 'busy' ? 'ready' : 'busy', }) this.props.onChange && this.props.onChange(nextFormValue.value) } getSubmitUrl() { if (typeof this.props.submitUrl == 'function') { return this.props.submitUrl(this.state.formValue.value) } else { return this.props.submitUrl } } handleSubmit = event => { event.preventDefault() if (this.state.submitStatus == 'busy') return if (this.props.onBeforeSubmit && this.props.onBeforeSubmit(this.state.formValue.value) === false) { return } this.setState( { submitStatus: 'busy', }, () => { post({ quiet: true, url: this.getSubmitUrl(), data: this.state.formValue.value, callback: (data, response) => { this.onChange( createValue({ schema: this.props.schema, value: data, onChange: this.onChange, }) ) this.setState({ submitStatus: 'success', dirty: false }) this.props.onSubmitSuccess && this.props.onSubmitSuccess(data, response) }, callbackError: response => { if (response.data && response.data.data && response.data.data.errors) { this.onChange( createValue({ schema: this.props.schema, value: this.state.formValue.value, onChange: this.onChange, params: { forceShowErrors: true }, errorList: validationErrors(response.data.data.errors), }) ) } this.props.onSubmitError && this.props.onSubmitError(response) this.setState({ submitStatus: 'failed' }) }, }) } ) } render() { const { render } = this.props const formProps = { // className, stylesheet: { Root: 'form' }, formValue: this.state.formValue, onSubmit: this.handleSubmit, } const childProps = { data: this.state.data, loading: this.state.loading, dirty: this.state.dirty, submitStatus: this.state.submitStatus, // also send these to child props? formValue: this.state.formValue, onSubmit: this.handleSubmit, } return <Fieldset {...formProps}>{renderComponent(render, childProps) || this.props.children}</Fieldset> } }