Skip to content

Commit cae148a

Browse files
committed
Automatically apply stage/subject filters per page
1 parent 8a923ca commit cae148a

File tree

3 files changed

+46
-21
lines changed

3 files changed

+46
-21
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,22 +73,22 @@ export const ContentSummaryListGroupItem = ({item, search, showBreadcrumb, noCar
7373
case CompletionState.IN_PROGRESS:
7474
questionIconLabel = "In progress question icon";
7575
questionIcon = siteSpecific(
76-
<svg style={{width: "60px", height: "60px"}} className={iconClasses} aria-label={questionIconLabel}><use href={`/assets/phy/icons/incomplete-hex.svg#icon`} xlinkHref={`/assets/phy/icons/incomplete-hex.svg#icon`}/></svg>,
76+
<svg className={iconClasses} aria-label={questionIconLabel}><use href={`/assets/phy/icons/incomplete-hex.svg#icon`} xlinkHref={`/assets/phy/icons/incomplete-hex.svg#icon`}/></svg>,
7777
<img src="/assets/common/icons/incorrect.svg" alt={questionIconLabel}/>
7878
);
7979
break;
8080
case CompletionState.ALL_CORRECT:
8181
questionIconLabel = "Complete question icon";
8282
questionIcon = siteSpecific(
83-
<svg style={{width: "60px", height: "60px"}} className={iconClasses} aria-label={questionIconLabel}><use href={`/assets/phy/icons/tick-rp-hex.svg#icon`} xlinkHref={`/assets/phy/icons/tick-rp-hex.svg#icon`}/></svg>,
83+
<svg className={iconClasses} aria-label={questionIconLabel}><use href={`/assets/phy/icons/tick-rp-hex.svg#icon`} xlinkHref={`/assets/phy/icons/tick-rp-hex.svg#icon`}/></svg>,
8484
<img src="/assets/common/icons/completed.svg" alt={questionIconLabel}/>
8585
);
8686
break;
8787
case CompletionState.NOT_ATTEMPTED:
8888
default:
8989
questionIconLabel = "Not attempted question icon";
9090
questionIcon = siteSpecific(
91-
<svg style={{width: "60px", height: "60px"}} className={iconClasses} aria-label={questionIconLabel}><use href={`/assets/phy/icons/question-hex.svg#icon`} xlinkHref={`/assets/phy/icons/question-hex.svg#icon`}/></svg>,
91+
<svg className={iconClasses} aria-label={questionIconLabel}><use href={`/assets/phy/icons/question-hex.svg#icon`} xlinkHref={`/assets/phy/icons/question-hex.svg#icon`}/></svg>,
9292
<img src="/assets/common/icons/not-started.svg" alt={questionIconLabel}/>
9393
);
9494
break;

src/app/components/elements/panels/QuestionFinderFilterPanel.tsx

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
below,
77
getFilteredExamBoardOptions,
88
getFilteredStageOptions,
9+
getUpdatedPageContext,
910
groupTagSelectionsByParent,
1011
isAda,
1112
isPhy,
@@ -17,7 +18,8 @@ import {
1718
TAG_ID,
1819
TAG_LEVEL,
1920
tags,
20-
useDeviceSize
21+
useDeviceSize,
22+
useUrlPageTheme
2123
} from "../../../services";
2224
import { Difficulty, ExamBoard } from "../../../../IsaacApiTypes";
2325
import { QuestionStatus } from "../../pages/QuestionFinder";
@@ -168,6 +170,7 @@ export function QuestionFinderFilterPanel(props: QuestionFinderFilterPanelProps)
168170
const [listState, listStateDispatch] = useReducer(listStateReducer, groupBaseTagOptions, initialiseListState);
169171
const deviceSize = useDeviceSize();
170172
const dispatch = useAppDispatch();
173+
const pageContext = useUrlPageTheme();
171174

172175
const [filtersVisible, setFiltersVisible] = useState<boolean>(above["lg"](deviceSize));
173176

@@ -223,13 +226,13 @@ export function QuestionFinderFilterPanel(props: QuestionFinderFilterPanelProps)
223226
</div>}
224227
</CardHeader>
225228
<CardBody className={classNames("p-0 m-0", {"d-none": isAda && below["md"](deviceSize) && !filtersVisible})}>
226-
<CollapsibleList
229+
{(isAda || !pageContext.stage) && <CollapsibleList
227230
title={listTitles.stage} expanded={listState.stage.state}
228231
toggle={() => listStateDispatch({type: "toggle", id: "stage", focus: below["md"](deviceSize)})}
229232
numberSelected={(isAda && searchStages.includes(STAGE.ALL)) ? searchStages.length - 1 : searchStages.length}
230233
>
231234
{getFilteredStageOptions().map((stage, index) => (
232-
<div className={classNames("w-100 ps-3 py-1", {"bg-white": isAda, "ms-2": isPhy})} key={index}>
235+
<div className={classNames("w-100 ps-3 py-1", {"bg-white": isAda, "ms-2": isPhy, "bg-grey": isPhy && searchStages.includes(stage.value)})} key={index}>
233236
<StyledCheckbox
234237
color="primary"
235238
checked={searchStages.includes(stage.value)}
@@ -238,7 +241,7 @@ export function QuestionFinderFilterPanel(props: QuestionFinderFilterPanelProps)
238241
/>
239242
</div>
240243
))}
241-
</CollapsibleList>
244+
</CollapsibleList>}
242245
{isAda && <CollapsibleList
243246
title={listTitles.examBoard} expanded={listState.examBoard.state}
244247
toggle={() => listStateDispatch({type: "toggle", id: "examBoard", focus: below["md"](deviceSize)})}
@@ -258,18 +261,16 @@ export function QuestionFinderFilterPanel(props: QuestionFinderFilterPanelProps)
258261
<CollapsibleList
259262
title={listTitles.topics} expanded={listState.topics.state}
260263
toggle={() => listStateDispatch({type: "toggle", id: "topics", focus: below["md"](deviceSize)})}
261-
numberSelected={siteSpecific(
262-
// Find the last non-zero tier in the tree
263-
// FIXME: Use `filter` and `at` when Safari supports it
264-
//
265-
// TODO CURRENTLY COUNTING NUMBER OF LAYERS
266-
getChoiceTreeLeaves(selections).length,
267-
searchTopics.length
268-
)}
264+
numberSelected={siteSpecific(getChoiceTreeLeaves(selections).length, searchTopics.length)}
269265
>
270266
{siteSpecific(
271267
<div>
272-
<HierarchyFilterHexagonal {...{tier: 0, index: TAG_LEVEL.subject, tiers, choices, selections: selections, questionFinderFilter: true, setTierSelection}} />
268+
<HierarchyFilterHexagonal {...{
269+
tier: pageContext.subject ? 1 : 0,
270+
index: pageContext.subject as TAG_ID ?? TAG_LEVEL.subject,
271+
tiers, choices, selections,
272+
questionFinderFilter: true, setTierSelection
273+
}}/>
273274
</div>,
274275
groupBaseTagOptions.map((tag, index) => (
275276
<CollapsibleList

src/app/components/pages/QuestionFinder.tsx

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ export const QuestionFinder = withRouter(({location}: RouteComponentProps) => {
129129

130130
const [searchTopics, setSearchTopics] = useState<string[]>(arrayFromPossibleCsv(params.topics));
131131
const [searchQuery, setSearchQuery] = useState<string>(params.query ? (params.query instanceof Array ? params.query[0] : params.query) : "");
132-
const [searchStages, setSearchStages] = useState<STAGE[]>(arrayFromPossibleCsv(params.stages) as STAGE[]);
132+
const [searchStages, setSearchStages] = useState<STAGE[]>(arrayFromPossibleCsv(pageContext.stage ? [pageContext.stage] : params.stages) as STAGE[]);
133133
const [searchDifficulties, setSearchDifficulties] = useState<Difficulty[]>(arrayFromPossibleCsv(params.difficulties) as Difficulty[]);
134134
const [searchExamBoards, setSearchExamBoards] = useState<ExamBoard[]>(arrayFromPossibleCsv(params.examBoards) as ExamBoard[]);
135135
const [searchStatuses, setSearchStatuses] = useState<QuestionStatus>(getInitialQuestionStatuses(params));
@@ -169,16 +169,21 @@ export const QuestionFinder = withRouter(({location}: RouteComponentProps) => {
169169

170170
const [selections, setSelections] = useState<ChoiceTree[]>(
171171
processTagHierarchy(
172-
arrayFromPossibleCsv(params.subjects),
172+
arrayFromPossibleCsv(pageContext.subject ? [pageContext.subject] : params.subjects),
173173
arrayFromPossibleCsv(params.fields),
174174
arrayFromPossibleCsv(params.topics)
175175
)
176176
);
177177

178-
const choices: ChoiceTree[] = [{"subject": tags.allSubjectTags.map(itemiseTag)}];
178+
const choices: ChoiceTree[] = [];
179+
if (!pageContext.subject) {
180+
choices.push({"subject": tags.allSubjectTags.map(itemiseTag)});
181+
} else {
182+
choices.push({});
183+
choices[0][pageContext.subject] = tags.getChildren(pageContext.subject as TAG_ID).map(itemiseTag);
184+
}
179185

180-
let tierIndex;
181-
for (tierIndex = 0; tierIndex < selections.length && tierIndex < 2; tierIndex++) {
186+
for (let tierIndex = 0; tierIndex < selections.length && tierIndex < 2; tierIndex++) {
182187
if (Object.keys(selections[tierIndex]).length > 0) {
183188
choices[tierIndex+1] = {};
184189
for (const v of Object.values(selections[tierIndex])) {
@@ -422,6 +427,23 @@ export const QuestionFinder = withRouter(({location}: RouteComponentProps) => {
422427
<IsaacSpinner />
423428
</div>;
424429

430+
const FilterTag = ({name}: {name: string}) => {
431+
return (
432+
<div className="tag">
433+
{name}
434+
</div>
435+
);
436+
};
437+
438+
const FilterSummary = () => {
439+
const selectionList: string[] = getChoiceTreeLeaves(selections).map(leaf => leaf.label);
440+
const statusList: string[] = Object.keys(searchStatuses).filter(status => searchStatuses[status as keyof QuestionStatus]);
441+
442+
const categories = [searchDifficulties, searchTopics, searchStages, searchExamBoards, statusList, searchBooks, selectionList];
443+
444+
return <div> {categories.map(c => c.map(c2 => <FilterTag key={c2} name={c2}/>))} </div>;
445+
};
446+
425447
return <Container id="finder-page" className={classNames("mb-5")} { ...(pageContext?.subject && { "data-bs-theme" : pageContext.subject })}>
426448
<TitleAndBreadcrumb
427449
currentPageTitle={siteSpecific("Question Finder", "Questions")}
@@ -476,6 +498,8 @@ export const QuestionFinder = withRouter(({location}: RouteComponentProps) => {
476498
</Col>
477499
</Row>}
478500

501+
<FilterSummary />
502+
479503
<Row className="mt-4 position-relative finder-panel">
480504
<Col lg={siteSpecific(4, 3)} md={12} xs={12} className={classNames("text-wrap my-2", {"d-none": isPhy})} data-testid="question-finder-filters">
481505
<QuestionFinderFilterPanel {...{

0 commit comments

Comments
 (0)