fix(client): store challenge panes sizes (#42519)
Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
@ -8,12 +8,23 @@ import envData from '../../../../../config/env.json';
|
|||||||
|
|
||||||
const { showUpcomingChanges } = envData;
|
const { showUpcomingChanges } = envData;
|
||||||
|
|
||||||
|
const paneType = {
|
||||||
|
flex: PropTypes.number
|
||||||
|
};
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
challengeFiles: PropTypes.object,
|
challengeFiles: PropTypes.object,
|
||||||
editor: PropTypes.element,
|
editor: PropTypes.element,
|
||||||
hasEditableBoundries: PropTypes.bool,
|
hasEditableBoundries: PropTypes.bool,
|
||||||
hasPreview: PropTypes.bool,
|
hasPreview: PropTypes.bool,
|
||||||
instructions: PropTypes.element,
|
instructions: PropTypes.element,
|
||||||
|
layoutState: PropTypes.shape({
|
||||||
|
codePane: paneType,
|
||||||
|
editorPane: paneType,
|
||||||
|
instructionPane: paneType,
|
||||||
|
previewPane: paneType,
|
||||||
|
testsPane: paneType
|
||||||
|
}),
|
||||||
preview: PropTypes.element,
|
preview: PropTypes.element,
|
||||||
resizeProps: PropTypes.shape({
|
resizeProps: PropTypes.shape({
|
||||||
onStopResize: PropTypes.func,
|
onStopResize: PropTypes.func,
|
||||||
@ -55,6 +66,7 @@ class DesktopLayout extends Component {
|
|||||||
editor,
|
editor,
|
||||||
testOutput,
|
testOutput,
|
||||||
hasPreview,
|
hasPreview,
|
||||||
|
layoutState,
|
||||||
preview,
|
preview,
|
||||||
hasEditableBoundries
|
hasEditableBoundries
|
||||||
} = this.props;
|
} = this.props;
|
||||||
@ -67,6 +79,8 @@ class DesktopLayout extends Component {
|
|||||||
? showPreview && hasPreview
|
? showPreview && hasPreview
|
||||||
: hasPreview;
|
: hasPreview;
|
||||||
const isConsoleDisplayable = projectBasedChallenge ? showConsole : true;
|
const isConsoleDisplayable = projectBasedChallenge ? showConsole : true;
|
||||||
|
const { codePane, editorPane, instructionPane, previewPane, testsPane } =
|
||||||
|
layoutState;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
@ -75,7 +89,11 @@ class DesktopLayout extends Component {
|
|||||||
)}
|
)}
|
||||||
<ReflexContainer className='desktop-layout' orientation='vertical'>
|
<ReflexContainer className='desktop-layout' orientation='vertical'>
|
||||||
{!projectBasedChallenge && (
|
{!projectBasedChallenge && (
|
||||||
<ReflexElement flex={1} {...resizeProps}>
|
<ReflexElement
|
||||||
|
flex={instructionPane.flex}
|
||||||
|
name='instructionPane'
|
||||||
|
{...resizeProps}
|
||||||
|
>
|
||||||
{instructions}
|
{instructions}
|
||||||
</ReflexElement>
|
</ReflexElement>
|
||||||
)}
|
)}
|
||||||
@ -83,20 +101,34 @@ class DesktopLayout extends Component {
|
|||||||
<ReflexSplitter propagate={true} {...resizeProps} />
|
<ReflexSplitter propagate={true} {...resizeProps} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ReflexElement flex={1} {...resizeProps}>
|
<ReflexElement
|
||||||
|
flex={editorPane.flex}
|
||||||
|
name='editorPane'
|
||||||
|
{...resizeProps}
|
||||||
|
>
|
||||||
{challengeFile && showUpcomingChanges && !hasEditableBoundries && (
|
{challengeFile && showUpcomingChanges && !hasEditableBoundries && (
|
||||||
<EditorTabs />
|
<EditorTabs />
|
||||||
)}
|
)}
|
||||||
{challengeFile && (
|
{challengeFile && (
|
||||||
<ReflexContainer key={challengeFile.key} orientation='horizontal'>
|
<ReflexContainer key={challengeFile.key} orientation='horizontal'>
|
||||||
<ReflexElement flex={1} {...reflexProps} {...resizeProps}>
|
<ReflexElement
|
||||||
|
flex={codePane.flex}
|
||||||
|
name='codePane'
|
||||||
|
{...reflexProps}
|
||||||
|
{...resizeProps}
|
||||||
|
>
|
||||||
{<Fragment>{editor}</Fragment>}
|
{<Fragment>{editor}</Fragment>}
|
||||||
</ReflexElement>
|
</ReflexElement>
|
||||||
{isConsoleDisplayable && (
|
{isConsoleDisplayable && (
|
||||||
<ReflexSplitter propagate={true} {...resizeProps} />
|
<ReflexSplitter propagate={true} {...resizeProps} />
|
||||||
)}
|
)}
|
||||||
{isConsoleDisplayable && (
|
{isConsoleDisplayable && (
|
||||||
<ReflexElement flex={0.25} {...reflexProps} {...resizeProps}>
|
<ReflexElement
|
||||||
|
flex={testsPane.flex}
|
||||||
|
name='testsPane'
|
||||||
|
{...reflexProps}
|
||||||
|
{...resizeProps}
|
||||||
|
>
|
||||||
{testOutput}
|
{testOutput}
|
||||||
</ReflexElement>
|
</ReflexElement>
|
||||||
)}
|
)}
|
||||||
@ -107,7 +139,11 @@ class DesktopLayout extends Component {
|
|||||||
<ReflexSplitter propagate={true} {...resizeProps} />
|
<ReflexSplitter propagate={true} {...resizeProps} />
|
||||||
)}
|
)}
|
||||||
{isPreviewDisplayable && (
|
{isPreviewDisplayable && (
|
||||||
<ReflexElement flex={0.7} {...resizeProps}>
|
<ReflexElement
|
||||||
|
flex={previewPane.flex}
|
||||||
|
name='previewPane'
|
||||||
|
{...resizeProps}
|
||||||
|
>
|
||||||
{preview}
|
{preview}
|
||||||
</ReflexElement>
|
</ReflexElement>
|
||||||
)}
|
)}
|
||||||
|
@ -22,7 +22,9 @@ import DesktopLayout from './DesktopLayout';
|
|||||||
import Hotkeys from '../components/Hotkeys';
|
import Hotkeys from '../components/Hotkeys';
|
||||||
|
|
||||||
import { getGuideUrl } from '../utils';
|
import { getGuideUrl } from '../utils';
|
||||||
|
import store from 'store';
|
||||||
import { challengeTypes } from '../../../../utils/challengeTypes';
|
import { challengeTypes } from '../../../../utils/challengeTypes';
|
||||||
|
import { isContained } from '../../../utils/is-contained';
|
||||||
import { ChallengeNode } from '../../../redux/prop-types';
|
import { ChallengeNode } from '../../../redux/prop-types';
|
||||||
import {
|
import {
|
||||||
createFiles,
|
createFiles,
|
||||||
@ -92,6 +94,24 @@ const propTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const MAX_MOBILE_WIDTH = 767;
|
const MAX_MOBILE_WIDTH = 767;
|
||||||
|
const REFLEX_LAYOUT = 'challenge-layout';
|
||||||
|
const BASE_LAYOUT = {
|
||||||
|
codePane: {
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
editorPane: {
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
instructionPane: {
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
previewPane: {
|
||||||
|
flex: 0.7
|
||||||
|
},
|
||||||
|
testsPane: {
|
||||||
|
flex: 0.25
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class ShowClassic extends Component {
|
class ShowClassic extends Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -108,13 +128,44 @@ class ShowClassic extends Component {
|
|||||||
|
|
||||||
this.containerRef = React.createRef();
|
this.containerRef = React.createRef();
|
||||||
this.editorRef = React.createRef();
|
this.editorRef = React.createRef();
|
||||||
|
// Holds the information of the panes sizes for desktop view
|
||||||
|
this.layoutState = this.getLayoutState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getLayoutState() {
|
||||||
|
const reflexLayout = store.get(REFLEX_LAYOUT);
|
||||||
|
|
||||||
|
// Validate if user has not done any resize of the panes
|
||||||
|
if (!reflexLayout) return BASE_LAYOUT;
|
||||||
|
|
||||||
|
// Check that the layout values stored are valid (exist in base layout). If
|
||||||
|
// not valid, it will fallback to the base layout values and be set on next
|
||||||
|
// user resize.
|
||||||
|
const isValidLayout = isContained(
|
||||||
|
Object.keys(BASE_LAYOUT),
|
||||||
|
Object.keys(reflexLayout)
|
||||||
|
);
|
||||||
|
|
||||||
|
return isValidLayout ? reflexLayout : BASE_LAYOUT;
|
||||||
|
}
|
||||||
|
|
||||||
onResize() {
|
onResize() {
|
||||||
this.setState({ resizing: true });
|
this.setState({ resizing: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
onStopResize() {
|
onStopResize(event) {
|
||||||
|
const { name, flex } = event.component.props;
|
||||||
|
|
||||||
this.setState({ resizing: false });
|
this.setState({ resizing: false });
|
||||||
|
|
||||||
|
// Only interested in tracking layout updates for ReflexElement's
|
||||||
|
if (!name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.layoutState[name].flex = flex;
|
||||||
|
|
||||||
|
store.set(REFLEX_LAYOUT, this.layoutState);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@ -325,6 +376,7 @@ class ShowClassic extends Component {
|
|||||||
instructions={this.renderInstructionsPanel({
|
instructions={this.renderInstructionsPanel({
|
||||||
showToolPanel: true
|
showToolPanel: true
|
||||||
})}
|
})}
|
||||||
|
layoutState={this.layoutState}
|
||||||
preview={this.renderPreview()}
|
preview={this.renderPreview()}
|
||||||
resizeProps={this.resizeProps}
|
resizeProps={this.resizeProps}
|
||||||
testOutput={this.renderTestOutput()}
|
testOutput={this.renderTestOutput()}
|
||||||
|
15
client/src/utils/is-contained.test.ts
Normal file
15
client/src/utils/is-contained.test.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { isContained } from './is-contained';
|
||||||
|
|
||||||
|
describe('client/src isContained', () => {
|
||||||
|
it('returns true if `arr1` values are contained in `arr2`', () => {
|
||||||
|
const arr1 = ['dog', 'cat'];
|
||||||
|
const arr2 = ['cat', 'dog'];
|
||||||
|
expect(isContained(arr1, arr2)).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true if `arr1` values are not contained in `arr2`', () => {
|
||||||
|
const arr1 = ['dog', 'cat'];
|
||||||
|
const arr2 = ['cat', 'monkey', 'bird'];
|
||||||
|
expect(isContained(arr1, arr2)).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
4
client/src/utils/is-contained.ts
Normal file
4
client/src/utils/is-contained.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
// Performs a check if the items in `value` exist in `other`
|
||||||
|
export function isContained(value: string[], other: string[]): boolean {
|
||||||
|
return value.every(i => other.includes(i));
|
||||||
|
}
|
Reference in New Issue
Block a user