Skip to content

Commit

Permalink
Revert "Revert "Merge pull request #23689 from code-dot-org/es6-instr…
Browse files Browse the repository at this point in the history
…uctions""

This reverts commit e0c0e74.
  • Loading branch information
islemaster committed Jul 16, 2018
1 parent ef6b643 commit e97c8f6
Show file tree
Hide file tree
Showing 4 changed files with 235 additions and 38 deletions.
12 changes: 6 additions & 6 deletions apps/src/templates/instructions/InstructionsDialogWrapper.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,25 @@ import { connect } from 'react-redux';
* into this component, though we're moving towards getting rid of this dialog
* anyways.
*/
const InstructionsDialogWrapper = React.createClass({
propTypes: {
export class UnwrappedInstructionsDialogWrapper extends React.Component {
static propTypes = {
isOpen: PropTypes.bool.isRequired,
autoClose: PropTypes.bool,
showInstructionsDialog: PropTypes.func.isRequired
},
};

componentWillReceiveProps(nextProps) {
if (!this.props.isOpen && nextProps.isOpen) {
this.props.showInstructionsDialog(nextProps.autoClose);
}
},
}

render() {
return null;
}
});
}

export default connect(state => ({
isOpen: state.instructionsDialog.open,
autoClose: state.instructionsDialog.autoClose,
}))(InstructionsDialogWrapper);
}))(UnwrappedInstructionsDialogWrapper);
61 changes: 29 additions & 32 deletions apps/src/templates/instructions/InstructionsWithWorkspace.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import $ from 'jquery';
import React, {PropTypes} from 'react';
import {connect} from 'react-redux';
var CodeWorkspaceContainer = require('../CodeWorkspaceContainer');
import CodeWorkspaceContainer from '../CodeWorkspaceContainer';
import TopInstructions from './TopInstructions';
var instructions = require('../../redux/instructions');
import {setInstructionsMaxHeightAvailable} from '../../redux/instructions';

