Skip to content

Commit 4e85334

Browse files
committed
Add list view to set tests page
1 parent 96207ce commit 4e85334

File tree

5 files changed

+125
-86
lines changed

5 files changed

+125
-86
lines changed

src/app/components/elements/list-groups/AbstractListViewItem.tsx

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { AffixButton } from "../AffixButton";
88
import { Spacer } from "../Spacer";
99
import { CompletionState } from "../../../../IsaacApiTypes";
1010
import { determineAudienceViews } from "../../../services/userViewingContext";
11-
import { above, below, TAG_ID, tags, useDeviceSize } from "../../../services";
11+
import { below, TAG_ID, tags, useDeviceSize } from "../../../services";
1212
import { PhyHexIcon } from "../svg/PhyHexIcon";
1313
import { TitleIconProps } from "../PageTitle";
1414

@@ -45,21 +45,15 @@ const Tags = ({tags}: {tags: {tag: string, url?: string}[];}) => {
4545
</>;
4646
};
4747

48-
const QuizLinks = (props: React.HTMLAttributes<HTMLSpanElement> & {previewUrl: string, testUrl: string}) => {
49-
const { previewUrl, testUrl, ...rest } = props;
48+
const QuizLinks = (props: React.HTMLAttributes<HTMLSpanElement> & {previewQuizUrl: string, quizButton: JSX.Element}) => {
49+
const { previewQuizUrl, quizButton, ...rest } = props;
5050
return <span {...rest} className={classNames(rest.className, "d-flex")}>
5151
<Spacer/>
52-
<Button to={previewUrl} color="keyline" className="set-quiz-button-md">
52+
<Button to={previewQuizUrl} color="keyline" tag={Link} className="set-quiz-button-md">
5353
Preview
5454
</Button>
5555
<span style={{minWidth: "20px"}}/>
56-
<AffixButton size="md" color="solid" to={testUrl} affix={{
57-
affix: "icon-right",
58-
position: "suffix",
59-
type: "icon"
60-
}}>
61-
Take the test
62-
</AffixButton>
56+
{quizButton}
6357
</span>;
6458
};
6559

@@ -79,17 +73,17 @@ export interface AbstractListViewItemProps {
7973
testTag?: string;
8074
url?: string;
8175
audienceViews?: ViewingContext[];
82-
previewUrl?: string;
83-
testUrl?: string;
76+
previewQuizUrl?: string;
77+
quizButton?: JSX.Element;
8478
isCard?: boolean;
8579
fullWidth?: boolean;
8680
}
8781

88-
export const AbstractListViewItem = ({icon, title, subject, subtitle, breadcrumb, status, tags, testTag, url, audienceViews, previewUrl, testUrl, isCard, fullWidth, ...rest}: AbstractListViewItemProps) => {
82+
export const AbstractListViewItem = ({icon, title, subject, subtitle, breadcrumb, status, tags, testTag, url, audienceViews, previewQuizUrl, quizButton, isCard, fullWidth, ...rest}: AbstractListViewItemProps) => {
8983
const deviceSize = useDeviceSize();
90-
const isQuiz: boolean = (previewUrl && testUrl) ? true : false;
84+
const isQuiz: boolean = (previewQuizUrl && quizButton) ? true : false;
9185

92-
fullWidth = fullWidth || below["sm"](deviceSize) || ((status || audienceViews || previewUrl || testUrl) ? false : true);
86+
fullWidth = fullWidth || below["sm"](deviceSize) || ((status || audienceViews || previewQuizUrl || quizButton) ? false : true);
9387
const colWidths = fullWidth ? [12,12,12,12,12] : isQuiz ? [12,6,6,6,6] : [12,8,7,6,7];
9488
const cardBody =
9589
<Row className="w-100 flex-row" {...rest}>
@@ -119,8 +113,8 @@ export const AbstractListViewItem = ({icon, title, subject, subtitle, breadcrumb
119113
{tags && <div className="d-flex py-3">
120114
<Tags tags={tags}/>
121115
</div>}
122-
{previewUrl && testUrl && fullWidth && <div className="d-flex d-md-none align-items-center">
123-
<QuizLinks previewUrl={previewUrl} testUrl={testUrl}/>
116+
{previewQuizUrl && quizButton && fullWidth && <div className="d-flex d-md-none align-items-center">
117+
<QuizLinks previewQuizUrl={previewQuizUrl} quizButton={quizButton}/>
124118
</div>}
125119
</div>
126120
</Col>
@@ -132,8 +126,8 @@ export const AbstractListViewItem = ({icon, title, subject, subtitle, breadcrumb
132126
{audienceViews && <Col md={4} lg={5} xl={4} xxl={3} className="d-none d-md-flex justify-content-end">
133127
<StageAndDifficultySummaryIcons audienceViews={audienceViews} stack spacerWidth={5} className={classNames({"list-view-border": audienceViews.length > 0})}/>
134128
</Col>}
135-
{previewUrl && testUrl && <Col md={6} className="d-none d-md-flex align-items-center justify-content-end">
136-
<QuizLinks previewUrl={previewUrl} testUrl={testUrl}/>
129+
{previewQuizUrl && quizButton && <Col md={6} className="d-none d-md-flex align-items-center justify-content-end">
130+
<QuizLinks previewQuizUrl={previewQuizUrl} quizButton={quizButton}/>
137131
</Col>}
138132
</>
139133
}
@@ -158,8 +152,6 @@ export const AbstractListView = ({items}: {items: ShortcutResponse[]}) => {
158152
status={item.state}
159153
url={item.url}
160154
audienceViews={determineAudienceViews(item.audience)}
161-
previewUrl={"item.previewUrl"}
162-
testUrl={"item.testUrl"}
163155
/>)}
164156
</ListGroup>;
165157
};

src/app/components/elements/list-groups/ListView.tsx

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,38 @@ import React from "react";
22
import { AbstractListViewItem, ListViewTagProps } from "./AbstractListViewItem";
33
import { ShortcutResponse, Subject, ViewingContext } from "../../../../IsaacAppTypes";
44
import { determineAudienceViews } from "../../../services/userViewingContext";
5-
import { DOCUMENT_TYPE, SEARCH_RESULT_TYPE, TAG_ID, TAG_LEVEL, tags } from "../../../services";
5+
import { DOCUMENT_TYPE, documentTypePathPrefix, SEARCH_RESULT_TYPE, TAG_ID, TAG_LEVEL, tags } from "../../../services";
66
import { Col, ListGroup, Row } from "reactstrap";
77
import { TitleIconProps } from "../PageTitle";
8+
import { AffixButton } from "../AffixButton";
9+
import { QuizSummaryDTO } from "../../../../IsaacApiTypes";
10+
import { Link } from "react-router-dom";
11+
import { showQuizSettingModal, useAppDispatch } from "../../../state";
12+
13+
export interface ListViewCardProps {
14+
item: ShortcutResponse;
15+
icon: TitleIconProps;
16+
subject?: Subject;
17+
tagList?: ListViewTagProps[];
18+
}
19+
20+
export const ListViewCard = ({item, icon, subject, tagList, ...rest}: ListViewCardProps) => {
21+
return <AbstractListViewItem
22+
icon={icon}
23+
title={item.title ?? ""}
24+
subject={subject}
25+
subtitle={item.subtitle}
26+
tags={tagList}
27+
isCard
28+
{...rest}
29+
/>;
30+
};
831

932
export const QuestionListViewItem = ({item, ...rest} : {item: ShortcutResponse}) => {
1033
const breadcrumb = tags.getByIdsAsHierarchy((item.tags || []) as TAG_ID[]).map(tag => tag.title);
1134
const audienceViews: ViewingContext[] = determineAudienceViews(item.audience);
1235
const itemSubject = tags.getSpecifiedTag(TAG_LEVEL.subject, item.tags as TAG_ID[])?.id as Subject;
36+
const url = `/${documentTypePathPrefix[DOCUMENT_TYPE.QUESTION]}/${item.id}`;
1337

1438
return <AbstractListViewItem
1539
icon={{type: "hex", icon: "list-icon-question", size: "sm"}}
@@ -18,7 +42,7 @@ export const QuestionListViewItem = ({item, ...rest} : {item: ShortcutResponse})
1842
subtitle={item.subtitle}
1943
breadcrumb={breadcrumb}
2044
status={item.state}
21-
url={item.url}
45+
url={url}
2246
audienceViews={audienceViews}
2347
{...rest}
2448
/>;
@@ -41,7 +65,7 @@ export const EventListViewItem = ({item, ...rest}: {item: ShortcutResponse}) =>
4165
const itemSubject = tags.getSpecifiedTag(TAG_LEVEL.subject, item.tags as TAG_ID[])?.id as Subject;
4266

4367
return <AbstractListViewItem
44-
icon={{type: "hex", icon: "list-icon-concept", size: "sm"}}
68+
icon={{type: "hex", icon: "list-icon-events", size: "sm"}}
4569
title={item.title ?? ""}
4670
subject={itemSubject}
4771
subtitle={item.subtitle}
@@ -50,21 +74,23 @@ export const EventListViewItem = ({item, ...rest}: {item: ShortcutResponse}) =>
5074
/>;
5175
};
5276

53-
export interface ListViewCardProps {
54-
item: ShortcutResponse;
55-
icon: TitleIconProps;
56-
subject?: Subject;
57-
tagList?: ListViewTagProps[];
58-
}
77+
export const QuizListViewItem = ({item, isQuizSetter, ...rest}: {item: QuizSummaryDTO, isQuizSetter?: boolean}) => {
78+
const dispatch = useAppDispatch();
79+
const itemSubject = tags.getSpecifiedTag(TAG_LEVEL.subject, item.tags as TAG_ID[])?.id as Subject;
80+
const quizButton = isQuizSetter ?
81+
<AffixButton size="md" color="solid" onClick={() => (dispatch(showQuizSettingModal(item)))} affix={{ affix: "icon-right", position: "suffix", type: "icon" }}>
82+
Set test
83+
</AffixButton> :
84+
<AffixButton size="md" color="solid" to={item.url} tag={Link} affix={{ affix: "icon-right", position: "suffix", type: "icon" }}>
85+
Take the test
86+
</AffixButton>;
5987

60-
export const ListViewCard = ({item, icon, subject, tagList, ...rest}: ListViewCardProps) => {
61-
return <AbstractListViewItem
62-
icon={icon}
88+
return <AbstractListViewItem
89+
icon={{type: "hex", icon: "list-icon-lessons", size: "sm"}}
6390
title={item.title ?? ""}
64-
subject={subject}
65-
subtitle={item.subtitle}
66-
tags={tagList}
67-
isCard
91+
subject={itemSubject}
92+
previewQuizUrl={`/test/preview/${item.id}`}
93+
quizButton={quizButton}
6894
{...rest}
6995
/>;
7096
};
@@ -89,16 +115,14 @@ export const ListViewCards = ({cards}: {cards: ListViewCardProps[]}) => {
89115
</div>;
90116
};
91117

92-
93-
export const ListView = ({items, ...rest}: {items: ShortcutResponse[], fullWidth?: boolean}) => {
118+
export const ListView = ({items, ...rest}: {items: ShortcutResponse[], fullWidth?: boolean, isQuizSetter?: boolean}) => {
94119

95120
// Cards (e.g. the subjects on the homepage) Xxxx
96121
// Questions X
97122
// Question Packs
98123
// Quick Quizzes
99124
// Concepts
100-
// Tests
101-
125+
// Tests x
102126

103127
return <ListGroup className="link-list list-group-links">
104128
{items.map((item, index) => {
@@ -113,9 +137,11 @@ export const ListView = ({items, ...rest}: {items: ShortcutResponse[], fullWidth
113137
case (DOCUMENT_TYPE.EVENT):
114138
return <EventListViewItem key={index} item={item} {...rest}/>;
115139
case (DOCUMENT_TYPE.TOPIC_SUMMARY):
116-
return <QuestionListViewItem key={index} item={item} {...rest}/>;
140+
return <ConceptListViewItem key={index} item={item} {...rest}/>;
117141
case (DOCUMENT_TYPE.GENERIC):
118142
return <QuestionListViewItem key={index} item={item} {...rest}/>;
143+
case (DOCUMENT_TYPE.QUIZ):
144+
return <QuizListViewItem key={index} item={item} {...rest}/>;
119145
default:
120146
// Do not render this item if there is no matching DOCUMENT_TYPE
121147
console.error("Not able to display item as a ListViewItem: ", item);

src/app/components/pages/QuestionFinder.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -423,8 +423,8 @@ export const QuestionFinder = withRouter(({location}: RouteComponentProps) => {
423423
</Row>
424424

425425
<Row className="mt-4 position-relative finder-panel">
426-
<Col lg={siteSpecific(4, 3)} md={12} xs={12} className="text-wrap my-2 d-lg-none" data-testid="question-finder-filters">
427-
<QuestionFinderFilterPanel {...{
426+
<Col lg={siteSpecific(4, 3)} md={12} xs={12} className={classNames("text-wrap my-2", {"d-lg-none": isPhy})} data-testid="question-finder-filters">
427+
<QuestionFinderFilterPanel {...{
428428
searchDifficulties, setSearchDifficulties,
429429
searchTopics, setSearchTopics,
430430
searchStages, setSearchStages,
@@ -435,7 +435,7 @@ export const QuestionFinder = withRouter(({location}: RouteComponentProps) => {
435435
tiers, choices, selections, setTierSelection,
436436
applyFilters, clearFilters,
437437
validFiltersSelected, searchDisabled, setSearchDisabled
438-
}} />
438+
}} /> {/* Temporarily disabled at >=lg to test list view until this filter is moved into the sidebar */}
439439
</Col>
440440
<Col lg={siteSpecific(12, 9)} md={12} xs={12} className="text-wrap my-2" data-testid="question-finder-results">
441441
<Card>

src/app/components/pages/quizzes/SetQuizzes.tsx

Lines changed: 45 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {ShowLoading} from "../../handlers/ShowLoading";
1212
import {QuizAssignmentDTO, QuizSummaryDTO, RegisteredUserDTO} from "../../../../IsaacApiTypes";
1313
import {TitleAndBreadcrumb} from "../../elements/TitleAndBreadcrumb";
1414
import {formatDate, formatISODateOnly} from "../../elements/DateString";
15-
import {AppQuizAssignment} from "../../../../IsaacAppTypes";
15+
import {AppQuizAssignment, ShortcutResponse} from "../../../../IsaacAppTypes";
1616
import {
1717
above,
1818
below, confirmThen,
@@ -38,6 +38,7 @@ import { useHistoryState } from "../../../state/actions/history";
3838
import classNames from "classnames";
3939
import { ExtendDueDateModal } from "../../elements/modals/ExtendDueDateModal";
4040
import { UncontrolledTooltip, Button, Table, UncontrolledButtonDropdown, DropdownToggle, DropdownMenu, DropdownItem, Row, Input, UncontrolledDropdown, Container, ListGroup, ListGroupItem, Col, Alert } from "reactstrap";
41+
import { ListView } from "../../elements/list-groups/ListView";
4142

4243
interface SetQuizzesPageProps extends RouteComponentProps {
4344
user: RegisteredUserDTO;
@@ -377,46 +378,50 @@ const SetQuizzesPageComponent = ({user}: SetQuizzesPageProps) => {
377378
placeholder="Search by title" aria-label="Search by title"
378379
/>
379380
{undeprecatedQuizzes.length === 0 && <p><em>There are no tests you can set which match your search term.</em></p>}
380-
<ListGroup className="mb-2 quiz-list">
381-
{undeprecatedQuizzes.map(quiz => <ListGroupItem className="p-0 bg-transparent" key={quiz.id}>
382-
<Row className="w-100">
383-
<Col xs={9} md={8} lg={9} className="d-flex align-items-center">
384-
<div className="p-3">
385-
<span className="mb-2 mb-sm-0 pe-2">{quiz.title}</span>
386-
{roleVisibilitySummary(quiz)}
387-
</div>
388-
</Col>
389-
<Col md={3} lg={2} className="py-3 justify-content-end justify-content-md-center justify-content-lg-end align-items-center d-none d-md-flex">
390-
<Button className={`d-none d-md-block h-4 p-0 ${above["md"](deviceSize) ? "set-quiz-button-md" : "btn-sm set-quiz-button-sm"}`} onClick={() => dispatch(showQuizSettingModal(quiz))}>
391-
{siteSpecific("Set Test", "Set test")}
392-
</Button>
393-
</Col>
394-
<Col md={1} className="d-flex justify-content-end align-items-center d-none d-md-flex p-0">
395-
<Link className={`my-3 d-flex justify-content-end me-1`} to={{pathname: `/test/preview/${quiz.id}`}}>
396-
<span>Preview</span>
397-
</Link>
398-
</Col>
399-
<Col xs={3} className="d-flex align-items-center justify-content-end">
400-
<UncontrolledButtonDropdown className="d-flex d-md-none ">
401-
<DropdownToggle caret className="text-nowrap" size="sm" color="link">
402-
Actions
403-
</DropdownToggle>
404-
<DropdownMenu>
405-
<DropdownItem onClick={() => dispatch(showQuizSettingModal(quiz))} style={{zIndex: '1'}}>
406-
{siteSpecific("Set Test", "Set test")}
407-
</DropdownItem>
408-
<DropdownItem divider />
409-
<Link className="w-100" style={{textDecoration: 'none'}} to={{pathname: `/test/preview/${quiz.id}`}}>
410-
<DropdownItem>
411-
Preview
381+
382+
{siteSpecific(
383+
<ListView items={undeprecatedQuizzes} isQuizSetter/>,
384+
385+
<ListGroup className="mb-2 quiz-list">
386+
{undeprecatedQuizzes.map(quiz => <ListGroupItem className="p-0 bg-transparent" key={quiz.id}>
387+
<Row className="w-100">
388+
<Col xs={9} md={8} lg={9} className="d-flex align-items-center">
389+
<div className="p-3">
390+
<span className="mb-2 mb-sm-0 pe-2">{quiz.title}</span>
391+
{roleVisibilitySummary(quiz)}
392+
</div>
393+
</Col>
394+
<Col md={3} lg={2} className="py-3 justify-content-end justify-content-md-center justify-content-lg-end align-items-center d-none d-md-flex">
395+
<Button className={`d-none d-md-block h-4 p-0 ${above["md"](deviceSize) ? "set-quiz-button-md" : "btn-sm set-quiz-button-sm"}`} onClick={() => dispatch(showQuizSettingModal(quiz))}>
396+
{siteSpecific("Set Test", "Set test")}
397+
</Button>
398+
</Col>
399+
<Col md={1} className="d-flex justify-content-end align-items-center d-none d-md-flex p-0">
400+
<Link className={`my-3 d-flex justify-content-end me-1`} to={{pathname: `/test/preview/${quiz.id}`}}>
401+
<span>Preview</span>
402+
</Link>
403+
</Col>
404+
<Col xs={3} className="d-flex align-items-center justify-content-end">
405+
<UncontrolledButtonDropdown className="d-flex d-md-none ">
406+
<DropdownToggle caret className="text-nowrap" size="sm" color="link">
407+
Actions
408+
</DropdownToggle>
409+
<DropdownMenu>
410+
<DropdownItem onClick={() => dispatch(showQuizSettingModal(quiz))} style={{zIndex: '1'}}>
411+
{siteSpecific("Set Test", "Set test")}
412412
</DropdownItem>
413-
</Link>
414-
</DropdownMenu>
415-
</UncontrolledButtonDropdown>
416-
</Col>
417-
</Row>
418-
</ListGroupItem>)}
419-
</ListGroup>
413+
<DropdownItem divider />
414+
<Link className="w-100" style={{textDecoration: 'none'}} to={{pathname: `/test/preview/${quiz.id}`}}>
415+
<DropdownItem>
416+
Preview
417+
</DropdownItem>
418+
</Link>
419+
</DropdownMenu>
420+
</UncontrolledButtonDropdown>
421+
</Col>
422+
</Row>
423+
</ListGroupItem>)}
424+
</ListGroup>)}
420425
</>}
421426
</ShowLoading>,
422427

0 commit comments

Comments
 (0)