Merge branch 'closing-animation'
This commit is contained in:
commit
4941e1ad3d
|
@ -25,5 +25,6 @@
|
|||
"comma-dangle": 0,
|
||||
"react/prop-types": 0,
|
||||
"react/no-did-mount-set-state": 0,
|
||||
"react/no-deprecated": 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,5 +10,8 @@
|
|||
"jest": true,
|
||||
"expect": true,
|
||||
"jasmine": true
|
||||
},
|
||||
"rules": {
|
||||
"react/jsx-key": 0,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { View, Text } from 'react-native';
|
||||
import { render } from './helpers';
|
||||
|
||||
import { MenuTrigger, MenuOptions } from '../src/index';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { View, Text } from 'react-native';
|
||||
import { render } from './helpers';
|
||||
import { MenuOptions, MenuTrigger } from '../src/index';
|
||||
import MenuOutside from '../src/renderers/MenuOutside';
|
||||
|
@ -106,7 +106,7 @@ describe('MenuContext', () => {
|
|||
);
|
||||
const { menuRegistry, menuActions } = instance.getChildContext();
|
||||
menuRegistry.subscribe(menu1);
|
||||
menuActions.openMenu('menu1');
|
||||
menuActions.openMenu('menu1').then(() => {
|
||||
expect(menuActions.isMenuOpen()).toEqual(true);
|
||||
expect(menu1._getOpened()).toEqual(true);
|
||||
initOutput.props.onLayout(defaultLayout);
|
||||
|
@ -120,6 +120,7 @@ describe('MenuContext', () => {
|
|||
// on open was called only once
|
||||
expect(menu1.props.onOpen.calls.count()).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should close menu', () => {
|
||||
const { output: initOutput, instance, renderer } = render(
|
||||
|
@ -166,7 +167,7 @@ describe('MenuContext', () => {
|
|||
const { menuRegistry, menuActions } = instance.getChildContext();
|
||||
initOutput.props.onLayout(defaultLayout);
|
||||
menuRegistry.subscribe(menu1);
|
||||
menuActions.openMenu('menu_not_existing');
|
||||
return menuActions.openMenu('menu_not_existing').then(() => {
|
||||
expect(menuActions.isMenuOpen()).toEqual(false);
|
||||
const output = renderer.getRenderOutput();
|
||||
const [ components, backdrop, options ] = output.props.children;
|
||||
|
@ -174,6 +175,7 @@ describe('MenuContext', () => {
|
|||
expect(backdrop).toBeFalsy();
|
||||
expect(options).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not open menu if not initialized', () => {
|
||||
const { output, instance } = render(
|
||||
|
@ -181,7 +183,7 @@ describe('MenuContext', () => {
|
|||
);
|
||||
const { menuRegistry, menuActions } = instance.getChildContext();
|
||||
menuRegistry.subscribe(menu1);
|
||||
menuActions.openMenu('menu1');
|
||||
menuActions.openMenu('menu1').then(() => {
|
||||
expect(menuActions.isMenuOpen()).toEqual(true);
|
||||
const [ components, backdrop, options ] = output.props.children;
|
||||
// on layout has not been not called
|
||||
|
@ -189,6 +191,7 @@ describe('MenuContext', () => {
|
|||
expect(backdrop).toBeFalsy();
|
||||
expect(options).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should update options layout', () => {
|
||||
const { output: initOutput, instance, renderer } = render(
|
||||
|
@ -197,7 +200,7 @@ describe('MenuContext', () => {
|
|||
const { menuRegistry, menuActions } = instance.getChildContext();
|
||||
initOutput.props.onLayout(defaultLayout);
|
||||
menuRegistry.subscribe(menu1);
|
||||
menuActions.openMenu('menu1');
|
||||
menuActions.openMenu('menu1').then(() => {
|
||||
const output = renderer.getRenderOutput();
|
||||
expect(output.props.children.length).toEqual(3);
|
||||
const options = output.props.children[2];
|
||||
|
@ -218,6 +221,7 @@ describe('MenuContext', () => {
|
|||
}
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
it('should render backdrop that will trigger onBackdropPress', () => {
|
||||
const { output: initOutput, instance, renderer } = render(
|
||||
|
@ -226,7 +230,7 @@ describe('MenuContext', () => {
|
|||
const { menuRegistry, menuActions } = instance.getChildContext();
|
||||
initOutput.props.onLayout(defaultLayout);
|
||||
menuRegistry.subscribe(menu1);
|
||||
menuActions.openMenu('menu1');
|
||||
menuActions.openMenu('menu1').then(() => {
|
||||
const output = renderer.getRenderOutput();
|
||||
expect(output.props.children.length).toEqual(3);
|
||||
const backdrop = output.props.children[1];
|
||||
|
@ -234,5 +238,6 @@ describe('MenuContext', () => {
|
|||
backdrop.props.onPress();
|
||||
expect(menu1.props.onBackdropPress).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Animated } from 'react-native';
|
||||
import { Animated, Text } from 'react-native';
|
||||
import { render } from '../helpers';
|
||||
|
||||
jest.dontMock('../../src/renderers/ContextMenu');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { View, Text } from 'react-native';
|
||||
import { render } from '../helpers';
|
||||
|
||||
jest.dontMock('../../src/renderers/MenuOutside');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { View, Text } from 'react-native';
|
||||
import { render } from '../helpers';
|
||||
|
||||
jest.dontMock('../../src/renderers/NotAnimatedContextMenu');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Animated } from 'react-native';
|
||||
import { Animated, Text } from 'react-native';
|
||||
import { render } from '../helpers';
|
||||
|
||||
jest.dontMock('../../src/renderers/SlideInMenu');
|
||||
|
|
|
@ -12,19 +12,23 @@ export default class ControlledExample extends Component {
|
|||
onOptionSelect(value) {
|
||||
alert(`Selected number: ${value}`);
|
||||
if (value === 1) {
|
||||
this.refs.menu.close();
|
||||
this.menu.close();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
openMenu() {
|
||||
this.refs.menu.open();
|
||||
this.menu.open();
|
||||
}
|
||||
|
||||
onRef = r => {
|
||||
this.menu = r;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<MenuContext style={{flexDirection: 'column', padding: 30}}>
|
||||
<Menu onSelect={value => this.onOptionSelect(value)} ref='menu'>
|
||||
<Menu onSelect={value => this.onOptionSelect(value)} ref={this.onRef}>
|
||||
<MenuTrigger text='Select option'/>
|
||||
<MenuOptions>
|
||||
<MenuOption value={1} text='One' />
|
||||
|
|
|
@ -46,7 +46,7 @@ const OriginalExample = React.createClass({
|
|||
});
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
return ( // eslint-disable-next-line react/no-string-refs
|
||||
<MenuContext style={{ flex: 1 }} ref="MenuContext">
|
||||
<View style={styles.topbar}>
|
||||
<Menu onSelect={this.setMessage}>
|
||||
|
|
30
package.json
30
package.json
|
@ -21,10 +21,10 @@
|
|||
},
|
||||
"homepage": "https://github.com/instea/react-native-popup-menu",
|
||||
"jest": {
|
||||
"scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
|
||||
"testFileExtensions": [
|
||||
"js"
|
||||
],
|
||||
"transform": {
|
||||
".*": "<rootDir>/node_modules/babel-jest"
|
||||
},
|
||||
"testRegex": ".*-test.js",
|
||||
"moduleFileExtensions": [
|
||||
"js"
|
||||
],
|
||||
|
@ -41,18 +41,20 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.8.0",
|
||||
"babel-eslint": "^6.0.4",
|
||||
"babel-jest": "^12.0.2",
|
||||
"babel-eslint": "^7.2.3",
|
||||
"babel-jest": "^20.0.1",
|
||||
"babel-polyfill": "^6.23.0",
|
||||
"babel-preset-react": "^6.5.0",
|
||||
"babel-preset-react-native": "^1.7.0",
|
||||
"babel-preset-react-native": "^1.9.2",
|
||||
"chai": "^3.5.0",
|
||||
"eslint": "^2.9.0",
|
||||
"eslint-plugin-react": "^5.1.1",
|
||||
"eslint": "^3.19.0",
|
||||
"eslint-plugin-react": "^7.0.0",
|
||||
"jasmine-reporters": "^2.1.1",
|
||||
"jest-cli": "^12.0.2",
|
||||
"mocha": "^2.4.5",
|
||||
"react": "^15.0.2",
|
||||
"react-addons-test-utils": "^15.0.2",
|
||||
"sinon": "^1.17.4"
|
||||
"jest-cli": "^20.0.1",
|
||||
"mocha": "^3.3.0",
|
||||
"react": "^15.5.4",
|
||||
"react-addons-test-utils": "^15.5.1",
|
||||
"react-dom": "^15.5.4",
|
||||
"sinon": "^2.2.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,37 +40,47 @@ export default class MenuContext extends Component {
|
|||
openMenu(name) {
|
||||
const menu = this._menuRegistry.getMenu(name);
|
||||
if (!menu) {
|
||||
return console.warn(`menu with name ${name} does not exist`);
|
||||
console.warn(`menu with name ${name} does not exist`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
debug('open menu', name);
|
||||
menu.instance._setOpened(true);
|
||||
this._notify();
|
||||
return Promise.resolve();
|
||||
return this._notify();
|
||||
}
|
||||
|
||||
closeMenu() {
|
||||
closeMenu() { // has no effect on controlled menus
|
||||
debug('close menu');
|
||||
const hideMenu = (this.refs.menuOptions
|
||||
&& this.refs.menuOptions.close
|
||||
&& this.refs.menuOptions.close()) || Promise.resolve();
|
||||
const hideBackdrop = this.refs.backdrop && this.refs.backdrop.close();
|
||||
const closePromise = Promise.all([hideMenu, hideBackdrop]);
|
||||
return closePromise.then(() => {
|
||||
this._menuRegistry.getAll().forEach(menu => {
|
||||
if (menu.instance._getOpened()) {
|
||||
menu.instance._setOpened(false);
|
||||
// invalidate trigger layout
|
||||
this._menuRegistry.updateLayoutInfo(menu.name, { triggerLayout: undefined });
|
||||
this._menuRegistry.getAll()
|
||||
.filter(menu => menu.instance._getOpened())
|
||||
.forEach(menu => menu.instance._setOpened(false));
|
||||
return this._notify();
|
||||
}
|
||||
|
||||
_invalidateTriggerLayouts() {
|
||||
// invalidate layouts for closed menus,
|
||||
// both controlled and uncontrolled menus
|
||||
this._menuRegistry.getAll()
|
||||
.filter(menu => !menu.instance._isOpen())
|
||||
.forEach(menu => {
|
||||
this._menuRegistry.updateLayoutInfo(menu.name, { triggerLayout: undefined });
|
||||
});
|
||||
this._notify();
|
||||
}).catch(console.error);
|
||||
}
|
||||
|
||||
_beforeClose(menu) {
|
||||
debug('before close', menu.name);
|
||||
const hideMenu = (this.optionsRef
|
||||
&& this.optionsRef.close
|
||||
&& this.optionsRef.close()) || Promise.resolve();
|
||||
const hideBackdrop = this.backdropRef && this.backdropRef.close();
|
||||
this._invalidateTriggerLayouts();
|
||||
return Promise.all([hideMenu, hideBackdrop]);
|
||||
}
|
||||
|
||||
toggleMenu(name) {
|
||||
const menu = this._menuRegistry.getMenu(name);
|
||||
if (!menu) {
|
||||
return console.warn(`menu with name ${name} does not exist`);
|
||||
console.warn(`menu with name ${name} does not exist`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
debug('toggle menu', name);
|
||||
if (menu.instance._getOpened()) {
|
||||
|
@ -87,19 +97,25 @@ export default class MenuContext extends Component {
|
|||
// set newly opened menu before any callbacks are called
|
||||
this.openedMenu = next === NULL ? undefined : next;
|
||||
if (!forceUpdate && !this._isRenderNeeded(prev, next)) {
|
||||
return;
|
||||
return Promise.resolve();
|
||||
}
|
||||
debug('notify: next menu:', next.name, ' prev menu:', prev.name);
|
||||
let afterSetState = undefined;
|
||||
let beforeSetState = () => Promise.resolve();
|
||||
if (prev.name !== next.name) {
|
||||
prev.instance && prev.instance.props.onClose();
|
||||
if (next.name) {
|
||||
if (prev !== NULL && !prev.instance._isOpen()) {
|
||||
beforeSetState = () => this._beforeClose(prev)
|
||||
.then(() => prev.instance.props.onClose());
|
||||
}
|
||||
if (next !== NULL) {
|
||||
next.instance.props.onOpen();
|
||||
afterSetState = () => this._initOpen(next);
|
||||
}
|
||||
}
|
||||
return beforeSetState().then(() => {
|
||||
this.setState({ openedMenu: this.openedMenu }, afterSetState);
|
||||
debug('notify ended');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -131,7 +147,11 @@ export default class MenuContext extends Component {
|
|||
{ this.props.children }
|
||||
</View>
|
||||
{shouldRenderMenu &&
|
||||
<Backdrop onPress={() => this._onBackdropPress()} style={customStyles.backdrop} ref='backdrop' />
|
||||
<Backdrop
|
||||
onPress={() => this._onBackdropPress()}
|
||||
style={customStyles.backdrop}
|
||||
ref={this.onBackdropRef}
|
||||
/>
|
||||
}
|
||||
{shouldRenderMenu &&
|
||||
this._makeOptions(this.state.openedMenu)
|
||||
|
@ -140,6 +160,14 @@ export default class MenuContext extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
onBackdropRef = r => {
|
||||
this.backdropRef = r;
|
||||
}
|
||||
|
||||
onOptionsRef = r => {
|
||||
this.optionsRef = r;
|
||||
}
|
||||
|
||||
_onBackdropPress() {
|
||||
debug('on backdrop press');
|
||||
this.state.openedMenu.instance.props.onBackdropPress();
|
||||
|
@ -181,7 +209,7 @@ export default class MenuContext extends Component {
|
|||
const props = { style, onLayout, layouts };
|
||||
const optionsType = isOutside ? MenuOutside : renderer;
|
||||
if (!isFunctional(optionsType)) {
|
||||
props.ref = 'menuOptions';
|
||||
props.ref = this.onOptionsRef;
|
||||
}
|
||||
return React.createElement(optionsType, props, optionsRenderer(options));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue