@financial-times/x-interaction
Advanced tools
Comparing version 1.0.0-beta.9 to 1.0.0-beta.10
@@ -221,3 +221,22 @@ const { h } = require('@financial-times/x-engine'); | ||
}); | ||
it('should pass changed outside props to state updaters', async () => { | ||
const Base = () => null; | ||
const Wrapped = withActions({ | ||
foo: () => ({ bar }) => ({ bar: bar + 5 }), | ||
})(Base); | ||
const target = mount(<Wrapped bar={5} />); | ||
target.setProps({ bar: 10 }); | ||
target.update(); | ||
await target.find(Base).prop('actions').foo(); | ||
target.update(); | ||
expect( | ||
target.find(Base).prop('bar') | ||
).toBe(15); | ||
}); | ||
describe('actionsRef', () => { | ||
@@ -224,0 +243,0 @@ it('should pass actions to actionsRef on mount and null on unmount', async () => { |
@@ -10,79 +10,137 @@ 'use strict'; | ||
function _objectWithoutPropertiesLoose(source, excluded) { | ||
if (source == null) return {}; | ||
var target = {}; | ||
var sourceKeys = Object.keys(source); | ||
var key, i; | ||
for (i = 0; i < sourceKeys.length; i++) { | ||
key = sourceKeys[i]; | ||
if (excluded.indexOf(key) >= 0) continue; | ||
target[key] = source[key]; | ||
} | ||
return target; | ||
} | ||
function _objectWithoutProperties(source, excluded) { | ||
if (source == null) return {}; | ||
var target = _objectWithoutPropertiesLoose(source, excluded); | ||
var key, i; | ||
if (Object.getOwnPropertySymbols) { | ||
var sourceSymbolKeys = Object.getOwnPropertySymbols(source); | ||
for (i = 0; i < sourceSymbolKeys.length; i++) { | ||
key = sourceSymbolKeys[i]; | ||
if (excluded.indexOf(key) >= 0) continue; | ||
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; | ||
target[key] = source[key]; | ||
} | ||
} | ||
return target; | ||
} | ||
const InteractionRender = ({ | ||
id, | ||
actions, | ||
state, | ||
initialState, | ||
inFlight, | ||
Component | ||
}) => xEngine.h(Component, Object.assign({}, initialState, state, { id, actions }, { isLoading: inFlight > 0 })); | ||
id, | ||
actions, | ||
state, | ||
initialState, | ||
inFlight, | ||
Component | ||
}) => xEngine.h(Component, Object.assign({}, initialState, state, { | ||
id, | ||
actions | ||
}, { | ||
isLoading: inFlight > 0 | ||
})); | ||
const mapValues = (obj, fn) => Object.keys(obj).reduce((mapped, key) => Object.assign(mapped, { | ||
[key]: fn(obj[key], key, obj) | ||
[key]: fn(obj[key], key, obj) | ||
}), {}); | ||
class InteractionClass extends xEngine.Component { | ||
constructor(props, ...args) { | ||
super(props, ...args); | ||
constructor(props, ...args) { | ||
super(props, ...args); | ||
this.state = { | ||
state: {}, | ||
inFlight: 0 | ||
}; | ||
this.createActions(props); | ||
} | ||
this.state = { | ||
state: {}, | ||
inFlight: 0 | ||
}; | ||
createActions(props) { | ||
this.actions = mapValues(props.actions, func => (...args) => new Promise(function ($return, $error) { | ||
let stateUpdate, nextState; | ||
// mark as loading one microtask later. if the action is synchronous then | ||
// setting loading back to false will happen in the same microtask and no | ||
// additional render will be scheduled. | ||
Promise.resolve().then(() => { | ||
this.setState(({ | ||
inFlight | ||
}) => ({ | ||
inFlight: inFlight + 1 | ||
})); | ||
}); | ||
return Promise.resolve(Promise.resolve(func(...args))).then(function ($await_3) { | ||
try { | ||
stateUpdate = $await_3; | ||
return Promise.resolve(new Promise(function ($return, $error) { | ||
if (typeof stateUpdate === 'function') { | ||
return Promise.resolve(Promise.resolve(stateUpdate(Object.assign({}, props.initialState, this.state.state)))).then(function ($await_4) { | ||
try { | ||
return $return(Object.assign(this.state.state, $await_4)); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
} | ||
this.actions = mapValues(props.actions, func => (...args) => new Promise(function ($return, $error) { | ||
let stateUpdate, nextState; | ||
return $return(Object.assign(this.state.state, stateUpdate)); | ||
}.bind(this))).then(function ($await_5) { | ||
try { | ||
nextState = $await_5; | ||
return $return(new Promise(resolve => this.setState({ | ||
state: nextState | ||
}, () => this.setState(({ | ||
inFlight | ||
}) => ({ | ||
inFlight: inFlight - 1 | ||
}), resolve)))); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
}.bind(this))); | ||
} | ||
// mark as loading one microtask later. if the action is synchronous then | ||
// setting loading back to false will happen in the same microtask and no | ||
// additional render will be scheduled. | ||
Promise.resolve().then(() => { | ||
this.setState(({ inFlight }) => ({ inFlight: inFlight + 1 })); | ||
}); | ||
componentWillReceiveProps(props) { | ||
this.createActions(props); | ||
} | ||
return Promise.resolve(func(...args)).then(function ($await_3) { | ||
try { | ||
stateUpdate = $await_3; | ||
return new Promise(function ($return, $error) { | ||
if (typeof stateUpdate === 'function') { | ||
return Promise.resolve(stateUpdate(Object.assign({}, props.initialState, this.state.state))).then(function ($await_4) { | ||
try { | ||
return $return(Object.assign(this.state.state, $await_4)); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
}return $return(Object.assign(this.state.state, stateUpdate)); | ||
}.bind(this)).then(function ($await_5) { | ||
try { | ||
nextState = $await_5; | ||
componentDidMount() { | ||
if (this.props.actionsRef) { | ||
this.props.actionsRef(this.actions); | ||
} | ||
} | ||
componentWillUnmount() { | ||
if (this.props.actionsRef) { | ||
this.props.actionsRef(null); | ||
} | ||
} | ||
return $return(new Promise(resolve => this.setState({ state: nextState }, () => this.setState(({ inFlight }) => ({ inFlight: inFlight - 1 }), resolve)))); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
}.bind(this))); | ||
} | ||
render() { | ||
return xEngine.h(InteractionRender, Object.assign({}, this.props, this.state, { | ||
actions: this.actions | ||
})); | ||
} | ||
componentDidMount() { | ||
if (this.props.actionsRef) { | ||
this.props.actionsRef(this.actions); | ||
} | ||
} | ||
componentWillUnmount() { | ||
if (this.props.actionsRef) { | ||
this.props.actionsRef(null); | ||
} | ||
} | ||
render() { | ||
return xEngine.h(InteractionRender, Object.assign({}, this.props, this.state, { actions: this.actions })); | ||
} | ||
} | ||
@@ -93,173 +151,179 @@ | ||
const InteractionSSR = ({ | ||
initialState, | ||
Component, | ||
id = `${getComponentName(Component)}-${shortId()}`, | ||
actions, | ||
serialiser | ||
initialState, | ||
Component, | ||
id = `${getComponentName(Component)}-${shortId()}`, | ||
actions, | ||
serialiser | ||
}) => { | ||
if (serialiser) { | ||
serialiser.addData({ | ||
id, | ||
Component, | ||
props: initialState | ||
}); | ||
} | ||
if (serialiser) { | ||
serialiser.addData({ | ||
id, | ||
Component, | ||
props: initialState | ||
}); | ||
} | ||
return xEngine.h( | ||
'div', | ||
{ 'data-x-dash-id': id }, | ||
xEngine.h(InteractionRender, { Component, initialState, id, actions }) | ||
); | ||
return xEngine.h("div", { | ||
"data-x-dash-id": id | ||
}, xEngine.h(InteractionRender, { | ||
Component, | ||
initialState, | ||
id, | ||
actions | ||
})); | ||
}; | ||
function wrapComponentName(Component, Enhanced) { | ||
const originalDisplayName = getComponentName(Component); | ||
Enhanced.displayName = `withActions(${originalDisplayName})`; | ||
Enhanced.wrappedDisplayName = originalDisplayName; | ||
const originalDisplayName = getComponentName(Component); | ||
Enhanced.displayName = `withActions(${originalDisplayName})`; | ||
Enhanced.wrappedDisplayName = originalDisplayName; | ||
} | ||
const registeredComponents = {}; | ||
function registerComponent(component) { | ||
registeredComponents[component.wrappedDisplayName] = component; | ||
registeredComponents[component.wrappedDisplayName] = component; | ||
} | ||
function getComponent(name) { | ||
return registeredComponents[name]; | ||
return registeredComponents[name]; | ||
} | ||
class HydrationWrapper extends xEngine.Component { | ||
render() { | ||
var _props = this.props; | ||
const Component = _props.Component, | ||
props = _props.props, | ||
id = _props.id; | ||
render() { | ||
const _this$props = this.props, | ||
Component = _this$props.Component, | ||
props = _this$props.props, | ||
id = _this$props.id; | ||
return xEngine.h(Component, Object.assign({}, props, { | ||
id: id, | ||
actionsRef: a => this.actions = a | ||
})); | ||
} | ||
return xEngine.h(Component, Object.assign({}, props, { id: id, actionsRef: a => this.actions = a })); | ||
} | ||
componentDidMount() { | ||
if (this.props.wrapper) { | ||
this.props.wrapper.addEventListener('x-interaction.trigger-action', this); | ||
} | ||
} | ||
componentDidMount() { | ||
if (this.props.wrapper) { | ||
this.props.wrapper.addEventListener('x-interaction.trigger-action', this); | ||
} | ||
} | ||
componentWillUnmount() { | ||
if (this.props.wrapper) { | ||
this.props.wrapper.removeEventListener('x-interaction.trigger-action', this); | ||
} | ||
} | ||
componentWillUnmount() { | ||
if (this.props.wrapper) { | ||
this.props.wrapper.removeEventListener('x-interaction.trigger-action', this); | ||
} | ||
} | ||
handleEvent(event) { | ||
const _event$detail = event.detail, | ||
action = _event$detail.action, | ||
_event$detail$args = _event$detail.args, | ||
args = _event$detail$args === void 0 ? [] : _event$detail$args; | ||
handleEvent(event) { | ||
var _event$detail = event.detail; | ||
const action = _event$detail.action; | ||
var _event$detail$args = _event$detail.args; | ||
const args = _event$detail$args === undefined ? [] : _event$detail$args; | ||
if (this.actions && this.actions[action]) { | ||
this.actions[action](...args); | ||
} | ||
} | ||
if (this.actions && this.actions[action]) { | ||
this.actions[action](...args); | ||
} | ||
} | ||
} | ||
function hydrate() { | ||
if (typeof window === 'undefined') { | ||
throw new Error('x-interaction hydrate should only be called in the browser'); | ||
} | ||
if (typeof window === 'undefined') { | ||
throw new Error('x-interaction hydrate should only be called in the browser'); | ||
} | ||
if (!('_xDashInteractionHydrationData' in window)) { | ||
throw new Error(`x-interaction hydrate was called without hydration data available. this can happen if you call hydrate before the serialised data is available, or if you're not including the hydration data with your server-rendered markup.`); | ||
} | ||
if (!('_xDashInteractionHydrationData' in window)) { | ||
throw new Error(`x-interaction hydrate was called without hydration data available. this can happen if you call hydrate before the serialised data is available, or if you're not including the hydration data with your server-rendered markup.`); | ||
} | ||
const serialiserOrdering = `make sure you're always outputting the serialiser's data in the same request that the serialiser was created. see https://financial-times.github.io/x-dash/components/x-interaction/#hydrating for more details.`; | ||
const serialiserOrdering = `make sure you're always outputting the serialiser's data in the same request that the serialiser was created. see https://financial-times.github.io/x-dash/components/x-interaction/#hydrating for more details.`; | ||
window._xDashInteractionHydrationData.forEach(({ id, component, props }) => { | ||
const wrapper = document.querySelector(`[data-x-dash-id="${id}"]`); | ||
window._xDashInteractionHydrationData.forEach(({ | ||
id, | ||
component, | ||
props | ||
}) => { | ||
const wrapper = document.querySelector(`[data-x-dash-id="${id}"]`); | ||
if (!wrapper) { | ||
throw new Error(`component markup for ${id} was not found on the page. It was expected to be an instance of ${component}. it's likely that this hydration data is from another request. ${serialiserOrdering}`); | ||
} | ||
if (!wrapper) { | ||
throw new Error(`component markup for ${id} was not found on the page. It was expected to be an instance of ${component}. it's likely that this hydration data is from another request. ${serialiserOrdering}`); | ||
} | ||
const Component = getComponent(component); | ||
const Component = getComponent(component); | ||
while (wrapper.firstChild) { | ||
wrapper.removeChild(wrapper.firstChild); | ||
} | ||
while (wrapper.firstChild) { | ||
wrapper.removeChild(wrapper.firstChild); | ||
} | ||
xEngine.render(xEngine.h(HydrationWrapper, { | ||
Component, | ||
props, | ||
id, | ||
wrapper | ||
}), wrapper); | ||
}); | ||
xEngine.render(xEngine.h(HydrationWrapper, { | ||
Component, | ||
props, | ||
id, | ||
wrapper | ||
}), wrapper); | ||
}); | ||
document.querySelectorAll('[data-x-dash-id]').forEach(element => { | ||
const xDashId = element.dataset.xDashId; | ||
document.querySelectorAll('[data-x-dash-id]').forEach(element => { | ||
const xDashId = element.dataset.xDashId; | ||
const hasData = window._xDashInteractionHydrationData.some(({ | ||
id | ||
}) => id === xDashId); | ||
const hasData = window._xDashInteractionHydrationData.some(({ id }) => id === xDashId); | ||
if (!hasData) { | ||
throw new Error(`found component markup for ${xDashId} without any hydration data. it's likely that its hydration data has been output in another request, or that the component was rendered after the serialisation data was output. ${serialiserOrdering}`); | ||
} | ||
}); | ||
if (!hasData) { | ||
throw new Error(`found component markup for ${xDashId} without any hydration data. it's likely that its hydration data has been output in another request, or that the component was rendered after the serialisation data was output. ${serialiserOrdering}`); | ||
} | ||
}); | ||
} | ||
const HydrationData = ({ serialiser }) => { | ||
if (serialiser) { | ||
const data = serialiser.flushHydrationData(); | ||
const HydrationData = ({ | ||
serialiser | ||
}) => { | ||
if (serialiser) { | ||
const data = serialiser.flushHydrationData(); | ||
return xEngine.h("script", { | ||
dangerouslySetInnerHTML: { | ||
__html: `window._xDashInteractionHydrationData = ${JSON.stringify(data)}` | ||
} | ||
}); | ||
} | ||
return xEngine.h('script', { dangerouslySetInnerHTML: { __html: `window._xDashInteractionHydrationData = ${JSON.stringify(data)}` } }); | ||
} | ||
return null; | ||
return null; | ||
}; | ||
class Serialiser { | ||
constructor() { | ||
this.destroyed = false; | ||
this.data = []; | ||
} | ||
constructor() { | ||
this.destroyed = false; | ||
this.data = []; | ||
} | ||
addData({ id, Component, props }) { | ||
if (this.destroyed) { | ||
throw new Error(`an interaction component was rendered after flushHydrationData was called. ensure you're outputting the hydration data after rendering every component`); | ||
} | ||
addData({ | ||
id, | ||
Component, | ||
props | ||
}) { | ||
if (this.destroyed) { | ||
throw new Error(`an interaction component was rendered after flushHydrationData was called. ensure you're outputting the hydration data after rendering every component`); | ||
} | ||
this.data.push({ | ||
id, | ||
component: getComponentName(Component), | ||
props | ||
}); | ||
} | ||
this.data.push({ | ||
id, | ||
component: getComponentName(Component), | ||
props | ||
}); | ||
} | ||
flushHydrationData() { | ||
if (this.destroyed) { | ||
throw new Error(`a Serialiser's flushHydrationData was called twice. ensure you're not reusing a Serialiser between requests`); | ||
} | ||
flushHydrationData() { | ||
if (this.destroyed) { | ||
throw new Error(`a Serialiser's flushHydrationData was called twice. ensure you're not reusing a Serialiser between requests`); | ||
} | ||
this.destroyed = true; | ||
return this.data; | ||
} | ||
this.destroyed = true; | ||
return this.data; | ||
} | ||
outputHydrationData() { | ||
return xEngine.render(xEngine.h(HydrationData, { serialiser: this })); | ||
} | ||
} | ||
var objectWithoutProperties = function (obj, keys) { | ||
var target = {}; | ||
for (var i in obj) { | ||
if (keys.indexOf(i) >= 0) continue; | ||
if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; | ||
target[i] = obj[i]; | ||
outputHydrationData() { | ||
return xEngine.render(xEngine.h(HydrationData, { | ||
serialiser: this | ||
})); | ||
} | ||
return target; | ||
}; | ||
} | ||
// use the class version for clientside and the static version for server | ||
const Interaction = typeof window !== 'undefined' ? InteractionClass : InteractionSSR; | ||
@@ -270,47 +334,45 @@ | ||
const withActions = (getActions, getDefaultState = {}) => Component => { | ||
const _wraps = { getActions, getDefaultState, Component }; | ||
const _wraps = { | ||
getActions, | ||
getDefaultState, | ||
Component | ||
}; // if the component we're wrapping is already wrapped, we don't want | ||
// to wrap it further. so, discard its wrapper and rewrap the original | ||
// component with the new actions on top | ||
// if the component we're wrapping is already wrapped, we don't want | ||
// to wrap it further. so, discard its wrapper and rewrap the original | ||
// component with the new actions on top | ||
if (Component._wraps) { | ||
const wrappedGetActions = Component._wraps.getActions; | ||
const wrappedGetDefaultState = Component._wraps.getDefaultState; | ||
if (Component._wraps) { | ||
const wrappedGetActions = Component._wraps.getActions; | ||
const wrappedGetDefaultState = Component._wraps.getDefaultState; | ||
Component = Component._wraps.Component; | ||
Component = Component._wraps.Component; | ||
getActions = initialState => Object.assign(invoke(wrappedGetActions, initialState), invoke(_wraps.getActions, initialState)); | ||
getActions = initialState => Object.assign(invoke(wrappedGetActions, initialState), invoke(_wraps.getActions, initialState)); | ||
getDefaultState = initialState => Object.assign(invoke(wrappedGetDefaultState, initialState), invoke(_wraps.getDefaultState, initialState)); | ||
} | ||
getDefaultState = initialState => Object.assign(invoke(wrappedGetDefaultState, initialState), invoke(_wraps.getDefaultState, initialState)); | ||
} | ||
function Enhanced(_ref) { | ||
let id = _ref.id, | ||
actionsRef = _ref.actionsRef, | ||
serialiser = _ref.serialiser, | ||
initialState = _objectWithoutProperties(_ref, ["id", "actionsRef", "serialiser"]); | ||
function Enhanced(_ref) { | ||
let id = _ref.id, | ||
actionsRef = _ref.actionsRef, | ||
serialiser = _ref.serialiser, | ||
initialState = objectWithoutProperties(_ref, ['id', 'actionsRef', 'serialiser']); | ||
const actions = invoke(getActions, initialState); | ||
const defaultState = invoke(getDefaultState, initialState); | ||
return xEngine.h(Interaction, { | ||
id, | ||
Component, | ||
initialState: Object.assign({}, defaultState, initialState), | ||
actionsRef, | ||
serialiser, | ||
actions | ||
}); | ||
} // store what we're wrapping for later wrappers to replace | ||
const actions = invoke(getActions, initialState); | ||
const defaultState = invoke(getDefaultState, initialState); | ||
return xEngine.h(Interaction, { | ||
id, | ||
Component, | ||
initialState: Object.assign({}, defaultState, initialState), | ||
actionsRef, | ||
serialiser, | ||
actions | ||
}); | ||
} | ||
Enhanced._wraps = _wraps; // set the displayName of the Enhanced component for debugging | ||
// store what we're wrapping for later wrappers to replace | ||
Enhanced._wraps = _wraps; | ||
wrapComponentName(Component, Enhanced); // register the component under its name for later hydration from serialised data | ||
// set the displayName of the Enhanced component for debugging | ||
wrapComponentName(Component, Enhanced); | ||
// register the component under its name for later hydration from serialised data | ||
registerComponent(Enhanced); | ||
return Enhanced; | ||
registerComponent(Enhanced); | ||
return Enhanced; | ||
}; | ||
@@ -317,0 +379,0 @@ |
@@ -10,37 +10,25 @@ 'use strict'; | ||
var InteractionRender = function InteractionRender(_ref) { | ||
var id = _ref.id, | ||
actions = _ref.actions, | ||
state = _ref.state, | ||
initialState = _ref.initialState, | ||
inFlight = _ref.inFlight, | ||
Component = _ref.Component; | ||
return xEngine.h(Component, Object.assign({}, initialState, state, { id: id, actions: actions }, { isLoading: inFlight > 0 })); | ||
}; | ||
var classCallCheck = function (instance, Constructor) { | ||
function _classCallCheck(instance, Constructor) { | ||
if (!(instance instanceof Constructor)) { | ||
throw new TypeError("Cannot call a class as a function"); | ||
} | ||
}; | ||
} | ||
var createClass = function () { | ||
function defineProperties(target, props) { | ||
for (var i = 0; i < props.length; i++) { | ||
var descriptor = props[i]; | ||
descriptor.enumerable = descriptor.enumerable || false; | ||
descriptor.configurable = true; | ||
if ("value" in descriptor) descriptor.writable = true; | ||
Object.defineProperty(target, descriptor.key, descriptor); | ||
} | ||
function _defineProperties(target, props) { | ||
for (var i = 0; i < props.length; i++) { | ||
var descriptor = props[i]; | ||
descriptor.enumerable = descriptor.enumerable || false; | ||
descriptor.configurable = true; | ||
if ("value" in descriptor) descriptor.writable = true; | ||
Object.defineProperty(target, descriptor.key, descriptor); | ||
} | ||
} | ||
return function (Constructor, protoProps, staticProps) { | ||
if (protoProps) defineProperties(Constructor.prototype, protoProps); | ||
if (staticProps) defineProperties(Constructor, staticProps); | ||
return Constructor; | ||
}; | ||
}(); | ||
function _createClass(Constructor, protoProps, staticProps) { | ||
if (protoProps) _defineProperties(Constructor.prototype, protoProps); | ||
if (staticProps) _defineProperties(Constructor, staticProps); | ||
return Constructor; | ||
} | ||
var defineProperty = function (obj, key, value) { | ||
function _defineProperty(obj, key, value) { | ||
if (key in obj) { | ||
@@ -58,7 +46,7 @@ Object.defineProperty(obj, key, { | ||
return obj; | ||
}; | ||
} | ||
var inherits = function (subClass, superClass) { | ||
function _inherits(subClass, superClass) { | ||
if (typeof superClass !== "function" && superClass !== null) { | ||
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); | ||
throw new TypeError("Super expression must either be null or a function"); | ||
} | ||
@@ -69,3 +57,2 @@ | ||
value: subClass, | ||
enumerable: false, | ||
writable: true, | ||
@@ -75,407 +62,512 @@ configurable: true | ||
}); | ||
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; | ||
}; | ||
if (superClass) _setPrototypeOf(subClass, superClass); | ||
} | ||
var objectWithoutProperties = function (obj, keys) { | ||
function _getPrototypeOf(o) { | ||
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { | ||
return o.__proto__ || Object.getPrototypeOf(o); | ||
}; | ||
return _getPrototypeOf(o); | ||
} | ||
function _setPrototypeOf(o, p) { | ||
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { | ||
o.__proto__ = p; | ||
return o; | ||
}; | ||
return _setPrototypeOf(o, p); | ||
} | ||
function _objectWithoutPropertiesLoose(source, excluded) { | ||
if (source == null) return {}; | ||
var target = {}; | ||
var sourceKeys = Object.keys(source); | ||
var key, i; | ||
for (var i in obj) { | ||
if (keys.indexOf(i) >= 0) continue; | ||
if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; | ||
target[i] = obj[i]; | ||
for (i = 0; i < sourceKeys.length; i++) { | ||
key = sourceKeys[i]; | ||
if (excluded.indexOf(key) >= 0) continue; | ||
target[key] = source[key]; | ||
} | ||
return target; | ||
}; | ||
} | ||
var possibleConstructorReturn = function (self, call) { | ||
if (!self) { | ||
function _objectWithoutProperties(source, excluded) { | ||
if (source == null) return {}; | ||
var target = _objectWithoutPropertiesLoose(source, excluded); | ||
var key, i; | ||
if (Object.getOwnPropertySymbols) { | ||
var sourceSymbolKeys = Object.getOwnPropertySymbols(source); | ||
for (i = 0; i < sourceSymbolKeys.length; i++) { | ||
key = sourceSymbolKeys[i]; | ||
if (excluded.indexOf(key) >= 0) continue; | ||
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; | ||
target[key] = source[key]; | ||
} | ||
} | ||
return target; | ||
} | ||
function _assertThisInitialized(self) { | ||
if (self === void 0) { | ||
throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); | ||
} | ||
return call && (typeof call === "object" || typeof call === "function") ? call : self; | ||
}; | ||
return self; | ||
} | ||
var toConsumableArray = function (arr) { | ||
function _possibleConstructorReturn(self, call) { | ||
if (call && (typeof call === "object" || typeof call === "function")) { | ||
return call; | ||
} | ||
return _assertThisInitialized(self); | ||
} | ||
function _toConsumableArray(arr) { | ||
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); | ||
} | ||
function _arrayWithoutHoles(arr) { | ||
if (Array.isArray(arr)) { | ||
for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; | ||
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; | ||
return arr2; | ||
} else { | ||
return Array.from(arr); | ||
} | ||
} | ||
function _iterableToArray(iter) { | ||
if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); | ||
} | ||
function _nonIterableSpread() { | ||
throw new TypeError("Invalid attempt to spread non-iterable instance"); | ||
} | ||
var InteractionRender = function InteractionRender(_ref) { | ||
var id = _ref.id, | ||
actions = _ref.actions, | ||
state = _ref.state, | ||
initialState = _ref.initialState, | ||
inFlight = _ref.inFlight, | ||
Component = _ref.Component; | ||
return xEngine.h(Component, Object.assign({}, initialState, state, { | ||
id: id, | ||
actions: actions | ||
}, { | ||
isLoading: inFlight > 0 | ||
})); | ||
}; | ||
var mapValues = function mapValues(obj, fn) { | ||
return Object.keys(obj).reduce(function (mapped, key) { | ||
return Object.assign(mapped, defineProperty({}, key, fn(obj[key], key, obj))); | ||
}, {}); | ||
return Object.keys(obj).reduce(function (mapped, key) { | ||
return Object.assign(mapped, _defineProperty({}, key, fn(obj[key], key, obj))); | ||
}, {}); | ||
}; | ||
var InteractionClass = function (_Component) { | ||
inherits(InteractionClass, _Component); | ||
var InteractionClass = | ||
/*#__PURE__*/ | ||
function (_Component) { | ||
_inherits(InteractionClass, _Component); | ||
function InteractionClass(props) { | ||
var _ref; | ||
function InteractionClass(props) { | ||
var _getPrototypeOf2; | ||
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { | ||
args[_key - 1] = arguments[_key]; | ||
} | ||
var _this; | ||
classCallCheck(this, InteractionClass); | ||
_classCallCheck(this, InteractionClass); | ||
var _this = possibleConstructorReturn(this, (_ref = InteractionClass.__proto__ || Object.getPrototypeOf(InteractionClass)).call.apply(_ref, [this, props].concat(args))); | ||
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { | ||
args[_key - 1] = arguments[_key]; | ||
} | ||
_this.state = { | ||
state: {}, | ||
inFlight: 0 | ||
}; | ||
_this = _possibleConstructorReturn(this, (_getPrototypeOf2 = _getPrototypeOf(InteractionClass)).call.apply(_getPrototypeOf2, [this, props].concat(args))); | ||
_this.state = { | ||
state: {}, | ||
inFlight: 0 | ||
}; | ||
_this.actions = mapValues(props.actions, function (func) { | ||
return function () { | ||
var $args = arguments;return new Promise(function ($return, $error) { | ||
var stateUpdate, nextState; | ||
_this.createActions(props); | ||
// mark as loading one microtask later. if the action is synchronous then | ||
// setting loading back to false will happen in the same microtask and no | ||
// additional render will be scheduled. | ||
Promise.resolve().then(function () { | ||
_this.setState(function (_ref2) { | ||
var inFlight = _ref2.inFlight; | ||
return { inFlight: inFlight + 1 }; | ||
}); | ||
}); | ||
return _this; | ||
} | ||
return Promise.resolve(func.apply(undefined, $args)).then(function ($await_3) { | ||
try { | ||
stateUpdate = $await_3; | ||
_createClass(InteractionClass, [{ | ||
key: "createActions", | ||
value: function createActions(props) { | ||
var _this2 = this; | ||
return new Promise(function ($return, $error) { | ||
if (typeof stateUpdate === 'function') { | ||
return Promise.resolve(stateUpdate(Object.assign({}, props.initialState, _this.state.state))).then(function ($await_4) { | ||
try { | ||
return $return(Object.assign(_this.state.state, $await_4)); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}, $error); | ||
}return $return(Object.assign(_this.state.state, stateUpdate)); | ||
}).then(function ($await_5) { | ||
try { | ||
nextState = $await_5; | ||
this.actions = mapValues(props.actions, function (func) { | ||
return function () { | ||
var $args = arguments; | ||
return new Promise(function ($return, $error) { | ||
var stateUpdate, nextState; | ||
// mark as loading one microtask later. if the action is synchronous then | ||
// setting loading back to false will happen in the same microtask and no | ||
// additional render will be scheduled. | ||
Promise.resolve().then(function () { | ||
_this2.setState(function (_ref) { | ||
var inFlight = _ref.inFlight; | ||
return { | ||
inFlight: inFlight + 1 | ||
}; | ||
}); | ||
}); | ||
return Promise.resolve(Promise.resolve(func.apply(void 0, $args))).then(function ($await_3) { | ||
try { | ||
stateUpdate = $await_3; | ||
return Promise.resolve(new Promise(function ($return, $error) { | ||
if (typeof stateUpdate === 'function') { | ||
return Promise.resolve(Promise.resolve(stateUpdate(Object.assign({}, props.initialState, _this2.state.state)))).then(function ($await_4) { | ||
try { | ||
return $return(Object.assign(_this2.state.state, $await_4)); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}, $error); | ||
} | ||
return $return(new Promise(function (resolve) { | ||
return _this.setState({ state: nextState }, function () { | ||
return _this.setState(function (_ref3) { | ||
var inFlight = _ref3.inFlight; | ||
return { inFlight: inFlight - 1 }; | ||
}, resolve); | ||
}); | ||
})); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}, $error); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}, $error); | ||
}); | ||
}; | ||
}); | ||
return _this; | ||
} | ||
return $return(Object.assign(_this2.state.state, stateUpdate)); | ||
})).then(function ($await_5) { | ||
try { | ||
nextState = $await_5; | ||
return $return(new Promise(function (resolve) { | ||
return _this2.setState({ | ||
state: nextState | ||
}, function () { | ||
return _this2.setState(function (_ref2) { | ||
var inFlight = _ref2.inFlight; | ||
return { | ||
inFlight: inFlight - 1 | ||
}; | ||
}, resolve); | ||
}); | ||
})); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}, $error); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}, $error); | ||
}); | ||
}; | ||
}); | ||
} | ||
}, { | ||
key: "componentWillReceiveProps", | ||
value: function componentWillReceiveProps(props) { | ||
this.createActions(props); | ||
} | ||
}, { | ||
key: "componentDidMount", | ||
value: function componentDidMount() { | ||
if (this.props.actionsRef) { | ||
this.props.actionsRef(this.actions); | ||
} | ||
} | ||
}, { | ||
key: "componentWillUnmount", | ||
value: function componentWillUnmount() { | ||
if (this.props.actionsRef) { | ||
this.props.actionsRef(null); | ||
} | ||
} | ||
}, { | ||
key: "render", | ||
value: function render() { | ||
return xEngine.h(InteractionRender, Object.assign({}, this.props, this.state, { | ||
actions: this.actions | ||
})); | ||
} | ||
}]); | ||
createClass(InteractionClass, [{ | ||
key: 'componentDidMount', | ||
value: function componentDidMount() { | ||
if (this.props.actionsRef) { | ||
this.props.actionsRef(this.actions); | ||
} | ||
} | ||
}, { | ||
key: 'componentWillUnmount', | ||
value: function componentWillUnmount() { | ||
if (this.props.actionsRef) { | ||
this.props.actionsRef(null); | ||
} | ||
} | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
return xEngine.h(InteractionRender, Object.assign({}, this.props, this.state, { actions: this.actions })); | ||
} | ||
}]); | ||
return InteractionClass; | ||
return InteractionClass; | ||
}(xEngine.Component); | ||
var getComponentName = function getComponentName(Component) { | ||
return Component.displayName || Component.name || 'Unknown'; | ||
return Component.displayName || Component.name || 'Unknown'; | ||
}; | ||
var InteractionSSR = function InteractionSSR(_ref) { | ||
var initialState = _ref.initialState, | ||
Component = _ref.Component, | ||
_ref$id = _ref.id, | ||
id = _ref$id === undefined ? getComponentName(Component) + '-' + shortId() : _ref$id, | ||
actions = _ref.actions, | ||
serialiser = _ref.serialiser; | ||
var initialState = _ref.initialState, | ||
Component = _ref.Component, | ||
_ref$id = _ref.id, | ||
id = _ref$id === void 0 ? "".concat(getComponentName(Component), "-").concat(shortId()) : _ref$id, | ||
actions = _ref.actions, | ||
serialiser = _ref.serialiser; | ||
if (serialiser) { | ||
serialiser.addData({ | ||
id: id, | ||
Component: Component, | ||
props: initialState | ||
}); | ||
} | ||
if (serialiser) { | ||
serialiser.addData({ | ||
id: id, | ||
Component: Component, | ||
props: initialState | ||
}); | ||
} | ||
return xEngine.h( | ||
'div', | ||
{ 'data-x-dash-id': id }, | ||
xEngine.h(InteractionRender, { Component: Component, initialState: initialState, id: id, actions: actions }) | ||
); | ||
return xEngine.h("div", { | ||
"data-x-dash-id": id | ||
}, xEngine.h(InteractionRender, { | ||
Component: Component, | ||
initialState: initialState, | ||
id: id, | ||
actions: actions | ||
})); | ||
}; | ||
function wrapComponentName(Component, Enhanced) { | ||
var originalDisplayName = getComponentName(Component); | ||
Enhanced.displayName = 'withActions(' + originalDisplayName + ')'; | ||
Enhanced.wrappedDisplayName = originalDisplayName; | ||
var originalDisplayName = getComponentName(Component); | ||
Enhanced.displayName = "withActions(".concat(originalDisplayName, ")"); | ||
Enhanced.wrappedDisplayName = originalDisplayName; | ||
} | ||
var registeredComponents = {}; | ||
function registerComponent(component) { | ||
registeredComponents[component.wrappedDisplayName] = component; | ||
registeredComponents[component.wrappedDisplayName] = component; | ||
} | ||
function getComponent(name) { | ||
return registeredComponents[name]; | ||
return registeredComponents[name]; | ||
} | ||
var HydrationWrapper = function (_Component) { | ||
inherits(HydrationWrapper, _Component); | ||
var HydrationWrapper = | ||
/*#__PURE__*/ | ||
function (_Component) { | ||
_inherits(HydrationWrapper, _Component); | ||
function HydrationWrapper() { | ||
classCallCheck(this, HydrationWrapper); | ||
return possibleConstructorReturn(this, (HydrationWrapper.__proto__ || Object.getPrototypeOf(HydrationWrapper)).apply(this, arguments)); | ||
} | ||
function HydrationWrapper() { | ||
_classCallCheck(this, HydrationWrapper); | ||
createClass(HydrationWrapper, [{ | ||
key: 'render', | ||
value: function render() { | ||
var _this2 = this; | ||
return _possibleConstructorReturn(this, _getPrototypeOf(HydrationWrapper).apply(this, arguments)); | ||
} | ||
var _props = this.props, | ||
Component = _props.Component, | ||
props = _props.props, | ||
id = _props.id; | ||
_createClass(HydrationWrapper, [{ | ||
key: "render", | ||
value: function render() { | ||
var _this = this; | ||
return xEngine.h(Component, Object.assign({}, props, { id: id, actionsRef: function actionsRef(a) { | ||
return _this2.actions = a; | ||
} })); | ||
} | ||
}, { | ||
key: 'componentDidMount', | ||
value: function componentDidMount() { | ||
if (this.props.wrapper) { | ||
this.props.wrapper.addEventListener('x-interaction.trigger-action', this); | ||
} | ||
} | ||
}, { | ||
key: 'componentWillUnmount', | ||
value: function componentWillUnmount() { | ||
if (this.props.wrapper) { | ||
this.props.wrapper.removeEventListener('x-interaction.trigger-action', this); | ||
} | ||
} | ||
}, { | ||
key: 'handleEvent', | ||
value: function handleEvent(event) { | ||
var _event$detail = event.detail, | ||
action = _event$detail.action, | ||
_event$detail$args = _event$detail.args, | ||
args = _event$detail$args === undefined ? [] : _event$detail$args; | ||
var _this$props = this.props, | ||
Component = _this$props.Component, | ||
props = _this$props.props, | ||
id = _this$props.id; | ||
return xEngine.h(Component, Object.assign({}, props, { | ||
id: id, | ||
actionsRef: function actionsRef(a) { | ||
return _this.actions = a; | ||
} | ||
})); | ||
} | ||
}, { | ||
key: "componentDidMount", | ||
value: function componentDidMount() { | ||
if (this.props.wrapper) { | ||
this.props.wrapper.addEventListener('x-interaction.trigger-action', this); | ||
} | ||
} | ||
}, { | ||
key: "componentWillUnmount", | ||
value: function componentWillUnmount() { | ||
if (this.props.wrapper) { | ||
this.props.wrapper.removeEventListener('x-interaction.trigger-action', this); | ||
} | ||
} | ||
}, { | ||
key: "handleEvent", | ||
value: function handleEvent(event) { | ||
var _event$detail = event.detail, | ||
action = _event$detail.action, | ||
_event$detail$args = _event$detail.args, | ||
args = _event$detail$args === void 0 ? [] : _event$detail$args; | ||
if (this.actions && this.actions[action]) { | ||
var _this$actions; | ||
if (this.actions && this.actions[action]) { | ||
var _actions; | ||
(_this$actions = this.actions)[action].apply(_this$actions, _toConsumableArray(args)); | ||
} | ||
} | ||
}]); | ||
(_actions = this.actions)[action].apply(_actions, toConsumableArray(args)); | ||
} | ||
} | ||
}]); | ||
return HydrationWrapper; | ||
return HydrationWrapper; | ||
}(xEngine.Component); | ||
function hydrate() { | ||
if (typeof window === 'undefined') { | ||
throw new Error('x-interaction hydrate should only be called in the browser'); | ||
} | ||
if (typeof window === 'undefined') { | ||
throw new Error('x-interaction hydrate should only be called in the browser'); | ||
} | ||
if (!('_xDashInteractionHydrationData' in window)) { | ||
throw new Error('x-interaction hydrate was called without hydration data available. this can happen if you call hydrate before the serialised data is available, or if you\'re not including the hydration data with your server-rendered markup.'); | ||
} | ||
if (!('_xDashInteractionHydrationData' in window)) { | ||
throw new Error("x-interaction hydrate was called without hydration data available. this can happen if you call hydrate before the serialised data is available, or if you're not including the hydration data with your server-rendered markup."); | ||
} | ||
var serialiserOrdering = 'make sure you\'re always outputting the serialiser\'s data in the same request that the serialiser was created. see https://financial-times.github.io/x-dash/components/x-interaction/#hydrating for more details.'; | ||
var serialiserOrdering = "make sure you're always outputting the serialiser's data in the same request that the serialiser was created. see https://financial-times.github.io/x-dash/components/x-interaction/#hydrating for more details."; | ||
window._xDashInteractionHydrationData.forEach(function (_ref) { | ||
var id = _ref.id, | ||
component = _ref.component, | ||
props = _ref.props; | ||
window._xDashInteractionHydrationData.forEach(function (_ref) { | ||
var id = _ref.id, | ||
component = _ref.component, | ||
props = _ref.props; | ||
var wrapper = document.querySelector("[data-x-dash-id=\"".concat(id, "\"]")); | ||
var wrapper = document.querySelector('[data-x-dash-id="' + id + '"]'); | ||
if (!wrapper) { | ||
throw new Error("component markup for ".concat(id, " was not found on the page. It was expected to be an instance of ").concat(component, ". it's likely that this hydration data is from another request. ").concat(serialiserOrdering)); | ||
} | ||
if (!wrapper) { | ||
throw new Error('component markup for ' + id + ' was not found on the page. It was expected to be an instance of ' + component + '. it\'s likely that this hydration data is from another request. ' + serialiserOrdering); | ||
} | ||
var Component = getComponent(component); | ||
var Component = getComponent(component); | ||
while (wrapper.firstChild) { | ||
wrapper.removeChild(wrapper.firstChild); | ||
} | ||
while (wrapper.firstChild) { | ||
wrapper.removeChild(wrapper.firstChild); | ||
} | ||
xEngine.render(xEngine.h(HydrationWrapper, { | ||
Component: Component, | ||
props: props, | ||
id: id, | ||
wrapper: wrapper | ||
}), wrapper); | ||
}); | ||
xEngine.render(xEngine.h(HydrationWrapper, { | ||
Component: Component, | ||
props: props, | ||
id: id, | ||
wrapper: wrapper | ||
}), wrapper); | ||
}); | ||
document.querySelectorAll('[data-x-dash-id]').forEach(function (element) { | ||
var xDashId = element.dataset.xDashId; | ||
document.querySelectorAll('[data-x-dash-id]').forEach(function (element) { | ||
var xDashId = element.dataset.xDashId; | ||
var hasData = window._xDashInteractionHydrationData.some(function (_ref2) { | ||
var id = _ref2.id; | ||
return id === xDashId; | ||
}); | ||
var hasData = window._xDashInteractionHydrationData.some(function (_ref2) { | ||
var id = _ref2.id; | ||
return id === xDashId; | ||
}); | ||
if (!hasData) { | ||
throw new Error('found component markup for ' + xDashId + ' without any hydration data. it\'s likely that its hydration data has been output in another request, or that the component was rendered after the serialisation data was output. ' + serialiserOrdering); | ||
} | ||
}); | ||
if (!hasData) { | ||
throw new Error("found component markup for ".concat(xDashId, " without any hydration data. it's likely that its hydration data has been output in another request, or that the component was rendered after the serialisation data was output. ").concat(serialiserOrdering)); | ||
} | ||
}); | ||
} | ||
var HydrationData = function HydrationData(_ref) { | ||
var serialiser = _ref.serialiser; | ||
var serialiser = _ref.serialiser; | ||
if (serialiser) { | ||
var data = serialiser.flushHydrationData(); | ||
if (serialiser) { | ||
var data = serialiser.flushHydrationData(); | ||
return xEngine.h("script", { | ||
dangerouslySetInnerHTML: { | ||
__html: "window._xDashInteractionHydrationData = ".concat(JSON.stringify(data)) | ||
} | ||
}); | ||
} | ||
return xEngine.h('script', { dangerouslySetInnerHTML: { __html: 'window._xDashInteractionHydrationData = ' + JSON.stringify(data) } }); | ||
} | ||
return null; | ||
return null; | ||
}; | ||
var Serialiser = function () { | ||
function Serialiser() { | ||
classCallCheck(this, Serialiser); | ||
var Serialiser = | ||
/*#__PURE__*/ | ||
function () { | ||
function Serialiser() { | ||
_classCallCheck(this, Serialiser); | ||
this.destroyed = false; | ||
this.data = []; | ||
} | ||
this.destroyed = false; | ||
this.data = []; | ||
} | ||
createClass(Serialiser, [{ | ||
key: 'addData', | ||
value: function addData(_ref) { | ||
var id = _ref.id, | ||
Component = _ref.Component, | ||
props = _ref.props; | ||
_createClass(Serialiser, [{ | ||
key: "addData", | ||
value: function addData(_ref) { | ||
var id = _ref.id, | ||
Component = _ref.Component, | ||
props = _ref.props; | ||
if (this.destroyed) { | ||
throw new Error('an interaction component was rendered after flushHydrationData was called. ensure you\'re outputting the hydration data after rendering every component'); | ||
} | ||
if (this.destroyed) { | ||
throw new Error("an interaction component was rendered after flushHydrationData was called. ensure you're outputting the hydration data after rendering every component"); | ||
} | ||
this.data.push({ | ||
id: id, | ||
component: getComponentName(Component), | ||
props: props | ||
}); | ||
} | ||
}, { | ||
key: 'flushHydrationData', | ||
value: function flushHydrationData() { | ||
if (this.destroyed) { | ||
throw new Error('a Serialiser\'s flushHydrationData was called twice. ensure you\'re not reusing a Serialiser between requests'); | ||
} | ||
this.data.push({ | ||
id: id, | ||
component: getComponentName(Component), | ||
props: props | ||
}); | ||
} | ||
}, { | ||
key: "flushHydrationData", | ||
value: function flushHydrationData() { | ||
if (this.destroyed) { | ||
throw new Error("a Serialiser's flushHydrationData was called twice. ensure you're not reusing a Serialiser between requests"); | ||
} | ||
this.destroyed = true; | ||
return this.data; | ||
} | ||
}, { | ||
key: 'outputHydrationData', | ||
value: function outputHydrationData() { | ||
return xEngine.render(xEngine.h(HydrationData, { serialiser: this })); | ||
} | ||
}]); | ||
return Serialiser; | ||
this.destroyed = true; | ||
return this.data; | ||
} | ||
}, { | ||
key: "outputHydrationData", | ||
value: function outputHydrationData() { | ||
return xEngine.render(xEngine.h(HydrationData, { | ||
serialiser: this | ||
})); | ||
} | ||
}]); | ||
return Serialiser; | ||
}(); | ||
// use the class version for clientside and the static version for server | ||
var Interaction = typeof window !== 'undefined' ? InteractionClass : InteractionSSR; | ||
var invoke = function invoke(fnOrObj) { | ||
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { | ||
args[_key - 1] = arguments[_key]; | ||
} | ||
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { | ||
args[_key - 1] = arguments[_key]; | ||
} | ||
return typeof fnOrObj === 'function' ? fnOrObj.apply(undefined, args) : fnOrObj; | ||
return typeof fnOrObj === 'function' ? fnOrObj.apply(void 0, args) : fnOrObj; | ||
}; | ||
var withActions = function withActions(getActions) { | ||
var getDefaultState = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
return function (Component) { | ||
var _wraps = { getActions: getActions, getDefaultState: getDefaultState, Component: Component }; | ||
var getDefaultState = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
return function (Component) { | ||
var _wraps = { | ||
getActions: getActions, | ||
getDefaultState: getDefaultState, | ||
Component: Component | ||
}; // if the component we're wrapping is already wrapped, we don't want | ||
// to wrap it further. so, discard its wrapper and rewrap the original | ||
// component with the new actions on top | ||
// if the component we're wrapping is already wrapped, we don't want | ||
// to wrap it further. so, discard its wrapper and rewrap the original | ||
// component with the new actions on top | ||
if (Component._wraps) { | ||
var wrappedGetActions = Component._wraps.getActions; | ||
var wrappedGetDefaultState = Component._wraps.getDefaultState; | ||
if (Component._wraps) { | ||
var wrappedGetActions = Component._wraps.getActions; | ||
var wrappedGetDefaultState = Component._wraps.getDefaultState; | ||
Component = Component._wraps.Component; | ||
Component = Component._wraps.Component; | ||
getActions = function getActions(initialState) { | ||
return Object.assign(invoke(wrappedGetActions, initialState), invoke(_wraps.getActions, initialState)); | ||
}; | ||
getActions = function getActions(initialState) { | ||
return Object.assign(invoke(wrappedGetActions, initialState), invoke(_wraps.getActions, initialState)); | ||
}; | ||
getDefaultState = function getDefaultState(initialState) { | ||
return Object.assign(invoke(wrappedGetDefaultState, initialState), invoke(_wraps.getDefaultState, initialState)); | ||
}; | ||
} | ||
getDefaultState = function getDefaultState(initialState) { | ||
return Object.assign(invoke(wrappedGetDefaultState, initialState), invoke(_wraps.getDefaultState, initialState)); | ||
}; | ||
} | ||
function Enhanced(_ref) { | ||
var id = _ref.id, | ||
actionsRef = _ref.actionsRef, | ||
serialiser = _ref.serialiser, | ||
initialState = _objectWithoutProperties(_ref, ["id", "actionsRef", "serialiser"]); | ||
function Enhanced(_ref) { | ||
var id = _ref.id, | ||
actionsRef = _ref.actionsRef, | ||
serialiser = _ref.serialiser, | ||
initialState = objectWithoutProperties(_ref, ['id', 'actionsRef', 'serialiser']); | ||
var actions = invoke(getActions, initialState); | ||
var defaultState = invoke(getDefaultState, initialState); | ||
return xEngine.h(Interaction, { | ||
id: id, | ||
Component: Component, | ||
initialState: Object.assign({}, defaultState, initialState), | ||
actionsRef: actionsRef, | ||
serialiser: serialiser, | ||
actions: actions | ||
}); | ||
} // store what we're wrapping for later wrappers to replace | ||
var actions = invoke(getActions, initialState); | ||
var defaultState = invoke(getDefaultState, initialState); | ||
return xEngine.h(Interaction, { | ||
id: id, | ||
Component: Component, | ||
initialState: Object.assign({}, defaultState, initialState), | ||
actionsRef: actionsRef, | ||
serialiser: serialiser, | ||
actions: actions | ||
}); | ||
} | ||
Enhanced._wraps = _wraps; // set the displayName of the Enhanced component for debugging | ||
// store what we're wrapping for later wrappers to replace | ||
Enhanced._wraps = _wraps; | ||
wrapComponentName(Component, Enhanced); // register the component under its name for later hydration from serialised data | ||
// set the displayName of the Enhanced component for debugging | ||
wrapComponentName(Component, Enhanced); | ||
// register the component under its name for later hydration from serialised data | ||
registerComponent(Enhanced); | ||
return Enhanced; | ||
}; | ||
registerComponent(Enhanced); | ||
return Enhanced; | ||
}; | ||
}; | ||
@@ -482,0 +574,0 @@ |
import { h, Component, render } from '@financial-times/x-engine'; | ||
import shortId from '@quarterto/short-id'; | ||
function _objectWithoutPropertiesLoose(source, excluded) { | ||
if (source == null) return {}; | ||
var target = {}; | ||
var sourceKeys = Object.keys(source); | ||
var key, i; | ||
for (i = 0; i < sourceKeys.length; i++) { | ||
key = sourceKeys[i]; | ||
if (excluded.indexOf(key) >= 0) continue; | ||
target[key] = source[key]; | ||
} | ||
return target; | ||
} | ||
function _objectWithoutProperties(source, excluded) { | ||
if (source == null) return {}; | ||
var target = _objectWithoutPropertiesLoose(source, excluded); | ||
var key, i; | ||
if (Object.getOwnPropertySymbols) { | ||
var sourceSymbolKeys = Object.getOwnPropertySymbols(source); | ||
for (i = 0; i < sourceSymbolKeys.length; i++) { | ||
key = sourceSymbolKeys[i]; | ||
if (excluded.indexOf(key) >= 0) continue; | ||
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; | ||
target[key] = source[key]; | ||
} | ||
} | ||
return target; | ||
} | ||
const InteractionRender = ({ | ||
id, | ||
actions, | ||
state, | ||
initialState, | ||
inFlight, | ||
Component: Component$$1 | ||
}) => h(Component$$1, Object.assign({}, initialState, state, { id, actions }, { isLoading: inFlight > 0 })); | ||
id, | ||
actions, | ||
state, | ||
initialState, | ||
inFlight, | ||
Component: Component$$1 | ||
}) => h(Component$$1, Object.assign({}, initialState, state, { | ||
id, | ||
actions | ||
}, { | ||
isLoading: inFlight > 0 | ||
})); | ||
const mapValues = (obj, fn) => Object.keys(obj).reduce((mapped, key) => Object.assign(mapped, { | ||
[key]: fn(obj[key], key, obj) | ||
[key]: fn(obj[key], key, obj) | ||
}), {}); | ||
class InteractionClass extends Component { | ||
constructor(props, ...args) { | ||
super(props, ...args); | ||
constructor(props, ...args) { | ||
super(props, ...args); | ||
this.state = { | ||
state: {}, | ||
inFlight: 0 | ||
}; | ||
this.createActions(props); | ||
} | ||
this.state = { | ||
state: {}, | ||
inFlight: 0 | ||
}; | ||
createActions(props) { | ||
this.actions = mapValues(props.actions, func => (...args) => new Promise(function ($return, $error) { | ||
let stateUpdate, nextState; | ||
// mark as loading one microtask later. if the action is synchronous then | ||
// setting loading back to false will happen in the same microtask and no | ||
// additional render will be scheduled. | ||
Promise.resolve().then(() => { | ||
this.setState(({ | ||
inFlight | ||
}) => ({ | ||
inFlight: inFlight + 1 | ||
})); | ||
}); | ||
return Promise.resolve(Promise.resolve(func(...args))).then(function ($await_3) { | ||
try { | ||
stateUpdate = $await_3; | ||
return Promise.resolve(new Promise(function ($return, $error) { | ||
if (typeof stateUpdate === 'function') { | ||
return Promise.resolve(Promise.resolve(stateUpdate(Object.assign({}, props.initialState, this.state.state)))).then(function ($await_4) { | ||
try { | ||
return $return(Object.assign(this.state.state, $await_4)); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
} | ||
this.actions = mapValues(props.actions, func => (...args) => new Promise(function ($return, $error) { | ||
let stateUpdate, nextState; | ||
return $return(Object.assign(this.state.state, stateUpdate)); | ||
}.bind(this))).then(function ($await_5) { | ||
try { | ||
nextState = $await_5; | ||
return $return(new Promise(resolve => this.setState({ | ||
state: nextState | ||
}, () => this.setState(({ | ||
inFlight | ||
}) => ({ | ||
inFlight: inFlight - 1 | ||
}), resolve)))); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
}.bind(this))); | ||
} | ||
// mark as loading one microtask later. if the action is synchronous then | ||
// setting loading back to false will happen in the same microtask and no | ||
// additional render will be scheduled. | ||
Promise.resolve().then(() => { | ||
this.setState(({ inFlight }) => ({ inFlight: inFlight + 1 })); | ||
}); | ||
componentWillReceiveProps(props) { | ||
this.createActions(props); | ||
} | ||
return Promise.resolve(func(...args)).then(function ($await_3) { | ||
try { | ||
stateUpdate = $await_3; | ||
return new Promise(function ($return, $error) { | ||
if (typeof stateUpdate === 'function') { | ||
return Promise.resolve(stateUpdate(Object.assign({}, props.initialState, this.state.state))).then(function ($await_4) { | ||
try { | ||
return $return(Object.assign(this.state.state, $await_4)); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
}return $return(Object.assign(this.state.state, stateUpdate)); | ||
}.bind(this)).then(function ($await_5) { | ||
try { | ||
nextState = $await_5; | ||
componentDidMount() { | ||
if (this.props.actionsRef) { | ||
this.props.actionsRef(this.actions); | ||
} | ||
} | ||
componentWillUnmount() { | ||
if (this.props.actionsRef) { | ||
this.props.actionsRef(null); | ||
} | ||
} | ||
return $return(new Promise(resolve => this.setState({ state: nextState }, () => this.setState(({ inFlight }) => ({ inFlight: inFlight - 1 }), resolve)))); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
}.bind(this))); | ||
} | ||
render() { | ||
return h(InteractionRender, Object.assign({}, this.props, this.state, { | ||
actions: this.actions | ||
})); | ||
} | ||
componentDidMount() { | ||
if (this.props.actionsRef) { | ||
this.props.actionsRef(this.actions); | ||
} | ||
} | ||
componentWillUnmount() { | ||
if (this.props.actionsRef) { | ||
this.props.actionsRef(null); | ||
} | ||
} | ||
render() { | ||
return h(InteractionRender, Object.assign({}, this.props, this.state, { actions: this.actions })); | ||
} | ||
} | ||
@@ -86,173 +144,179 @@ | ||
const InteractionSSR = ({ | ||
initialState, | ||
Component: Component$$1, | ||
id = `${getComponentName(Component$$1)}-${shortId()}`, | ||
actions, | ||
serialiser | ||
initialState, | ||
Component: Component$$1, | ||
id = `${getComponentName(Component$$1)}-${shortId()}`, | ||
actions, | ||
serialiser | ||
}) => { | ||
if (serialiser) { | ||
serialiser.addData({ | ||
id, | ||
Component: Component$$1, | ||
props: initialState | ||
}); | ||
} | ||
if (serialiser) { | ||
serialiser.addData({ | ||
id, | ||
Component: Component$$1, | ||
props: initialState | ||
}); | ||
} | ||
return h( | ||
'div', | ||
{ 'data-x-dash-id': id }, | ||
h(InteractionRender, { Component: Component$$1, initialState, id, actions }) | ||
); | ||
return h("div", { | ||
"data-x-dash-id": id | ||
}, h(InteractionRender, { | ||
Component: Component$$1, | ||
initialState, | ||
id, | ||
actions | ||
})); | ||
}; | ||
function wrapComponentName(Component$$1, Enhanced) { | ||
const originalDisplayName = getComponentName(Component$$1); | ||
Enhanced.displayName = `withActions(${originalDisplayName})`; | ||
Enhanced.wrappedDisplayName = originalDisplayName; | ||
const originalDisplayName = getComponentName(Component$$1); | ||
Enhanced.displayName = `withActions(${originalDisplayName})`; | ||
Enhanced.wrappedDisplayName = originalDisplayName; | ||
} | ||
const registeredComponents = {}; | ||
function registerComponent(component) { | ||
registeredComponents[component.wrappedDisplayName] = component; | ||
registeredComponents[component.wrappedDisplayName] = component; | ||
} | ||
function getComponent(name) { | ||
return registeredComponents[name]; | ||
return registeredComponents[name]; | ||
} | ||
class HydrationWrapper extends Component { | ||
render() { | ||
var _props = this.props; | ||
const Component$$1 = _props.Component, | ||
props = _props.props, | ||
id = _props.id; | ||
render() { | ||
const _this$props = this.props, | ||
Component$$1 = _this$props.Component, | ||
props = _this$props.props, | ||
id = _this$props.id; | ||
return h(Component$$1, Object.assign({}, props, { | ||
id: id, | ||
actionsRef: a => this.actions = a | ||
})); | ||
} | ||
return h(Component$$1, Object.assign({}, props, { id: id, actionsRef: a => this.actions = a })); | ||
} | ||
componentDidMount() { | ||
if (this.props.wrapper) { | ||
this.props.wrapper.addEventListener('x-interaction.trigger-action', this); | ||
} | ||
} | ||
componentDidMount() { | ||
if (this.props.wrapper) { | ||
this.props.wrapper.addEventListener('x-interaction.trigger-action', this); | ||
} | ||
} | ||
componentWillUnmount() { | ||
if (this.props.wrapper) { | ||
this.props.wrapper.removeEventListener('x-interaction.trigger-action', this); | ||
} | ||
} | ||
componentWillUnmount() { | ||
if (this.props.wrapper) { | ||
this.props.wrapper.removeEventListener('x-interaction.trigger-action', this); | ||
} | ||
} | ||
handleEvent(event) { | ||
const _event$detail = event.detail, | ||
action = _event$detail.action, | ||
_event$detail$args = _event$detail.args, | ||
args = _event$detail$args === void 0 ? [] : _event$detail$args; | ||
handleEvent(event) { | ||
var _event$detail = event.detail; | ||
const action = _event$detail.action; | ||
var _event$detail$args = _event$detail.args; | ||
const args = _event$detail$args === undefined ? [] : _event$detail$args; | ||
if (this.actions && this.actions[action]) { | ||
this.actions[action](...args); | ||
} | ||
} | ||
if (this.actions && this.actions[action]) { | ||
this.actions[action](...args); | ||
} | ||
} | ||
} | ||
function hydrate() { | ||
if (typeof window === 'undefined') { | ||
throw new Error('x-interaction hydrate should only be called in the browser'); | ||
} | ||
if (typeof window === 'undefined') { | ||
throw new Error('x-interaction hydrate should only be called in the browser'); | ||
} | ||
if (!('_xDashInteractionHydrationData' in window)) { | ||
throw new Error(`x-interaction hydrate was called without hydration data available. this can happen if you call hydrate before the serialised data is available, or if you're not including the hydration data with your server-rendered markup.`); | ||
} | ||
if (!('_xDashInteractionHydrationData' in window)) { | ||
throw new Error(`x-interaction hydrate was called without hydration data available. this can happen if you call hydrate before the serialised data is available, or if you're not including the hydration data with your server-rendered markup.`); | ||
} | ||
const serialiserOrdering = `make sure you're always outputting the serialiser's data in the same request that the serialiser was created. see https://financial-times.github.io/x-dash/components/x-interaction/#hydrating for more details.`; | ||
const serialiserOrdering = `make sure you're always outputting the serialiser's data in the same request that the serialiser was created. see https://financial-times.github.io/x-dash/components/x-interaction/#hydrating for more details.`; | ||
window._xDashInteractionHydrationData.forEach(({ id, component, props }) => { | ||
const wrapper = document.querySelector(`[data-x-dash-id="${id}"]`); | ||
window._xDashInteractionHydrationData.forEach(({ | ||
id, | ||
component, | ||
props | ||
}) => { | ||
const wrapper = document.querySelector(`[data-x-dash-id="${id}"]`); | ||
if (!wrapper) { | ||
throw new Error(`component markup for ${id} was not found on the page. It was expected to be an instance of ${component}. it's likely that this hydration data is from another request. ${serialiserOrdering}`); | ||
} | ||
if (!wrapper) { | ||
throw new Error(`component markup for ${id} was not found on the page. It was expected to be an instance of ${component}. it's likely that this hydration data is from another request. ${serialiserOrdering}`); | ||
} | ||
const Component$$1 = getComponent(component); | ||
const Component$$1 = getComponent(component); | ||
while (wrapper.firstChild) { | ||
wrapper.removeChild(wrapper.firstChild); | ||
} | ||
while (wrapper.firstChild) { | ||
wrapper.removeChild(wrapper.firstChild); | ||
} | ||
render(h(HydrationWrapper, { | ||
Component: Component$$1, | ||
props, | ||
id, | ||
wrapper | ||
}), wrapper); | ||
}); | ||
render(h(HydrationWrapper, { | ||
Component: Component$$1, | ||
props, | ||
id, | ||
wrapper | ||
}), wrapper); | ||
}); | ||
document.querySelectorAll('[data-x-dash-id]').forEach(element => { | ||
const xDashId = element.dataset.xDashId; | ||
document.querySelectorAll('[data-x-dash-id]').forEach(element => { | ||
const xDashId = element.dataset.xDashId; | ||
const hasData = window._xDashInteractionHydrationData.some(({ | ||
id | ||
}) => id === xDashId); | ||
const hasData = window._xDashInteractionHydrationData.some(({ id }) => id === xDashId); | ||
if (!hasData) { | ||
throw new Error(`found component markup for ${xDashId} without any hydration data. it's likely that its hydration data has been output in another request, or that the component was rendered after the serialisation data was output. ${serialiserOrdering}`); | ||
} | ||
}); | ||
if (!hasData) { | ||
throw new Error(`found component markup for ${xDashId} without any hydration data. it's likely that its hydration data has been output in another request, or that the component was rendered after the serialisation data was output. ${serialiserOrdering}`); | ||
} | ||
}); | ||
} | ||
const HydrationData = ({ serialiser }) => { | ||
if (serialiser) { | ||
const data = serialiser.flushHydrationData(); | ||
const HydrationData = ({ | ||
serialiser | ||
}) => { | ||
if (serialiser) { | ||
const data = serialiser.flushHydrationData(); | ||
return h("script", { | ||
dangerouslySetInnerHTML: { | ||
__html: `window._xDashInteractionHydrationData = ${JSON.stringify(data)}` | ||
} | ||
}); | ||
} | ||
return h('script', { dangerouslySetInnerHTML: { __html: `window._xDashInteractionHydrationData = ${JSON.stringify(data)}` } }); | ||
} | ||
return null; | ||
return null; | ||
}; | ||
class Serialiser { | ||
constructor() { | ||
this.destroyed = false; | ||
this.data = []; | ||
} | ||
constructor() { | ||
this.destroyed = false; | ||
this.data = []; | ||
} | ||
addData({ id, Component: Component$$1, props }) { | ||
if (this.destroyed) { | ||
throw new Error(`an interaction component was rendered after flushHydrationData was called. ensure you're outputting the hydration data after rendering every component`); | ||
} | ||
addData({ | ||
id, | ||
Component: Component$$1, | ||
props | ||
}) { | ||
if (this.destroyed) { | ||
throw new Error(`an interaction component was rendered after flushHydrationData was called. ensure you're outputting the hydration data after rendering every component`); | ||
} | ||
this.data.push({ | ||
id, | ||
component: getComponentName(Component$$1), | ||
props | ||
}); | ||
} | ||
this.data.push({ | ||
id, | ||
component: getComponentName(Component$$1), | ||
props | ||
}); | ||
} | ||
flushHydrationData() { | ||
if (this.destroyed) { | ||
throw new Error(`a Serialiser's flushHydrationData was called twice. ensure you're not reusing a Serialiser between requests`); | ||
} | ||
flushHydrationData() { | ||
if (this.destroyed) { | ||
throw new Error(`a Serialiser's flushHydrationData was called twice. ensure you're not reusing a Serialiser between requests`); | ||
} | ||
this.destroyed = true; | ||
return this.data; | ||
} | ||
this.destroyed = true; | ||
return this.data; | ||
} | ||
outputHydrationData() { | ||
return render(h(HydrationData, { serialiser: this })); | ||
} | ||
} | ||
var objectWithoutProperties = function (obj, keys) { | ||
var target = {}; | ||
for (var i in obj) { | ||
if (keys.indexOf(i) >= 0) continue; | ||
if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; | ||
target[i] = obj[i]; | ||
outputHydrationData() { | ||
return render(h(HydrationData, { | ||
serialiser: this | ||
})); | ||
} | ||
return target; | ||
}; | ||
} | ||
// use the class version for clientside and the static version for server | ||
const Interaction = typeof window !== 'undefined' ? InteractionClass : InteractionSSR; | ||
@@ -263,49 +327,47 @@ | ||
const withActions = (getActions, getDefaultState = {}) => Component$$1 => { | ||
const _wraps = { getActions, getDefaultState, Component: Component$$1 }; | ||
const _wraps = { | ||
getActions, | ||
getDefaultState, | ||
Component: Component$$1 | ||
}; // if the component we're wrapping is already wrapped, we don't want | ||
// to wrap it further. so, discard its wrapper and rewrap the original | ||
// component with the new actions on top | ||
// if the component we're wrapping is already wrapped, we don't want | ||
// to wrap it further. so, discard its wrapper and rewrap the original | ||
// component with the new actions on top | ||
if (Component$$1._wraps) { | ||
const wrappedGetActions = Component$$1._wraps.getActions; | ||
const wrappedGetDefaultState = Component$$1._wraps.getDefaultState; | ||
if (Component$$1._wraps) { | ||
const wrappedGetActions = Component$$1._wraps.getActions; | ||
const wrappedGetDefaultState = Component$$1._wraps.getDefaultState; | ||
Component$$1 = Component$$1._wraps.Component; | ||
Component$$1 = Component$$1._wraps.Component; | ||
getActions = initialState => Object.assign(invoke(wrappedGetActions, initialState), invoke(_wraps.getActions, initialState)); | ||
getActions = initialState => Object.assign(invoke(wrappedGetActions, initialState), invoke(_wraps.getActions, initialState)); | ||
getDefaultState = initialState => Object.assign(invoke(wrappedGetDefaultState, initialState), invoke(_wraps.getDefaultState, initialState)); | ||
} | ||
getDefaultState = initialState => Object.assign(invoke(wrappedGetDefaultState, initialState), invoke(_wraps.getDefaultState, initialState)); | ||
} | ||
function Enhanced(_ref) { | ||
let id = _ref.id, | ||
actionsRef = _ref.actionsRef, | ||
serialiser = _ref.serialiser, | ||
initialState = _objectWithoutProperties(_ref, ["id", "actionsRef", "serialiser"]); | ||
function Enhanced(_ref) { | ||
let id = _ref.id, | ||
actionsRef = _ref.actionsRef, | ||
serialiser = _ref.serialiser, | ||
initialState = objectWithoutProperties(_ref, ['id', 'actionsRef', 'serialiser']); | ||
const actions = invoke(getActions, initialState); | ||
const defaultState = invoke(getDefaultState, initialState); | ||
return h(Interaction, { | ||
id, | ||
Component: Component$$1, | ||
initialState: Object.assign({}, defaultState, initialState), | ||
actionsRef, | ||
serialiser, | ||
actions | ||
}); | ||
} // store what we're wrapping for later wrappers to replace | ||
const actions = invoke(getActions, initialState); | ||
const defaultState = invoke(getDefaultState, initialState); | ||
return h(Interaction, { | ||
id, | ||
Component: Component$$1, | ||
initialState: Object.assign({}, defaultState, initialState), | ||
actionsRef, | ||
serialiser, | ||
actions | ||
}); | ||
} | ||
Enhanced._wraps = _wraps; // set the displayName of the Enhanced component for debugging | ||
// store what we're wrapping for later wrappers to replace | ||
Enhanced._wraps = _wraps; | ||
wrapComponentName(Component$$1, Enhanced); // register the component under its name for later hydration from serialised data | ||
// set the displayName of the Enhanced component for debugging | ||
wrapComponentName(Component$$1, Enhanced); | ||
// register the component under its name for later hydration from serialised data | ||
registerComponent(Enhanced); | ||
return Enhanced; | ||
registerComponent(Enhanced); | ||
return Enhanced; | ||
}; | ||
export { withActions, hydrate, HydrationData, Serialiser }; |
{ | ||
"name": "@financial-times/x-interaction", | ||
"version": "1.0.0-beta.9", | ||
"version": "1.0.0-beta.10", | ||
"description": "This module enables you to write x-dash components that respond to events and change their own data.", | ||
@@ -19,3 +19,3 @@ "main": "dist/Interaction.cjs.js", | ||
"dependencies": { | ||
"@financial-times/x-engine": "^1.0.0-beta.9", | ||
"@financial-times/x-engine": "^1.0.0-beta.10", | ||
"@quarterto/short-id": "^1.1.0" | ||
@@ -22,0 +22,0 @@ }, |
@@ -14,2 +14,6 @@ import { h, Component } from '@financial-times/x-engine'; | ||
this.createActions(props); | ||
} | ||
createActions(props) { | ||
this.actions = mapValues(props.actions, (func) => async (...args) => { | ||
@@ -44,2 +48,6 @@ // mark as loading one microtask later. if the action is synchronous then | ||
componentWillReceiveProps(props) { | ||
this.createActions(props); | ||
} | ||
componentDidMount() { | ||
@@ -46,0 +54,0 @@ if(this.props.actionsRef) { |
67886
1615