Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hide Columns #624

Merged
merged 28 commits into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0144c47
Initial functionality for column hiding
KevinWu098 Jun 13, 2023
8c98b36
Added functionality to SectionTable (the header row)
KevinWu098 Jun 13, 2023
7dcf057
Minor styling fix to the colunSelector Select element
KevinWu098 Jun 13, 2023
a6dcbe6
Spring Cleaning for some code quality 😋, abstractedaway a few things,…
KevinWu098 Jun 13, 2023
7897fae
Removed redundant code
KevinWu098 Jun 30, 2023
eb6eb87
Removed unused code
KevinWu098 Jun 30, 2023
7ceccdb
Removed redundant code a la SectionTable
KevinWu098 Jun 30, 2023
bd3b142
Changed object array naming for parity with SectionTable & CoursePane…
KevinWu098 Jun 30, 2023
f1ddcbd
Merge branch 'main' into columnFilter
KevinWu098 Jun 30, 2023
449fc61
Cleaned up RightPaneStore as part of resolving merge conflicts
KevinWu098 Jun 30, 2023
260a8b2
Extracted ColumnFilter component
KevinWu098 Jun 30, 2023
6e274de
Column filter refactor (#653)
ap0nia Aug 15, 2023
ee805fa
refactor: provide context for empty select value
ap0nia Aug 15, 2023
3e9d947
refactor: remove extraneous react imports
ap0nia Aug 15, 2023
1f2b0b6
refactor: specify type only import
ap0nia Aug 15, 2023
ade680e
refactor: clean up
ap0nia Aug 15, 2023
f28076d
Merge branch 'main' into columnFilter
KevinWu098 Aug 19, 2023
bd4b014
Fix bad merge
KevinWu098 Aug 20, 2023
af5c96a
fix: un-idiomatic react code
ap0nia Aug 20, 2023
00c6e96
style: cleaned up newline spacing
ap0nia Aug 20, 2023
5a5509c
style: === 0
ap0nia Aug 20, 2023
0da3ee8
fix: important typo
ap0nia Aug 20, 2023
61f6f76
refactor: removed extraneous dependencies
ap0nia Aug 20, 2023
63bd530
fix: wrong boolean conditional
ap0nia Aug 20, 2023
d795600
refactor: extracted day parser
ap0nia Aug 21, 2023
ad3f0f1
Merge branch 'main' into columnFilter
ap0nia Aug 21, 2023
7dcb699
Correct merge by renaming highlightAdded to allowHighlight
KevinWu098 Aug 21, 2023
24966fb
Remove colorAndDelete prop
KevinWu098 Aug 21, 2023
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
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { IconButton, Theme, Tooltip } from '@material-ui/core';
import { IconButton, Select, Theme, Tooltip } from '@material-ui/core';
import { withStyles } from '@material-ui/core/styles';
import { ClassNameMap, Styles } from '@material-ui/core/styles/withStyles';
import { ArrowBack, Refresh } from '@material-ui/icons';
import { PureComponent } from 'react';
import { ArrowBack, MoreVert, Refresh } from '@material-ui/icons';
import { Checkbox, FormControl, ListItemText, MenuItem } from '@material-ui/core';
import { ChangeEvent, PureComponent } from 'react';
import RightPaneStore from '../RightPaneStore';
import { isDarkMode } from '$lib/helpers';

const styles: Styles<Theme, object> = {
buttonRow: {
Expand All @@ -24,14 +27,52 @@ const styles: Styles<Theme, object> = {
},
};

const columnList: { value: string; label: string }[] = [
{ value: 'sectionCode', label: 'Code' },
{ value: 'sectionDetails', label: 'Type' },
{ value: 'instructors', label: 'Instructors' },
{ value: 'dayAndTime', label: 'Times' },
{ value: 'location', label: 'Places' },
{ value: 'sectionEnrollment', label: 'Enrollment' },
{ value: 'restrictions', label: 'Restrictions' },
{ value: 'status', label: 'Status' },
];

interface CoursePaneButtonRowProps {
classes: ClassNameMap;
showSearch: boolean;
columnSelector: boolean;
KevinWu098 marked this conversation as resolved.
Show resolved Hide resolved
onDismissSearchResults: () => void;
onRefreshSearch: () => void;
}

class CoursePaneButtonRow extends PureComponent<CoursePaneButtonRowProps> {
state = {
columnSelector: false,
activeColumns: [
KevinWu098 marked this conversation as resolved.
Show resolved Hide resolved
'sectionCode',
'sectionDetails',
'instructors',
'dayAndTime',
'location',
'sectionEnrollment',
'restrictions',
'status',
],
};

handleClick = () => {
this.setState((prevState: CoursePaneButtonRowProps) => ({
columnSelector: !prevState.columnSelector,
}));
};

handleChange = (event: ChangeEvent<{ restrictions?: string | undefined; value: unknown }>) => {
this.setState({ activeColumns: event.target.value }, () => {
RightPaneStore.setActiveColumns(this.state.activeColumns);
});
};

render() {
const { classes } = this.props;

Expand All @@ -48,6 +89,36 @@ class CoursePaneButtonRow extends PureComponent<CoursePaneButtonRowProps> {
<Refresh />
</IconButton>
</Tooltip>

<Tooltip title="Hide Columns">
<IconButton onClick={this.handleClick} className={classes.button}>
<MoreVert />
</IconButton>
</Tooltip>

{this.state.columnSelector && (
<FormControl className={classes.formControl}>
<Select
style={{ width: '0', zIndex: -1, color: isDarkMode() ? '#303030' : '#FAFAFA' }}
inputProps={{ IconComponent: () => null }} // some styling nonsense to Thanos snap away the little arrow icon: https://aguidehub.com/blog/2023-02-25-how-to-hide-arrow-from-mui-select-in-react-js/
open={this.state.columnSelector}
KevinWu098 marked this conversation as resolved.
Show resolved Hide resolved
multiple
onClose={this.handleClick}
value={this.state.activeColumns}
onChange={this.handleChange}
>
{columnList.map((column) => (
<MenuItem key={column.value} value={column.value} style={{ maxWidth: '200px' }}>
<Checkbox
checked={this.state.activeColumns.indexOf(column.value) >= 0}
color="default"
/>
<ListItemText primary={column.label} />
</MenuItem>
))}
</Select>
</FormControl>
KevinWu098 marked this conversation as resolved.
Show resolved Hide resolved
)}
</div>
);
}
Expand Down
16 changes: 16 additions & 0 deletions apps/antalmanac/src/components/RightPane/RightPaneStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ const defaultFormValues: Record<string, string> = {
room: '',
};

// If there's a best practice that doesn't use 'let', LMK!
const activeColumns = [
KevinWu098 marked this conversation as resolved.
Show resolved Hide resolved
'sectionCode',
'sectionDetails',
'instructors',
'dayAndTime',
'location',
'sectionEnrollment',
'restrictions',
'status',
];

export interface BuildingFocusInfo {
location: string; // E.g., ICS 174
courseName: string;
Expand Down Expand Up @@ -118,6 +130,10 @@ class RightPaneStore extends EventEmitter {
* I think the latter is reasonable.
*/
};

setActiveColumns = (columns: string[]) => {
this.emit('columnChange', columns);
};
}

const store = new RightPaneStore();
Expand Down
115 changes: 74 additions & 41 deletions apps/antalmanac/src/components/RightPane/SectionTable/SectionTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import { withStyles } from '@material-ui/core/styles';
import { Assessment, Help, RateReview } from '@material-ui/icons';
import ShowChartIcon from '@material-ui/icons/ShowChart';
// import AlmanacGraph from '../EnrollmentGraph/EnrollmentGraph'; uncomment when we get past enrollment data back and restore the files (https://github.com/icssc/AntAlmanac/tree/5e89e035e66f00608042871d43730ba785f756b0/src/components/RightPane/SectionTable/EnrollmentGraph)
import { useEffect, useState } from 'react';
import { MOBILE_BREAKPOINT } from '../../../globals';
import RightPaneStore from '../RightPaneStore';
import CourseInfoBar from './CourseInfoBar';
import CourseInfoButton from './CourseInfoButton';
import GradesPopup from './GradesPopup';
Expand Down Expand Up @@ -64,12 +66,52 @@ const styles = {
},
};

const TableHeadColumns: { value: string; label: string }[] = [
KevinWu098 marked this conversation as resolved.
Show resolved Hide resolved
{ value: 'sectionCode', label: 'Code' },
{ value: 'sectionDetails', label: 'Type' },
{ value: 'instructors', label: 'Instructors' },
{ value: 'dayAndTime', label: 'Times' },
{ value: 'location', label: 'Places' },
{ value: 'sectionEnrollment', label: 'Enrollment' },
{ value: 'restrictions', label: 'Rstr' },
{ value: 'status', label: 'Status' },
];

const SectionTable = (props: SectionTableProps) => {
const { classes, courseDetails, term, colorAndDelete, highlightAdded, scheduleNames, analyticsCategory } = props;
const courseId = courseDetails.deptCode.replaceAll(' ', '') + courseDetails.courseNumber;
const encodedDept = encodeURIComponent(courseDetails.deptCode);
const isMobileScreen = useMediaQuery(`(max-width: ${MOBILE_BREAKPOINT}`);

const [columns, setColumns] = useState([
KevinWu098 marked this conversation as resolved.
Show resolved Hide resolved
'sectionCode',
'sectionDetails',
'instructors',
'dayAndTime',
'location',
'sectionEnrollment',
'restrictions',
'status',
]);

const [activeTableHeadColumns, setActiveTableHeadColumns] = useState(
TableHeadColumns.filter((column) => columns.includes(column.value))
);

useEffect(() => {
RightPaneStore.on('columnChange', (columns) => {
setColumns(columns);
setActiveTableHeadColumns(TableHeadColumns.filter((column) => columns.includes(column.value)));
});

return () => {
RightPaneStore.removeListener('columnChange', (columns) => {
setColumns(columns);
setActiveTableHeadColumns(TableHeadColumns.filter((column) => columns.includes(column.value)));
});
};
KevinWu098 marked this conversation as resolved.
Show resolved Hide resolved
}, [columns]);

return (
<>
<div
Expand Down Expand Up @@ -125,47 +167,38 @@ const SectionTable = (props: SectionTableProps) => {
<TableHead>
<TableRow>
<TableCell classes={{ sizeSmall: classes?.cellPadding }} className={classes?.row} />
<TableCell classes={{ sizeSmall: classes?.cellPadding }} className={classes?.row}>
Code
</TableCell>
<TableCell classes={{ sizeSmall: classes?.cellPadding }} className={classes?.row}>
Type
</TableCell>
<TableCell classes={{ sizeSmall: classes?.cellPadding }} className={classes?.row}>
Instructors
</TableCell>
<TableCell classes={{ sizeSmall: classes?.cellPadding }} className={classes?.row}>
Times
</TableCell>
<TableCell classes={{ sizeSmall: classes?.cellPadding }} className={classes?.row}>
Places
</TableCell>
<TableCell classes={{ sizeSmall: classes?.cellPadding }} className={classes?.row}>
<div className={classes?.flex}>
<span className={classes?.iconMargin}>Enrollment</span>
{!isMobileScreen && (
<Tooltip
title={
<Typography>
Enrolled/Capacity
<br />
Waitlist
<br />
New-Only Reserved
</Typography>
}
>
<Help fontSize="small" />
</Tooltip>
)}
</div>
</TableCell>
<TableCell classes={{ sizeSmall: classes?.cellPadding }} className={classes?.row}>
Rstr
</TableCell>
<TableCell classes={{ sizeSmall: classes?.cellPadding }} className={classes?.row}>
Status
</TableCell>
{activeTableHeadColumns.map((column) => {
return (
<TableCell
classes={{ sizeSmall: classes?.cellPadding }}
className={classes?.row}
key={column.label}
>
{!(column.label === 'Enrollment') ? (
column.label
) : (
<div className={classes?.flex}>
<span className={classes?.iconMargin}>{column.label}</span>
{!isMobileScreen && (
<Tooltip
title={
<Typography>
Enrolled/Capacity
<br />
Waitlist
<br />
New-Only Reserved
</Typography>
}
>
<Help fontSize="small" />
</Tooltip>
)}
</div>
)}
</TableCell>
);
})}
</TableRow>
</TableHead>
<TableBody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,26 +351,65 @@ interface SectionTableBodyProps {
scheduleNames: string[];
}

const TableBodyColumns = [
{ name: 'scheduleAdd', Component: ScheduleAddCell },
{ name: 'colorAndDelete', Component: ColorAndDelete },
{ name: 'sectionCode', Component: CourseCodeCell },
{ name: 'sectionDetails', Component: SectionDetailsCell },
{ name: 'instructors', Component: InstructorsCell },
{ name: 'dayAndTime', Component: DayAndTimeCell },
{ name: 'location', Component: LocationsCell },
{ name: 'sectionEnrollment', Component: SectionEnrollmentCell },
{ name: 'restrictions', Component: RestrictionsCell },
{ name: 'status', Component: StatusCell },
];

//TODO: SectionNum name parity -> SectionNumber
const SectionTableBody = withStyles(styles)((props: SectionTableBodyProps) => {
const { classes, section, courseDetails, term, colorAndDelete, highlightAdded, scheduleNames } = props;
const [addedCourse, setAddedCourse] = useState(colorAndDelete);

const [columns, setColumns] = useState([
// 'scheduleAdd',
// 'colorAndDelete', // these aren't really columns, so I'm leaving them out (and it makes my life easier too)
'sectionCode',
'sectionDetails',
'instructors',
'dayAndTime',
'location',
'sectionEnrollment',
'restrictions',
'status',
]);

const [activeTableBodyColumns, setActiveTableBodyColumns] = useState(
TableBodyColumns.filter((column) => columns.includes(column.name))
);

useEffect(() => {
const toggleHighlight = () => {
const doAdd = AppStore.getAddedSectionCodes().has(`${section.sectionCode} ${term}`);
setAddedCourse(doAdd);
};

toggleHighlight();

AppStore.on('addedCoursesChange', toggleHighlight);
AppStore.on('currentScheduleIndexChange', toggleHighlight);
RightPaneStore.on('columnChange', (columns) => {
setColumns(columns);
setActiveTableBodyColumns(TableBodyColumns.filter((column) => columns.includes(column.name)));
});

return () => {
AppStore.removeListener('addedCoursesChange', toggleHighlight);
AppStore.removeListener('currentScheduleIndexChange', toggleHighlight);
RightPaneStore.removeListener('columnChange', (columns) => {
setColumns(columns);
setActiveTableBodyColumns(TableBodyColumns.filter((column) => columns.includes(column.name)));
});
};
}, [section.sectionCode, term]); //should only run once on first render since these shouldn't change.
}, [section.sectionCode, term, columns]); //should only run once on first render since these shouldn't change.

return (
<TableRow
Expand All @@ -387,32 +426,26 @@ const SectionTableBody = withStyles(styles)((props: SectionTableBodyProps) => {
) : (
<ColorAndDelete color={section.color} sectionCode={section.sectionCode} term={term} />
)}
<CourseCodeCell sectionCode={section.sectionCode} />
<SectionDetailsCell
sectionType={section.sectionType as SectionType}
sectionNum={section.sectionNum}
units={parseFloat(section.units)}
/>
<InstructorsCell instructors={section.instructors} />
<DayAndTimeCell meetings={section.meetings} />
<LocationsCell
meetings={section.meetings}
courseName={courseDetails.deptCode + ' ' + courseDetails.courseNumber}
/>
<SectionEnrollmentCell
numCurrentlyEnrolled={section.numCurrentlyEnrolled}
maxCapacity={parseInt(section.maxCapacity)}
numOnWaitlist={section.numOnWaitlist}
numNewOnlyReserved={section.numNewOnlyReserved}
/>
<RestrictionsCell restrictions={section.restrictions} />
<StatusCell
term={term}
status={section.status}
sectionCode={section.sectionCode}
courseTitle={courseDetails.courseTitle}
courseNumber={courseDetails.courseNumber}
/>

{activeTableBodyColumns.map((column) => {
const Component = column.Component;
return (
// All of this is a little bulky, so if the props can be added specifically to activeTableBodyColumns, LMK!
<Component
key={column.name}
section={section}
courseDetails={courseDetails}
term={term}
scheduleNames={scheduleNames}
{...section}
sectionType={section.sectionType as SectionType}
maxCapacity={parseInt(section.maxCapacity)}
units={parseFloat(section.units)}
courseName={courseDetails.deptCode + ' ' + courseDetails.courseNumber}
{...courseDetails}
/>
);
})}
</TableRow>
);
});
Expand Down