From 72751ac463f1de438c8cec70c396687ae1177604 Mon Sep 17 00:00:00 2001 From: boygirl Date: Sun, 24 Dec 2017 17:31:47 -0800 Subject: [PATCH 01/12] safety commit - getExternalMutation --- src/victory-util/add-events.js | 32 ++++++++++++++++++++++---------- src/victory-util/events.js | 8 ++++++++ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/victory-util/add-events.js b/src/victory-util/add-events.js index 49f00d7..964029d 100644 --- a/src/victory-util/add-events.js +++ b/src/victory-util/add-events.js @@ -21,6 +21,7 @@ export default (WrappedComponent, options) => { const getScopedEvents = Events.getScopedEvents.bind(this); this.getEvents = partialRight(Events.getEvents.bind(this), getScopedEvents); this.getEventState = Events.getEventState.bind(this); + this.getExternalMutation = Events.getExternalMutation.bind(this); const calculatedValues = this.getCalculatedValues(this.props); this.cacheValues(calculatedValues); this.stateChanges = this.getStateChanges(this.props, calculatedValues); @@ -34,7 +35,6 @@ export default (WrappedComponent, options) => { this.cacheValues(calculatedValues); return true; } - // check for any state changes triggered by events or shared events const calculatedState = this.getStateChanges(nextProps, calculatedValues); if (!Collection.areVictoryPropsEqual(this.calculatedState, calculatedState)) { @@ -58,11 +58,6 @@ export default (WrappedComponent, options) => { const { hasEvents, getSharedEventState } = calculatedValues; if (!hasEvents) { return {}; } - const getState = (key, type) => { - const result = defaults({}, this.getEventState(key, type), getSharedEventState(key, type)); - return isEmpty(result) ? undefined : result; - }; - options = options || {}; const components = options.components || defaultComponents; return components.map((component) => { @@ -71,8 +66,10 @@ export default (WrappedComponent, options) => { return undefined; } else { return typeof component.index !== "undefined" ? - getState(component.index, component.name) : - calculatedValues.dataKeys.map((key) => getState(key, component.name)); + this.getState(component.index, component.name, getSharedEventState) : + calculatedValues.dataKeys.map( + (key) => this.getState(key, component.name, getSharedEventState) + ); } }).filter(Boolean); } @@ -118,6 +115,22 @@ export default (WrappedComponent, options) => { return props.events; } + getState(key, type, getSharedEventState) { + getSharedEventState = getSharedEventState || this.getSharedEventState; + const baseState = defaults( + {}, this.getEventState(key, type), getSharedEventState(key, type) + ); + const externalMutation = this.props.externalEventMutation; + if (externalMutation) { + return defaults( + {}, + this.getExternalMutation(key, type, baseState), + baseState + ); + } + return isEmpty(baseState) ? undefined : baseState; + } + getComponentProps(component, type, index) { const { role } = WrappedComponent; const key = this.dataKeys && this.dataKeys[index] || index; @@ -129,8 +142,7 @@ export default (WrappedComponent, options) => { const baseEvents = this.getEvents(this.props, type, key); const componentProps = defaults( { index, key: `${role}-${type}-${key}` }, - this.getEventState(key, type), - this.getSharedEventState(key, type), + this.getState(key, type), component.props, baseProps ); diff --git a/src/victory-util/events.js b/src/victory-util/events.js index 4d47c2c..a496ce3 100644 --- a/src/victory-util/events.js +++ b/src/victory-util/events.js @@ -207,7 +207,15 @@ export default { return state[childType] && state[childType][eventKey] && state[childType][eventKey][namespace]; + }, + getExternalMutation(key, type, baseState, mutation, baseProps) { + mutation = mutation || this.props.externalEventMutations; + baseProps = baseProps || this.baseProps; + // some of the args will be provided by context + // parse the mutation object and return a portion of the state object. + // To be merged in add-events + return {}; }, /* Returns an array of defaultEvents from sub-components of a given component. From f48138f73cc96cfd6fbb1c8ccb64a70b83fd48bb Mon Sep 17 00:00:00 2001 From: boygirl Date: Wed, 27 Dec 2017 22:47:02 -0800 Subject: [PATCH 02/12] safety commit with componentWillReceiveProps --- src/victory-util/add-events.js | 15 ++++--- src/victory-util/events.js | 76 +++++++++++++++++++++++++++++++--- 2 files changed, 80 insertions(+), 11 deletions(-) diff --git a/src/victory-util/add-events.js b/src/victory-util/add-events.js index 964029d..05bc202 100644 --- a/src/victory-util/add-events.js +++ b/src/victory-util/add-events.js @@ -24,7 +24,13 @@ export default (WrappedComponent, options) => { this.getExternalMutation = Events.getExternalMutation.bind(this); const calculatedValues = this.getCalculatedValues(this.props); this.cacheValues(calculatedValues); - this.stateChanges = this.getStateChanges(this.props, calculatedValues); + this.calculatedState = this.getStateChanges(this.props, calculatedValues); + } + + componentWillReceiveProps(nextProps) { + const calculatedValues = this.getCalculatedValues(nextProps); + const stateChanges = this.getStateChanges(nextProps, calculatedValues); + console.log(stateChanges) } shouldComponentUpdate(nextProps) { @@ -48,7 +54,6 @@ export default (WrappedComponent, options) => { this.cacheValues(calculatedValues); return true; } - return false; } @@ -120,11 +125,11 @@ export default (WrappedComponent, options) => { const baseState = defaults( {}, this.getEventState(key, type), getSharedEventState(key, type) ); - const externalMutation = this.props.externalEventMutation; - if (externalMutation) { + const externalMutation = this.props.externalEventMutations; + if (externalMutation && !isEmpty(externalMutation)) { return defaults( {}, - this.getExternalMutation(key, type, baseState), + this.getExternalMutation(key, type, externalMutation), baseState ); } diff --git a/src/victory-util/events.js b/src/victory-util/events.js index a496ce3..eb18ad7 100644 --- a/src/victory-util/events.js +++ b/src/victory-util/events.js @@ -209,13 +209,77 @@ export default { state[childType][eventKey][namespace]; }, - getExternalMutation(key, type, baseState, mutation, baseProps) { - mutation = mutation || this.props.externalEventMutations; + getExternalMutations(mutations, baseProps, baseState, name) { baseProps = baseProps || this.baseProps; - // some of the args will be provided by context - // parse the mutation object and return a portion of the state object. - // To be merged in add-events - return {}; + baseState = baseState || this.state || {}; + const getParentMutation = () => { + const parentMutations = mutations.filter((m) => m.target === "parent"); + if (isEmpty(parentMutations)) { + return undefined; + } + return parentMutations.reduce((memo, curr) => { + const mutation = isFunction(curr.mutation) ? curr.mutation : () => null; + const currentProps = merge({}, baseProps.parent, baseState.parent); + const mutatedProps = mutation(currentProps, "parent"); + return assign({}, memo, mutatedProps); + }, {}); + } + const getMutation = (key, target) => { + const targetMutations = mutations.filter((m) => m.target === target); + if (isEmpty(targetMutations)) { + return undefined; + } + const keyMutations = target === "parent" ? + targetMutations : + targetMutations.filter((m) => m.eventKey === key || m.eventKey === "all"); + const currentState = target === "parent" ? + baseState[target] : baseState[key] && baseState[key][target] || undefined; + const currentProps = target === "parent" ? baseProps[target] : baseProps[key][target]; + return keyMutations.reduce((memo, curr) => { + const mutation = isFunction(curr.mutation) ? curr.mutation : () => null; + const currentState = baseState && baseState[key] && baseState[key][target] || {}; + const currentProps = merge({}, baseProps[key][target], currentState); + return assign({}, memo, currentProps); + }, {}); + } + return baseProps.keys.map((key) => { + if (key === "parent") { + return getParentMutation() || baseState.parent; + } + }); + } + + getExternalMutation(key, target, mutations, baseState, baseProps) { + baseProps = baseProps || this.baseProps; + baseState = baseState || this.state; + // return baseProps.map((key) => { + // if (key === "parent") { + // return target === "parent" ? + // } + // }); + + mutations = Array.isArray(mutations) ? mutations : [mutations]; + // find any mutation objects that match the target + const targetMutations = mutations.filter((m) => m.target === target); + if (isEmpty(targetMutations)) { + return {}; + } + const keyMutations = targetMutations.filter((m) => m.eventKey === key || m.eventKey === "all"); + if (isEmpty(keyMutations)) { + return {}; + } + const getTargetProps = (type) => { + const base = type === "props" ? baseProps : baseState || {}; + return key === "parent" ? base.parent : base[key] && base[key][target]; + }; + + return keyMutations.reduce((memo, curr) => { + const currentMutation = curr.mutation( + assign({}, getTargetProps("props"), getTargetProps("state"), baseProps) + ); + return merge({}, memo, currentMutation); + }, {}); + }, /* Returns an array of defaultEvents from sub-components of a given component. From 4993a2b6c0092260e977fab672560d6c95706d2b Mon Sep 17 00:00:00 2001 From: boygirl Date: Sun, 31 Dec 2017 17:45:45 -0800 Subject: [PATCH 03/12] working event mutation on single component --- src/victory-util/add-events.js | 26 +++---- src/victory-util/events.js | 133 ++++++++++++++++++++------------- 2 files changed, 90 insertions(+), 69 deletions(-) diff --git a/src/victory-util/add-events.js b/src/victory-util/add-events.js index 05bc202..ad1ee25 100644 --- a/src/victory-util/add-events.js +++ b/src/victory-util/add-events.js @@ -1,6 +1,6 @@ import React from "react"; import { - defaults, assign, keys, isFunction, partialRight, pick, without, isEmpty + defaults, assign, keys, isFunction, partialRight, pick, without, isEmpty, defaultsDeep, merge } from "lodash"; import Events from "./events"; import Collection from "./collection"; @@ -21,21 +21,22 @@ export default (WrappedComponent, options) => { const getScopedEvents = Events.getScopedEvents.bind(this); this.getEvents = partialRight(Events.getEvents.bind(this), getScopedEvents); this.getEventState = Events.getEventState.bind(this); - this.getExternalMutation = Events.getExternalMutation.bind(this); const calculatedValues = this.getCalculatedValues(this.props); this.cacheValues(calculatedValues); - this.calculatedState = this.getStateChanges(this.props, calculatedValues); } componentWillReceiveProps(nextProps) { - const calculatedValues = this.getCalculatedValues(nextProps); - const stateChanges = this.getStateChanges(nextProps, calculatedValues); - console.log(stateChanges) + const externalEventMutations = nextProps.externalEventMutations; + if (externalEventMutations) { + this.externalMutation = Events.getExternalMutations( + externalEventMutations, this.baseProps, this.state + ); + this.setState(defaultsDeep({}, this.externalMutation, this.state)); + } } shouldComponentUpdate(nextProps) { const calculatedValues = this.getCalculatedValues(nextProps); - // re-render without additional checks when component is animated if (this.props.animate || this.props.animating) { this.cacheValues(calculatedValues); @@ -125,14 +126,6 @@ export default (WrappedComponent, options) => { const baseState = defaults( {}, this.getEventState(key, type), getSharedEventState(key, type) ); - const externalMutation = this.props.externalEventMutations; - if (externalMutation && !isEmpty(externalMutation)) { - return defaults( - {}, - this.getExternalMutation(key, type, externalMutation), - baseState - ); - } return isEmpty(baseState) ? undefined : baseState; } @@ -147,7 +140,8 @@ export default (WrappedComponent, options) => { const baseEvents = this.getEvents(this.props, type, key); const componentProps = defaults( { index, key: `${role}-${type}-${key}` }, - this.getState(key, type), + this.getEventState(key, type), + this.getSharedEventState(key, type), component.props, baseProps ); diff --git a/src/victory-util/events.js b/src/victory-util/events.js index eb18ad7..aa1f861 100644 --- a/src/victory-util/events.js +++ b/src/victory-util/events.js @@ -1,4 +1,6 @@ -import { assign, extend, merge, partial, isEmpty, isFunction, without } from "lodash"; +import { + assign, extend, merge, partial, isEmpty, isFunction, without, keys, pickBy +} from "lodash"; export default { /* Returns all own and shared events that should be attached to a single target element, @@ -209,77 +211,102 @@ export default { state[childType][eventKey][namespace]; }, - getExternalMutations(mutations, baseProps, baseState, name) { + // getExternalMutations(mutations, baseProps, baseState, name) { + + // baseProps = baseProps || this.baseProps; + // baseState = baseState || this.state || {}; + // const getParentMutation = () => { + // const parentMutations = mutations.filter((m) => m.target === "parent"); + // if (isEmpty(parentMutations)) { + // return undefined; + // } + // return parentMutations.reduce((memo, curr) => { + // const mutation = isFunction(curr.mutation) ? curr.mutation : () => null; + // const currentProps = merge({}, baseProps.parent, baseState.parent); + // const mutatedProps = mutation(currentProps, "parent"); + // return assign({}, memo, mutatedProps); + // }, {}); + // }; + // const getMutation = (key, target) => { + // const targetMutations = mutations.filter((m) => m.target === target); + // if (isEmpty(targetMutations)) { + // return undefined; + // } + // const keyMutations = key === "parent" ? + // targetMutations : + // targetMutations.filter((m) => m.eventKey === key || m.eventKey === "all"); + // const currentState = key === "parent" ? + // baseState[key] || {} : baseState[key] && baseState[key][target] || {}; + // const currentProps = key === "parent" ? + // baseProps[key] || {} : baseProps[key][target] || {}; + // return keyMutations.reduce((memo, curr) => { + // const mutation = isFunction(curr.mutation) ? curr.mutation : () => null; + // const mutatedProps = merge({}, currentProps, currentState); + // return assign({}, memo, mutation(mutatedProps)); + // }, {}); + // }; + + // return baseProps.keys.reduce((memo, key) => { + // if (key === "parent") { + // memo[key] = getMutation(key) || baseState.parent; + // return memo; + // } else { + // memo[key] = memo[key].keys.reduce(m, target) => {} + // } + // }, {}); + // }, + + getExternalMutations(mutations, baseProps, baseState) { baseProps = baseProps || this.baseProps; - baseState = baseState || this.state || {}; - const getParentMutation = () => { - const parentMutations = mutations.filter((m) => m.target === "parent"); - if (isEmpty(parentMutations)) { - return undefined; - } - return parentMutations.reduce((memo, curr) => { - const mutation = isFunction(curr.mutation) ? curr.mutation : () => null; - const currentProps = merge({}, baseProps.parent, baseState.parent); - const mutatedProps = mutation(currentProps, "parent"); - return assign({}, memo, mutatedProps); - }, {}); - } - const getMutation = (key, target) => { - const targetMutations = mutations.filter((m) => m.target === target); - if (isEmpty(targetMutations)) { - return undefined; - } - const keyMutations = target === "parent" ? - targetMutations : - targetMutations.filter((m) => m.eventKey === key || m.eventKey === "all"); - const currentState = target === "parent" ? - baseState[target] : baseState[key] && baseState[key][target] || undefined; - const currentProps = target === "parent" ? baseProps[target] : baseProps[key][target]; - return keyMutations.reduce((memo, curr) => { - const mutation = isFunction(curr.mutation) ? curr.mutation : () => null; - const currentState = baseState && baseState[key] && baseState[key][target] || {}; - const currentProps = merge({}, baseProps[key][target], currentState); - return assign({}, memo, currentProps); - }, {}); - } - return baseProps.keys.map((key) => { + baseState = baseState || this.state; + + const getTargetProps = (identifier, type) => { + const { childName, target, key } = identifier; + const baseType = type === "props" ? baseProps : baseState || {}; + const base = (childName === undefined || childName === null || !baseType[childName]) ? + baseType : baseType[childName]; + return key === "parent" ? base.parent : base[key] && base[key][target]; + }; + + const propKeys = keys(baseProps); + return propKeys.reduce((memo, key) => { if (key === "parent") { - return getParentMutation() || baseState.parent; + const identifier = { key, target: "parent" }; + const targetState = getTargetProps(identifier, "state"); + const targetProps = getTargetProps(identifier, "state"); + memo[key] = this.getExternalMutation(key, "parent", mutations, targetState, targetProps); + } else { + const targets = keys(baseProps[key]); + memo[key] = targets.reduce((m, target) => { + const identifier = { key, target }; + const targetState = getTargetProps(identifier, "state"); + const targetProps = getTargetProps(identifier, "state"); + m[target] = this.getExternalMutation(key, target, mutations, targetState, targetProps); + return pickBy(m, (v) => typeof v !== "undefined"); + }, {}); } - }); - } + return pickBy(memo, (v) => typeof v !== "undefined"); + }, {}); + }, getExternalMutation(key, target, mutations, baseState, baseProps) { - baseProps = baseProps || this.baseProps; - baseState = baseState || this.state; - // return baseProps.map((key) => { - // if (key === "parent") { - // return target === "parent" ? - // } - // }); - mutations = Array.isArray(mutations) ? mutations : [mutations]; // find any mutation objects that match the target const targetMutations = mutations.filter((m) => m.target === target); if (isEmpty(targetMutations)) { - return {}; + return undefined; } const keyMutations = targetMutations.filter((m) => m.eventKey === key || m.eventKey === "all"); if (isEmpty(keyMutations)) { - return {}; + return undefined; } - const getTargetProps = (type) => { - const base = type === "props" ? baseProps : baseState || {}; - return key === "parent" ? base.parent : base[key] && base[key][target]; - }; return keyMutations.reduce((memo, curr) => { const currentMutation = curr.mutation( - assign({}, getTargetProps("props"), getTargetProps("state"), baseProps) + assign({}, baseProps, baseState) ); return merge({}, memo, currentMutation); }, {}); - }, /* Returns an array of defaultEvents from sub-components of a given component. From 0f6fa34ea58edf6d4e6e79ebfd35312fb9c8e8af Mon Sep 17 00:00:00 2001 From: boygirl Date: Sun, 31 Dec 2017 20:51:38 -0800 Subject: [PATCH 04/12] external mutations working with shared events --- .../victory-shared-events.js | 25 ++++++++++- src/victory-util/events.js | 42 +++++++++++++------ 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/src/victory-shared-events/victory-shared-events.js b/src/victory-shared-events/victory-shared-events.js index ea21c50..8d03b17 100644 --- a/src/victory-shared-events/victory-shared-events.js +++ b/src/victory-shared-events/victory-shared-events.js @@ -1,4 +1,4 @@ -import { assign, isFunction, partialRight, defaults, fromPairs } from "lodash"; +import { assign, isFunction, partialRight, defaults, defaultsDeep, fromPairs } from "lodash"; import React from "react"; import PropTypes from "prop-types"; import CustomPropTypes from "../victory-util/prop-types"; @@ -37,6 +37,20 @@ export default class VictorySharedEvents extends React.Component { ]), target: PropTypes.string })), + externalEventMutations: PropTypes.arrayOf(PropTypes.shape({ + childName: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.array + ]), + eventKey: PropTypes.oneOfType([ + PropTypes.array, + PropTypes.func, + CustomPropTypes.allOfType([CustomPropTypes.integer, CustomPropTypes.nonNegative]), + PropTypes.string + ]), + mutation: PropTypes.function, + target: PropTypes.string + })), groupComponent: PropTypes.node }; @@ -72,6 +86,12 @@ export default class VictorySharedEvents extends React.Component { componentWillReceiveProps(newProps) { this.setUpChildren(newProps); + if (newProps.externalEventMutations) { + const mutations = Events.getExternalMutationsWithChildren( + newProps.externalEventMutations, this.baseProps, this.state, this.childNames + ); + this.setState(defaultsDeep({}, mutations, this.state)); + } } getTimer() { @@ -100,6 +120,7 @@ export default class VictorySharedEvents extends React.Component { this.childComponents = React.Children.toArray(props.children); const childBaseProps = this.getBasePropsFromChildren(this.childComponents); const parentBaseProps = props.container ? props.container.props : {}; + this.childNames = Object.keys(childBaseProps); this.baseProps = assign({}, childBaseProps, { parent: parentBaseProps }); } } @@ -147,7 +168,7 @@ export default class VictorySharedEvents extends React.Component { getEventState: partialRight(this.getEventState, name) }; return memo.concat(React.cloneElement(child, assign( - { key: `events-${name}`, sharedEvents, eventKey }, + { key: `events-${name}`, sharedEvents, eventKey, name }, child.props ))); } else { diff --git a/src/victory-util/events.js b/src/victory-util/events.js index aa1f861..70a2efe 100644 --- a/src/victory-util/events.js +++ b/src/victory-util/events.js @@ -256,15 +256,26 @@ export default { // }, {}); // }, - getExternalMutations(mutations, baseProps, baseState) { + getExternalMutationsWithChildren(mutations, baseProps, baseState, childNames) { + baseProps = baseProps || this.baseProps; + baseState = baseState || this.state; + + return childNames.reduce((memo, childName) => { + memo[childName] = this.getExternalMutations( + mutations, baseProps[childName], baseState[childName], childName + ); + return pickBy(memo, (v) => !isEmpty(v)); + }, {}); + + }, + + getExternalMutations(mutations, baseProps, baseState, childName) { baseProps = baseProps || this.baseProps; baseState = baseState || this.state; const getTargetProps = (identifier, type) => { - const { childName, target, key } = identifier; - const baseType = type === "props" ? baseProps : baseState || {}; - const base = (childName === undefined || childName === null || !baseType[childName]) ? - baseType : baseType[childName]; + const { target, key } = identifier; + const base = type === "props" ? baseProps : baseState || {}; return key === "parent" ? base.parent : base[key] && base[key][target]; }; @@ -274,25 +285,30 @@ export default { const identifier = { key, target: "parent" }; const targetState = getTargetProps(identifier, "state"); const targetProps = getTargetProps(identifier, "state"); - memo[key] = this.getExternalMutation(key, "parent", mutations, targetState, targetProps); + memo[key] = this.getExternalMutation(identifier, mutations, targetState, targetProps); } else { const targets = keys(baseProps[key]); memo[key] = targets.reduce((m, target) => { - const identifier = { key, target }; + const identifier = { key, target, childName }; const targetState = getTargetProps(identifier, "state"); const targetProps = getTargetProps(identifier, "state"); - m[target] = this.getExternalMutation(key, target, mutations, targetState, targetProps); - return pickBy(m, (v) => typeof v !== "undefined"); + m[target] = this.getExternalMutation(identifier, mutations, targetState, targetProps); + return pickBy(m, (v) => !isEmpty(v)); }, {}); } - return pickBy(memo, (v) => typeof v !== "undefined"); + return pickBy(memo, (v) => !isEmpty(v)); }, {}); }, - getExternalMutation(key, target, mutations, baseState, baseProps) { + getExternalMutation(identifier, mutations, baseState, baseProps) { + const { key, target, childName } = identifier; mutations = Array.isArray(mutations) ? mutations : [mutations]; + let scopedMutations = mutations; + if (childName) { + scopedMutations = mutations.filter((m) => m.childName === childName); + } // find any mutation objects that match the target - const targetMutations = mutations.filter((m) => m.target === target); + const targetMutations = scopedMutations.filter((m) => m.target === target); if (isEmpty(targetMutations)) { return undefined; } @@ -302,7 +318,7 @@ export default { } return keyMutations.reduce((memo, curr) => { - const currentMutation = curr.mutation( + const currentMutation = curr && curr.mutation( assign({}, baseProps, baseState) ); return merge({}, memo, currentMutation); From b3679fbf50e8c7fec24a62b99a84d63a7615fdeb Mon Sep 17 00:00:00 2001 From: boygirl Date: Sun, 31 Dec 2017 23:17:03 -0800 Subject: [PATCH 05/12] working mutation clearing --- .../victory-shared-events.js | 6 ++- src/victory-util/events.js | 41 ++++++++++--------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/victory-shared-events/victory-shared-events.js b/src/victory-shared-events/victory-shared-events.js index 8d03b17..21eea0f 100644 --- a/src/victory-shared-events/victory-shared-events.js +++ b/src/victory-shared-events/victory-shared-events.js @@ -1,4 +1,4 @@ -import { assign, isFunction, partialRight, defaults, defaultsDeep, fromPairs } from "lodash"; +import { assign, isFunction, partialRight, defaults, merge, defaultsDeep, fromPairs } from "lodash"; import React from "react"; import PropTypes from "prop-types"; import CustomPropTypes from "../victory-util/prop-types"; @@ -90,7 +90,9 @@ export default class VictorySharedEvents extends React.Component { const mutations = Events.getExternalMutationsWithChildren( newProps.externalEventMutations, this.baseProps, this.state, this.childNames ); - this.setState(defaultsDeep({}, mutations, this.state)); + console.log(this.state, mutations, defaultsDeep({}, mutations, this.state)) + // this.setState(defaults({}, mutations, this.state)); + this.setState(mutations); } } diff --git a/src/victory-util/events.js b/src/victory-util/events.js index 70a2efe..01da92f 100644 --- a/src/victory-util/events.js +++ b/src/victory-util/events.js @@ -1,5 +1,5 @@ import { - assign, extend, merge, partial, isEmpty, isFunction, without, keys, pickBy + assign, extend, merge, partial, isEmpty, isFunction, without, keys, pickBy, defaults, uniq } from "lodash"; export default { @@ -261,42 +261,43 @@ export default { baseState = baseState || this.state; return childNames.reduce((memo, childName) => { - memo[childName] = this.getExternalMutations( + const childState = baseState[childName]; + const mutation = this.getExternalMutations( mutations, baseProps[childName], baseState[childName], childName ); - return pickBy(memo, (v) => !isEmpty(v)); + memo[childName] = mutation ? mutation : childState; + return memo; }, {}); }, getExternalMutations(mutations, baseProps, baseState, childName) { - baseProps = baseProps || this.baseProps; - baseState = baseState || this.state; - - const getTargetProps = (identifier, type) => { - const { target, key } = identifier; - const base = type === "props" ? baseProps : baseState || {}; - return key === "parent" ? base.parent : base[key] && base[key][target]; - }; + baseProps = baseProps || {}; + baseState = baseState || {}; const propKeys = keys(baseProps); return propKeys.reduce((memo, key) => { + const keyState = baseState[key]; + const keyProps = baseProps[key]; if (key === "parent") { const identifier = { key, target: "parent" }; - const targetState = getTargetProps(identifier, "state"); - const targetProps = getTargetProps(identifier, "state"); - memo[key] = this.getExternalMutation(identifier, mutations, targetState, targetProps); + const mutation = this.getExternalMutation(identifier, mutations, keyState, keyProps); + memo[key] = typeof mutation !== "undefined" ? mutation : keyState; } else { - const targets = keys(baseProps[key]); + const targets = uniq(keys(keyProps).concat(keys(keyState))); memo[key] = targets.reduce((m, target) => { const identifier = { key, target, childName }; - const targetState = getTargetProps(identifier, "state"); - const targetProps = getTargetProps(identifier, "state"); - m[target] = this.getExternalMutation(identifier, mutations, targetState, targetProps); - return pickBy(m, (v) => !isEmpty(v)); + const targetState = keyState && keyState[target]; + const targetProps = keyProps && keyProps[target]; + const mutation = this.getExternalMutation(identifier, mutations, targetState, targetProps); + m[target] = typeof mutation !== "undefined" ? + mutation : targetState; + // Allow empty objects so that props can be cleared + return pickBy(m, (v) => typeof v !== "undefined"); }, {}); } - return pickBy(memo, (v) => !isEmpty(v)); + return memo; + // return pickBy(memo, (v) => typeof v !== "undefined"); }, {}); }, From 41d23b83d89a03fb95ff25fc617fe48c11e90230 Mon Sep 17 00:00:00 2001 From: boygirl Date: Sun, 31 Dec 2017 23:51:30 -0800 Subject: [PATCH 06/12] clean up external events methods --- .../victory-shared-events.js | 7 +- src/victory-util/events.js | 99 ++++++++----------- 2 files changed, 45 insertions(+), 61 deletions(-) diff --git a/src/victory-shared-events/victory-shared-events.js b/src/victory-shared-events/victory-shared-events.js index 21eea0f..c075abd 100644 --- a/src/victory-shared-events/victory-shared-events.js +++ b/src/victory-shared-events/victory-shared-events.js @@ -86,12 +86,11 @@ export default class VictorySharedEvents extends React.Component { componentWillReceiveProps(newProps) { this.setUpChildren(newProps); - if (newProps.externalEventMutations) { + const { externalEventMutations } = newProps; + if (externalEventMutations) { const mutations = Events.getExternalMutationsWithChildren( - newProps.externalEventMutations, this.baseProps, this.state, this.childNames + externalEventMutations, this.baseProps, this.state, this.childNames ); - console.log(this.state, mutations, defaultsDeep({}, mutations, this.state)) - // this.setState(defaults({}, mutations, this.state)); this.setState(mutations); } } diff --git a/src/victory-util/events.js b/src/victory-util/events.js index 01da92f..e9f9e45 100644 --- a/src/victory-util/events.js +++ b/src/victory-util/events.js @@ -211,51 +211,16 @@ export default { state[childType][eventKey][namespace]; }, - // getExternalMutations(mutations, baseProps, baseState, name) { - - // baseProps = baseProps || this.baseProps; - // baseState = baseState || this.state || {}; - // const getParentMutation = () => { - // const parentMutations = mutations.filter((m) => m.target === "parent"); - // if (isEmpty(parentMutations)) { - // return undefined; - // } - // return parentMutations.reduce((memo, curr) => { - // const mutation = isFunction(curr.mutation) ? curr.mutation : () => null; - // const currentProps = merge({}, baseProps.parent, baseState.parent); - // const mutatedProps = mutation(currentProps, "parent"); - // return assign({}, memo, mutatedProps); - // }, {}); - // }; - // const getMutation = (key, target) => { - // const targetMutations = mutations.filter((m) => m.target === target); - // if (isEmpty(targetMutations)) { - // return undefined; - // } - // const keyMutations = key === "parent" ? - // targetMutations : - // targetMutations.filter((m) => m.eventKey === key || m.eventKey === "all"); - // const currentState = key === "parent" ? - // baseState[key] || {} : baseState[key] && baseState[key][target] || {}; - // const currentProps = key === "parent" ? - // baseProps[key] || {} : baseProps[key][target] || {}; - // return keyMutations.reduce((memo, curr) => { - // const mutation = isFunction(curr.mutation) ? curr.mutation : () => null; - // const mutatedProps = merge({}, currentProps, currentState); - // return assign({}, memo, mutation(mutatedProps)); - // }, {}); - // }; - - // return baseProps.keys.reduce((memo, key) => { - // if (key === "parent") { - // memo[key] = getMutation(key) || baseState.parent; - // return memo; - // } else { - // memo[key] = memo[key].keys.reduce(m, target) => {} - // } - // }, {}); - // }, - +/** + * Returns a set of all mutations for shared events + * + * @param {Array} mutations an array of mutations objects + * @param {Object} baseProps an object that describes all props for children of VictorySharedEvents + * @param {Object} baseState an object that describes state for children of VictorySharedEvents + * @param {Array} childNames an array of childNames + * + * @return {Object} a object describing all mutations for VictorySharedEvents + */ getExternalMutationsWithChildren(mutations, baseProps, baseState, childNames) { baseProps = baseProps || this.baseProps; baseState = baseState || this.state; @@ -266,42 +231,62 @@ export default { mutations, baseProps[childName], baseState[childName], childName ); memo[childName] = mutation ? mutation : childState; - return memo; + return pickBy(memo, (v) => !isEmpty(v)); }, {}); }, +/** + * Returns a set of all mutations for a component + * + * @param {Array} mutations an array of mutations objects + * @param {Object} baseProps a props object (scoped to a childName when used by shared events) + * @param {Object} baseState a state object (scoped to a childName when used by shared events) + * @param {String} childName an optional childName + * + * @return {Object} a object describing mutations for a given component + */ getExternalMutations(mutations, baseProps, baseState, childName) { baseProps = baseProps || {}; baseState = baseState || {}; const propKeys = keys(baseProps); return propKeys.reduce((memo, key) => { - const keyState = baseState[key]; - const keyProps = baseProps[key]; + const keyState = baseState[key] || {}; + const keyProps = baseProps[key] || {}; if (key === "parent") { const identifier = { key, target: "parent" }; - const mutation = this.getExternalMutation(identifier, mutations, keyState, keyProps); + const mutation = this.getExternalMutation(mutations, keyProps, keyState, identifier); memo[key] = typeof mutation !== "undefined" ? mutation : keyState; } else { + // use keys from both state and props so that elements not intially included in baseProps + // will be used. (i.e. labels) const targets = uniq(keys(keyProps).concat(keys(keyState))); memo[key] = targets.reduce((m, target) => { const identifier = { key, target, childName }; - const targetState = keyState && keyState[target]; - const targetProps = keyProps && keyProps[target]; - const mutation = this.getExternalMutation(identifier, mutations, targetState, targetProps); - m[target] = typeof mutation !== "undefined" ? - mutation : targetState; + const mutation = this.getExternalMutation( + mutations, keyProps[target], keyState[target], identifier + ); + m[target] = typeof mutation !== "undefined" ? mutation : keyState[target]; // Allow empty objects so that props can be cleared return pickBy(m, (v) => typeof v !== "undefined"); }, {}); } - return memo; - // return pickBy(memo, (v) => typeof v !== "undefined"); + return pickBy(memo, (v) => !isEmpty(v)); }, {}); }, - getExternalMutation(identifier, mutations, baseState, baseProps) { +/** + * Returns a set of mutations for a particular element given scoped baseProps and baseState + * + * @param {Array} mutations an array of mutations objects + * @param {Object} baseProps a props object (scoped the element specified by the identifier) + * @param {Object} baseState a state object (scoped the element specified by the identifier) + * @param {Object} identifier { key, target, childName } + * + * @return {Object | undefined} a object describing mutations for a given element, or undefined + */ + getExternalMutation(mutations, baseProps, baseState, identifier) { const { key, target, childName } = identifier; mutations = Array.isArray(mutations) ? mutations : [mutations]; let scopedMutations = mutations; From dde2c4877268560903a6d9e8a10fb57a306f7c15 Mon Sep 17 00:00:00 2001 From: boygirl Date: Mon, 1 Jan 2018 00:04:17 -0800 Subject: [PATCH 07/12] fix scu logic --- src/victory-util/add-events.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/victory-util/add-events.js b/src/victory-util/add-events.js index ad1ee25..8147590 100644 --- a/src/victory-util/add-events.js +++ b/src/victory-util/add-events.js @@ -26,22 +26,23 @@ export default (WrappedComponent, options) => { } componentWillReceiveProps(nextProps) { - const externalEventMutations = nextProps.externalEventMutations; - if (externalEventMutations) { + const { externalEventMutations, sharedEvents } = nextProps; + if (externalEventMutations && !sharedEvents) { this.externalMutation = Events.getExternalMutations( externalEventMutations, this.baseProps, this.state ); - this.setState(defaultsDeep({}, this.externalMutation, this.state)); + this.setState(this.externalMutation); } } - shouldComponentUpdate(nextProps) { + shouldComponentUpdate(nextProps, nextState) { const calculatedValues = this.getCalculatedValues(nextProps); // re-render without additional checks when component is animated if (this.props.animate || this.props.animating) { this.cacheValues(calculatedValues); return true; } + // check for any state changes triggered by events or shared events const calculatedState = this.getStateChanges(nextProps, calculatedValues); if (!Collection.areVictoryPropsEqual(this.calculatedState, calculatedState)) { @@ -55,9 +56,17 @@ export default (WrappedComponent, options) => { this.cacheValues(calculatedValues); return true; } + + // check whether state has changed + if (!Collection.areVictoryPropsEqual(this.state, nextState)) { + this.cacheValues(calculatedValues); + return true; + } + return false; } + // compile all state changes from own and parent state. Order doesn't matter, as any state // state change should trigger a re-render getStateChanges(props, calculatedValues) { From 56c43d9f4325b70ae5f174904c4b83c2afe255c5 Mon Sep 17 00:00:00 2001 From: boygirl Date: Mon, 1 Jan 2018 14:17:31 -0800 Subject: [PATCH 08/12] clean up lifecycle methods --- .../victory-shared-events.js | 31 +++++---- src/victory-util/add-events.js | 65 +++++++++++-------- 2 files changed, 57 insertions(+), 39 deletions(-) diff --git a/src/victory-shared-events/victory-shared-events.js b/src/victory-shared-events/victory-shared-events.js index c075abd..cb19996 100644 --- a/src/victory-shared-events/victory-shared-events.js +++ b/src/victory-shared-events/victory-shared-events.js @@ -1,4 +1,4 @@ -import { assign, isFunction, partialRight, defaults, merge, defaultsDeep, fromPairs } from "lodash"; +import { assign, isFunction, partialRight, defaults, isEmpty, fromPairs } from "lodash"; import React from "react"; import PropTypes from "prop-types"; import CustomPropTypes from "../victory-util/prop-types"; @@ -86,13 +86,6 @@ export default class VictorySharedEvents extends React.Component { componentWillReceiveProps(newProps) { this.setUpChildren(newProps); - const { externalEventMutations } = newProps; - if (externalEventMutations) { - const mutations = Events.getExternalMutationsWithChildren( - externalEventMutations, this.baseProps, this.state, this.childNames - ); - this.setState(mutations); - } } getTimer() { @@ -117,12 +110,26 @@ export default class VictorySharedEvents extends React.Component { setUpChildren(props) { this.events = this.getAllEvents(props); - if (this.events) { - this.childComponents = React.Children.toArray(props.children); + const { externalEventMutations, container, children } = props; + if (this.events || !isEmpty(externalEventMutations)) { + this.childComponents = React.Children.toArray(children); const childBaseProps = this.getBasePropsFromChildren(this.childComponents); - const parentBaseProps = props.container ? props.container.props : {}; - this.childNames = Object.keys(childBaseProps); + const parentBaseProps = container ? container.props : {}; + const childNames = Object.keys(childBaseProps); this.baseProps = assign({}, childBaseProps, { parent: parentBaseProps }); + + if (!isEmpty(externalEventMutations)) { + const externalMutations = Events.getExternalMutationsWithChildren( + externalEventMutations, this.baseProps, this.state, childNames + ); + const callbacks = externalEventMutations.reduce((memo, mutation) => { + memo = isFunction(mutation.callback) ? memo.concat(mutation.callback) : memo; + return memo; + }, []); + const compiledCallbacks = callbacks.length ? + () => { callbacks.forEach((c) => c()); } : undefined; + this.setState(externalMutations, compiledCallbacks); + } } } diff --git a/src/victory-util/add-events.js b/src/victory-util/add-events.js index 8147590..05671b3 100644 --- a/src/victory-util/add-events.js +++ b/src/victory-util/add-events.js @@ -1,7 +1,5 @@ import React from "react"; -import { - defaults, assign, keys, isFunction, partialRight, pick, without, isEmpty, defaultsDeep, merge -} from "lodash"; +import { defaults, assign, keys, isFunction, partialRight, pick, without, isEmpty } from "lodash"; import Events from "./events"; import Collection from "./collection"; import VictoryTransition from "../victory-transition/victory-transition"; @@ -23,20 +21,18 @@ export default (WrappedComponent, options) => { this.getEventState = Events.getEventState.bind(this); const calculatedValues = this.getCalculatedValues(this.props); this.cacheValues(calculatedValues); + this.applyExternalMutations(this.props, calculatedValues); } componentWillReceiveProps(nextProps) { - const { externalEventMutations, sharedEvents } = nextProps; - if (externalEventMutations && !sharedEvents) { - this.externalMutation = Events.getExternalMutations( - externalEventMutations, this.baseProps, this.state - ); - this.setState(this.externalMutation); - } + const calculatedValues = this.getCalculatedValues(nextProps); + this.applyExternalMutations(nextProps, calculatedValues); } - shouldComponentUpdate(nextProps, nextState) { + // eslint-disable-next-line max-statements + shouldComponentUpdate(nextProps) { const calculatedValues = this.getCalculatedValues(nextProps); + const { externalMutations } = calculatedValues; // re-render without additional checks when component is animated if (this.props.animate || this.props.animating) { this.cacheValues(calculatedValues); @@ -57,8 +53,8 @@ export default (WrappedComponent, options) => { return true; } - // check whether state has changed - if (!Collection.areVictoryPropsEqual(this.state, nextState)) { + // check whether external mutations match + if (!Collection.areVictoryPropsEqual(this.externaMutations, externalMutations)) { this.cacheValues(calculatedValues); return true; } @@ -66,6 +62,18 @@ export default (WrappedComponent, options) => { return false; } + applyExternalMutations(props, calculatedValues) { + const { externalMutations } = calculatedValues; + if (!isEmpty(externalMutations)) { + const callbacks = props.externalEventMutations.reduce((memo, mutation) => { + memo = isFunction(mutation.callback) ? memo.concat(mutation.callback) : memo; + return memo; + }, []); + const compiledCallbacks = callbacks.length ? + () => { callbacks.forEach((c) => c()); } : undefined; + this.setState(externalMutations, compiledCallbacks); + } + } // compile all state changes from own and parent state. Order doesn't matter, as any state // state change should trigger a re-render @@ -75,22 +83,28 @@ export default (WrappedComponent, options) => { options = options || {}; const components = options.components || defaultComponents; + + const getState = (key, type) => { + const baseState = defaults( + {}, this.getEventState(key, type), getSharedEventState(key, type) + ); + return isEmpty(baseState) ? undefined : baseState; + }; + return components.map((component) => { if (!props.standalone && component.name === "parent") { // don't check for changes on parent props for non-standalone components return undefined; } else { return typeof component.index !== "undefined" ? - this.getState(component.index, component.name, getSharedEventState) : - calculatedValues.dataKeys.map( - (key) => this.getState(key, component.name, getSharedEventState) - ); + getState(component.index, component.name) : + calculatedValues.dataKeys.map((key) => getState(key, component.name)); } }).filter(Boolean); } getCalculatedValues(props) { - const { sharedEvents } = props; + const { sharedEvents, externalEventMutations } = props; const components = WrappedComponent.expectedComponents; const componentEvents = Events.getComponentEvents(props, components); const getSharedEventState = sharedEvents && isFunction(sharedEvents.getEventState) ? @@ -99,8 +113,13 @@ export default (WrappedComponent, options) => { const dataKeys = keys(baseProps).filter((key) => key !== "parent"); const hasEvents = props.events || props.sharedEvents || componentEvents; const events = this.getAllEvents(props); + const externalMutations = isEmpty(externalEventMutations) || sharedEvents ? undefined : + Events.getExternalMutations( + externalEventMutations, baseProps, this.state + ); return { - componentEvents, getSharedEventState, baseProps, dataKeys, hasEvents, events + componentEvents, getSharedEventState, baseProps, dataKeys, + hasEvents, events, externalMutations }; } @@ -130,14 +149,6 @@ export default (WrappedComponent, options) => { return props.events; } - getState(key, type, getSharedEventState) { - getSharedEventState = getSharedEventState || this.getSharedEventState; - const baseState = defaults( - {}, this.getEventState(key, type), getSharedEventState(key, type) - ); - return isEmpty(baseState) ? undefined : baseState; - } - getComponentProps(component, type, index) { const { role } = WrappedComponent; const key = this.dataKeys && this.dataKeys[index] || index; From da6315e2e3ada963101daf364c9adaec133c8295 Mon Sep 17 00:00:00 2001 From: boygirl Date: Mon, 1 Jan 2018 14:24:01 -0800 Subject: [PATCH 09/12] clean up new events methods --- src/victory-util/events.js | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/victory-util/events.js b/src/victory-util/events.js index e9f9e45..cb5771e 100644 --- a/src/victory-util/events.js +++ b/src/victory-util/events.js @@ -1,6 +1,4 @@ -import { - assign, extend, merge, partial, isEmpty, isFunction, without, keys, pickBy, defaults, uniq -} from "lodash"; +import { assign, extend, merge, partial, isEmpty, isFunction, without, pickBy, uniq } from "lodash"; export default { /* Returns all own and shared events that should be attached to a single target element, @@ -222,8 +220,8 @@ export default { * @return {Object} a object describing all mutations for VictorySharedEvents */ getExternalMutationsWithChildren(mutations, baseProps, baseState, childNames) { - baseProps = baseProps || this.baseProps; - baseState = baseState || this.state; + baseProps = baseProps || {}; + baseState = baseState || {}; return childNames.reduce((memo, childName) => { const childState = baseState[childName]; @@ -250,8 +248,8 @@ export default { baseProps = baseProps || {}; baseState = baseState || {}; - const propKeys = keys(baseProps); - return propKeys.reduce((memo, key) => { + const keys = Object.keys(baseProps); + return keys.reduce((memo, key) => { const keyState = baseState[key] || {}; const keyProps = baseProps[key] || {}; if (key === "parent") { @@ -261,14 +259,14 @@ export default { } else { // use keys from both state and props so that elements not intially included in baseProps // will be used. (i.e. labels) - const targets = uniq(keys(keyProps).concat(keys(keyState))); + const targets = uniq(Object.keys(keyProps).concat(Object.keys(keyState))); memo[key] = targets.reduce((m, target) => { const identifier = { key, target, childName }; const mutation = this.getExternalMutation( mutations, keyProps[target], keyState[target], identifier ); m[target] = typeof mutation !== "undefined" ? mutation : keyState[target]; - // Allow empty objects so that props can be cleared + // Allow empty objects so that mutation state can be cleared return pickBy(m, (v) => typeof v !== "undefined"); }, {}); } @@ -304,9 +302,8 @@ export default { } return keyMutations.reduce((memo, curr) => { - const currentMutation = curr && curr.mutation( - assign({}, baseProps, baseState) - ); + const mutationFunction = curr && isFunction(curr.mutation) ? curr.mutation : () => undefined; + const currentMutation = mutationFunction(assign({}, baseProps, baseState)); return merge({}, memo, currentMutation); }, {}); }, From 7efa7f7fb76c9a3446c9e3859541d19cfc2ea7c5 Mon Sep 17 00:00:00 2001 From: boygirl Date: Mon, 1 Jan 2018 15:03:36 -0800 Subject: [PATCH 10/12] add support for arrays targets, eventKeys and childNames --- .../victory-shared-events.js | 6 ++- src/victory-util/events.js | 47 ++++++++++++------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/victory-shared-events/victory-shared-events.js b/src/victory-shared-events/victory-shared-events.js index cb19996..e16fc62 100644 --- a/src/victory-shared-events/victory-shared-events.js +++ b/src/victory-shared-events/victory-shared-events.js @@ -44,12 +44,14 @@ export default class VictorySharedEvents extends React.Component { ]), eventKey: PropTypes.oneOfType([ PropTypes.array, - PropTypes.func, CustomPropTypes.allOfType([CustomPropTypes.integer, CustomPropTypes.nonNegative]), PropTypes.string ]), mutation: PropTypes.function, - target: PropTypes.string + target: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.array + ]) })), groupComponent: PropTypes.node }; diff --git a/src/victory-util/events.js b/src/victory-util/events.js index cb5771e..92a9ec0 100644 --- a/src/victory-util/events.js +++ b/src/victory-util/events.js @@ -1,4 +1,6 @@ -import { assign, extend, merge, partial, isEmpty, isFunction, without, pickBy, uniq } from "lodash"; +import { + assign, extend, merge, partial, isEmpty, isFunction, without, pickBy, uniq, includes +} from "lodash"; export default { /* Returns all own and shared events that should be attached to a single target element, @@ -248,20 +250,20 @@ export default { baseProps = baseProps || {}; baseState = baseState || {}; - const keys = Object.keys(baseProps); - return keys.reduce((memo, key) => { - const keyState = baseState[key] || {}; - const keyProps = baseProps[key] || {}; - if (key === "parent") { - const identifier = { key, target: "parent" }; + const eventKeys = Object.keys(baseProps); + return eventKeys.reduce((memo, eventKey) => { + const keyState = baseState[eventKey] || {}; + const keyProps = baseProps[eventKey] || {}; + if (eventKey === "parent") { + const identifier = { eventKey, target: "parent" }; const mutation = this.getExternalMutation(mutations, keyProps, keyState, identifier); - memo[key] = typeof mutation !== "undefined" ? mutation : keyState; + memo[eventKey] = typeof mutation !== "undefined" ? mutation : keyState; } else { // use keys from both state and props so that elements not intially included in baseProps // will be used. (i.e. labels) const targets = uniq(Object.keys(keyProps).concat(Object.keys(keyState))); - memo[key] = targets.reduce((m, target) => { - const identifier = { key, target, childName }; + memo[eventKey] = targets.reduce((m, target) => { + const identifier = { eventKey, target, childName }; const mutation = this.getExternalMutation( mutations, keyProps[target], keyState[target], identifier ); @@ -280,27 +282,38 @@ export default { * @param {Array} mutations an array of mutations objects * @param {Object} baseProps a props object (scoped the element specified by the identifier) * @param {Object} baseState a state object (scoped the element specified by the identifier) - * @param {Object} identifier { key, target, childName } + * @param {Object} identifier { eventKey, target, childName } * * @return {Object | undefined} a object describing mutations for a given element, or undefined */ getExternalMutation(mutations, baseProps, baseState, identifier) { - const { key, target, childName } = identifier; + + const filterMutations = (mutation, type) => { + if (typeof mutation[type] === "string") { + return mutation[type] === "all" || mutation[type] === identifier[type]; + } else if (Array.isArray(mutation[type])) { + // coerce arrays to strings before matching + const stringArray = mutation[type].map((m) => `${m}`); + return includes(stringArray, identifier[type]); + } else { + return false; + } + }; + mutations = Array.isArray(mutations) ? mutations : [mutations]; let scopedMutations = mutations; - if (childName) { - scopedMutations = mutations.filter((m) => m.childName === childName); + if (identifier.childName) { + scopedMutations = mutations.filter((m) => filterMutations(m, "childName")); } // find any mutation objects that match the target - const targetMutations = scopedMutations.filter((m) => m.target === target); + const targetMutations = scopedMutations.filter((m) => filterMutations(m, "target")); if (isEmpty(targetMutations)) { return undefined; } - const keyMutations = targetMutations.filter((m) => m.eventKey === key || m.eventKey === "all"); + const keyMutations = targetMutations.filter((m) => filterMutations(m, "eventKey")); if (isEmpty(keyMutations)) { return undefined; } - return keyMutations.reduce((memo, curr) => { const mutationFunction = curr && isFunction(curr.mutation) ? curr.mutation : () => undefined; const currentMutation = mutationFunction(assign({}, baseProps, baseState)); From ca4e91d7acbd6d2f24126694f00290f9df9c6fd7 Mon Sep 17 00:00:00 2001 From: boygirl Date: Mon, 1 Jan 2018 16:11:53 -0800 Subject: [PATCH 11/12] add new prop for victory-legend --- src/victory-legend/victory-legend.js | 16 ++++++++++++++++ src/victory-util/events.js | 9 +++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/victory-legend/victory-legend.js b/src/victory-legend/victory-legend.js index fa73826..d81c42f 100644 --- a/src/victory-legend/victory-legend.js +++ b/src/victory-legend/victory-legend.js @@ -70,6 +70,22 @@ class VictoryLegend extends React.Component { ]), eventHandlers: PropTypes.object })), + externalEventMutations: PropTypes.arrayOf(PropTypes.shape({ + childName: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.array + ]), + eventKey: PropTypes.oneOfType([ + PropTypes.array, + CustomPropTypes.allOfType([CustomPropTypes.integer, CustomPropTypes.nonNegative]), + PropTypes.string + ]), + mutation: PropTypes.function, + target: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.array + ]) + })), groupComponent: PropTypes.element, gutter: PropTypes.oneOfType([ CustomPropTypes.nonNegative, diff --git a/src/victory-util/events.js b/src/victory-util/events.js index 92a9ec0..2596191 100644 --- a/src/victory-util/events.js +++ b/src/victory-util/events.js @@ -257,7 +257,8 @@ export default { if (eventKey === "parent") { const identifier = { eventKey, target: "parent" }; const mutation = this.getExternalMutation(mutations, keyProps, keyState, identifier); - memo[eventKey] = typeof mutation !== "undefined" ? mutation : keyState; + memo[eventKey] = typeof mutation !== "undefined" ? + assign({}, keyState, mutation) : keyState; } else { // use keys from both state and props so that elements not intially included in baseProps // will be used. (i.e. labels) @@ -267,9 +268,9 @@ export default { const mutation = this.getExternalMutation( mutations, keyProps[target], keyState[target], identifier ); - m[target] = typeof mutation !== "undefined" ? mutation : keyState[target]; - // Allow empty objects so that mutation state can be cleared - return pickBy(m, (v) => typeof v !== "undefined"); + m[target] = typeof mutation !== "undefined" ? + assign({}, keyState[target], mutation) : keyState[target]; + return pickBy(m, (v) => !isEmpty(v)); }, {}); } return pickBy(memo, (v) => !isEmpty(v)); From a228a145acc198d029308c6419cc3ad70f5f724b Mon Sep 17 00:00:00 2001 From: boygirl Date: Mon, 1 Jan 2018 16:37:17 -0800 Subject: [PATCH 12/12] add callback to prop definition --- src/victory-legend/victory-legend.js | 1 + src/victory-shared-events/victory-shared-events.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/victory-legend/victory-legend.js b/src/victory-legend/victory-legend.js index d81c42f..01d4a7e 100644 --- a/src/victory-legend/victory-legend.js +++ b/src/victory-legend/victory-legend.js @@ -71,6 +71,7 @@ class VictoryLegend extends React.Component { eventHandlers: PropTypes.object })), externalEventMutations: PropTypes.arrayOf(PropTypes.shape({ + callback: PropTypes.function, childName: PropTypes.oneOfType([ PropTypes.string, PropTypes.array diff --git a/src/victory-shared-events/victory-shared-events.js b/src/victory-shared-events/victory-shared-events.js index e16fc62..566698f 100644 --- a/src/victory-shared-events/victory-shared-events.js +++ b/src/victory-shared-events/victory-shared-events.js @@ -38,6 +38,7 @@ export default class VictorySharedEvents extends React.Component { target: PropTypes.string })), externalEventMutations: PropTypes.arrayOf(PropTypes.shape({ + callback: PropTypes.function, childName: PropTypes.oneOfType([ PropTypes.string, PropTypes.array