Skip to content

Commit

Permalink
Create new Link component to properly manage routed links
Browse files Browse the repository at this point in the history
For #211. This provides a standard pattern to follow when a link is
required that pushes a new route using the ReasonReact router. This
component has been set up to intercept Command / Control key mouse
clicks to properly open those requests in a new tab, and provides a
built-in confirmation function.
  • Loading branch information
harigopal committed Feb 21, 2020
1 parent 15b5ca5 commit 1dd17b8
Show file tree
Hide file tree
Showing 12 changed files with 116 additions and 124 deletions.
13 changes: 3 additions & 10 deletions app/javascript/courses/curricula/components/CoursesCurriculum.re
Expand Up @@ -14,11 +14,6 @@ type state = {
notice: Notice.t,
};

let selectTarget = (target, event) => {
event |> ReactEvent.Mouse.preventDefault;
ReasonReactRouter.push("/targets/" ++ (target |> Target.id));
};

let targetStatusClasses = targetStatus => {
let statusClasses =
"curriculum__target-status--"
Expand All @@ -35,19 +30,18 @@ let rendertarget = (target, statusOfTargets) => {
"Could not find targetStatus for listed target with ID " ++ targetId,
);

<a
<Link
href={"/targets/" ++ targetId}
key={"target-" ++ targetId}
className="bg-white border-t p-6 flex items-center justify-between hover:bg-gray-200 hover:text-primary-500 cursor-pointer"
ariaLabel={"Select Target " ++ targetId}
onClick={selectTarget(target)}>
ariaLabel={"Select Target " ++ targetId}>
<span className="font-semibold text-left leading-snug">
{target |> Target.title |> str}
</span>
<span className={targetStatusClasses(targetStatus)}>
{targetStatus |> TargetStatus.statusToString |> str}
</span>
</a>;
</Link>;
};

let renderTargetGroup = (targetGroup, targets, statusOfTargets) => {
Expand Down Expand Up @@ -500,7 +494,6 @@ let make =
addSubmissionCB={addSubmission(setState)}
targets
statusOfTargets={state.statusOfTargets}
changeTargetCB=selectTarget
users
evaluationCriteria
coaches
Expand Down
Expand Up @@ -258,8 +258,7 @@ let overlayStatus = (course, target, targetStatus, preview) =>
let renderLockReason = reason =>
renderLocked(reason |> TargetStatus.lockReasonToString);

let prerequisitesIncomplete =
(reason, target, targets, statusOfTargets, changeTargetCB) => {
let prerequisitesIncomplete = (reason, target, targets, statusOfTargets) => {
let prerequisiteTargetIds = target |> Target.prerequisiteTargetIds;
let prerequisiteTargets =
targets
Expand All @@ -278,39 +277,31 @@ let prerequisitesIncomplete =
ts |> TargetStatus.targetId == (target |> Target.id)
);

<a
<Link
href={"/targets/" ++ (target |> Target.id)}
ariaLabel={"Select Target " ++ (target |> Target.id)}
key={target |> Target.id}
className="bg-white border-t px-6 py-4 relative z-10 flex items-center justify-between hover:bg-gray-200 hover:text-primary-500 cursor-pointer"
onClick={changeTargetCB(target)}>
className="bg-white border-t px-6 py-4 relative z-10 flex items-center justify-between hover:bg-gray-200 hover:text-primary-500 cursor-pointer">
<span className="font-semibold text-left leading-snug">
{target |> Target.title |> str}
</span>
<span className={targetStatusClasses(targetStatus)}>
{targetStatus |> TargetStatus.statusToString |> str}
</span>
</a>;
</Link>;
})
|> Array.of_list
|> React.array}
</div>
</div>;
};

