chore: lint-format-docs-tests-bugfixes (#105)

* docs: add jsdoc

* feat: change enums to as const objects

* chore(test): add themeColors.test.ts

* fix: fix tests and bugs with strings.ts util

* fix: path alias imports and tsconfig file bug

* fix: remove --max-warnings 0
This commit is contained in:
doprz
2024-02-22 22:42:58 -06:00
parent 8ab60c9f01
commit 8a6e9070e0
134 changed files with 986 additions and 623 deletions

View File

@@ -1,6 +1,4 @@
import { ScrapedRow } from '@shared/types/Course';
import React, { useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import type { ScrapedRow } from '@shared/types/Course';
import useInfiniteScroll from '@views/hooks/useInfiniteScroll';
import { CourseCatalogScraper } from '@views/lib/CourseCatalogScraper';
import { SiteSupport } from '@views/lib/getSiteSupport';
@@ -9,6 +7,9 @@ import {
loadNextCourseCatalogPage,
removePaginationButtons,
} from '@views/lib/loadNextCourseCatalogPage';
import React, { useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import styles from './AutoLoad.module.scss';
type Props = {

View File

@@ -1,7 +1,8 @@
import Popup from '@views/components/common/Popup/Popup';
import React from 'react';
import { Course } from 'src/shared/types/Course';
import { UserSchedule } from 'src/shared/types/UserSchedule';
import type { Course } from 'src/shared/types/Course';
import type { UserSchedule } from 'src/shared/types/UserSchedule';
import Description from './Description';
import GradeDistribution from './GradeDistribution';
import HeadingAndActions from './HeadingAndActions';
@@ -12,6 +13,16 @@ interface CourseCatalogInjectedPopupProps {
onClose: () => void;
}
/**
* CourseCatalogInjectedPopup component displays a popup with course details.
*
* @component
* @param {CourseCatalogInjectedPopupProps} props - The component props.
* @param {Course} props.course - The course object containing course details.
* @param {Schedule} props.activeSchedule - The active schedule object.
* @param {Function} props.onClose - The function to close the popup.
* @returns {JSX.Element} The CourseCatalogInjectedPopup component.
*/
const CourseCatalogInjectedPopup: React.FC<CourseCatalogInjectedPopupProps> = ({ course, activeSchedule, onClose }) => (
<Popup overlay className='max-w-[780px] px-6' onClose={onClose}>
<div className='flex flex-col'>

View File

@@ -1,11 +1,19 @@
import Text from '@views/components/common/Text/Text';
import clsx from 'clsx';
import React from 'react';
import Text from '../../common/Text/Text';
interface DescriptionProps {
lines: string[];
}
/**
* Renders the description component.
*
* @component
* @param {DescriptionProps} props - The component props.
* @param {string[]} props.lines - The lines of text to render.
* @returns {JSX.Element} The rendered description component.
*/
const Description: React.FC<DescriptionProps> = ({ lines }: DescriptionProps) => {
const keywords = ['prerequisite', 'restricted'];
return (

View File

@@ -1,29 +1,31 @@
import type { Course } from '@shared/types/Course';
import type { Distribution, LetterGrade } from '@shared/types/Distribution';
import { colors } from '@shared/util/themeColors';
import Spinner from '@views/components/common/Spinner/Spinner';
import Text from '@views/components/common/Text/Text';
import Highcharts from 'highcharts';
import HighchartsReact from 'highcharts-react-official';
import React from 'react';
import { Course } from 'src/shared/types/Course';
import { Distribution, LetterGrade } from 'src/shared/types/Distribution';
import { colors } from 'src/shared/util/themeColors';
import {
NoDataError,
queryAggregateDistribution,
querySemesterDistribution,
} from 'src/views/lib/database/queryDistribution';
} from '@views/lib/database/queryDistribution';
import Highcharts from 'highcharts';
import HighchartsReact from 'highcharts-react-official';
import React from 'react';
interface GradeDistributionProps {
course: Course;
}
enum DataStatus {
LOADING = 'LOADING',
FOUND = 'FOUND',
NOT_FOUND = 'NOT_FOUND',
ERROR = 'ERROR',
}
const DataStatus = {
LOADING: 'LOADING',
FOUND: 'FOUND',
NOT_FOUND: 'NOT_FOUND',
ERROR: 'ERROR',
} as const;
const GRADE_COLORS: Record<LetterGrade, string> = {
type DataStatusType = (typeof DataStatus)[keyof typeof DataStatus];
const GRADE_COLORS = {
A: colors.gradeDistribution.a,
'A-': colors.gradeDistribution.aminus,
'B+': colors.gradeDistribution.bplus,
@@ -36,12 +38,20 @@ const GRADE_COLORS: Record<LetterGrade, string> = {
D: colors.gradeDistribution.d,
'D-': colors.gradeDistribution.dminus,
F: colors.gradeDistribution.f,
};
} as const satisfies Record<LetterGrade, string>;
/**
* Renders the grade distribution chart for a specific course.
*
* @component
* @param {GradeDistributionProps} props - The component props.
* @param {Course} props.course - The course for which to display the grade distribution.
* @returns {JSX.Element} The grade distribution chart component.
*/
const GradeDistribution: React.FC<GradeDistributionProps> = ({ course }) => {
const [semester, setSemester] = React.useState('Aggregate');
const [distributions, setDistributions] = React.useState<Record<string, Distribution>>({});
const [status, setStatus] = React.useState(DataStatus.LOADING);
const [status, setStatus] = React.useState<DataStatusType>(DataStatus.LOADING);
const ref = React.useRef<HighchartsReact.RefObject>(null);
const chartData = React.useMemo(() => {

View File

@@ -1,20 +1,21 @@
import React, { useState } from 'react';
import { Button } from '@views/components/common/Button/Button';
import { Chip, flagMap } from '@views/components/common/Chip/Chip';
import Divider from '@views/components/common/Divider/Divider';
import Text from '@views/components/common/Text/Text';
import React, { useState } from 'react';
import addCourse from 'src/pages/background/lib/addCourse';
import removeCourse from 'src/pages/background/lib/removeCourse';
import type { Course } from 'src/shared/types/Course';
import type { UserSchedule } from 'src/shared/types/UserSchedule';
import { openTabFromContentScript } from 'src/views/lib/openNewTabFromContentScript';
import { Course } from 'src/shared/types/Course';
import { UserSchedule } from 'src/shared/types/UserSchedule';
import Add from '~icons/material-symbols/add';
import Remove from '~icons/material-symbols/remove';
import CalendarMonth from '~icons/material-symbols/calendar-month';
import CloseIcon from '~icons/material-symbols/close';
import Copy from '~icons/material-symbols/content-copy';
import Description from '~icons/material-symbols/description';
import Mood from '~icons/material-symbols/mood';
import Remove from '~icons/material-symbols/remove';
import Reviews from '~icons/material-symbols/reviews';
interface HeadingAndActionProps {
@@ -26,7 +27,11 @@ interface HeadingAndActionProps {
onClose: () => void;
}
export const handleOpenCalendar = async () => { // Not sure if it's bad practice to export this
/**
* Opens the calendar in a new tab.
* @returns {Promise<void>} A promise that resolves when the tab is opened.
*/
export const handleOpenCalendar = async () => {
const url = chrome.runtime.getURL('calendar.html');
await openTabFromContentScript(url);
};
@@ -41,7 +46,7 @@ const HeadingAndActions: React.FC<HeadingAndActionProps> = ({ course, onClose, a
const { courseName, department, number: courseNumber, uniqueId, instructors, flags, schedule } = course;
const [courseAdded, setCourseAdded] = useState<boolean>(
activeSchedule.courses.some(course => course.uniqueId === uniqueId)
);
);
const instructorString = instructors
.map(instructor => {
@@ -74,8 +79,7 @@ const HeadingAndActions: React.FC<HeadingAndActionProps> = ({ course, onClose, a
const handleAddOrRemoveCourse = async () => {
if (!courseAdded) {
await addCourse(activeSchedule.name, course);
}
else {
} else {
await removeCourse(activeSchedule.name, course);
}
setCourseAdded(!courseAdded);
@@ -137,7 +141,12 @@ const HeadingAndActions: React.FC<HeadingAndActionProps> = ({ course, onClose, a
<Button variant='outline' color='ut-orange' icon={Description} onClick={handleOpenPastSyllabi}>
Past Syllabi
</Button>
<Button variant='filled' color={!courseAdded ? 'ut-green' : 'ut-red'} icon={!courseAdded ? Add : Remove} onClick={handleAddOrRemoveCourse}>
<Button
variant='filled'
color={!courseAdded ? 'ut-green' : 'ut-red'}
icon={!courseAdded ? Add : Remove}
onClick={handleAddOrRemoveCourse}
>
{!courseAdded ? 'Add Course' : 'Remove Course'}
</Button>
</div>

View File

@@ -1,29 +1,36 @@
import { Course } from '@shared/types/Course';
import clsx from 'clsx';
import React, { useEffect, useState } from 'react';
import type { Course } from '@shared/types/Course';
import Card from '@views/components/common/Card/Card';
import Spinner from '@views/components/common/Spinner/Spinner';
import Text from '@views/components/common/Text/Text';
import { CourseCatalogScraper } from '@views/lib/CourseCatalogScraper';
import { SiteSupport } from '@views/lib/getSiteSupport';
import Card from '../../../common/Card/Card';
import clsx from 'clsx';
import React, { useEffect, useState } from 'react';
import styles from './CourseDescription.module.scss';
type Props = {
course: Course;
};
enum LoadStatus {
LOADING = 'LOADING',
DONE = 'DONE',
ERROR = 'ERROR',
}
const LoadStatus = {
LOADING: 'LOADING',
DONE: 'DONE',
ERROR: 'ERROR',
} as const;
type LoadStatusType = (typeof LoadStatus)[keyof typeof LoadStatus];
/**
* Renders the course description component.
*
* @param {Props} props - The component props.
* @param {Course} props.course - The course object.
* @returns {JSX.Element} The rendered course description component.
*/
export default function CourseDescription({ course }: Props) {
const [description, setDescription] = useState<string[]>([]);
const [status, setStatus] = useState<LoadStatus>(LoadStatus.LOADING);
const [status, setStatus] = useState<LoadStatusType>(LoadStatus.LOADING);
useEffect(() => {
fetchDescription(course)
@@ -71,11 +78,7 @@ function DescriptionLine({ line }: LineProps) {
[styles.restriction]: lowerCaseLine.includes('restrict'),
});
return (
<Text className={className} /* size='medium' */>
{line}
</Text>
);
return <Text className={className} /* size='medium' */>{line}</Text>;
}
async function fetchDescription(course: Course): Promise<string[]> {

View File

@@ -1,11 +1,12 @@
import { background } from '@shared/messages';
import { Course } from '@shared/types/Course';
import { UserSchedule } from '@shared/types/UserSchedule';
import type { Course } from '@shared/types/Course';
import type { UserSchedule } from '@shared/types/UserSchedule';
import { Button } from '@views/components/common/Button/Button';
import Card from '@views/components/common/Card/Card';
import Icon from '@views/components/common/Icon/Icon';
import Text from '@views/components/common/Text/Text';
import React from 'react';
import styles from './CourseButtons.module.scss';
type Props = {

View File

@@ -1,15 +1,17 @@
import { Course } from '@shared/types/Course';
import { UserSchedule } from '@shared/types/UserSchedule';
import React from 'react';
import type { Course } from '@shared/types/Course';
import type { UserSchedule } from '@shared/types/UserSchedule';
import { Button } from '@views/components/common/Button/Button';
import Card from '@views/components/common/Card/Card';
import Icon from '@views/components/common/Icon/Icon';
import Link from '@views/components/common/Link/Link';
import Text from '@views/components/common/Text/Text';
import { Button } from 'src/views/components/common/Button/Button';
import React from 'react';
import CloseIcon from '~icons/material-symbols/close';
import CopyIcon from '~icons/material-symbols/content-copy';
import CourseButtons from './CourseButtons/CourseButtons';
import styles from './CourseHeader.module.scss';
import CopyIcon from '~icons/material-symbols/content-copy';
import CloseIcon from '~icons/material-symbols/close';
type Props = {
course: Course;
@@ -40,7 +42,7 @@ export default function CourseHeader({ course, activeSchedule, onClose }: Props)
<Button icon={CopyIcon} variant='single' className='mr-1 px-2' color='ut-burntorange'>
{course.uniqueId}
</Button>
<button className='btn bg-transparent p-0'>
<button className='bg-transparent p-0 btn'>
<CloseIcon className='h-7 w-7' />
</button>
</div>

View File

@@ -1,6 +1,7 @@
import { Course } from '@shared/types/Course';
import { UserSchedule } from '@shared/types/UserSchedule';
import type { Course } from '@shared/types/Course';
import type { UserSchedule } from '@shared/types/UserSchedule';
import React from 'react';
import Popup from '../../common/Popup/Popup';
import CourseDescription from './CourseDescription/CourseDescription';
import CourseHeader from './CourseHeader/CourseHeader';

View File

@@ -1,9 +1,5 @@
/* eslint-disable no-nested-ternary */
import { Course, Semester } from '@shared/types/Course';
import { Distribution, LetterGrade } from '@shared/types/Distribution';
import Highcharts from 'highcharts';
import HighchartsReact from 'highcharts-react-official';
import React, { useEffect, useRef, useState } from 'react';
import type { Course, Semester } from '@shared/types/Course';
import type { Distribution, LetterGrade } from '@shared/types/Distribution';
import Card from '@views/components/common/Card/Card';
import Icon from '@views/components/common/Icon/Icon';
import Spinner from '@views/components/common/Spinner/Spinner';
@@ -14,20 +10,26 @@ import {
querySemesterDistribution,
} from '@views/lib/database/queryDistribution';
import colors from '@views/styles/colors.module.scss';
import Highcharts from 'highcharts';
import HighchartsReact from 'highcharts-react-official';
import React, { useEffect, useRef, useState } from 'react';
import styles from './GradeDistribution.module.scss';
enum DataStatus {
LOADING = 'LOADING',
FOUND = 'FOUND',
NOT_FOUND = 'NOT_FOUND',
ERROR = 'ERROR',
}
const DataStatus = {
LOADING: 'LOADING',
FOUND: 'FOUND',
NOT_FOUND: 'NOT_FOUND',
ERROR: 'ERROR',
} as const;
type DataStatusType = (typeof DataStatus)[keyof typeof DataStatus];
interface Props {
course: Course;
}
const GRADE_COLORS: Record<LetterGrade, string> = {
const GRADE_COLORS = {
A: colors.turtle_pond,
'A-': colors.turtle_pond,
'B+': colors.cactus,
@@ -40,7 +42,7 @@ const GRADE_COLORS: Record<LetterGrade, string> = {
D: colors.tangerine,
'D-': colors.tangerine,
F: colors.speedway_brick,
};
} as const satisfies Record<LetterGrade, string>;
/**
* A chart to fetch and display the grade distribution for a course
@@ -51,7 +53,7 @@ export default function GradeDistribution({ course }: Props) {
const [semesters, setSemesters] = useState<Semester[]>([]);
const [selectedSemester, setSelectedSemester] = useState<Semester | null>(null);
const [distribution, setDistribution] = useState<Distribution | null>(null);
const [status, setStatus] = useState<DataStatus>(DataStatus.LOADING);
const [status, setStatus] = useState<DataStatusType>(DataStatus.LOADING);
const [chartOptions, setChartOptions] = useState<Highcharts.Options>({
title: {
@@ -206,7 +208,7 @@ export default function GradeDistribution({ course }: Props) {
<Text color='speedway_brick' /* size='medium' weight='semi_bold' */>
There was an error fetching the grade distribution data
</Text>
<Icon color='speedway_brick' /* size='large' */ name='sentiment_dissatisfied' />
<Icon color='speedway_brick' /* size='large' */ name='sentiment_dissatisfied' />
</Card>
)}
{status === DataStatus.NOT_FOUND && (

View File

@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import Link from '../../common/Link/Link';
import Text from '../../common/Text/Text';
import styles from './RecruitmentBanner.module.scss';
@@ -50,6 +51,10 @@ export default function RecruitmentBanner() {
);
}
/**
* Determines if recruitment can be done from the current department.
* @returns {boolean} True if recruitment can be done from the current department, false otherwise.
*/
export function canRecruitFrom(): boolean {
const params = ['fos_fl', 'fos_cn'];
let department = '';

View File

@@ -1,4 +1,5 @@
import React, { PropsWithChildren, useEffect, useState } from 'react';
import type { PropsWithChildren } from 'react';
import React, { useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
/**

View File

@@ -1,12 +1,14 @@
import { Course, ScrapedRow } from '@shared/types/Course';
import { UserSchedule } from '@shared/types/UserSchedule';
import type { Course, ScrapedRow } from '@shared/types/Course';
import type { UserSchedule } from '@shared/types/UserSchedule';
import React, { useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
import { Button } from '../../common/Button/Button';
import styles from './TableRow.module.scss';
import ConflictsWithWarning from '../../common/ConflictsWithWarning/ConflictsWithWarning';
import AddIcon from '~icons/material-symbols/add-circle';
import { Button } from '../../common/Button/Button';
import ConflictsWithWarning from '../../common/ConflictsWithWarning/ConflictsWithWarning';
import styles from './TableRow.module.scss';
interface Props {
isSelected: boolean;
row: ScrapedRow;

View File

@@ -1,5 +1,6 @@
import { ScrapedRow } from '@shared/types/Course';
import type { ScrapedRow } from '@shared/types/Course';
import { useEffect } from 'react';
import styles from './TableSubheading.module.scss';
interface Props {