Vulcan/packages/vulcan-lib/lib/modules/components.js
Erik Schannen c3f33cb7e0 Changes to SmartForm behaviour
- Moved UI portions of FormComponent to FormComponentInner.jsx in vulcan-ui-bootstrap

 - Added user alert when the user navigates to another route while there are unsaved changed in the form (disabled by default)

 - Added setting forms.warnUnsavedChanges and SmartForm property warnUnsavedChanges to enable user alert

 - Added optional Revert button in FormSubmits to allow the user to discard any changes to the form; this is activated by passing a "revertCallback" property to SmartForm (which can be empty: () => {})

 - Added two functions that form components can access in the child context: refetchForm() to refetch the document from the database (in case it was updated by a background process), isChanged() to determine if there are any unsaved changes

 - For any phrases I have added to en_US.js I also added it to es_ES.js and fr_FR.js with the comment // TODO: translate

 - Updated Form.clearForm and Form.mutationSuccessCallback so that the user can continue working on the document after submitting it

 - The form now scrolls the flash message into view when a submit results in errors

 - Fixed bugs in FormComponent.shouldComponentUpdate() and Form.getDocument()

 - Fixed bug in FormComponent.handleChange() - number fields could not be cleared, only set to 0

 - Fixed a bug in FormComponent.getValue() - it returned the initial value of a checkbox even after it was set to false, and a number even after it was set to 0
2018-05-08 20:09:42 -04:00

139 lines
4.8 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { compose } from 'react-apollo'; // note: at the moment, compose@react-apollo === compose@redux ; see https://github.com/apollostack/react-apollo/blob/master/src/index.ts#L4-L7
import React from 'react';
export const Components = {}; // will be populated on startup (see vulcan:routing)
export const ComponentsTable = {} // storage for infos about components
/**
* Register a Vulcan component with a name, a raw component than can be extended
* and one or more optional higher order components.
*
* @param {String} name The name of the component to register.
* @param {React Component} rawComponent Interchangeable/extendable component.
* @param {...Function} hocs The HOCs to compose with the raw component.
*
* Note: when a component is registered without higher order component, `hocs` will be
* an empty array, and it's ok!
* See https://github.com/reactjs/redux/blob/master/src/compose.js#L13-L15
*
* @returns Structure of a component in the list:
*
* ComponentsTable.Foo = {
* name: 'Foo',
* hocs: [fn1, fn2],
* rawComponent: React.Component,
* call: () => compose(...hocs)(rawComponent),
* }
*
*/
export const registerComponent = (name, rawComponent, ...hocs) => {
// console.log('// registering component');
// console.log(name);
// console.log('raw component', rawComponent);
// console.log('higher order components', hocs);
// store the component in the table
ComponentsTable[name] = {
name,
rawComponent,
hocs,
};
};
/**
* Get a component registered with registerComponent(name, component, ...hocs).
*
* @param {String} name The name of the component to get.
* @returns {Function|React Component} A (wrapped) React component
*/
export const getComponent = (name) => {
const component = ComponentsTable[name];
if (!component) {
throw new Error(`Component ${name} not registered.`)
}
const hocs = component.hocs.map(hoc => Array.isArray(hoc) ? hoc[0](hoc[1]) : hoc);
return compose(...hocs)(component.rawComponent)
};
/**
* Populate the lookup table for components to be callable
* Called once on app startup
**/
export const populateComponentsApp = () => {
// loop over each component in the list
Object.keys(ComponentsTable).map(name => {
// populate an entry in the lookup table
Components[name] = getComponent(name);
// uncomment for debug
// console.log('init component:', name);
});
}
/**
* Get the **raw** (original) component registered with registerComponent
* without the possible HOCs wrapping it.
*
* @param {String} name The name of the component to get.
* @returns {Function|React Component} An interchangeable/extendable React component
*/
export const getRawComponent = (name) => {
return ComponentsTable[name].rawComponent;
};
/**
* Replace a Vulcan component with the same name with a new component or
* an extension of the raw component and one or more optional higher order components.
* This function keeps track of the previous HOCs and wrap the new HOCs around previous ones
*
* @param {String} name The name of the component to register.
* @param {React Component} rawComponent Interchangeable/extendable component.
* @param {...Function} hocs The HOCs to compose with the raw component.
* @returns {Function|React Component} A component callable with Components[name]
*
* Note: when a component is registered without higher order component, `hocs` will be
* an empty array, and it's ok!
* See https://github.com/reactjs/redux/blob/master/src/compose.js#L13-L15
*/
export const replaceComponent = (name, newComponent, ...newHocs) => {
const previousComponent = ComponentsTable[name];
// xxx : throw an error if the previous component doesn't exist
// console.log('// replacing component');
// console.log(name);
// console.log(newComponent);
// console.log('new hocs', newHocs);
// console.log('previous hocs', previousComponent.hocs);
return registerComponent(name, newComponent, ...newHocs, ...previousComponent.hocs);
};
export const copyHoCs = (sourceComponent, targetComponent) => {
return compose(...sourceComponent.hocs)(targetComponent);
}
/**
* Returns an instance of the given component name of function
* @param {string|function} component A component or registered component name
* @param {Object} [props] Optional properties to pass to the component
*/
//eslint-disable-next-line react/display-name
export const instantiateComponent = (component, props) => {
if (!component) {
return null;
} else if (typeof component === 'string') {
const Component = getComponent(component);
return <Component {...props}/>
} else if (typeof component === 'function' && component.prototype && component.prototype.isReactComponent) {
const Component = component;
return <Component {...props}/>
} else if (typeof component === 'function') {
return component(props);
} else {
return component;
}
};