let handleLocked =
(target, targets, targetStatus, statusOfTargets, changeTargetCB) =>
let handleLocked = (target, targets, targetStatus, statusOfTargets) =>
switch (targetStatus |> TargetStatus.status) {
| Locked(reason) =>
switch (reason) {
| PrerequisitesIncomplete =>
prerequisitesIncomplete(
reason,
target,
targets,
statusOfTargets,
changeTargetCB,
)
prerequisitesIncomplete(reason, target, targets, statusOfTargets)
| CourseLocked
| AccessLocked
| LevelLocked => renderLockReason(reason)
Expand Down Expand Up @@ -470,9 +461,7 @@ let handlePendingStudents = (targetStatus, targetDetails, users) =>
| (Some(_) | None, Locked(_) | Pending | Submitted | Passed | Failed) => React.null
};

let performQuickNavigation = (url, send, event) => {
event |> ReactEvent.Mouse.preventDefault;

let performQuickNavigation = (send, _event) => {
// Scroll to the top of the overlay before pushing the new URL.
Webapi.Dom.(
switch (document |> Document.getElementById("target-overlay")) {
Expand All @@ -483,8 +472,6 @@ let performQuickNavigation = (url, send, event) => {

// Clear loaded target details.
send(ClearTargetDetails);

ReasonReactRouter.push(url);
};

let navigationLink = (direction, url, send) => {
Expand All @@ -499,14 +486,14 @@ let navigationLink = (direction, url, send) => {
<FaIcon classes={"fas " ++ icon} />
);

<a
<Link
href=url
onClick={performQuickNavigation(url, send)}
onClick={performQuickNavigation(send)}
className="block p-2 md:p-4 text-center border rounded-lg bg-gray-100 hover:bg-gray-200">
{arrow(leftIcon)}
<span className="mx-2 hidden md:inline"> {text |> str} </span>
{arrow(rightIcon)}
</a>;
</Link>;
};

let scrollOverlayToTop = _event => {
Expand Down Expand Up @@ -559,7 +546,6 @@ let make =
~addSubmissionCB,
~targets,
~statusOfTargets,
~changeTargetCB,
~users,
~evaluationCriteria,
~coaches,
Expand All @@ -583,13 +569,7 @@ let make =
<div className="bg-gray-100 border-b border-gray-400 px-3">
<div className="course-overlay__header-container pt-12 lg:pt-0 mx-auto">
{overlayStatus(course, target, targetStatus, preview)}
{handleLocked(
target,
targets,
targetStatus,
statusOfTargets,
changeTargetCB,
)}
{handleLocked(target, targets, targetStatus, statusOfTargets)}
{handlePendingStudents(targetStatus, state.targetDetails, users)}
{switch (state.targetDetails) {
| Some(targetDetails) =>
Expand Down
Expand Up @@ -14,11 +14,6 @@ type state = {
selectedLevel: option(Level.t),
};

let openOverlay = (submissionId, event) => {
event |> ReactEvent.Mouse.preventDefault;
ReasonReactRouter.push("/submissions/" ++ submissionId);
};

let updateLevel = (level, setState) => {
setState(state =>
{...state, selectedLevel: level, reviewedSubmissions: Unloaded}
Expand Down Expand Up @@ -221,14 +216,12 @@ let make = (~levels, ~pendingSubmissions, ~courseId, ~currentCoach) => {
submissions={state.pendingSubmissions}
levels
selectedLevel={state.selectedLevel}
openOverlayCB=openOverlay
/>
| ReviewedSubmissions =>
<CoursesReview__ShowReviewedSubmissions
courseId
selectedLevel={state.selectedLevel}
levels
openOverlayCB=openOverlay
reviewedSubmissions={state.reviewedSubmissions}
updateReviewedSubmissionsCB={updateReviewedSubmissions(
~setState,
Expand Down
Expand Up @@ -5,7 +5,7 @@ open CoursesReview__Types;
let str = React.string;

[@react.component]
let make = (~submissions, ~levels, ~selectedLevel, ~openOverlayCB) => {
let make = (~submissions, ~levels, ~selectedLevel) => {
let submissionToShow =
(
switch (selectedLevel) {
Expand All @@ -31,13 +31,12 @@ let make = (~submissions, ~levels, ~selectedLevel, ~openOverlayCB) => {
| _ =>
submissionToShow
|> Array.map(submission =>
<a
<Link
href={"/submissions/" ++ (submission |> SubmissionInfo.id)}
key={submission |> SubmissionInfo.id}
ariaLabel={
"pending-submission-card-" ++ (submission |> SubmissionInfo.id)
}
onClick={openOverlayCB(submission |> SubmissionInfo.id)}
className="flex flex-col md:flex-row items-start md:items-center justify-between bg-white border-l-3 border-orange-400 p-3 md:py-6 md:px-5 mt-4 cursor-pointer rounded-r-lg shadow hover:border-primary-500 hover:text-primary-500 hover:shadow-md">
<div className="w-full md:w-3/4">
<div className="block text-sm md:pr-2">
Expand Down Expand Up @@ -73,7 +72,7 @@ let make = (~submissions, ~levels, ~selectedLevel, ~openOverlayCB) => {
{submission |> SubmissionInfo.timeDistance |> str}
</div>
</div>
</a>
</Link>
)
|> React.array
}}
Expand Down
Expand Up @@ -121,15 +121,14 @@ let submissionCardClasses = status =>
}
);

let showSubmission = (submissions, levels, openOverlayCB) =>
let showSubmission = (submissions, levels) =>
<div>
{submissions
|> SubmissionInfo.sort
|> Array.map(submission =>
<a
<Link
href={"/submissions/" ++ (submission |> SubmissionInfo.id)}
key={submission |> SubmissionInfo.id}
onClick={openOverlayCB(submission |> SubmissionInfo.id)}
ariaLabel={
"reviewed-submission-card-" ++ (submission |> SubmissionInfo.id)
}
Expand Down Expand Up @@ -173,12 +172,12 @@ let showSubmission = (submissions, levels, openOverlayCB) =>
</div>
| None => React.null
}}
</a>
</Link>
)
|> React.array}
</div>;

let showSubmissions = (reviewedSubmissions, levels, openOverlayCB) =>
let showSubmissions = (reviewedSubmissions, levels) =>
reviewedSubmissions |> ArrayUtils.isEmpty
? <div
className="course-review__reviewed-empty text-lg font-semibold text-center py-4">
Expand All @@ -187,15 +186,14 @@ let showSubmissions = (reviewedSubmissions, levels, openOverlayCB) =>
</h5>
<img className="w-3/4 md:w-1/2 mx-auto mt-2" src=reviewedEmptyImage />
</div>
: showSubmission(reviewedSubmissions, levels, openOverlayCB);
: showSubmission(reviewedSubmissions, levels);

[@react.component]
let make =
(
~courseId,
~selectedLevel,
~levels,
~openOverlayCB,
~reviewedSubmissions,
~updateReviewedSubmissionsCB,
) => {
Expand Down Expand Up @@ -226,7 +224,7 @@ let make =
SkeletonLoading.multiple(~count=10, ~element=SkeletonLoading.card())
| PartiallyLoaded(reviewedSubmissions, cursor) =>
<div>
{showSubmissions(reviewedSubmissions, levels, openOverlayCB)}
{showSubmissions(reviewedSubmissions, levels)}
{loading
? SkeletonLoading.multiple(
~count=3,
Expand All @@ -248,7 +246,7 @@ let make =
</button>}
</div>
| FullyLoaded(reviewedSubmissions) =>
showSubmissions(reviewedSubmissions, levels, openOverlayCB)
showSubmissions(reviewedSubmissions, levels)
}}
</div>;
};
Expand Up @@ -245,11 +245,6 @@ let updateSearchInputString = (setState, event) => {
setState(state => {...state, searchInputString});
};

let openOverlayCB = (studentId, event) => {
event |> ReactEvent.Mouse.preventDefault;
ReasonReactRouter.push("/students/" ++ studentId ++ "/report");
};

let disableSearchButton = state => {
switch (state.searchInputString) {
| None => true
Expand Down Expand Up @@ -371,12 +366,7 @@ let make = (~levels, ~course, ~userId, ~teamCoaches) => {
)
| PartiallyLoaded(teams, cursor) =>
<div>
<CoursesStudents__TeamsList
levels
teams
openOverlayCB
teamCoaches
/>
<CoursesStudents__TeamsList levels teams teamCoaches />
{switch (state.loading) {
| LoadingMore =>
SkeletonLoading.multiple(
Expand All @@ -403,12 +393,7 @@ let make = (~levels, ~course, ~userId, ~teamCoaches) => {
}}
</div>
| FullyLoaded(teams) =>
<CoursesStudents__TeamsList
levels
teams
openOverlayCB
teamCoaches
/>
<CoursesStudents__TeamsList levels teams teamCoaches />
}}
</div>
</div>
Expand Down
Expand Up @@ -424,10 +424,8 @@ let coachInfo = (teamCoaches, studentDetails) => {
: React.null;
};

let navigateToStudent = (setState, path, event) => {
event |> ReactEvent.Mouse.preventDefault;
let navigateToStudent = (setState, _event) => {
setState(_ => initialState);
ReasonReactRouter.push(path);
};

let otherTeamMembers = (setState, studentId, studentDetails) =>
Expand All @@ -441,10 +439,10 @@ let otherTeamMembers = (setState, studentId, studentDetails) =>
let path =
"/students/" ++ (student |> TeamInfo.studentId) ++ "/report";

<a
<Link
className="block"
href=path
onClick={navigateToStudent(setState, path)}
onClick={navigateToStudent(setState)}
key={student |> TeamInfo.studentId}>
{userInfo(
~key={
Expand All @@ -454,7 +452,7 @@ let otherTeamMembers = (setState, studentId, studentDetails) =>
~name=student |> TeamInfo.studentName,
~title=student |> TeamInfo.studentTitle,
)}
</a>;
</Link>;
})
|> React.array}
</div>;
Expand Down

0 comments on commit 1dd17b8

Please sign in to comment.