feat: create action row and style Multi-file editor (#41579)

* feat: add action row & style editor

* fix: separate conditional for splittter and reflex element

* fix: move jaws whenever a line is deleted or added

* feat: keep line indicator inside editable region

* feat: add submit button and restyle decorator

Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
Ahmad Abdolsaheb
2021-04-09 23:18:54 +03:00
committed by GitHub
parent c1ee2720b3
commit f075837311
6 changed files with 347 additions and 128 deletions

View File

@ -1,13 +1,58 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import EditorTabs from './EditorTabs'; import EditorTabs from './EditorTabs';
const ActionRow = () => ( const propTypes = {
<div className='action-row'> block: PropTypes.string,
<EditorTabs /> showConsole: PropTypes.bool,
</div> showNotes: PropTypes.bool,
); showPreview: PropTypes.bool,
superBlock: PropTypes.string,
switchDisplayTab: PropTypes.func
};
const ActionRow = ({ switchDisplayTab, showPreview, showConsole }) => {
const restartStep = () => {
console.log('restart');
};
return (
<div className='action-row'>
<div>
<h5 className='breadcrumbs-demo'>
Responsive Web Design &gt; Basic HTML Cat Photo App &gt;{' '}
<span>Step 23 of 213</span>
</h5>
</div>
<div className='tabs-row'>
<EditorTabs />
<button
className='restart-step-tab'
onClick={() => restartStep()}
role='tab'
>
Restart Step
</button>
<div className='panel-display-tabs'>
<button
className={showConsole ? 'active-tab' : ''}
onClick={() => switchDisplayTab('showConsole')}
role='tab'
>
JS Console
</button>
<button
className={showPreview ? 'active-tab' : ''}
onClick={() => switchDisplayTab('showPreview')}
role='tab'
>
Show Preview
</button>
</div>
</div>
</div>
);
};
ActionRow.propTypes = propTypes;
ActionRow.displayName = 'ActionRow'; ActionRow.displayName = 'ActionRow';
export default ActionRow; export default ActionRow;

View File

@ -29,6 +29,20 @@ const reflexProps = {
}; };
class DesktopLayout extends Component { class DesktopLayout extends Component {
constructor(props) {
super(props);
this.state = { showNotes: false, showPreview: true, showConsole: false };
this.switchDisplayTab = this.switchDisplayTab.bind(this);
}
switchDisplayTab(displayTab) {
this.setState(state => {
return {
[displayTab]: !state[displayTab]
};
});
}
getChallengeFile() { getChallengeFile() {
const { challengeFiles } = this.props; const { challengeFiles } = this.props;
return first(Object.keys(challengeFiles).map(key => challengeFiles[key])); return first(Object.keys(challengeFiles).map(key => challengeFiles[key]));
@ -45,16 +59,30 @@ class DesktopLayout extends Component {
hasEditableBoundries hasEditableBoundries
} = this.props; } = this.props;
const { showPreview, showConsole } = this.state;
const challengeFile = this.getChallengeFile(); const challengeFile = this.getChallengeFile();
const projectBasedChallenge = showUpcomingChanges && hasEditableBoundries;
const isPreviewDisplayable = projectBasedChallenge
? showPreview && hasPreview
: hasPreview;
const isConsoleDisplayable = projectBasedChallenge ? showConsole : true;
return ( return (
<Fragment> <Fragment>
{showUpcomingChanges && hasEditableBoundries && <ActionRow />} {projectBasedChallenge && (
<ActionRow switchDisplayTab={this.switchDisplayTab} {...this.state} />
)}
<ReflexContainer className='desktop-layout' orientation='vertical'> <ReflexContainer className='desktop-layout' orientation='vertical'>
<ReflexElement flex={1} {...resizeProps}> {!projectBasedChallenge && (
{instructions} <ReflexElement flex={1} {...resizeProps}>
</ReflexElement> {instructions}
<ReflexSplitter propagate={true} {...resizeProps} /> </ReflexElement>
)}
{!projectBasedChallenge && (
<ReflexSplitter propagate={true} {...resizeProps} />
)}
<ReflexElement flex={1} {...resizeProps}> <ReflexElement flex={1} {...resizeProps}>
{challengeFile && ( {challengeFile && (
<ReflexContainer key={challengeFile.key} orientation='horizontal'> <ReflexContainer key={challengeFile.key} orientation='horizontal'>
@ -68,15 +96,21 @@ class DesktopLayout extends Component {
</Fragment> </Fragment>
} }
</ReflexElement> </ReflexElement>
<ReflexSplitter propagate={true} {...resizeProps} /> {isConsoleDisplayable && (
<ReflexElement flex={0.25} {...reflexProps} {...resizeProps}> <ReflexSplitter propagate={true} {...resizeProps} />
{testOutput} )}
</ReflexElement> {isConsoleDisplayable && (
<ReflexElement flex={0.25} {...reflexProps} {...resizeProps}>
{testOutput}
</ReflexElement>
)}
</ReflexContainer> </ReflexContainer>
)} )}
</ReflexElement> </ReflexElement>
{hasPreview && <ReflexSplitter propagate={true} {...resizeProps} />} {isPreviewDisplayable && (
{hasPreview && ( <ReflexSplitter propagate={true} {...resizeProps} />
)}
{isPreviewDisplayable && (
<ReflexElement flex={0.7} {...resizeProps}> <ReflexElement flex={0.7} {...resizeProps}>
{preview} {preview}
</ReflexElement> </ReflexElement>

View File

@ -12,7 +12,9 @@ import {
saveEditorContent, saveEditorContent,
setEditorFocusability, setEditorFocusability,
setAccessibilityMode, setAccessibilityMode,
updateFile updateFile,
challengeTestsSelector,
submitChallenge
} from '../redux'; } from '../redux';
import { userSelector, isDonationModalOpenSelector } from '../../../redux'; import { userSelector, isDonationModalOpenSelector } from '../../../redux';
import { Loader } from '../../../components/helpers'; import { Loader } from '../../../components/helpers';
@ -43,6 +45,8 @@ const propTypes = {
saveEditorContent: PropTypes.func.isRequired, saveEditorContent: PropTypes.func.isRequired,
setAccessibilityMode: PropTypes.func.isRequired, setAccessibilityMode: PropTypes.func.isRequired,
setEditorFocusability: PropTypes.func, setEditorFocusability: PropTypes.func,
submitChallenge: PropTypes.func,
tests: PropTypes.arrayOf(PropTypes.object),
theme: PropTypes.string, theme: PropTypes.string,
updateFile: PropTypes.func.isRequired updateFile: PropTypes.func.isRequired
}; };
@ -53,11 +57,20 @@ const mapStateToProps = createSelector(
inAccessibilityModeSelector, inAccessibilityModeSelector,
isDonationModalOpenSelector, isDonationModalOpenSelector,
userSelector, userSelector,
(canFocus, output, accessibilityMode, open, { theme = 'default' }) => ({ challengeTestsSelector,
(
canFocus,
output,
accessibilityMode,
open,
{ theme = 'default' },
tests
) => ({
canFocus: open ? false : canFocus, canFocus: open ? false : canFocus,
output, output,
inAccessibilityMode: accessibilityMode, inAccessibilityMode: accessibilityMode,
theme theme,
tests
}) })
); );
@ -66,7 +79,8 @@ const mapDispatchToProps = {
saveEditorContent, saveEditorContent,
setAccessibilityMode, setAccessibilityMode,
setEditorFocusability, setEditorFocusability,
updateFile updateFile,
submitChallenge
}; };
const modeMap = { const modeMap = {
@ -140,6 +154,7 @@ class Editor extends Component {
viewZoneId: null, viewZoneId: null,
startEditDecId: null, startEditDecId: null,
endEditDecId: null, endEditDecId: null,
insideEditDecId: null,
viewZoneHeight: null viewZoneHeight: null
}; };
@ -182,6 +197,13 @@ class Editor extends Component {
this.focusOnEditor = this.focusOnEditor.bind(this); this.focusOnEditor = this.focusOnEditor.bind(this);
} }
getEditableRegion = () => {
const { challengeFiles, fileKey } = this.props;
return challengeFiles[fileKey].editableRegionBoundaries
? [...challengeFiles[fileKey].editableRegionBoundaries]
: [];
};
editorWillMount = monaco => { editorWillMount = monaco => {
this._monaco = monaco; this._monaco = monaco;
const { challengeFiles, fileKey } = this.props; const { challengeFiles, fileKey } = this.props;
@ -201,9 +223,7 @@ class Editor extends Component {
); );
this.data.model = model; this.data.model = model;
const editableRegion = challengeFiles[fileKey].editableRegionBoundaries const editableRegion = this.getEditableRegion();
? [...challengeFiles[fileKey].editableRegionBoundaries]
: [];
if (editableRegion.length === 2) if (editableRegion.length === 2)
this.decorateForbiddenRanges(editableRegion); this.decorateForbiddenRanges(editableRegion);
@ -224,7 +244,6 @@ class Editor extends Component {
editorDidMount = (editor, monaco) => { editorDidMount = (editor, monaco) => {
this._editor = editor; this._editor = editor;
const { challengeFiles, fileKey } = this.props;
editor.updateOptions({ editor.updateOptions({
accessibilitySupport: this.props.inAccessibilityMode ? 'on' : 'auto' accessibilitySupport: this.props.inAccessibilityMode ? 'on' : 'auto'
}); });
@ -286,9 +305,7 @@ class Editor extends Component {
} }
}); });
const editableBoundaries = challengeFiles[fileKey].editableRegionBoundaries const editableBoundaries = this.getEditableRegion();
? [...challengeFiles[fileKey].editableRegionBoundaries]
: [];
if (editableBoundaries.length === 2) { if (editableBoundaries.length === 2) {
// TODO: is there a nicer approach/way of organising everything that // TODO: is there a nicer approach/way of organising everything that
@ -363,7 +380,7 @@ class Editor extends Component {
this.data.viewZoneHeight = domNode.offsetHeight; this.data.viewZoneHeight = domNode.offsetHeight;
var background = document.createElement('div'); var background = document.createElement('div');
background.style.background = 'lightgreen'; // background.style.background = 'lightgreen';
// We have to wait for the viewZone to finish rendering before adjusting the // We have to wait for the viewZone to finish rendering before adjusting the
// position of the overlayWidget (i.e. trigger it via onComputedHeight). If // position of the overlayWidget (i.e. trigger it via onComputedHeight). If
@ -392,7 +409,7 @@ class Editor extends Component {
this.data.outputZoneHeight = outputNode.offsetHeight; this.data.outputZoneHeight = outputNode.offsetHeight;
var background = document.createElement('div'); var background = document.createElement('div');
background.style.background = 'lightpink'; // background.style.background = 'lightpink';
// We have to wait for the viewZone to finish rendering before adjusting the // We have to wait for the viewZone to finish rendering before adjusting the
// position of the overlayWidget (i.e. trigger it via onComputedHeight). If // position of the overlayWidget (i.e. trigger it via onComputedHeight). If
@ -413,19 +430,14 @@ class Editor extends Component {
const { description } = this.props; const { description } = this.props;
var domNode = document.createElement('div'); var domNode = document.createElement('div');
var desc = document.createElement('div'); var desc = document.createElement('div');
var button = document.createElement('button'); var descContainer = document.createElement('div');
button.innerHTML = 'Run the Tests (Ctrl + Enter)'; descContainer.classList.add('description-container');
button.onclick = () => { domNode.classList.add('editor-upper-jaw');
const { executeChallenge } = this.props; domNode.appendChild(descContainer);
executeChallenge(); descContainer.appendChild(desc);
};
domNode.appendChild(desc);
domNode.appendChild(button);
desc.innerHTML = description; desc.innerHTML = description;
// desc.style.background = 'white';
desc.style.background = 'white'; // domNode.style.background = 'lightgreen';
domNode.style.background = 'lightgreen';
// TODO: the solution is probably just to use an overlay that's forced to // TODO: the solution is probably just to use an overlay that's forced to
// follow the decorations. // follow the decorations.
// TODO: this is enough for Firefox, but Chrome needs more before the // TODO: this is enough for Firefox, but Chrome needs more before the
@ -436,7 +448,7 @@ class Editor extends Component {
domNode.setAttribute('aria-hidden', true); domNode.setAttribute('aria-hidden', true);
domNode.style.background = 'lightYellow'; // domNode.style.background = 'lightYellow';
domNode.style.left = this._editor.getLayoutInfo().contentLeft + 'px'; domNode.style.left = this._editor.getLayoutInfo().contentLeft + 'px';
domNode.style.width = this._editor.getLayoutInfo().contentWidth + 'px'; domNode.style.width = this._editor.getLayoutInfo().contentWidth + 'px';
domNode.style.top = this.getViewZoneTop(); domNode.style.top = this.getViewZoneTop();
@ -449,11 +461,23 @@ class Editor extends Component {
const outputNode = document.createElement('div'); const outputNode = document.createElement('div');
const statusNode = document.createElement('div'); const statusNode = document.createElement('div');
const hintNode = document.createElement('div'); const hintNode = document.createElement('div');
outputNode.appendChild(statusNode); const editorActionRow = document.createElement('div');
outputNode.appendChild(hintNode); editorActionRow.classList.add('action-row-container');
outputNode.classList.add('editor-lower-jaw');
outputNode.appendChild(editorActionRow);
hintNode.setAttribute('id', 'test-output'); hintNode.setAttribute('id', 'test-output');
statusNode.setAttribute('id', 'test-status'); statusNode.setAttribute('id', 'test-status');
statusNode.innerHTML = '// tests'; var button = document.createElement('button');
button.setAttribute('id', 'test-button');
button.classList.add('btn-block');
button.innerHTML = 'Check Your Code (Ctrl + Enter)';
editorActionRow.appendChild(button);
editorActionRow.appendChild(statusNode);
editorActionRow.appendChild(hintNode);
button.onclick = () => {
const { executeChallenge } = this.props;
executeChallenge();
};
// TODO: does it? // TODO: does it?
// The z-index needs increasing as ViewZones default to below the lines. // The z-index needs increasing as ViewZones default to below the lines.
@ -528,6 +552,19 @@ class Editor extends Component {
return target.deltaDecorations(oldIds, [lineDecoration]); return target.deltaDecorations(oldIds, [lineDecoration]);
} }
highlightEditableLines(stickiness, target, range, oldIds = []) {
const lineDecoration = {
range,
options: {
isWholeLine: true,
linesDecorationsClassName: 'myEditableLineDecoration',
className: 'do-not-edit',
stickiness
}
};
return target.deltaDecorations(oldIds, [lineDecoration]);
}
highlightText(stickiness, target, range, oldIds = []) { highlightText(stickiness, target, range, oldIds = []) {
const inlineDecoration = { const inlineDecoration = {
range, range,
@ -655,6 +692,17 @@ class Editor extends Component {
return this.positionsToRange(model, positions); return this.positionsToRange(model, positions);
}); });
const editableRange = this.positionsToRange(model, [
editableRegion[0] + 1,
editableRegion[1] - 1
]);
this.data.insideEditDecId = this.highlightEditableLines(
this._monaco.editor.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges,
model,
editableRange
);
// if the forbidden range includes the top of the editor // if the forbidden range includes the top of the editor
// we simply don't add those decorations // we simply don't add those decorations
if (forbiddenRanges[0][1] > 0) { if (forbiddenRanges[0][1] > 0) {
@ -743,53 +791,25 @@ class Editor extends Component {
}); });
}; };
// Make sure the zone tracks the decoration (i.e. the region) // Make sure the zone tracks the decoration (i.e. the region), which might
const handleHintsZoneChange = id => { // have changed if a line has been added or removed
const startOfZone = toStartOfLine( const handleHintsZoneChange = () => {
model.getDecorationRange(id) if (newLineRanges.length > 0 || deletedLine > 0) {
).collapseToStart(); this.updateOutputZone();
// the decoration needs adjusting if the user creates a line immediately
// before the greyed out region...
const lineOneRange = this.translateRange(startOfZone, -2);
// or immediately after it
const lineTwoRange = this.translateRange(startOfZone, -1);
for (const lineRange of newLineRanges) {
const shouldMoveZone = this._monaco.Range.areIntersectingOrTouching(
lineRange,
lineOneRange.plusRange(lineTwoRange)
);
if (shouldMoveZone) {
this.updateOutputZone();
}
} }
}; };
// Make sure the zone tracks the decoration (i.e. the region) // Make sure the zone tracks the decoration (i.e. the region), which might
const handleDescriptionZoneChange = id => { // have changed if a line has been added or removed
const endOfZone = toLastLine( const handleDescriptionZoneChange = () => {
model.getDecorationRange(id) if (newLineRanges.length > 0 || deletedLine > 0) {
).collapseToStart(); this.updateViewZone();
// the decoration needs adjusting if the user creates a line immediately
// before the editable region.
const lineOneRange = this.translateRange(endOfZone, -1);
for (const lineRange of newLineRanges) {
const shouldMoveZone = this._monaco.Range.areIntersectingOrTouching(
lineRange,
lineOneRange
);
if (shouldMoveZone) {
this.updateViewZone();
}
} }
}; };
// Stops the greyed out region from covering the editable region. Does not // Stops the greyed out region from covering the editable region. Does not
// change the font decoration. // change the font decoration.
const preventOverlap = id => { const preventOverlap = (id, stickiness, highlightFunction) => {
// Even though the decoration covers the whole line, it has a // Even though the decoration covers the whole line, it has a
// startColumn that moves. toStartOfLine ensures that the // startColumn that moves. toStartOfLine ensures that the
// comparison detects if any change has occurred on that line // comparison detects if any change has occurred on that line
@ -821,17 +841,15 @@ class Editor extends Component {
if (touchingDeleted) { if (touchingDeleted) {
// TODO: if they undo this should be reversed // TODO: if they undo this should be reversed
const decorations = this.highlightLines( const decorations = highlightFunction(
this._monaco.editor.TrackedRangeStickiness stickiness,
.NeverGrowsWhenTypingAtEdges,
model, model,
newCoveringRange, newCoveringRange,
[id] id
); );
this.updateOutputZone(); this.updateOutputZone();
// when there's a change, decorations will be [oldId, newId] return decorations;
return decorations.slice(-1)[0];
} else { } else {
return id; return id;
} }
@ -839,7 +857,17 @@ class Editor extends Component {
// we only need to handle the special case of the second region being // we only need to handle the special case of the second region being
// pulled up, the first region already behaves correctly. // pulled up, the first region already behaves correctly.
this.data.endEditDecId = preventOverlap(this.data.endEditDecId); this.data.endEditDecId = preventOverlap(
this.data.endEditDecId,
this._monaco.editor.TrackedRangeStickiness.GrowsOnlyWhenTypingBefore,
this.highlightLines
);
this.data.insideEditDecId = preventOverlap(
this.data.insideEditDecId,
this._monaco.editor.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges,
this.highlightEditableLines
);
// TODO: do the same for the description widget // TODO: do the same for the description widget
// this has to be handle differently, because we care about the END // this has to be handle differently, because we care about the END
@ -847,10 +875,10 @@ class Editor extends Component {
// if the editable region includes the first line, the first decoration // if the editable region includes the first line, the first decoration
// will be missing. // will be missing.
if (this.data.startEditDecId) { if (this.data.startEditDecId) {
handleDescriptionZoneChange(this.data.startEditDecId); handleDescriptionZoneChange();
warnUser(this.data.startEditDecId); warnUser(this.data.startEditDecId);
} }
handleHintsZoneChange(this.data.endEditDecId); handleHintsZoneChange();
warnUser(this.data.endEditDecId); warnUser(this.data.endEditDecId);
}); });
} }
@ -889,7 +917,47 @@ class Editor extends Component {
} }
if (this._editor) { if (this._editor) {
const { output } = this.props; const { output, tests } = this.props;
const editableRegion = this.getEditableRegion();
if (this.props.tests !== prevProps.tests && editableRegion.length === 2) {
const challengeComplete = tests.every(test => test.pass && !test.err);
const chellengeHasErrors = tests.some(test => test.err);
if (challengeComplete) {
let testButton = document.getElementById('test-button');
testButton.innerHTML =
'Submit your code and go to next challenge (Ctrl + Enter)';
testButton.onclick = () => {
const { submitChallenge } = this.props;
submitChallenge();
};
let editableRegionDecorators = document.getElementsByClassName(
'myEditableLineDecoration'
);
if (editableRegionDecorators.length > 0) {
for (var i of editableRegionDecorators) {
i.classList.add('tests-passed');
}
}
document.getElementById('test-output').innerHTML = '';
document.getElementById('test-status').innerHTML =
'&#9989; Step completed.';
} else if (chellengeHasErrors) {
const wordsArray = [
"Not quite. Here's a hint:",
'Try again. This might help:',
'Keep trying. A quick hint for you:',
"You're getting there. This may help:",
"Hang in there. You'll get there. A hint:",
"Don't give up. Here's a hint to get you thinking:"
];
document.getElementById('test-status').innerHTML = `✖️ ${
wordsArray[Math.floor(Math.random() * wordsArray.length)]
}`;
document.getElementById('test-output').innerHTML = `${output[1]}`;
}
}
if (this.props.output !== prevProps.output && this._outputNode) { if (this.props.output !== prevProps.output && this._outputNode) {
// TODO: output gets wiped when the preview gets updated, keeping the // TODO: output gets wiped when the preview gets updated, keeping the
// display is an anti-pattern (the render should not ignore props!). // display is an anti-pattern (the render should not ignore props!).
@ -897,14 +965,6 @@ class Editor extends Component {
// (shownHint,maybe) and have that persist through previews. But, for // (shownHint,maybe) and have that persist through previews. But, for
// now: // now:
if (output) { if (output) {
if (output[0]) {
document.getElementById('test-status').innerHTML = output[0];
}
if (output[1]) {
document.getElementById('test-output').innerHTML = output[1];
}
// if either id exists, the editable region exists // if either id exists, the editable region exists
// TODO: add a layer of abstraction: we should be interacting with // TODO: add a layer of abstraction: we should be interacting with
// the editable region, not the ids // the editable region, not the ids

View File

@ -6,10 +6,6 @@
color: var(--primary-color); color: var(--primary-color);
} }
[widgetid='my.overlay.widget'] {
padding: 10px;
}
.vs .monaco-scrollable-element > .scrollbar > .slider { .vs .monaco-scrollable-element > .scrollbar > .slider {
z-index: 11; z-index: 11;
} }
@ -17,3 +13,64 @@
.editor-container { .editor-container {
background: var(--editor-background); background: var(--editor-background);
} }
.breadcrumbs-demo {
font-size: 16px;
}
.breadcrumbs-demo span {
font-weight: bold;
}
.tabs-row {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.action-row button {
padding: 4px 16px;
}
.active-tab {
border-color: var(--secondary-color);
background-color: var(--secondary-color);
color: var(--secondary-background);
}
/* [widgetid='my.overlay.widget'] {
padding: 10px;
} */
.editor-upper-jaw,
.editor-lower-jaw {
padding: 15px 15px 15px 0px;
}
.action-row-container,
.description-container {
background-color: var(--secondary-background);
padding: 10px;
border: 2px solid var(--tertiary-background);
max-width: 700px;
}
#description p:last-child {
margin: 0px;
}
#test-status {
padding-bottom: 5px;
padding-top: 5px;
}
.myEditableLineDecoration {
background-color: var(--gray-45);
width: 15px !important;
margin-left: 5px !important;
margin-right: 5px !important;
}
.myEditableLineDecoration.tests-passed {
background-color: #4caf50;
}

View File

@ -0,0 +1,40 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from '../../../components/helpers/index';
import './challenge-title.css';
import i18next from 'i18next';
const propTypes = {
block: PropTypes.string,
superBlock: PropTypes.string
};
function BreadCrumb({ block, superBlock }) {
return (
<div className='challenge-title-breadcrumbs'>
<Link
className='breadcrumb-left'
state={{ breadcrumbBlockClick: block }}
to={`/learn/${superBlock}`}
>
<span className='ellipsis'>
{i18next.t(`intro:${superBlock}.title`)}
</span>
</Link>
<div className='breadcrumb-center' />
<Link
className='breadcrumb-right'
state={{ breadcrumbBlockClick: block }}
to={`/learn/${superBlock}/#${block}`}
>
{i18next.t(`intro:${superBlock}.blocks.${block}.title`)}
</Link>
</div>
);
}
BreadCrumb.displayName = 'BreadCrumb';
BreadCrumb.propTypes = propTypes;
export default BreadCrumb;

View File

@ -1,10 +1,11 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Link } from '../../../components/helpers/index'; import { Link } from '../../../components/helpers/index';
import i18next from 'i18next';
import './challenge-title.css'; import './challenge-title.css';
import GreenPass from '../../../assets/icons/GreenPass'; import GreenPass from '../../../assets/icons/GreenPass';
import i18next from 'i18next'; import BreadCrumb from './BreadCrumb';
const propTypes = { const propTypes = {
block: PropTypes.string, block: PropTypes.string,
@ -31,25 +32,7 @@ function ChallengeTitle({
{i18next.t('misc.translation-pending')} {i18next.t('misc.translation-pending')}
</Link> </Link>
)} )}
<div className='challenge-title-breadcrumbs'> <BreadCrumb block={block} superBlock={superBlock} />
<Link
className='breadcrumb-left'
state={{ breadcrumbBlockClick: block }}
to={`/learn/${superBlock}`}
>
<span className='ellipsis'>
{i18next.t(`intro:${superBlock}.title`)}
</span>
</Link>
<div className='breadcrumb-center' />
<Link
className='breadcrumb-right'
state={{ breadcrumbBlockClick: block }}
to={`/learn/${superBlock}/#${block}`}
>
{i18next.t(`intro:${superBlock}.blocks.${block}.title`)}
</Link>
</div>
<div className='challenge-title'> <div className='challenge-title'>
<div className='title-text'> <div className='title-text'>
<b>{children}</b> <b>{children}</b>