/**
* A component representing the right side of the screen in our app. In particular
Expand All @@ -12,43 +12,40 @@ var instructions = require('../../redux/instructions');
* Owns maxHeightAvailable for instructions, updating as appropriate on window
* resize events
*/
var InstructionsWithWorkspace = React.createClass({
propTypes: {
export class UnwrappedInstructionsWithWorkspace extends React.Component {
static propTypes = {
children: PropTypes.node,
// props provided via connect
instructionsHeight: PropTypes.number.isRequired,

setInstructionsMaxHeightAvailable: PropTypes.func.isRequired,
children: PropTypes.node,
},
};

getInitialState() {
// only used so that we can rerender when resized
return {
windowWidth: undefined,
windowHeight: undefined
};
},
// only used so that we can rerender when resized
state = {
windowWidth: undefined,
windowHeight: undefined
};

/**
* Called when the window resizes. Look to see if width/height changed, then
* call adjustTopPaneHeight as our maxHeight may need adjusting.
*/
onResize() {
onResize = () => {
const {
windowWidth: lastWindowWidth,
windowHeight: lastWindowHeight
} = this.state;
const windowWidth = $(window).width();
const windowHeight = $(window).height();

// We fire window resize events when the grippy is dragged so that non-React
// controlled components are able to rerender the editor. If width/height
// didn't change, we don't need to do anything else here
if (windowWidth === this.state.windowWidth &&
windowHeight === this.state.windowHeight) {
if (windowWidth === lastWindowWidth && windowHeight === lastWindowHeight) {
return;
}

this.setState({
windowWidth: $(window).width(),
windowHeight: $(window).height()
});
this.setState({windowWidth, windowHeight});

// Determine what the maximum size of our instructions is based off of the
// size of the code workspace.
Expand All @@ -62,8 +59,8 @@ var InstructionsWithWorkspace = React.createClass({
const DEBUGGER_RESERVE = 120;
const INSTRUCTIONS_RESERVE = 150;

const instructionsHeight = this.props.instructionsHeight;
const codeWorkspaceHeight = this.refs.codeWorkspaceContainer
const {instructionsHeight, setInstructionsMaxHeightAvailable} = this.props;
const codeWorkspaceHeight = this.codeWorkspaceContainer
.getWrappedInstance().getRenderedHeight();
if (codeWorkspaceHeight === 0) {
// We haven't initialized the codeWorkspace yet. No need to change the
Expand All @@ -79,40 +76,40 @@ var InstructionsWithWorkspace = React.createClass({
// we have to instructions, and the other 2/3 to the workspace
maxInstructionsHeight = Math.round(totalHeight / 3);
}
this.props.setInstructionsMaxHeightAvailable(maxInstructionsHeight);
},
setInstructionsMaxHeightAvailable(maxInstructionsHeight);
};

componentDidMount() {
window.addEventListener('resize', this.onResize);
},
}

componentWillUnmount() {
window.removeEventListener('resize', this.onResize);
},
}

render() {
return (
<span>
<TopInstructions/>
<CodeWorkspaceContainer
ref="codeWorkspaceContainer"
ref={el => this.codeWorkspaceContainer = el}
topMargin={this.props.instructionsHeight}
>
{this.props.children}
</CodeWorkspaceContainer>
</span>
);
}
});
}

module.exports = connect(function propsFromStore(state) {
export default connect(function propsFromStore(state) {
return {
instructionsHeight: state.instructions.renderedHeight
};
}, function propsFromDispatch(dispatch) {
return {
setInstructionsMaxHeightAvailable(maxHeight) {
dispatch(instructions.setInstructionsMaxHeightAvailable(maxHeight));
dispatch(setInstructionsMaxHeightAvailable(maxHeight));
}
};
})(InstructionsWithWorkspace);
})(UnwrappedInstructionsWithWorkspace);
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React from 'react';
import {mount} from 'enzyme';
import sinon from 'sinon';
import {expect} from '../../../util/configuredChai';
import {
UnwrappedInstructionsDialogWrapper as InstructionsDialogWrapper
} from '@cdo/apps/templates/instructions/InstructionsDialogWrapper';

describe('InstructionsDialogWrapper', () => {
let spy, wrapper;

beforeEach(() => {
spy = sinon.spy();
});

afterEach(() => {
if (wrapper) {
wrapper.unmount();
}
});

it('renders nothing', () => {
wrapper = mount(
<InstructionsDialogWrapper
isOpen={true}
showInstructionsDialog={spy}
/>
);
expect(wrapper).to.be.empty;
});

it('does not call showInstructionsDialog on first render', () => {
wrapper = mount(
<InstructionsDialogWrapper
isOpen={true}
showInstructionsDialog={spy}
/>
);
expect(spy).not.to.have.been.called;
});

it('calls showInstructionsDialog every time props change to open', () => {
wrapper = mount(
<InstructionsDialogWrapper
isOpen={false}
showInstructionsDialog={spy}
/>
);
expect(spy).not.to.have.been.called;
wrapper.setProps({isOpen: true});
expect(spy).to.have.been.calledOnce;
wrapper.setProps({isOpen: true});
expect(spy).to.have.been.calledOnce;
wrapper.setProps({isOpen: false});
expect(spy).to.have.been.calledOnce;
wrapper.setProps({isOpen: false});
expect(spy).to.have.been.calledOnce;
wrapper.setProps({isOpen: true});
expect(spy).to.have.been.calledTwice;
});

it('passes optional autoClose prop to showInstructionsDialog', () => {
wrapper = mount(
<InstructionsDialogWrapper
isOpen={false}
showInstructionsDialog={spy}
/>
);
expect(spy).not.to.have.been.called;
wrapper.setProps({isOpen: true, autoClose: true});
expect(spy).to.have.been.calledOnce.and.calledWith(true);
wrapper.setProps({isOpen: false});
expect(spy).to.have.been.calledOnce;
wrapper.setProps({isOpen: true, autoClose: false});
expect(spy).to.have.been.calledTwice.and.calledWith(false);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import $ from 'jquery';
import React from 'react';
import sinon from 'sinon';
import {shallow} from 'enzyme';
import {expect} from '../../../util/configuredChai';
import {
UnwrappedInstructionsWithWorkspace as InstructionsWithWorkspace
} from '@cdo/apps/templates/instructions/InstructionsWithWorkspace';

describe('InstructionsWithWorkspace', () => {
it('renders instructions and code workspace', () => {
const wrapper = shallow(
<InstructionsWithWorkspace
instructionsHeight={400}
setInstructionsMaxHeightAvailable={() => {}}
/>
);
expect(wrapper).to.have.descendants('Connect(TopInstructions)');
expect(wrapper).to.have.descendants('Connect(CodeWorkspaceContainer)');
});

it('initially does not know window width or height', () => {
const wrapper = shallow(
<InstructionsWithWorkspace
instructionsHeight={400}
setInstructionsMaxHeightAvailable={() => {}}
/>
);
expect(wrapper.state()).to.deep.equal({
windowWidth: undefined,
windowHeight: undefined
});
});

describe('onResize', () => {
let setInstructionsMaxHeightAvailable;

beforeEach(() => {
setInstructionsMaxHeightAvailable = sinon.spy();

sinon.stub($.fn, 'width').returns(1024);
sinon.stub($.fn, 'height').returns(768);
});

afterEach(() => {
$.fn.width.restore();
$.fn.height.restore();
});

function setupComponent({instructionsHeight = 400, codeWorkspaceHeight = 100} = {}) {
const wrapper = shallow(
<InstructionsWithWorkspace
instructionsHeight={instructionsHeight}
setInstructionsMaxHeightAvailable={setInstructionsMaxHeightAvailable}
/>
);

// Fake ref to inner object, since we're shallow rendering.
wrapper.instance().codeWorkspaceContainer = {
getWrappedInstance: () => ({
getRenderedHeight: () => codeWorkspaceHeight
})
};

return wrapper;
}

it('does nothing if window size has not changed', () => {
const wrapper = setupComponent();

wrapper.setState({
windowWidth: 640,
windowHeight: 480
});
$.fn.width.returns(640);
$.fn.height.returns(480);
sinon.spy(wrapper.instance(), 'setState');

wrapper.instance().onResize();
expect(setInstructionsMaxHeightAvailable).not.to.have.been.called;
expect(wrapper.instance().setState).not.to.have.been.called;
});

it('handles resize', () => {
const wrapper = setupComponent();
wrapper.instance().onResize();
expect(setInstructionsMaxHeightAvailable).to.have.been.calledOnce
.and.calledWith(230);
});

it('breakpoint in behavior at total height of 420 (meets all reserves)', () => {
let wrapper;

wrapper = setupComponent({instructionsHeight: 18, codeWorkspaceHeight: 400});
wrapper.instance().onResize();
expect(setInstructionsMaxHeightAvailable).to.have.been.calledWith(139);

setInstructionsMaxHeightAvailable.reset();

wrapper = setupComponent({instructionsHeight: 19, codeWorkspaceHeight: 400});
wrapper.instance().onResize();
expect(setInstructionsMaxHeightAvailable).to.have.been.calledWith(140);

setInstructionsMaxHeightAvailable.reset();

wrapper = setupComponent({instructionsHeight: 20, codeWorkspaceHeight: 400});
wrapper.instance().onResize();
expect(setInstructionsMaxHeightAvailable).to.have.been.calledWith(150);

setInstructionsMaxHeightAvailable.reset();

wrapper = setupComponent({instructionsHeight: 21, codeWorkspaceHeight: 400});
wrapper.instance().onResize();
expect(setInstructionsMaxHeightAvailable).to.have.been.calledWith(151);
});

it('skips callback if codeWorkspaceContainer is not initialized', () => {
const wrapper = setupComponent({codeWorkspaceHeight: 0});
wrapper.instance().onResize();
expect(setInstructionsMaxHeightAvailable).not.to.have.been.called;
});
});
});

0 comments on commit e97c8f6

Please sign in to comment.