Skip to content

Commit 8418b49

Browse files
committed
Move filter panel into sidebar for Isaac
1 parent 93ff4ed commit 8418b49

File tree

7 files changed

+123
-58
lines changed

7 files changed

+123
-58
lines changed

src/app/components/elements/CollapsibleList.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Col, Row } from "reactstrap";
33
import { Spacer } from "./Spacer";
44
import { FilterCount } from "./svg/FilterCount";
55
import classNames from "classnames";
6+
import { isAda, isPhy } from "../../services";
67

78
export interface CollapsibleListProps {
89
title?: string;
@@ -37,7 +38,7 @@ export const CollapsibleList = (props: CollapsibleListProps) => {
3738

3839
return <Col className={props.className} data-targetHeight={(headRef.current?.offsetHeight ?? 0) + (expanded ? expandedHeight : 0)}>
3940
<div className="row collapsible-head" ref={headRef}>
40-
<button className={classNames("w-100 d-flex align-items-center p-3 bg-white text-start", {"ps-4": props.asSubList})} onClick={toggle}>
41+
<button className={classNames("w-100 d-flex align-items-center p-3 text-start", {"bg-white": isAda, "bg-transparent": isPhy, "ps-4": props.asSubList})} onClick={toggle}>
4142
{title && <span>{title}</span>}
4243
<Spacer/>
4344
{(props.numberSelected ?? 0) > 0

src/app/components/elements/layout/SidebarLayout.tsx

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { Link } from "react-router-dom";
1010
import { Tag } from "../../../../IsaacAppTypes";
1111
import { AffixButton } from "../AffixButton";
1212
import { getHumanContext } from "../../../services/pageContext";
13+
import { QuestionFinderFilterPanel, QuestionFinderFilterPanelProps } from "../panels/QuestionFinderFilterPanel";
1314

1415
export const SidebarLayout = (props: RowProps) => {
1516
const { className, ...rest } = props;
@@ -285,17 +286,48 @@ export const GenericConceptsSidebar = (props: SidebarProps) => {
285286
return <ContentSidebar {...props}/>;
286287
};
287288

289+
interface FilterDropdownProps extends React.HTMLAttributes<HTMLLabelElement> {
290+
tag: Tag;
291+
questionFilters: Tag[];
292+
setQuestionFilters: React.Dispatch<React.SetStateAction<Tag[]>>;
293+
tagCounts?: Record<string, number>;
294+
}
295+
296+
const FilterDropdownBase = (props: FilterCheckboxBaseProps) => {
297+
const { id, checked, onInputChange, filterTitle, count, ...rest } = props;
298+
return <Label {...rest} className="d-flex align-items-center filters-checkbox py-2 mb-1">
299+
<Input id={`problem-search-${id}`} type="checkbox" checked={checked ?? false} onChange={onInputChange} />
300+
<span className="ms-3">{filterTitle}</span>
301+
{isDefined(count) && <span className="badge rounded-pill ms-2">{count}</span>}
302+
</Label>;
303+
};
304+
305+
const FilterDropdown = (props : FilterDropdownProps) => {
306+
const {tag, questionFilters, setQuestionFilters, tagCounts, ...rest} = props;
307+
const [checked, setChecked] = useState(questionFilters.includes(tag));
308+
309+
useEffect(() => {
310+
setChecked(questionFilters.includes(tag));
311+
}, [questionFilters, tag]);
312+
313+
return <FilterDropdownBase {...rest} id={tag.id} checked={checked}
314+
onInputChange={(e: ChangeEvent<HTMLInputElement>) => setQuestionFilters(f => e.target.checked ? [...f, tag] : f.filter(c => c !== tag))}
315+
filterTitle={tag.title} count={tagCounts && isDefined(tagCounts[tag.id]) ? tagCounts[tag.id] : undefined}
316+
/>;
317+
};
318+
288319
interface QuestionFinderSidebarProps extends SidebarProps {
289320
searchText: string;
290321
setSearchText: React.Dispatch<React.SetStateAction<string>>;
291322
questionFilters: Tag[];
292323
setQuestionFilters: React.Dispatch<React.SetStateAction<Tag[]>>;
293-
applicableTags: Tag[];
324+
topLevelFilters: string[];
294325
tagCounts?: Record<string, number>;
326+
questionFinderFilterPanelProps: QuestionFinderFilterPanelProps
295327
}
296328

297329
export const QuestionFinderSidebar = (props: QuestionFinderSidebarProps) => {
298-
const { searchText, setSearchText, questionFilters, setQuestionFilters, applicableTags, tagCounts, ...rest } = props;
330+
const { searchText, setSearchText, questionFilters, setQuestionFilters, topLevelFilters, tagCounts, questionFinderFilterPanelProps, ...rest } = props;
299331

300332
const pageContext = useAppSelector(selectors.pageContext.context);
301333

@@ -309,14 +341,11 @@ export const QuestionFinderSidebar = (props: QuestionFinderSidebarProps) => {
309341
onChange={(e: ChangeEvent<HTMLInputElement>) => setSearchText(e.target.value)}
310342
/>
311343

312-
<div className="section-divider"/>
313-
314-
<div className="d-flex flex-column">
315-
<h5>Filter questions by</h5>
316-
<AllFiltersCheckbox conceptFilters={questionFilters} setConceptFilters={setQuestionFilters} tagCounts={tagCounts} />
317-
<div className="section-divider-small"/>
318-
{applicableTags.map(tag => <FilterCheckbox key={tag.id} tag={tag} conceptFilters={questionFilters} setConceptFilters={setQuestionFilters} tagCounts={tagCounts}/>)}
319-
</div>
344+
345+
<QuestionFinderFilterPanel {...questionFinderFilterPanelProps} />
346+
{/*<AllFiltersCheckbox conceptFilters={questionFilters} setConceptFilters={setQuestionFilters} tagCounts={tagCounts} /> */}
347+
{/*{topLevelFilters.map(filter => <FilterDropdown key={filter.id} tag={tag} conceptFilters={questionFilters} setConceptFilters={setQuestionFilters} tagCounts={tagCounts}/>)}*/}
348+
320349

321350
<div className="section-divider"/>
322351

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 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 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>,
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 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 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>,
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 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 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>,
9292
<img src="/assets/common/icons/not-started.svg" alt={questionIconLabel}/>
9393
);
9494
break;

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

Lines changed: 44 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ const listTitles: { [field in keyof TopLevelListsState]: string } = {
117117
questionStatus: siteSpecific("Status", "Question status")
118118
};
119119

120-
interface QuestionFinderFilterPanelProps {
120+
export interface QuestionFinderFilterPanelProps {
121121
searchDifficulties: Difficulty[]; setSearchDifficulties: Dispatch<SetStateAction<Difficulty[]>>;
122122
searchTopics: string[], setSearchTopics: Dispatch<SetStateAction<string[]>>;
123123
searchStages: STAGE[], setSearchStages: Dispatch<SetStateAction<STAGE[]>>;
@@ -165,36 +165,40 @@ export function QuestionFinderFilterPanel(props: QuestionFinderFilterPanelProps)
165165
}
166166
};
167167

168-
return <Card>
168+
return <div className={classNames({"card": isAda})}>
169169
<CardHeader className="finder-header pl-3" onClick={(e) => {
170170
// the filters panel can only be collapsed when it is not a sidebar
171171
// (changing screen size after collapsing does not re-expand it but the options become visible)
172172
if (below["md"](deviceSize)) handleFilterPanelExpansion(e);
173173
}}>
174-
<div>
175-
<img
176-
src="/assets/common/icons/filter-icon.svg"
177-
alt="Filter"
178-
style={{width: 18}}
179-
className="ms-1 me-2"
180-
/>
181-
<b>Filter by</b>
182-
</div>
183-
<Spacer/>
184-
{validFiltersSelected && <div className="pe-1 pe-lg-0">
185-
<button
186-
className={"text-black pe-lg-0 py-0 me-2 me-lg-0 bg-white bg-opacity-10 btn-link"}
187-
onClick={(e) => {
188-
e.stopPropagation();
189-
clearFilters();
190-
}}
191-
>
192-
Clear all
193-
</button>
194-
</div>}
195-
{below["md"](deviceSize) && <div>
174+
{siteSpecific(
175+
<h6>Filter questions by</h6>,
176+
<>
177+
<div>
178+
<img
179+
src="/assets/common/icons/filter-icon.svg"
180+
alt="Filter"
181+
style={{width: 18}}
182+
className="ms-1 me-2"
183+
/>
184+
<b>Filter by</b>
185+
</div>
186+
<Spacer/>
187+
{validFiltersSelected && <div className="pe-1 pe-lg-0">
188+
<button
189+
className={classNames("text-black pe-lg-0 py-0 me-2 me-lg-0 bg-opacity-10 btn-link", {"bg-white": isAda})}
190+
onClick={(e) => {
191+
e.stopPropagation();
192+
clearFilters();
193+
}}
194+
>
195+
Clear all
196+
</button>
197+
</div>}
198+
</>)}
199+
{below["md"](deviceSize) && isAda && <div>
196200
<button
197-
className="bg-white bg-opacity-10 p-0"
201+
className="bg-opacity-10 p-0 bg-white"
198202
onClick={handleFilterPanelExpansion}
199203
>
200204
<img
@@ -207,14 +211,14 @@ export function QuestionFinderFilterPanel(props: QuestionFinderFilterPanelProps)
207211
</button>
208212
</div>}
209213
</CardHeader>
210-
<CardBody className={classNames("p-0 m-0", {"d-none": below["md"](deviceSize) && !filtersVisible})}>
214+
<CardBody className={classNames("p-0 m-0", {"d-none": isAda && below["md"](deviceSize) && !filtersVisible})}>
211215
<CollapsibleList
212216
title={listTitles.stage} expanded={listState.stage.state}
213217
toggle={() => listStateDispatch({type: "toggle", id: "stage", focus: below["md"](deviceSize)})}
214218
numberSelected={(isAda && searchStages.includes(STAGE.ALL)) ? searchStages.length - 1 : searchStages.length}
215219
>
216220
{getFilteredStageOptions().map((stage, index) => (
217-
<div className="w-100 ps-3 py-1 bg-white" key={index}>
221+
<div className={classNames("w-100 ps-3 py-1", {"bg-white": isAda})} key={index}>
218222
<StyledCheckbox
219223
color="primary"
220224
checked={searchStages.includes(stage.value)}
@@ -230,7 +234,7 @@ export function QuestionFinderFilterPanel(props: QuestionFinderFilterPanelProps)
230234
numberSelected={searchExamBoards.length}
231235
>
232236
{getFilteredExamBoardOptions({byStages: searchStages}).map((board, index) => (
233-
<div className="w-100 ps-3 py-1 bg-white" key={index}>
237+
<div className={classNames("w-100 ps-3 py-1", {"bg-white": isAda})} key={index}>
234238
<StyledCheckbox
235239
color="primary"
236240
checked={searchExamBoards.includes(board.value)}
@@ -263,7 +267,7 @@ export function QuestionFinderFilterPanel(props: QuestionFinderFilterPanelProps)
263267
toggle={() => listStateDispatch({type: "toggle", id: `topics ${sublistDelimiter} ${tag.label}`, focus: true})}
264268
>
265269
{tag.options.map((topic, index) => (
266-
<div className="w-100 ps-3 py-1 bg-white" key={index}>
270+
<div className={classNames("w-100 ps-3 py-1", {"bg-white": isAda})} key={index}>
267271
<StyledCheckbox
268272
color="primary"
269273
checked={searchTopics.includes(topic.value)}
@@ -288,15 +292,15 @@ export function QuestionFinderFilterPanel(props: QuestionFinderFilterPanelProps)
288292
numberSelected={searchDifficulties.length}
289293
>
290294
<button
291-
className="p-0 bg-white h-min-content btn-link d-flex ps-3 py-2"
295+
className={classNames("p-0 h-min-content btn-link d-flex ps-3 py-2", {"bg-white": isAda, "bg-transparent": isPhy})}
292296
onClick={(e) => {
293297
e.preventDefault();
294298
dispatch(openActiveModal(questionFinderDifficultyModal()));
295299
}}>
296300
<b className="small text-start">{siteSpecific("Learn more about difficulty levels", "What do the difficulty levels mean?")}</b>
297301
</button>
298302
{SIMPLE_DIFFICULTY_ITEM_OPTIONS.map((difficulty, index) => (
299-
<div className="w-100 ps-3 py-1 bg-white" key={index}>
303+
<div className={classNames("w-100 ps-3 py-1", {"bg-white": isAda})} key={index}>
300304
<StyledCheckbox
301305
color="primary"
302306
checked={searchDifficulties.includes(difficulty.value)}
@@ -307,7 +311,7 @@ export function QuestionFinderFilterPanel(props: QuestionFinderFilterPanelProps)
307311
)}
308312
label={<div className="d-flex align-items-center">
309313
<span className="me-2">{difficulty.label}</span>
310-
<DifficultyIcons difficulty={difficulty.value} blank className="mt-n2"/>
314+
<DifficultyIcons difficulty={difficulty.value} blank className={classNames({"mt-n2": isAda, "mt-2": isPhy})}/>
311315
</div>}
312316
/>
313317
</div>
@@ -319,7 +323,7 @@ export function QuestionFinderFilterPanel(props: QuestionFinderFilterPanelProps)
319323
numberSelected={excludeBooks ? 1 : searchBooks.length}
320324
>
321325
<>
322-
<div className="w-100 ps-3 py-1 bg-white">
326+
<div className={classNames("w-100 ps-3 py-1", {"bg-white": isAda})}>
323327
<StyledCheckbox
324328
color="primary"
325329
checked={excludeBooks}
@@ -328,7 +332,7 @@ export function QuestionFinderFilterPanel(props: QuestionFinderFilterPanelProps)
328332
/>
329333
</div>
330334
{bookOptions.map((book, index) => (
331-
<div className="w-100 ps-3 py-1 bg-white" key={index}>
335+
<div className={classNames("w-100 ps-3 py-1", {"bg-white": isAda})} key={index}>
332336
<StyledCheckbox
333337
color="primary" disabled={excludeBooks}
334338
checked={searchBooks.includes(book.value) && !excludeBooks}
@@ -348,7 +352,7 @@ export function QuestionFinderFilterPanel(props: QuestionFinderFilterPanelProps)
348352
toggle={() => listStateDispatch({type: "toggle", id: "questionStatus", focus: below["md"](deviceSize)})}
349353
numberSelected={Object.values(searchStatuses).reduce((acc, item) => acc + item, 0)}
350354
>
351-
<div className="w-100 ps-3 py-1 bg-white d-flex align-items-center">
355+
<div className={classNames("w-100 ps-3 py-1 d-flex align-items-center", {"bg-white": isAda})}>
352356
<StyledCheckbox
353357
color="primary"
354358
checked={searchStatuses.notAttempted}
@@ -371,7 +375,7 @@ export function QuestionFinderFilterPanel(props: QuestionFinderFilterPanelProps)
371375
</div>}
372376
/>
373377
</div>
374-
<div className="w-100 ps-3 py-1 bg-white d-flex align-items-center">
378+
<div className={classNames("w-100 ps-3 py-1 d-flex align-items-center", {"bg-white": isAda})}>
375379
<StyledCheckbox
376380
color="primary"
377381
checked={searchStatuses.complete}
@@ -394,7 +398,7 @@ export function QuestionFinderFilterPanel(props: QuestionFinderFilterPanelProps)
394398
</div>}
395399
/>
396400
</div>
397-
<div className="w-100 ps-3 py-1 bg-white d-flex align-items-center">
401+
<div className={classNames("w-100 ps-3 py-1 d-flex align-items-center", {"bg-white": isAda})}>
398402
<StyledCheckbox
399403
color="primary"
400404
checked={searchStatuses.tryAgain}
@@ -442,14 +446,14 @@ export function QuestionFinderFilterPanel(props: QuestionFinderFilterPanelProps)
442446
</span>}
443447
/>
444448
</div>}*/}
445-
<Col className="text-center py-3 filter-btn bg-white border-radius-2">
449+
{isAda && <Col className="text-center py-3 filter-btn bg-white border-radius-2">
446450
<Button onClick={() => {
447451
applyFilters();
448452
setSearchDisabled(true);
449453
}} disabled={searchDisabled}>
450454
Apply filters
451455
</Button>
452-
</Col>
456+
</Col>}
453457
</CardBody>
454-
</Card>;
458+
</div>;
455459
}

0 commit comments

Comments
 (0)