fix: simplify mobile layout tabs (#44431)

* fix: simplify mobile layout tabs

Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>


Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
Tom
2021-12-15 07:52:44 -06:00
committed by GitHub
parent 62071589d3
commit 580a51f7a7
25 changed files with 60 additions and 52 deletions

View File

@ -80,6 +80,7 @@ exports.createPages = function createPages({ graphql, actions, reporter }) {
fields { fields {
slug slug
} }
hasEditableBoundaries
id id
order order
required { required {

View File

@ -139,6 +139,7 @@ export type ChallengeNode = {
forumTopicId: number; forumTopicId: number;
guideUrl: string; guideUrl: string;
head: string[]; head: string[];
hasEditableBoundaries: boolean;
helpCategory: string; helpCategory: string;
id: string; id: string;
instructions: string; instructions: string;

View File

@ -1,52 +1,55 @@
import { TabPane, Tabs } from '@freecodecamp/react-bootstrap'; import { TabPane, Tabs } from '@freecodecamp/react-bootstrap';
import i18next from 'i18next'; import i18next from 'i18next';
import React, { Component, ReactElement } from 'react'; import React, { Component, ReactElement } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux';
import { createStructuredSelector } from 'reselect';
import envData from '../../../../../config/env.json'; import envData from '../../../../../config/env.json';
import ToolPanel from '../components/tool-panel'; import ToolPanel from '../components/tool-panel';
import { currentTabSelector, moveToTab } from '../redux';
import EditorTabs from './editor-tabs'; import EditorTabs from './editor-tabs';
const { showUpcomingChanges } = envData; const { showUpcomingChanges } = envData;
const mapStateToProps = createStructuredSelector({
currentTab: currentTabSelector as (state: unknown) => number
});
const mapDispatchToProps = (dispatch: Dispatch) =>
bindActionCreators(
{
moveToTab
},
dispatch
);
interface MobileLayoutProps { interface MobileLayoutProps {
currentTab: number;
editor: JSX.Element | null; editor: JSX.Element | null;
guideUrl: string; guideUrl: string;
hasEditableBoundaries: boolean; hasEditableBoundaries: boolean;
hasNotes: boolean; hasNotes: boolean;
hasPreview: boolean; hasPreview: boolean;
instructions: JSX.Element; instructions: JSX.Element;
moveToTab: typeof moveToTab;
notes: ReactElement; notes: ReactElement;
preview: JSX.Element; preview: JSX.Element;
testOutput: JSX.Element; testOutput: JSX.Element;
videoUrl: string; videoUrl: string;
usesMultifileEditor: boolean; usesMultifileEditor: boolean;
} }
class MobileLayout extends Component<MobileLayoutProps> {
enum Tab {
Editor = 'editor',
Preview = 'preview',
Console = 'console',
Notes = 'notes',
Instructions = 'instructions'
}
interface MobileLayoutState {
currentTab: Tab;
}
class MobileLayout extends Component<MobileLayoutProps, MobileLayoutState> {
static displayName: string; static displayName: string;
componentDidMount() {
if (this.props.currentTab !== 1) this.props.moveToTab(1); state: MobileLayoutState = {
} currentTab: this.props.hasEditableBoundaries ? Tab.Editor : Tab.Instructions
};
switchTab = (tab: Tab) => {
this.setState({
currentTab: tab
});
};
render() { render() {
const { currentTab } = this.state;
const { const {
currentTab,
moveToTab,
hasEditableBoundaries, hasEditableBoundaries,
instructions, instructions,
editor, editor,
@ -69,26 +72,24 @@ class MobileLayout extends Component<MobileLayoutProps> {
// but still needs a way to switch between the different tabs. // but still needs a way to switch between the different tabs.
const projectBasedChallenge = showUpcomingChanges && usesMultifileEditor; const projectBasedChallenge = showUpcomingChanges && usesMultifileEditor;
const eventKeys = [1, 2, 3, 4, 5];
return ( return (
<> <>
<Tabs <Tabs
activeKey={currentTab} activeKey={currentTab}
defaultActiveKey={1} defaultActiveKey={currentTab}
id='challenge-page-tabs' id='challenge-page-tabs'
onSelect={moveToTab} onSelect={this.switchTab}
> >
{!hasEditableBoundaries && ( {!hasEditableBoundaries && (
<TabPane <TabPane
eventKey={eventKeys.shift()} eventKey={Tab.Instructions}
title={i18next.t('learn.editor-tabs.info')} title={i18next.t('learn.editor-tabs.info')}
> >
{instructions} {instructions}
</TabPane> </TabPane>
)} )}
<TabPane <TabPane
eventKey={eventKeys.shift()} eventKey={Tab.Editor}
title={i18next.t('learn.editor-tabs.code')} title={i18next.t('learn.editor-tabs.code')}
{...editorTabPaneProps} {...editorTabPaneProps}
> >
@ -96,7 +97,7 @@ class MobileLayout extends Component<MobileLayoutProps> {
{editor} {editor}
</TabPane> </TabPane>
<TabPane <TabPane
eventKey={eventKeys.shift()} eventKey={Tab.Console}
title={i18next.t('learn.editor-tabs.tests')} title={i18next.t('learn.editor-tabs.tests')}
{...editorTabPaneProps} {...editorTabPaneProps}
> >
@ -104,7 +105,7 @@ class MobileLayout extends Component<MobileLayoutProps> {
</TabPane> </TabPane>
{hasNotes && projectBasedChallenge && ( {hasNotes && projectBasedChallenge && (
<TabPane <TabPane
eventKey={eventKeys.shift()} eventKey={Tab.Notes}
title={i18next.t('learn.editor-tabs.notes')} title={i18next.t('learn.editor-tabs.notes')}
> >
{notes} {notes}
@ -112,7 +113,7 @@ class MobileLayout extends Component<MobileLayoutProps> {
)} )}
{hasPreview && ( {hasPreview && (
<TabPane <TabPane
eventKey={eventKeys.shift()} eventKey={Tab.Preview}
title={i18next.t('learn.editor-tabs.preview')} title={i18next.t('learn.editor-tabs.preview')}
> >
{preview} {preview}
@ -127,4 +128,4 @@ class MobileLayout extends Component<MobileLayoutProps> {
MobileLayout.displayName = 'MobileLayout'; MobileLayout.displayName = 'MobileLayout';
export default connect(mapStateToProps, mapDispatchToProps)(MobileLayout); export default MobileLayout;

View File

@ -398,20 +398,12 @@ class ShowClassic extends Component<ShowClassicProps, ShowClassicState> {
); );
} }
hasEditableBoundaries() {
const { challengeFiles } = this.props;
return (
challengeFiles?.some(
challengeFile => challengeFile.editableRegionBoundaries?.length === 2
) ?? false
);
}
render() { render() {
const { const {
block, block,
fields: { blockName }, fields: { blockName },
forumTopicId, forumTopicId,
hasEditableBoundaries,
superBlock, superBlock,
title, title,
usesMultifileEditor, usesMultifileEditor,
@ -443,7 +435,7 @@ class ShowClassic extends Component<ShowClassicProps, ShowClassicState> {
<MobileLayout <MobileLayout
editor={this.renderEditor()} editor={this.renderEditor()}
guideUrl={getGuideUrl({ forumTopicId, title })} guideUrl={getGuideUrl({ forumTopicId, title })}
hasEditableBoundaries={this.hasEditableBoundaries()} hasEditableBoundaries={hasEditableBoundaries}
hasNotes={!!notes} hasNotes={!!notes}
hasPreview={this.hasPreview()} hasPreview={this.hasPreview()}
instructions={this.renderInstructionsPanel({ instructions={this.renderInstructionsPanel({
@ -461,7 +453,7 @@ class ShowClassic extends Component<ShowClassicProps, ShowClassicState> {
block={block} block={block}
challengeFiles={challengeFiles} challengeFiles={challengeFiles}
editor={this.renderEditor()} editor={this.renderEditor()}
hasEditableBoundaries={this.hasEditableBoundaries()} hasEditableBoundaries={hasEditableBoundaries}
hasNotes={!!notes} hasNotes={!!notes}
hasPreview={this.hasPreview()} hasPreview={this.hasPreview()}
instructions={this.renderInstructionsPanel({ instructions={this.renderInstructionsPanel({
@ -504,6 +496,7 @@ export const query = graphql`
block block
title title
description description
hasEditableBoundaries
instructions instructions
notes notes
removeComments removeComments

View File

@ -40,8 +40,6 @@ export const actionTypes = createTypes(
'stopResetting', 'stopResetting',
'submitChallenge', 'submitChallenge',
'moveToTab',
'setEditorFocusability', 'setEditorFocusability',
'toggleVisibleEditor' 'toggleVisibleEditor'
], ],

View File

@ -120,8 +120,6 @@ export const resetChallenge = createAction(actionTypes.resetChallenge);
export const stopResetting = createAction(actionTypes.stopResetting); export const stopResetting = createAction(actionTypes.stopResetting);
export const submitChallenge = createAction(actionTypes.submitChallenge); export const submitChallenge = createAction(actionTypes.submitChallenge);
export const moveToTab = createAction(actionTypes.moveToTab);
export const setEditorFocusability = createAction( export const setEditorFocusability = createAction(
actionTypes.setEditorFocusability actionTypes.setEditorFocusability
); );
@ -344,10 +342,6 @@ export const reducer = handleActions(
[payload]: true [payload]: true
} }
}), }),
[actionTypes.moveToTab]: (state, { payload }) => ({
...state,
currentTab: payload
}),
[actionTypes.executeChallenge]: state => ({ [actionTypes.executeChallenge]: state => ({
...state, ...state,
currentTab: 3 currentTab: 3

View File

@ -2,6 +2,7 @@
"name": "Basic JavaScript RPG Game", "name": "Basic JavaScript RPG Game",
"isUpcomingChange": true, "isUpcomingChange": true,
"usesMultifileEditor": true, "usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "basic-javascript-rpg-game", "dashedName": "basic-javascript-rpg-game",
"order": 10, "order": 10,
"time": "2 hours", "time": "2 hours",

View File

@ -2,6 +2,7 @@
"name": "D3 Dashboard", "name": "D3 Dashboard",
"isUpcomingChange": true, "isUpcomingChange": true,
"usesMultifileEditor": true, "usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "d3-dashboard", "dashedName": "d3-dashboard",
"order": 3, "order": 3,
"time": "5 hours", "time": "5 hours",

View File

@ -2,6 +2,7 @@
"name": "Functional Programming Spreadsheet", "name": "Functional Programming Spreadsheet",
"isUpcomingChange": true, "isUpcomingChange": true,
"usesMultifileEditor": true, "usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "functional-programming-spreadsheet", "dashedName": "functional-programming-spreadsheet",
"order": 12, "order": 12,
"time": "2 hours", "time": "2 hours",

View File

@ -2,6 +2,7 @@
"name": "Intermediate JavaScript Calorie Counter", "name": "Intermediate JavaScript Calorie Counter",
"isUpcomingChange": true, "isUpcomingChange": true,
"usesMultifileEditor": true, "usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "intermediate-javascript-calorie-counter", "dashedName": "intermediate-javascript-calorie-counter",
"order": 11, "order": 11,
"time": "2 hours", "time": "2 hours",

View File

@ -2,6 +2,7 @@
"name": "Learn Accessibility by Building a Quiz", "name": "Learn Accessibility by Building a Quiz",
"isUpcomingChange": true, "isUpcomingChange": true,
"usesMultifileEditor": true, "usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "learn-accessibility-by-building-a-quiz", "dashedName": "learn-accessibility-by-building-a-quiz",
"order": 42, "order": 42,
"time": "5 hours", "time": "5 hours",

View File

@ -2,6 +2,7 @@
"name": "Learn Basic CSS by Building a Cafe Menu", "name": "Learn Basic CSS by Building a Cafe Menu",
"isUpcomingChange": true, "isUpcomingChange": true,
"usesMultifileEditor": true, "usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "learn-basic-css-by-building-a-cafe-menu", "dashedName": "learn-basic-css-by-building-a-cafe-menu",
"order": 10, "order": 10,
"time": "5 hours", "time": "5 hours",

View File

@ -2,6 +2,7 @@
"name": "Learn CSS Animation by Building a Ferris Wheel", "name": "Learn CSS Animation by Building a Ferris Wheel",
"isUpcomingChange": true, "isUpcomingChange": true,
"usesMultifileEditor": true, "usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "learn-css-animation-by-building-a-ferris-wheel", "dashedName": "learn-css-animation-by-building-a-ferris-wheel",
"order": 15, "order": 15,
"time": "5 hours", "time": "5 hours",

View File

@ -2,6 +2,7 @@
"name": "Learn CSS Flexbox by Building a Photo Gallery", "name": "Learn CSS Flexbox by Building a Photo Gallery",
"isUpcomingChange": true, "isUpcomingChange": true,
"usesMultifileEditor": true, "usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "learn-css-flexbox-by-building-a-photo-gallery", "dashedName": "learn-css-flexbox-by-building-a-photo-gallery",
"order": 20, "order": 20,
"time": "5 hours", "time": "5 hours",

View File

@ -2,6 +2,7 @@
"name": "Learn CSS Grid by Building a Magazine", "name": "Learn CSS Grid by Building a Magazine",
"isUpcomingChange": true, "isUpcomingChange": true,
"usesMultifileEditor": true, "usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "learn-css-grid-by-building-a-magazine", "dashedName": "learn-css-grid-by-building-a-magazine",
"order": 16, "order": 16,
"time": "5 hours", "time": "5 hours",

View File

@ -2,6 +2,7 @@
"name": "Learn CSS Variables by Building a City Skyline", "name": "Learn CSS Variables by Building a City Skyline",
"isUpcomingChange": true, "isUpcomingChange": true,
"usesMultifileEditor": true, "usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "learn-css-variables-by-building-a-city-skyline", "dashedName": "learn-css-variables-by-building-a-city-skyline",
"order": 8, "order": 8,
"time": "5 hours", "time": "5 hours",

View File

@ -2,6 +2,7 @@
"name": "Learn HTML by Building a Cat Photo App", "name": "Learn HTML by Building a Cat Photo App",
"isUpcomingChange": true, "isUpcomingChange": true,
"usesMultifileEditor": true, "usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "learn-html-by-building-a-cat-photo-app", "dashedName": "learn-html-by-building-a-cat-photo-app",
"order": 9, "order": 9,
"time": "5 hours", "time": "5 hours",

View File

@ -2,6 +2,7 @@
"name": "Learn HTML Forms by Building a Registration Form", "name": "Learn HTML Forms by Building a Registration Form",
"isUpcomingChange": true, "isUpcomingChange": true,
"usesMultifileEditor": true, "usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "learn-html-forms-by-building-a-registration-form", "dashedName": "learn-html-forms-by-building-a-registration-form",
"order": 23, "order": 23,
"time": "5 hours", "time": "5 hours",

View File

@ -2,6 +2,7 @@
"name": "Learn Intermediate CSS by Building a Picasso Painting", "name": "Learn Intermediate CSS by Building a Picasso Painting",
"isUpcomingChange": true, "isUpcomingChange": true,
"usesMultifileEditor": true, "usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "learn-intermediate-css-by-building-a-picasso-painting", "dashedName": "learn-intermediate-css-by-building-a-picasso-painting",
"order": 11, "order": 11,
"time": "5 hours", "time": "5 hours",

View File

@ -2,6 +2,7 @@
"name": "Learn Responsive Web Design by Building a Piano", "name": "Learn Responsive Web Design by Building a Piano",
"isUpcomingChange": true, "isUpcomingChange": true,
"usesMultifileEditor": true, "usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "learn-responsive-web-design-by-building-a-piano", "dashedName": "learn-responsive-web-design-by-building-a-piano",
"order": 13, "order": 13,
"time": "5 hours", "time": "5 hours",

View File

@ -2,6 +2,7 @@
"name": "Learn the CSS Box Model by Building a Rothko Painting", "name": "Learn the CSS Box Model by Building a Rothko Painting",
"isUpcomingChange": true, "isUpcomingChange": true,
"usesMultifileEditor": true, "usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "learn-the-css-box-model-by-building-a-rothko-painting", "dashedName": "learn-the-css-box-model-by-building-a-rothko-painting",
"order": 12, "order": 12,
"time": "5 hours", "time": "5 hours",

View File

@ -2,6 +2,7 @@
"name": "Learn Typography by Building a Nutrition Label", "name": "Learn Typography by Building a Nutrition Label",
"isUpcomingChange": true, "isUpcomingChange": true,
"usesMultifileEditor": true, "usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "learn-typography-by-building-a-nutrition-label", "dashedName": "learn-typography-by-building-a-nutrition-label",
"order": 25, "order": 25,
"time": "5 hours", "time": "5 hours",

View File

@ -296,6 +296,7 @@ ${getFullPath('english')}
); );
const { const {
name: blockName, name: blockName,
hasEditableBoundaries,
order, order,
isPrivate, isPrivate,
required = [], required = [],
@ -304,6 +305,7 @@ ${getFullPath('english')}
usesMultifileEditor usesMultifileEditor
} = meta; } = meta;
challenge.block = dasherize(blockName); challenge.block = dasherize(blockName);
challenge.hasEditableBoundaries = !!hasEditableBoundaries;
challenge.order = order; challenge.order = order;
const superOrder = getSuperOrder(superBlock); const superOrder = getSuperOrder(superBlock);
if (superOrder !== null) challenge.superOrder = superOrder; if (superOrder !== null) challenge.superOrder = superOrder;

View File

@ -39,6 +39,7 @@ const schema = Joi.object()
}), }),
challengeFiles: Joi.array().items(fileJoi), challengeFiles: Joi.array().items(fileJoi),
guideUrl: Joi.string().uri({ scheme: 'https' }), guideUrl: Joi.string().uri({ scheme: 'https' }),
hasEditableBoundaries: Joi.boolean(),
helpCategory: Joi.valid( helpCategory: Joi.valid(
'JavaScript', 'JavaScript',
'HTML-CSS', 'HTML-CSS',

View File

@ -2,6 +2,7 @@
"name": "", "name": "",
"isUpcomingChange": true, "isUpcomingChange": true,
"usesMultifileEditor": true, "usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "", "dashedName": "",
"order": 42, "order": 42,
"time": "5 hours", "time": "5 hours",