Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/components/status/assessment.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import useOption from "lib/hooks/use-option";
import useTranslate from "lib/hooks/use-translate";
import {activeState} from "lib/recoil";
import style from "./style.scss";
import useStatusTranslate from "./use-status-translate";

function Button({assessment}) {
const listener = useListener();
const options = useOption("status") || {};
const redirect = get(options, "allowRedirect", true);
const setActive = useSetRecoilState(activeState);
const statusTranslate = useStatusTranslate({assessment});
const translate = useTranslate();

if(assessment.completed) {
Expand All @@ -25,7 +27,7 @@ function Button({assessment}) {
}

if(assessment.link && redirect) {
return <a href={assessment.link}>{translate("status.start")}</a>;
return <a href={assessment.link}>{statusTranslate("start")}</a>;
}

if(assessment.loading) {
Expand All @@ -37,7 +39,7 @@ function Button({assessment}) {
setActive({...assessment});
};

return <button onClick={start} type="button">{translate("status.start")}</button>;
return <button onClick={start} type="button">{statusTranslate("start")}</button>;
}

Button.propTypes = {
Expand All @@ -50,8 +52,11 @@ Button.propTypes = {
};

function Assessment({assessment}) {
const statusTranslate = useStatusTranslate({assessment});
const translate = useTranslate();
const surveyName = assessment.surveyName || translate(`survey.${assessment.surveyType}_assessment`);
const surveyName = statusTranslate("survey")
|| assessment.surveyName
|| translate(`survey.${assessment.surveyType}_assessment`);

return (
<div className={[style.assessment, assessment.completed && style.inactive].filter(Boolean).join(" ")}>
Expand Down
10 changes: 5 additions & 5 deletions src/components/status/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,26 @@ import {useRecoilRefresher_UNSTABLE as useRecoilRefresher} from "recoil";
import HelpButton from "components/common/help/button";
import HelpModal from "components/common/help/modal";
import useOption from "lib/hooks/use-option";
import useTranslate from "lib/hooks/use-translate";
import {orderState} from "lib/recoil";
import style from "./style.scss";
import useStatusTranslate from "./use-status-translate";

export default function Error() {
const refreshOrder = useRecoilRefresher(orderState);
const showHelp = useOption("showHelp");
const [showHelpModal, setShowHelpModal] = useState(false);
const translate = useTranslate();
const translate = useStatusTranslate();
const retry = () => { refreshOrder(); };

return (
<div className={style.container}>
<div className={style.header}>
{showHelp && <span className={style.helpSpacer} />}
<div>{translate("status.heading")}</div>
<div>{translate("heading")}</div>
{showHelp && <HelpButton onClick={() => setShowHelpModal(true)} />}
</div>
<div className={style.p}>{translate("status.error")}</div>
<button className={style.retry} onClick={retry} type="button">{translate("status.try_again")}</button>
<div className={style.p}>{translate("error")}</div>
<button className={style.retry} onClick={retry} type="button">{translate("try_again")}</button>
{showHelpModal && <HelpModal show={showHelpModal} setShow={setShowHelpModal} />}
</div>
);
Expand Down
9 changes: 5 additions & 4 deletions src/components/status/index.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import {useEffect, useState} from "react";
import HelpButton from "components/common/help/button";
import HelpModal from "components/common/help/modal";
import Markdown from "components/common/markdown";
import useActive from "lib/hooks/use-active";
import useComponentEvents from "lib/hooks/use-component-events";
import useOption from "lib/hooks/use-option";
import useOrder from "lib/hooks/use-order";
import useTranslate from "lib/hooks/use-translate";
import Assessment from "./assessment";
import Error from "./error";
import Loading from "./loading";
import Skipped from "./skipped";
import style from "./style.scss";
import Timeout from "./timeout";
import useStatusTranslate from "./use-status-translate";

export default function Status() {
const active = useActive();
const [activeLoading, setActiveLoading] = useState(false);
const order = useOrder();
const showHelp = useOption("showHelp");
const [showHelpModal, setShowHelpModal] = useState(false);
const translate = useTranslate();
const translate = useStatusTranslate();
const assessments = order?.assessments || [];

useComponentEvents("Status", {order});
Expand All @@ -44,10 +45,10 @@ export default function Status() {
<div className={style.container}>
<div className={style.header}>
{showHelp && <span className={style.helpSpacer} />}
<div>{translate("status.heading")}</div>
<div>{translate("heading")}</div>
{showHelp && <HelpButton onClick={() => setShowHelpModal(true)} />}
</div>
<div className={style.p}>{translate("status.text")}</div>
<Markdown>{translate("text")}</Markdown>
{assessments.map((assessment) => <Assessment key={assessment.id} assessment={assessment} />)}
{showHelpModal && <HelpModal show={showHelpModal} setShow={setShowHelpModal} />}
</div>
Expand Down
6 changes: 3 additions & 3 deletions src/components/status/loading.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import useTranslate from "lib/hooks/use-translate";
import style from "./style.scss";
import useStatusTranslate from "./use-status-translate";

export default function Loading() {
const translate = useTranslate();
const translate = useStatusTranslate();

return (
<div className={style.container}>
<img alt="" src="https://cdn.traitify.com/widgets/status/loading-sonar.gif" />
<div className={[style.bold, style.p].join(" ")}>{translate("status.loading")}</div>
<div className={[style.bold, style.p].join(" ")}>{translate("loading")}</div>
</div>
);
}
4 changes: 4 additions & 0 deletions src/components/status/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
max-width: $breakpoint-sm;
padding: $buffer-lg;

:global(.traitify--markdown) {
margin-bottom: $buffer-section;
text-align: center;
}
.retry {
display: block;
margin: 0 auto;
Expand Down
8 changes: 4 additions & 4 deletions src/components/status/timeout.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import useListener from "lib/hooks/use-listener";
import useTranslate from "lib/hooks/use-translate";
import style from "./style.scss";
import useStatusTranslate from "./use-status-translate";

export default function Timeout() {
const listener = useListener();
const translate = useTranslate();
const translate = useStatusTranslate();
const retry = () => { listener.trigger("Order.polling", {status: "on"}); };

return (
<div className={style.container}>
<div className={style.spacer} />
<div className={[style.bold, style.p].join(" ")}>{translate("status.timeout")}</div>
<button className={style.retry} onClick={retry} type="button">{translate("status.try_again")}</button>
<div className={[style.bold, style.p].join(" ")}>{translate("timeout")}</div>
<button className={style.retry} onClick={retry} type="button">{translate("try_again")}</button>
</div>
);
}
23 changes: 23 additions & 0 deletions src/components/status/use-status-translate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {useCallback} from "react";
import useOrder from "lib/hooks/use-order";
import useTranslate from "lib/hooks/use-translate";

export default function useStatusTranslate({assessment} = {}) {
const order = useOrder();
const translate = useTranslate();

return useCallback((key, ...options) => {
const assessments = assessment ? [assessment] : order?.assessments || [];
const cognitive = assessments.some(({surveyType}) => surveyType === "cognitive");
const personality = assessments.some(({surveyType}) => surveyType === "personality");
const interview = assessments.some(({vendor}) => vendor === "crosschq");
const keys = [
personality && interview && "status.personality_interview",
cognitive && interview && "status.cognitive_interview",
interview && "status.interview",
"status"
].filter(Boolean).map((prefix) => [prefix, key].join("."));

return translate(keys, ...options);
}, [assessment, order, translate]);
}
8 changes: 8 additions & 0 deletions src/lib/common/array/find-map.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default function findMap(array, callback) {
let result;
array.some((item, index) => {
result = callback(item, index);
return result;
});
return result || undefined;
}
3 changes: 2 additions & 1 deletion src/lib/common/order-from-query.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ export function assessmentFromQuery(response) {
profileID: response.profile_id || response.profileId || dig(response, "profile_ids", 0),
skipped: response.isSkipped || response.skipped || false,
surveyID: response.deck_id || response.surveyId || response.surveyKey,
surveyName: response.surveyName || response.name
surveyName: response.surveyName || response.name,
vendor: response.vendor
};

// NOTE: Prevent overriding with blanks
Expand Down
14 changes: 13 additions & 1 deletion src/lib/i18n-data/en-us.json
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,20 @@
"slide_error": "Unable to load more slides at this time",
"sort": "Sort",
"status": {
"cognitive_interview": {
"heading": "Cognitive Assessment & Interview"
},
"error": "There was an error with your assessments",
"heading": "Your Application Assessments",
"interview": {
"heading": "Interview",
"start": "Start Interview",
"survey": "Crosschq AI Interviewer",
"text": "Dive into an AI video chat to ask questions, practice your interview, and learn more about the details of this job.\n\nThis is a chat with an Artificial Employee that has been trained on our company culture and the details around this job."
},
"interview_personality": {
"heading": "Personality Assessment & Interview"
},
"loading": "Loading your assessment",
"start": "Start Assessment",
"text": "As part of your application, we'd like to ask you to complete the following assessments. Please click on the button next to the assessment name. This will take you to where you'll complete the assessment(s). Please return here once you've completed all assessments.",
Expand Down Expand Up @@ -245,4 +257,4 @@
"try_again": "Click Here to Try Again",
"view_on_onet": "View Career Details on O*Net",
"yes": "Yes"
}
}
12 changes: 9 additions & 3 deletions src/lib/i18n.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import findMap from "./common/array/find-map";
import dig from "./common/object/dig";
import merge from "./common/object/merge";
import {isArray} from "./common/object/type";
import i18nData from "./i18n-data";

export default class I18n {
Expand Down Expand Up @@ -31,10 +33,14 @@ export default class I18n {
...originData
};
};
// NOTE: Returns first translation if key is an array
translate = (locale, _key, options) => {
const keys = _key.split(".");
let result = dig(this.data, locale.toLowerCase(), ...keys);
if(!result && locale.toLowerCase() !== "en-us") { result = dig(this.data, "en-us", ...keys); }
const keys = isArray(_key) ? _key : [_key];
let result = findMap(keys, (key) => dig(this.data, locale.toLowerCase(), ...key.split(".")));

if(!result && locale.toLowerCase() !== "en-us") {
result = findMap(keys, (key) => dig(this.data, "en-us", ...key.split(".")));
}
if(!result || !options) { return result; }

return result.replace(/%\{[a-z_]*\}/g, (r) => options[r.slice(2, -1)]);
Expand Down
12 changes: 6 additions & 6 deletions test/components/__snapshots__/status.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@ exports[`Status order renders component 1`] = `
/>
</button>
</div>
<div
className="p"
<p
className="traitify--markdown"
>
As part of your application, we'd like to ask you to complete the following assessments. Please click on the button next to the assessment name. This will take you to where you'll complete the assessment(s). Please return here once you've completed all assessments.
</div>
</p>
<div
className="assessment inactive"
>
Expand Down Expand Up @@ -280,11 +280,11 @@ exports[`Status recommendation renders component 1`] = `
/>
</button>
</div>
<div
className="p"
<p
className="traitify--markdown"
>
As part of your application, we'd like to ask you to complete the following assessments. Please click on the button next to the assessment name. This will take you to where you'll complete the assessment(s). Please return here once you've completed all assessments.
</div>
</p>
<div
className="assessment inactive"
>
Expand Down
3 changes: 3 additions & 0 deletions test/components/status.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import recommendationCompleted from "support/data/recommendation/completed";
import recommendationIncomplete from "support/data/recommendation/incomplete";
import flushAsync from "support/flush-async";
import useContainer from "support/hooks/use-container";
import useGlobalMock from "support/hooks/use-global-mock";

describe("Status", () => {
let component;
Expand Down Expand Up @@ -56,6 +57,8 @@ describe("Status", () => {
describe("order", () => {
let orderResponse;

useGlobalMock(console, "warn");

beforeEach(() => {
orderResponse = mutable(orderCompleted);
order = orderFromQuery({data: {order: orderResponse}});
Expand Down
38 changes: 38 additions & 0 deletions test/lib/common/array/find-map.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import findMap from "lib/common/array/find-map";

describe("Array", () => {
describe("findMap", () => {
it("returns first mapped truthy value", () => {
const value = findMap([1, 2, 3], (item) => item > 1 && item * 10);

expect(value).toBe(20);
});

it("returns undefined when nothing matches", () => {
const value = findMap([1, 2, 3], () => null);

expect(value).toBeUndefined();
});

it("returns undefined for empty array", () => {
const value = findMap([], () => "anything");

expect(value).toBeUndefined();
});

it("passes item and index to callback", () => {
const callback = jest.fn();
findMap(["a", "b"], callback);

expect(callback).toHaveBeenCalledWith("a", 0);
expect(callback).toHaveBeenCalledWith("b", 1);
});

it("short-circuits after first truthy result", () => {
const callback = jest.fn((item) => item === "b" && item.toUpperCase());
findMap(["a", "b", "c"], callback);

expect(callback).toHaveBeenCalledTimes(2);
});
});
});
19 changes: 18 additions & 1 deletion test/lib/i18n.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,24 @@ describe("I18n", () => {
it("allows misses", () => {
const translation = translate("tacos");

expect(translation).toBeNull();
expect(translation).toBeUndefined();
});

describe("with array of keys", () => {
it("returns first match", () => {
i18n.addTranslations("en-US", {data: {tacos: "I like tacos", pizza: "I like pizza"}});
const translation = translate(["burgers", "tacos", "pizza"]);

expect(translation).toBe("I like tacos");
});

it("prefers current locale", () => {
i18n.addTranslations("en-US", {data: {tacos: "I like tacos"}});
i18n.addTranslations("es-US", {data: {pizza: "Me gusta pizza"}});
const translation = translateES(["tacos", "pizza"]);

expect(translation).toBe("Me gusta pizza");
});
});
});
});