125 lines
4.2 KiB
JavaScript
125 lines
4.2 KiB
JavaScript
/* global gettext */
|
|
import React from 'react';
|
|
import isFunction from 'lodash/isFunction';
|
|
|
|
const Page = ({ children }) => children;
|
|
const Header = () => null;
|
|
const Closer = () => null;
|
|
const ErrorPage = () => null;
|
|
export default class Wizard extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.findSubComponentByType = this.findSubComponentByType.bind(this);
|
|
this.handleNext = this.handleNext.bind(this);
|
|
this.state = {
|
|
currentPage: 1,
|
|
totalPages: 0,
|
|
pages: [],
|
|
wizardContext: {},
|
|
}
|
|
|
|
this.wizardComplete = this.wizardComplete.bind(this);
|
|
}
|
|
|
|
componentDidMount() {
|
|
const pages = this.findSubComponentByType(Wizard.Page.name);
|
|
const totalPages = pages.length;
|
|
const wizardContext = this.props.wizardContext;
|
|
const closer = this.findSubComponentByType(Wizard.Closer.name)[0];
|
|
pages.push(closer);
|
|
this.setState({ pages, totalPages, wizardContext });
|
|
}
|
|
|
|
handleNext() {
|
|
if (this.state.currentPage < this.props.children.length) {
|
|
this.setState(prevState => ({ currentPage: prevState.currentPage + 1 }))
|
|
}
|
|
}
|
|
|
|
findSubComponentByType(type) {
|
|
return React.Children.toArray(this.props.children).filter(child => child.type.name === type)
|
|
}
|
|
|
|
// this needs to handle the case of no provided header
|
|
renderHeader() {
|
|
const header = this.findSubComponentByType(Wizard.Header.name)[0];
|
|
return header.props.children({ currentPage: this.state.currentPage, totalPages: this.state.totalPages })
|
|
}
|
|
|
|
renderPage() {
|
|
if (this.state.totalPages) {
|
|
const page = this.state.pages[this.state.currentPage - 1];
|
|
if (page.type.name === Wizard.Closer.name) {
|
|
return page.props.children;
|
|
}
|
|
|
|
if (isFunction(page.props.children)) {
|
|
return page.props.children({ wizardConsumer: this.props.wizardContext });
|
|
} else {
|
|
return page.props.children;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// this needs to handle the case of no provided errorPage
|
|
renderError() {
|
|
const errorPage = this.findSubComponentByType(Wizard.ErrorPage.name)[0];
|
|
return (
|
|
<div className="wizard-container" role="dialog" aria-label={gettext("demographics questionnaire")}>
|
|
<div className="wizard-header">
|
|
{errorPage.props.children}
|
|
</div>
|
|
<div className="wizard-footer justify-content-end h-100 d-flex flex-column">
|
|
<button className="wizard-button colored" arial-label={gettext("close questionnaire")} onClick={this.props.onWizardComplete}>{gettext("Close")}</button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Utility method that helps determine if the learner is on the final page of the modal.
|
|
*/
|
|
onFinalPage() {
|
|
return this.state.pages.length === this.state.currentPage;
|
|
}
|
|
|
|
/**
|
|
* Utility method for closing the modal and returning the learner back to the Course Dashboard.
|
|
* If a learner is on the final page of the modal, meaning they have answered all of the
|
|
* questions, clicking the "Return to my dashboard" button will also dismiss the CTA from the
|
|
* course dashboard.
|
|
*/
|
|
async wizardComplete() {
|
|
if (this.onFinalPage()) {
|
|
this.props.dismissBanner();
|
|
}
|
|
|
|
this.props.onWizardComplete();
|
|
}
|
|
|
|
render() {
|
|
const finalPage = this.onFinalPage();
|
|
if (this.props.error) {
|
|
return this.renderError();
|
|
}
|
|
return (
|
|
<div className="wizard-container" role="dialog" aria-label={gettext("demographics questionnaire")}>
|
|
<div className="wizard-header mb-4">
|
|
{this.state.totalPages >= this.state.currentPage && this.renderHeader()}
|
|
</div>
|
|
{this.renderPage()}
|
|
<div className="wizard-footer justify-content-end h-100 d-flex flex-column">
|
|
<button className={`wizard-button ${finalPage && 'colored'}`} onClick={this.wizardComplete} aria-label={gettext("finish later")}>{finalPage ? gettext("Return to my dashboard") : gettext("Finish later")}</button>
|
|
<button className="wizard-button colored" hidden={finalPage} onClick={this.handleNext} aria-label={gettext("next page")}>{gettext("Next")}</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
Wizard.Page = Page;
|
|
Wizard.Header = Header;
|
|
Wizard.Closer = Closer;
|
|
Wizard.ErrorPage = ErrorPage;
|