Methods
# setMaterialsComponent(MaterialsComponent)
You can use this to customize the component that displays the materials of the lesson.
Parameters:
Name | Type | Description |
---|---|---|
MaterialsComponent |
React.ComponentType.<QuizMaterialsComponentProps> |
Example
...
import {
Dimensions,
} from "react-native";
import HTML from "react-native-render-html";
import {RenderListPrefix} from "@src/components/Course/CourseMaterials"
const ent = require("ent");
const DEVICE_WIDTH = Dimensions.get("window").width;
export const applyCustomCode = (externalCodeSetup) => {
externalCodeSetup.quizApi.setMaterialsComponent(props => {
const {
tagsStyles,
materialsStyles,
baseFontStyle,
materials,
onLinkPress,
global,
colors
} = props;
return (
<HTML
tagsStyles={{...tagsStyles, ...materialsStyles}}
baseFontStyle={baseFontStyle(15)}
html={ent.decode(materials)}
imagesMaxWidth={DEVICE_WIDTH - 32}
onLinkPress={onLinkPress}
listsPrefixesRenderers={{
ul: (attrib, children, styles, passProps) => (
<RenderListPrefix
parent={"ul"}
colors={colors}
global={global}
passProps={passProps}
/>
),
ol: (attrib, children, styles, passProps) => (
<RenderListPrefix
parent={"ol"}
colors={colors}
global={global}
passProps={passProps}
/>
)
}}
/>
);
});
}
# setOfflineComponent(OfflineComponent)
You can use this hook to customize the component that displays a "Come back later. Quizzes are not available offline" message. The component is visible when attempting to start a quiz while the device is offline.
Parameters:
Name | Type | Description |
---|---|---|
OfflineComponent |
React.ComponentType.<QuizOfflineComponentProps> |
Example
...
import Icon from "@src/components/Icon";
export const applyCustomCode = (externalCodeSetup) => {
externalCodeSetup.quizApi.setOfflineComponent(
({global, t, wrapperCompsStyle, renderNav, styles}) => (
<View style={[wrapperCompsStyle, styles.wrapper]} key={"intro"}>
{renderNav()}
<View style={styles.container}>
<Icon
tintColor={"#DCDDDF"}
icon={{fontIconName: "wifi-slash", weight: 400}}
styles={global.emptyIcon}
/>
<Text style={[global.emptyTitle, styles.title]}>
{t("quiz:offlineAlertTitle")}
</Text>
<Text style={[global.emptyDesc, styles.message]}>
{t("quiz:offlineAlertMessage")}
</Text>
</View>
</View>
)
);
}
# setPrevNextComponent(PrevNextComponent)
You can use this to replace the previous and next buttons on the quiz screen.
Parameters:
Name | Type | Description |
---|---|---|
PrevNextComponent |
React.ComponentType.<QuizPrevNextComponentProps> |
Example
//In custom_code/components/PrevNext.js
import React from "react";
import { Text, View, StyleSheet } from "react-native";
import AppTouchableOpacity from "@src/components/AppTouchableOpacity";
import Icon from "@src/components/Icon";
import { shadeColor } from "@src/utils";
export const onObjectClick = (
object,
onQuizClick,
onTopicClick,
onLessonClick
) => {
if (!!!object) {
return false;
}
switch (object.type) {
case "quiz":
onQuizClick(object.parentType, object.parent)(object);
break;
case "topic":
onTopicClick(object, object.parent);
break;
case "lesson":
onLessonClick(object);
break;
}
};
const PrevNext = ({
global,
colors,
t,
prevObject,
nextObject,
courseId,
onQuizClick,
onLessonClick,
onTopicClick,
nextLockedAlert
}) => {
return (
<View style={[global.row]}>
<AppTouchableOpacity
style={[
global.wrappedButton,
global.wrappedTextButton,
{ marginRight: 4, backgroundColor: "purple" }
]}
onPress={() => {
if (prevObject !== "disabled") {
onObjectClick(prevObject, onQuizClick, onTopicClick, onLessonClick);
}
}}
>
<View style={global.row}>
<View style={global.linkWithArrow}>
<Text
style={[
global.wrappedTextButtonLabel,
{
color:
!!!prevObject || prevObject === "disabled"
? shadeColor(colors.headerIconColor, 0.4)
: "red"
}
]}
>
{t("lesson:prevButtonText")}
</Text>
</View>
</View>
</AppTouchableOpacity>
<AppTouchableOpacity
style={[global.wrappedButton, global.wrappedTextButton, {backgroundColor: "purple"}]}
onPress={() => {
if (nextObject !== "disabled") {
onObjectClick(nextObject, onQuizClick, onTopicClick, onLessonClick);
} else if (typeof nextLockedAlert === "function") {
nextLockedAlert();
}
}}
>
<View style={global.row}>
<View style={global.linkWithArrow}>
<Text
style={[
global.wrappedTextButtonLabel,
{
color:
!!!nextObject || nextObject === "disabled"
? shadeColor(colors.headerIconColor, 0.4)
: "blue"
}
]}
>
{t("lesson:nextButtonText")}
</Text>
</View>
</View>
</AppTouchableOpacity>
</View>
);
};
export default PrevNext;
//In custom_code/index.js...
...
import PrevNextComponent from './components/PrevNext';
export const applyCustomCode = externalCodeSetup => {
externalCodeSetup.quizApi.setPrevNextComponent((props) => <PrevNextComponent {...props} />);
}
# setQuestionClosedRenderMode(questionClosedRenderMode)
It overrides the Closed Question type render mode. The default mode is htmlparser
. If the question is not rendering correctly, you can try setting it to webview
mode.
Parameters:
Name | Type | Description |
---|---|---|
questionClosedRenderMode |
QuestionClosedRenderMode | Available values: |
Example
externalCodeSetup.quizApi.setQuestionClosedRenderMode("webview")
# setQuestionOverviewButtons(QuestionOverviewButtons)
You can use this hook to customize the "Review Question" and "Quiz summary" buttons in the QuizSingleScreen.
Parameters:
Name | Type | Description |
---|---|---|
QuestionOverviewButtons |
QuestionOverviewButtonsProps |
Example
...
import QuestionOverviewButtons from "@src/components/QuestionOverview/QuestionOverviewButtons";
export const applyCustomCode = (externalCodeSetup) => {
externalCodeSetup.questionApiHooks.setQuestionOverviewButtons(
({
style,
global,
t,
labels,
onReviewPress,
onSummaryPress,
disableSummaryButton
}) => (
<QuestionOverviewButtons
style={style}
global={global}
reviewQuestionLabel={t("quiz:reviewQuestionLabel", {
question: labels.question
})}
summaryQuestionLabel={t("quiz:quizSummaryLabel", {
quiz: labels.quiz
})}
onReviewPress={onReviewPress}
onSummaryPress={onSummaryPress}
disableSummaryButton={disableSummaryButton}
/>
)
);
}
# setQuizScreenHeader(QuizScreenHeader)
You can use this hook to customize the header of the Quiz Single Screen which by default, contains the back-to-course button and the previous/next buttons.
Parameters:
Name | Type | Description |
---|---|---|
QuizScreenHeader |
React.ComponentType.<QuizScreenHeaderProps> |
Example
//In custom_code/components/QuizScreenHeader.js...
import React from "react";
import {View, Text} from "react-native";
import Animated from "react-native-reanimated";
import {DEVICE_WIDTH} from "@src/styles/global";
import AuthWrapper from "@src/components/AuthWrapper";
const Header = ({
headerLeftStyle,
style,
global,
colors,
backToCourse,
headerRightAuthWrapperProps,
prevNext,
quiz,
renderQuizTimer
}) => {
return (
<Animated.View
style={[
global.row,
global.fakeHeader,
{
backgroundColor: "transparent",
paddingHorizontal: 10,
overflow: "hidden"
},
{
width: DEVICE_WIDTH
},
style
]}
>
<View
style={[
{
alignItems: "center",
justifyContent: "center",
flexDirection: "row",
flex: 1,
height: "100%",
backgroundColor: "gray",
borderRadius: 20
}
]}
>
<View style={[global.headerButtonLeft, headerLeftStyle]}>
{backToCourse}
</View>
<View style={[global.headerCustomTitle]}>
{renderQuizTimer(quiz, global, colors)}
</View>
<View style={[global.headerButtonRight]}>
<AuthWrapper
actionOnGuestLogin={"hide"}
{...headerRightAuthWrapperProps}
>
{prevNext}
</AuthWrapper>
</View>
</View>
</Animated.View>
);
};
export default Header;
//In custom_code/index.js...
import QuizScreenHeader from "./components/QuizScreenHeader";
export const applyCustomCode = externalCodeSetup => {
externalCodeSetup.quizApi.setQuizScreenHeader(props => <QuizScreenHeader {...props} />)
}
# setQuizStartButton(QuizStartButton)
You can use this to customize the "Start Quiz" button that appears when opening the QuizSingleScreen.
Parameters:
Name | Type | Description |
---|---|---|
QuizStartButton |
React.ComponentType.<QuizStartButtonProps> |
Example
import AppButton from "@src/components/AppButton";
export const applyCustomCode = (externalCodeSetup) => {
externalCodeSetup.quizApi.setQuizStartButton(props => {
const {
showStart,
styles,
global,
quiz,
checkForm,
startQuiz,
quizStartLoading,
isResumeQuiz,
t,
labels
} = props;
return showStart ? (
<View
style={[
styles.buttonWrap,
{paddingBottom: styles.containerPadding},
global.quizStartButtonContainer
]}
>
<AppButton
style={[
styles.button,
{
backgroundColor: styles.buttonBackground
},
global.quizStartButton
]}
onPress={() => {
if (quiz.formActivated && !checkForm(quiz)) {
return false;
} else if (!quizStartLoading) {
startQuiz(quiz);
}
}}
label={t(isResumeQuiz ? "quiz:continue" : "quiz:start", {
quiz: labels.quiz
})}
labelStyle={global.quizStartButtonLabel}
global={global}
loading={quizStartLoading}
loadingStyle={global.quizStartButtonLoading}
/>
</View>
) : (
<></>
);
});
}
# setQuizTimer(QuizTimer)
You can use this hook to customize the QuizTimer component.
Parameters:
Name | Type | Description |
---|---|---|
QuizTimer |
React.ComponentType.<QuizTimerProps> |
Example
...
import Icon from "@src/components/Icon";
import TimeCountDown from "@src/components/TimerCountDown";
export const applyCustomCode = (externalCodeSetup: ExternalCodeSetup) => {
externalCodeSetup.quizApi.setQuizTimer(
({
showTimer,
item,
global,
colors,
styles,
timerRef,
onTimePassed,
onTimeOver
}) =>
showTimer ? (
<View style={styles.container}>
<Icon
icon={{fontIconName: "stopwatch", weight: 400}}
webIcon={"IconArrowBack"}
tintColor={colors.textIconColor}
styles={styles.icon}
/>
<TimeCountDown
ref={timerRef}
paused={false}
onTimePassed={onTimePassed}
onTimeOver={onTimeOver}
secondsLimit={item.timeLimit}
textProps={{style: global.timer}}
/>
<Text>The timer</Text>
</View>
) : (
<></>
)
);
}
# setQuizTitleComponent(QuizTitleComponent)
You can use this to replace the quiz's title component. For example, you can use this to add more details to the quiz's title.
Parameters:
Name | Type | Description |
---|---|---|
QuizTitleComponent |
React.ComponentType.<QuizTitleComponentProps> |
Example
//In custom_code/components/QuizTitle.js...
import React from "react";
import {View, Text} from "react-native";
import Animated from "react-native-reanimated";
const QuizTitle = ({
quiz,
global,
colors,
paddingTop = 14,
setHeaderHeight,
t,
labels,
questions,
currentSwiperPosition,
quizOrder,
quizTotalCount
}) => {
const paddingBottom = 14;
return (
<Animated.View
style={[
{
backgroundColor: colors.bodyFrontBg,
width: "100%",
shadowOffset: {width: 0, height: 1},
shadowRadius: 1,
shadowColor: "#000",
shadowOpacity: 0.05
}
]}
>
<View
style={[
global.row,
{
justifyContent: "space-between",
alignItems: "flex-start",
paddingTop,
paddingBottom
}
]}
onLayout={event => {
const {height} = event.nativeEvent.layout;
typeof setHeaderHeight === "function" && setHeaderHeight(height);
}}
>
<Animated.View
style={{
flex: 1,
paddingHorizontal: 20
}}
>
<Animated.Text
style={[
global.courseHeaderTitle,
{marginBottom: 5}
]}
>
{quiz.title}
</Animated.Text>
{
!quiz.hideQuestionPositionOverview &&
!quiz.hideQuestionNumbering &&
currentSwiperPosition > 0 ? (
<Text style={global.courseHeaderSubTitle}>
{t("quiz:questionCount", {
question: labels.question,
current: currentSwiperPosition,
total: questions
? questions.size
: ""
})}
</Text>
) : (
<Text style={global.courseHeaderSubTitle}>
{`Quiz ${quizOrder + 1} of ${quizTotalCount}`}
</Text>
)
}
<Text style={global.courseHeaderSubTitle}>
Author: {quiz.author.name}
</Text>
<Text style={global.courseHeaderSubTitle}>
Completed: {quiz.completed.toString()}
</Text>
</Animated.View>
</View>
</Animated.View>
);
};
export default QuizTitle;
//In custom_code/index.js...
...
import QuizTitle from "./components/QuizTitle";
export const applyCustomCode = externalCodeSetup => {
externalCodeSetup.quizApi.setQuizTitleComponent(props => <QuizTitle {...props} />)
}
# setWebViewContentComponent(WebViewContentComponent)
You can use this hook to replace the webview being used in the quiz content. For example, you can choose to replace it with the default react-native webview.
Parameters:
Name | Type | Description |
---|---|---|
WebViewContentComponent |
React.ComponentType.<QWebViewContentComponentProps> |
Example
...
import WebViewWithMore from "@src/components/WebViewWithMore";
export const applyCustomCode = (externalCodeSetup) => {
externalCodeSetup.quizApi.setWebViewContentComponent(
({
online,
t,
onShouldStartLoadWithRequest,
height,
source,
global,
colors,
ModalHeaderComponent
}) => (
<WebViewWithMore
online={online}
t={t}
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
height={height}
source={source}
global={global}
colors={colors}
ModalHeaderComponent={ModalHeaderComponent}
/>
)
);
}
# setWrapQuestionHtmlFilter(wrapQuestionHtmlFilter)
We use an HTML wrapper for "Fill in the blank" questions to accept input answers. You can use this to change the HTML output.
Parameters:
Name | Type | Description |
---|---|---|
wrapQuestionHtmlFilter |
OverrideQuestionHTMLWrapperCallback |
Example
externalCodeSetup.quizApi.setWrapQuestionHtmlFilter((HTMLWrapper, inputHtml, css) => {
const disableZoom = true;
const myNewHtml =
`
<!DOCTYPE html>
<html>
<head>
<title>Topic Content</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1 ${disableZoom ? "maximum-scale=1.0" : ""} ">
<style type="text/css">
${css}
.content {
width: 100%;
overflow: hidden;
padding-bottom: 4px;
}
</style>
</head>
<body>
<div class="content">
` +
inputHtml +
`
<p> Please use UPPERCASE characters only</p>
</div>
</body>
</html>
`;
return myNewHtml;
})