initial replacement with new context API - WIP
This commit is contained in:
parent
37d0a0f8e1
commit
1bfab39fa2
|
@ -57,7 +57,7 @@ describe('MenuTrigger', () => {
|
|||
<MenuTrigger menuName='menu1' />
|
||||
);
|
||||
const menuActions = { openMenu: createSpy() };
|
||||
instance.context = { menuActions };
|
||||
instance.props.ctx = { menuActions };
|
||||
nthChild(output, 1).props.onPress();
|
||||
expect(menuActions.openMenu).toHaveBeenCalledWith('menu1');
|
||||
expect(menuActions.openMenu.calls.count()).toEqual(1);
|
||||
|
@ -68,7 +68,7 @@ describe('MenuTrigger', () => {
|
|||
<MenuTrigger menuName='menu1' disabled={true} />
|
||||
);
|
||||
const menuActions = { openMenu: createSpy() };
|
||||
instance.context = { menuActions };
|
||||
instance.props.ctx = { menuActions };
|
||||
nthChild(output, 1).props.onPress();
|
||||
expect(menuActions.openMenu).not.toHaveBeenCalled();
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "react-native-popup-menu",
|
||||
"version": "0.12.3",
|
||||
"version": "0.13.0-rc",
|
||||
"description": "extensible popup/context menu for react native",
|
||||
"main": "src/index.js",
|
||||
"directories": {
|
||||
|
@ -61,5 +61,6 @@
|
|||
"react-dom": "^15.5.4",
|
||||
"react-test-renderer": "^15.6.1",
|
||||
"sinon": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
|
|
27
src/Menu.js
27
src/Menu.js
|
@ -5,17 +5,19 @@ import { MenuOptions, MenuTrigger } from './index';
|
|||
import ContextMenu from './renderers/ContextMenu';
|
||||
import { makeName } from './helpers';
|
||||
import { debug } from './logger';
|
||||
import { withCtx } from './MenuProvider';
|
||||
|
||||
const isRegularComponent = c => c.type !== MenuOptions && c.type !== MenuTrigger;
|
||||
const isTrigger = c => c.type === MenuTrigger;
|
||||
const isMenuOptions = c => c.type === MenuOptions;
|
||||
|
||||
export default class Menu extends Component {
|
||||
class Menu extends Component {
|
||||
|
||||
constructor(props, ctx) {
|
||||
super(props, ctx);
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this._name = this.props.name || makeName();
|
||||
this._forceClose = false;
|
||||
const { ctx } = props;
|
||||
if(!(ctx && ctx.menuActions)) {
|
||||
throw new Error("Menu component must be ancestor of MenuProvider");
|
||||
}
|
||||
|
@ -26,24 +28,24 @@ export default class Menu extends Component {
|
|||
return;
|
||||
}
|
||||
debug('subscribing menu', this._name);
|
||||
this.context.menuRegistry.subscribe(this);
|
||||
this.context.menuActions._notify();
|
||||
this.props.ctx.menuRegistry.subscribe(this);
|
||||
this.props.ctx.menuActions._notify();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
// force update if menu is opened as its content might have changed
|
||||
const force = this._isOpen();
|
||||
debug('component did update', this._name, force);
|
||||
this.context.menuActions._notify(force);
|
||||
this.props.ctx.menuActions._notify(force);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
debug('unsubscribing menu', this._name);
|
||||
if (this._isOpen()) {
|
||||
this._forceClose = true;
|
||||
this.context.menuActions._notify();
|
||||
this.props.ctx.menuActions._notify();
|
||||
}
|
||||
this.context.menuRegistry.unsubscribe(this);
|
||||
this.props.ctx.menuRegistry.unsubscribe(this);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
|
@ -53,11 +55,11 @@ export default class Menu extends Component {
|
|||
}
|
||||
|
||||
open() {
|
||||
return this.context.menuActions.openMenu(this._name);
|
||||
return this.props.ctx.menuActions.openMenu(this._name);
|
||||
}
|
||||
|
||||
close() {
|
||||
return this.context.menuActions.closeMenu();
|
||||
return this.props.ctx.menuActions.closeMenu();
|
||||
}
|
||||
|
||||
getName() {
|
||||
|
@ -156,7 +158,4 @@ Menu.defaultProps = {
|
|||
onBackdropPress: () => {},
|
||||
};
|
||||
|
||||
Menu.contextTypes = {
|
||||
menuRegistry: PropTypes.object,
|
||||
menuActions: PropTypes.object,
|
||||
};
|
||||
export default withCtx(Menu);
|
||||
|
|
|
@ -3,8 +3,10 @@ import PropTypes from 'prop-types';
|
|||
import { View, StyleSheet, Text } from 'react-native';
|
||||
import { debug } from './logger';
|
||||
import { makeTouchable } from './helpers';
|
||||
import { withCtx } from './MenuProvider';
|
||||
|
||||
export default class MenuOption extends Component {
|
||||
|
||||
class MenuOption extends Component {
|
||||
|
||||
_onSelect() {
|
||||
const { value } = this.props;
|
||||
|
@ -12,17 +14,17 @@ export default class MenuOption extends Component {
|
|||
const shouldClose = onSelect(value) !== false;
|
||||
debug('select option', value, shouldClose);
|
||||
if (shouldClose) {
|
||||
this.context.menuActions.closeMenu();
|
||||
this.props.ctx.menuActions.closeMenu();
|
||||
}
|
||||
}
|
||||
|
||||
_getMenusOnSelect() {
|
||||
const menu = this.context.menuActions._getOpenedMenu();
|
||||
const menu = this.props.ctx.menuActions._getOpenedMenu();
|
||||
return menu.instance.props.onSelect;
|
||||
}
|
||||
|
||||
_getCustomStyles() {
|
||||
const { optionsCustomStyles } = this.context.menuActions._getOpenedMenu();
|
||||
const { optionsCustomStyles } = this.props.ctx.menuActions._getOpenedMenu();
|
||||
return {
|
||||
...optionsCustomStyles,
|
||||
...this.props.customStyles,
|
||||
|
@ -81,10 +83,6 @@ MenuOption.defaultProps = {
|
|||
customStyles: {},
|
||||
};
|
||||
|
||||
MenuOption.contextTypes = {
|
||||
menuActions: PropTypes.object,
|
||||
};
|
||||
|
||||
const defaultStyles = StyleSheet.create({
|
||||
option: {
|
||||
padding: 5,
|
||||
|
@ -94,3 +92,5 @@ const defaultStyles = StyleSheet.create({
|
|||
color: '#ccc',
|
||||
},
|
||||
});
|
||||
|
||||
export default withCtx(MenuOption);
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { View } from 'react-native';
|
||||
import { withCtx } from './MenuProvider';
|
||||
|
||||
class MenuOptions extends React.Component {
|
||||
class _MenuOptions extends React.Component {
|
||||
|
||||
updateCustomStyles(_props) {
|
||||
const { customStyles } = _props
|
||||
const menu = this.context.menuActions._getOpenedMenu()
|
||||
const menu = this.props.ctx.menuActions._getOpenedMenu()
|
||||
const menuName = menu.instance.getName()
|
||||
this.context.menuRegistry.setOptionsCustomStyles(menuName, customStyles)
|
||||
this.props.ctx.menuRegistry.setOptionsCustomStyles(menuName, customStyles)
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
|
@ -29,6 +30,8 @@ class MenuOptions extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
const MenuOptions = withCtx(_MenuOptions)
|
||||
|
||||
MenuOptions.propTypes = {
|
||||
customStyles: PropTypes.object,
|
||||
renderOptionsContainer: PropTypes.func,
|
||||
|
@ -43,9 +46,4 @@ MenuOptions.defaultProps = {
|
|||
customStyles: {},
|
||||
};
|
||||
|
||||
MenuOptions.contextTypes = {
|
||||
menuRegistry: PropTypes.object,
|
||||
menuActions: PropTypes.object,
|
||||
};
|
||||
|
||||
export default MenuOptions;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import React, { Component } from 'react';
|
||||
import React, { Component, createContext } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { View, BackHandler } from 'react-native';
|
||||
|
||||
import { withContext } from './with-context';
|
||||
import makeMenuRegistry from './menuRegistry';
|
||||
import MenuPlaceholder from './MenuPlaceholder';
|
||||
import { measure } from './helpers';
|
||||
|
@ -14,15 +16,18 @@ const layoutsEqual = (a, b) => (
|
|||
|
||||
const isFunctional = Component => !Component.prototype.render;
|
||||
|
||||
if (!createContext) {
|
||||
console.warn('New React context API not available - are you using RN 0.55+ ?')
|
||||
}
|
||||
const PopupMenuContext = createContext({})
|
||||
export const withCtx = withContext(PopupMenuContext, "ctx");
|
||||
|
||||
export default class MenuProvider extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this._menuRegistry = makeMenuRegistry();
|
||||
this._isMenuClosing = false;
|
||||
}
|
||||
|
||||
getChildContext() {
|
||||
const menuActions = {
|
||||
openMenu: name => this.openMenu(name),
|
||||
closeMenu: () => this.closeMenu(),
|
||||
|
@ -31,8 +36,7 @@ export default class MenuProvider extends Component {
|
|||
_getOpenedMenu: () => this._getOpenedMenu(),
|
||||
_notify: force => this._notify(force),
|
||||
};
|
||||
const menuRegistry = this._menuRegistry;
|
||||
return { menuRegistry, menuActions };
|
||||
this.menuCtx = { menuRegistry: this._menuRegistry, menuActions }
|
||||
}
|
||||
|
||||
_handleBackButton = () => {
|
||||
|
@ -196,21 +200,23 @@ export default class MenuProvider extends Component {
|
|||
const { style, customStyles } = this.props;
|
||||
debug('render menu', this.isMenuOpen(), this._ownLayout);
|
||||
return (
|
||||
<View style={{flex:1}} onLayout={this._onLayout}>
|
||||
<View style={[
|
||||
{flex:1},
|
||||
customStyles.menuContextWrapper,
|
||||
customStyles.menuProviderWrapper,
|
||||
style,
|
||||
]}>
|
||||
{ this.props.children }
|
||||
<PopupMenuContext.Provider value={this.menuCtx}>
|
||||
<View style={{flex:1}} onLayout={this._onLayout}>
|
||||
<View style={[
|
||||
{flex:1},
|
||||
customStyles.menuContextWrapper,
|
||||
customStyles.menuProviderWrapper,
|
||||
style,
|
||||
]}>
|
||||
{ this.props.children }
|
||||
</View>
|
||||
<MenuPlaceholder
|
||||
ctx={this}
|
||||
backdropStyles={customStyles.backdrop}
|
||||
ref={this._onPlaceholderRef}
|
||||
/>
|
||||
</View>
|
||||
<MenuPlaceholder
|
||||
ctx={this}
|
||||
backdropStyles={customStyles.backdrop}
|
||||
ref={this._onPlaceholderRef}
|
||||
/>
|
||||
</View>
|
||||
</PopupMenuContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -235,7 +241,6 @@ export default class MenuProvider extends Component {
|
|||
if (menu) {
|
||||
menu.instance.props.onBackdropPress();
|
||||
}
|
||||
|
||||
this.closeMenu();
|
||||
}
|
||||
|
||||
|
@ -311,8 +316,3 @@ MenuProvider.defaultProps = {
|
|||
customStyles: {},
|
||||
backHandler: false,
|
||||
};
|
||||
|
||||
MenuProvider.childContextTypes = {
|
||||
menuRegistry: PropTypes.object,
|
||||
menuActions: PropTypes.object,
|
||||
};
|
||||
|
|
|
@ -3,13 +3,14 @@ import PropTypes from 'prop-types';
|
|||
import { View, Text } from 'react-native';
|
||||
import { debug } from './logger.js';
|
||||
import { makeTouchable } from './helpers';
|
||||
import { withCtx } from './MenuProvider';
|
||||
|
||||
export default class MenuTrigger extends Component {
|
||||
class MenuTrigger extends Component {
|
||||
|
||||
_onPress() {
|
||||
debug('trigger onPress');
|
||||
this.props.onPress && this.props.onPress();
|
||||
this.context.menuActions.openMenu(this.props.menuName);
|
||||
this.props.ctx.menuActions.openMenu(this.props.menuName);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -45,7 +46,4 @@ MenuTrigger.defaultProps = {
|
|||
customStyles: {},
|
||||
};
|
||||
|
||||
MenuTrigger.contextTypes = {
|
||||
menuActions: PropTypes.object,
|
||||
};
|
||||
|
||||
export default withCtx(MenuTrigger)
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
import React from "react";
|
||||
|
||||
export function withContext(Context, propName = "context") {
|
||||
return function wrap(Component) {
|
||||
class EnhanceContext extends React.Component {
|
||||
render() {
|
||||
const { forwardedRef, ...rest } = this.props;
|
||||
|
||||
return (
|
||||
<Context.Consumer>
|
||||
{value => {
|
||||
const custom = {
|
||||
[propName]: value,
|
||||
ref: forwardedRef
|
||||
};
|
||||
return <Component {...custom} {...rest} />;
|
||||
}}
|
||||
</Context.Consumer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const name = Component.displayName || Component.name || "Component";
|
||||
const consumerName =
|
||||
Context.Consumer.displayName ||
|
||||
Context.Consumer.name ||
|
||||
"Context.Consumer";
|
||||
|
||||
function enhanceForwardRef(props, ref) {
|
||||
return <EnhanceContext {...props} forwardedRef={ref} />;
|
||||
}
|
||||
|
||||
enhanceForwardRef.displayName = `enhanceContext-${consumerName}(${name})`;
|
||||
|
||||
enhanceForwardRef.defaultProps = Component.defaultProps;
|
||||
enhanceForwardRef.propTypes = Component.propTypes;
|
||||
|
||||
return React.forwardRef(enhanceForwardRef);
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue