feat: add eslint-plugin-tsdoc (#430)

* feat: add eslint-plugin-tsdoc

* feat(doc): update current jsdoc to tsdoc specification

* chore: update deps

* feat: update warn to error for jsdoc and tsdoc

* chore(doc): lint
This commit is contained in:
doprz
2024-11-16 00:20:36 -06:00
committed by GitHub
parent c41467c617
commit e987fbbe8e
55 changed files with 1439 additions and 1317 deletions

View File

@@ -23,6 +23,7 @@ module.exports = {
'import', 'import',
'import-essentials', 'import-essentials',
'jsdoc', 'jsdoc',
'eslint-plugin-tsdoc',
'react-prefer-function-component', 'react-prefer-function-component',
'@typescript-eslint', '@typescript-eslint',
'simple-import-sort', 'simple-import-sort',
@@ -143,7 +144,7 @@ module.exports = {
'jsdoc/newline-after-description': 'off', 'jsdoc/newline-after-description': 'off',
'react/require-default-props': 'off', 'react/require-default-props': 'off',
'jsdoc/require-jsdoc': [ 'jsdoc/require-jsdoc': [
'warn', 'error',
{ {
enableFixer: false, enableFixer: false,
publicOnly: true, publicOnly: true,
@@ -171,6 +172,7 @@ module.exports = {
], ],
}, },
], ],
'tsdoc/syntax': 'error',
'@typescript-eslint/no-explicit-any': 'error', '@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-unused-vars': [ '@typescript-eslint/no-unused-vars': [
'warn', 'warn',

View File

@@ -30,7 +30,7 @@
"@headlessui/react": "^2.2.0", "@headlessui/react": "^2.2.0",
"@hello-pangea/dnd": "^17.0.0", "@hello-pangea/dnd": "^17.0.0",
"@octokit/rest": "^21.0.2", "@octokit/rest": "^21.0.2",
"@sentry/react": "^8.36.0", "@sentry/react": "^8.38.0",
"@unocss/vite": "^0.63.6", "@unocss/vite": "^0.63.6",
"@vitejs/plugin-react": "^4.3.3", "@vitejs/plugin-react": "^4.3.3",
"chrome-extension-toolkit": "^0.0.54", "chrome-extension-toolkit": "^0.0.54",
@@ -48,7 +48,7 @@
"react-markdown": "^9.0.1", "react-markdown": "^9.0.1",
"react-syntax-highlighter": "^15.6.1", "react-syntax-highlighter": "^15.6.1",
"remark-gfm": "^4.0.0", "remark-gfm": "^4.0.0",
"sass": "^1.80.6", "sass": "^1.81.0",
"simple-git": "^3.27.0", "simple-git": "^3.27.0",
"sql.js": "1.11.0" "sql.js": "1.11.0"
}, },
@@ -59,25 +59,25 @@
"@commitlint/types": "^19.5.0", "@commitlint/types": "^19.5.0",
"@crxjs/vite-plugin": "2.0.0-beta.21", "@crxjs/vite-plugin": "2.0.0-beta.21",
"@iconify-json/bi": "^1.2.1", "@iconify-json/bi": "^1.2.1",
"@iconify-json/iconoir": "^1.2.3", "@iconify-json/iconoir": "^1.2.4",
"@iconify-json/material-symbols": "^1.2.6", "@iconify-json/material-symbols": "^1.2.6",
"@iconify-json/ri": "^1.2.3", "@iconify-json/ri": "^1.2.3",
"@semantic-release/exec": "^6.0.3", "@semantic-release/exec": "^6.0.3",
"@sentry/types": "^8.36.0", "@sentry/types": "^8.38.0",
"@storybook/addon-designs": "^8.0.4", "@storybook/addon-designs": "^8.0.4",
"@storybook/addon-essentials": "^8.4.1", "@storybook/addon-essentials": "^8.4.4",
"@storybook/addon-links": "^8.4.1", "@storybook/addon-links": "^8.4.4",
"@storybook/blocks": "^8.4.1", "@storybook/blocks": "^8.4.4",
"@storybook/react": "^8.4.1", "@storybook/react": "^8.4.4",
"@storybook/react-vite": "^8.4.1", "@storybook/react-vite": "^8.4.4",
"@storybook/test": "^8.4.1", "@storybook/test": "^8.4.4",
"@svgr/core": "^8.1.0", "@svgr/core": "^8.1.0",
"@svgr/plugin-jsx": "^8.1.0", "@svgr/plugin-jsx": "^8.1.0",
"@types/chrome": "^0.0.273", "@types/chrome": "^0.0.273",
"@types/conventional-changelog": "^3.1.5", "@types/conventional-changelog": "^3.1.5",
"@types/gulp": "^4.0.17", "@types/gulp": "^4.0.17",
"@types/gulp-zip": "^4.0.4", "@types/gulp-zip": "^4.0.4",
"@types/node": "^22.8.7", "@types/node": "^22.9.0",
"@types/prompts": "^2.4.9", "@types/prompts": "^2.4.9",
"@types/react": "^18.3.12", "@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1", "@types/react-dom": "^18.3.1",
@@ -95,10 +95,10 @@
"@unocss/transformer-directives": "^0.63.6", "@unocss/transformer-directives": "^0.63.6",
"@unocss/transformer-variant-group": "^0.63.6", "@unocss/transformer-variant-group": "^0.63.6",
"@vitejs/plugin-react-swc": "^3.7.1", "@vitejs/plugin-react-swc": "^3.7.1",
"@vitest/coverage-v8": "^2.1.4", "@vitest/coverage-v8": "^2.1.5",
"@vitest/ui": "^2.1.4", "@vitest/ui": "^2.1.5",
"chalk": "^5.3.0", "chalk": "^5.3.0",
"chromatic": "^11.16.5", "chromatic": "^11.18.1",
"cssnano": "^7.0.6", "cssnano": "^7.0.6",
"cssnano-preset-advanced": "^7.0.6", "cssnano-preset-advanced": "^7.0.6",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
@@ -111,7 +111,7 @@
"eslint-import-resolver-typescript": "^3.6.3", "eslint-import-resolver-typescript": "^3.6.3",
"eslint-plugin-import": "^2.31.0", "eslint-plugin-import": "^2.31.0",
"eslint-plugin-import-essentials": "^0.2.1", "eslint-plugin-import-essentials": "^0.2.1",
"eslint-plugin-jsdoc": "^50.4.3", "eslint-plugin-jsdoc": "^50.5.0",
"eslint-plugin-prettier": "^5.2.1", "eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react": "^7.37.2", "eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-hooks": "^4.6.2",
@@ -119,22 +119,23 @@
"eslint-plugin-react-refresh": "^0.4.14", "eslint-plugin-react-refresh": "^0.4.14",
"eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-simple-import-sort": "^12.1.1",
"eslint-plugin-storybook": "^0.9.0", "eslint-plugin-storybook": "^0.9.0",
"eslint-plugin-tsdoc": "^0.3.0",
"gulp": "^5.0.0", "gulp": "^5.0.0",
"gulp-execa": "^7.0.1", "gulp-execa": "^7.0.1",
"gulp-zip": "^6.0.0", "gulp-zip": "^6.0.0",
"path": "^0.12.7", "path": "^0.12.7",
"postcss": "^8.4.47", "postcss": "^8.4.49",
"prettier": "^3.3.3", "prettier": "^3.3.3",
"react-dev-utils": "^12.0.1", "react-dev-utils": "^12.0.1",
"semantic-release": "^24.2.0", "semantic-release": "^24.2.0",
"storybook": "^8.4.1", "storybook": "^8.4.4",
"typescript": "^5.6.3", "typescript": "^5.6.3",
"unocss": "^0.63.6", "unocss": "^0.63.6",
"unocss-preset-primitives": "0.0.2-beta.1", "unocss-preset-primitives": "0.0.2-beta.1",
"unplugin-icons": "^0.19.3", "unplugin-icons": "^0.19.3",
"vite": "^5.4.10", "vite": "^5.4.11",
"vite-plugin-inspect": "^0.8.7", "vite-plugin-inspect": "^0.8.7",
"vitest": "^2.1.4" "vitest": "^2.1.5"
}, },
"pnpm": { "pnpm": {
"patchedDependencies": { "patchedDependencies": {

2213
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,8 +8,8 @@ const manifest = chrome.runtime.getManifest();
/** /**
* Handles editing the storage for a specific area. * Handles editing the storage for a specific area.
* *
* @param {string} areaName - The name of the storage area. * @param areaName - The name of the storage area.
* @returns {Function} - A function that accepts changes and sets them in the storage. * @returns A function that accepts changes and sets them in the storage.
*/ */
const handleEditStorage = (areaName: 'local' | 'sync' | 'session') => (changes: Record<string, unknown>) => { const handleEditStorage = (areaName: 'local' | 'sync' | 'session') => (changes: Record<string, unknown>) => {
chrome.storage[areaName].set(changes); chrome.storage[areaName].set(changes);

View File

@@ -5,8 +5,9 @@ import type { Serialized } from 'chrome-extension-toolkit';
/** /**
* Creates a new schedule with the given name * Creates a new schedule with the given name
* @param scheduleName the name of the schedule to create *
* @returns undefined if successful, otherwise an error message * @param scheduleName - The name of the schedule to create
* @returns Undefined if successful, otherwise an error message
*/ */
export default async function createSchedule(scheduleName: string) { export default async function createSchedule(scheduleName: string) {
const schedules = await UserScheduleStore.get('schedules'); const schedules = await UserScheduleStore.get('schedules');

View File

@@ -5,8 +5,9 @@ import handleDuplicate from './handleDuplicate';
/** /**
* Creates a new schedule with the given name * Creates a new schedule with the given name
* @param scheduleName the name of the schedule to create *
* @returns undefined if successful, otherwise an error message * @param scheduleName - The name of the schedule to create
* @returns Undefined if successful, otherwise an error message
*/ */
export default async function duplicateSchedule(scheduleId: string): Promise<string | undefined> { export default async function duplicateSchedule(scheduleId: string): Promise<string | undefined> {
const schedules = await UserScheduleStore.get('schedules'); const schedules = await UserScheduleStore.get('schedules');

View File

@@ -3,9 +3,10 @@ import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
/** /**
* Duplicates a new schedule with the given name. * Duplicates a new schedule with the given name.
* Assumes that each schedule has a unique name. * Assumes that each schedule has a unique name.
* @param scheduleName the name of the schedule to handle duplication for *
* @param schedules the list of UserSchedules to find existing names * @param scheduleName - The name of the schedule to handle duplication for
* @returns the new name for the schedule, of the form `{baseName}({index})` * @param schedules - The list of UserSchedules to find existing names
* @returns The new name for the schedule, of the form `{baseName}({index})`
*/ */
export default async function handleDuplicate(scheduleName: string): Promise<string> { export default async function handleDuplicate(scheduleName: string): Promise<string> {
const schedules = await UserScheduleStore.get('schedules'); const schedules = await UserScheduleStore.get('schedules');

View File

@@ -2,7 +2,7 @@ interface CESMessage {
/** /**
* Opens the CES page for the specified instructor * Opens the CES page for the specified instructor
* *
* @param data first and last name of the instructor * @param data - First and last name of the instructor
*/ */
openCESPage: (data: { instructorFirstName: string; instructorLastName: string }) => chrome.tabs.Tab; openCESPage: (data: { instructorFirstName: string; instructorLastName: string }) => chrome.tabs.Tab;
} }

View File

@@ -4,17 +4,22 @@
export default interface TabManagementMessages { export default interface TabManagementMessages {
/** /**
* Opens a new tab with the given URL * Opens a new tab with the given URL
* @param data The URL to open *
* @param data - The URL to open
*/ */
openNewTab: (data: { url: string }) => chrome.tabs.Tab; openNewTab: (data: { url: string }) => chrome.tabs.Tab;
/** /**
* Gets the ID of the current tab (the tab that sent the message) * Gets the ID of the current tab (the tab that sent the message)
*
* @returns The ID of the current tab * @returns The ID of the current tab
*/ */
getTabId: () => number; getTabId: () => number;
/** /**
* Removes the tab with the given ID * Removes the tab with the given ID
* @param data The ID of the tab to remove *
* @param data - The ID of the tab to remove
* @returns The ID of the tab that was removed * @returns The ID of the tab that was removed
*/ */
removeTab: (data: { tabId: number }) => void; removeTab: (data: { tabId: number }) => void;

View File

@@ -6,48 +6,61 @@ import type { Course } from '@shared/types/Course';
export interface UserScheduleMessages { export interface UserScheduleMessages {
/** /**
* Add a course to a schedule * Add a course to a schedule
* @param data the schedule id and course to add *
* @param data - The schedule id and course to add
*/ */
addCourse: (data: { scheduleId: string; course: Course }) => void; addCourse: (data: { scheduleId: string; course: Course }) => void;
/** /**
* Adds a course by URL * Adds a course by URL
* @param data *
* @param data - The URL of the course to add
* @returns Response of the requested course URL * @returns Response of the requested course URL
*/ */
addCourseByURL: (data: { url: string; method: string; body?: string; response: 'json' | 'text' }) => string; addCourseByURL: (data: { url: string; method: string; body?: string; response: 'json' | 'text' }) => string;
/** /**
* Remove a course from a schedule * Remove a course from a schedule
* @param data the schedule id and course to remove *
* @param data - The schedule id and course to remove
*/ */
removeCourse: (data: { scheduleId: string; course: Course }) => void; removeCourse: (data: { scheduleId: string; course: Course }) => void;
/** /**
* Clears all courses from a schedule * Clears all courses from a schedule
* @param data the id of the schedule to clear *
* @param data - The id of the schedule to clear
*/ */
clearCourses: (data: { scheduleId: string }) => void; clearCourses: (data: { scheduleId: string }) => void;
/** /**
* Switches the active schedule to the one specified * Switches the active schedule to the one specified
* @param data the id of the schedule to switch to *
* @param data - The id of the schedule to switch to
*/ */
switchSchedule: (data: { scheduleId: string }) => void; switchSchedule: (data: { scheduleId: string }) => void;
/** /**
* Creates a new schedule with the specified name * Creates a new schedule with the specified name
* @param data the name of the schedule to create *
* @returns undefined if successful, otherwise an error message * @param data - The name of the schedule to create
* @returns Undefined if successful, otherwise an error message
*/ */
createSchedule: (data: { scheduleName: string }) => string | undefined; createSchedule: (data: { scheduleName: string }) => string | undefined;
/** /**
* Deletes a schedule with the specified name * Deletes a schedule with the specified name
* @param data the id of the schedule to delete *
* @returns undefined if successful, otherwise an error message * @param data - The id of the schedule to delete
* @returns Undefined if successful, otherwise an error message
*/ */
deleteSchedule: (data: { scheduleId: string }) => string | undefined; deleteSchedule: (data: { scheduleId: string }) => string | undefined;
/** /**
* Renames a schedule with the specified name * Renames a schedule with the specified name
* @param data the id of the schedule to rename and the new name *
* @returns undefined if successful, otherwise an error message * @param data - The id of the schedule to rename and the new name
* @returns Undefined if successful, otherwise an error message
*/ */
renameSchedule: (data: { scheduleId: string; newName: string }) => string | undefined; renameSchedule: (data: { scheduleId: string; newName: string }) => string | undefined;
} }

View File

@@ -30,7 +30,8 @@ export const OptionsStore = createSyncStore<IOptionsStore>({
/** /**
* Initializes the settings by retrieving the values from the OptionsStore. * Initializes the settings by retrieving the values from the OptionsStore.
* @returns {Promise<IOptionsStore>} A promise that resolves to an object satisfying the IOptionsStore interface. *
* @returns A promise that resolves to an object satisfying the IOptionsStore interface.
*/ */
export const initSettings = async () => export const initSettings = async () =>
({ ({

View File

@@ -1,6 +1,5 @@
/** /**
* Represents cached data with its fetch timestamp * Represents cached data with its fetch timestamp
* @template T The type of the cached data
*/ */
export type CachedData<T> = { export type CachedData<T> = {
data: T; data: T;

View File

@@ -95,8 +95,9 @@ export class Course {
/** /**
* Gets a list of all the conflicts between this course and another course (i.e. if they have a meeting at the same time) * Gets a list of all the conflicts between this course and another course (i.e. if they have a meeting at the same time)
* @param other another course to compare this course to *
* @returns a list of all the conflicts between this course and the other course as a tuple of the two conflicting meetings * @param other - Another course to compare this course to
* @returns A list of all the conflicts between this course and the other course as a tuple of the two conflicting meetings
*/ */
getConflicts(other: Course): [CourseMeeting, CourseMeeting][] { getConflicts(other: Course): [CourseMeeting, CourseMeeting][] {
const conflicts: [CourseMeeting, CourseMeeting][] = []; const conflicts: [CourseMeeting, CourseMeeting][] = [];

View File

@@ -48,9 +48,10 @@ export class CourseMeeting {
/** /**
* Whether or not this meeting conflicts with another meeting * Whether or not this meeting conflicts with another meeting
* MWF 10:00 am - 11:00 am conflicts with a F 10:00 am - 10:30 am *
* @param meeting the meeting to check for conflicts with * @remarks MWF 10:00 am - 11:00 am conflicts with a F 10:00 am - 10:30 am
* @returns true if the given meeting conflicts with this meeting, false otherwise * @param meeting - The meeting to check for conflicts with
* @returns True if the given meeting conflicts with this meeting, false otherwise
*/ */
isConflicting(meeting: CourseMeeting): boolean { isConflicting(meeting: CourseMeeting): boolean {
const { days, startTime, endTime } = this; const { days, startTime, endTime } = this;
@@ -64,8 +65,9 @@ export class CourseMeeting {
/** /**
* Return the string representation of the days of the week that this meeting is taught * Return the string representation of the days of the week that this meeting is taught
* @param options options for the string representation *
* @returns string representation of the days of the week that this meeting is taught * @param options - Options for the string representation
* @returns String representation of the days of the week that this meeting is taught
*/ */
getDaysString(options: DaysStringOptions): string { getDaysString(options: DaysStringOptions): string {
let { format, separator } = options; let { format, separator } = options;
@@ -86,8 +88,9 @@ export class CourseMeeting {
/** /**
* Return the string representation of the time range for the course * Return the string representation of the time range for the course
* @param options options for the string representation *
* @returns string representation of the time range for the course * @param options - Options for the string representation
* @returns String representation of the time range for the course
*/ */
getTimeString(options: TimeStringOptions): string { getTimeString(options: TimeStringOptions): string {
const { startTime, endTime } = this; const { startTime, endTime } = this;

View File

@@ -20,9 +20,10 @@ export class CourseSchedule {
/** /**
* Given a string representation of the meeting information for a class, parse it into a CourseMeeting object * Given a string representation of the meeting information for a class, parse it into a CourseMeeting object
* @param dayLine a string representation of the days of the week that the course is taught: MWF, TR, etc. *
* @param timeLine a string representation of a time-range that the course is taught: 10:00 am - 11:00 am, 1:00 pm - 2:00 pm, etc. * @param dayLine - A string representation of the days of the week that the course is taught: MWF, TR, etc.
* @param locLine a string representation of the location that the course is taught in: JGB 2.302, etc. * @param timeLine - A string representation of a time-range that the course is taught: 10:00 am - 11:00 am, 1:00 pm - 2:00 pm, etc.
* @param locLine - A string representation of the location that the course is taught in: JGB 2.302, etc.
* @returns CourseMeeting object representing the meeting information * @returns CourseMeeting object representing the meeting information
*/ */
static parse(dayLine: string, timeLine: string, locLine: string): CourseMeeting { static parse(dayLine: string, timeLine: string, locLine: string): CourseMeeting {

View File

@@ -18,7 +18,8 @@ export default class Instructor {
/** /**
* Get the URL to the instructor's directory page on the UT Directory website * Get the URL to the instructor's directory page on the UT Directory website
* @returns a URL string to the instructor's directory page *
* @returns A URL string to the instructor's directory page
*/ */
getDirectoryUrl(): string { getDirectoryUrl(): string {
const name = this.toString({ const name = this.toString({
@@ -36,8 +37,9 @@ export default class Instructor {
/** /**
* Get a string representation of the instructor * Get a string representation of the instructor
* @param options the options for how to format the instructor string *
* @returns a string representation of the instructor * @param options - The options for how to format the instructor string
* @returns A string representation of the instructor
*/ */
toString(options: InstructorFormatOptions): string { toString(options: InstructorFormatOptions): string {
const { firstName, lastName, fullName } = this; const { firstName, lastName, fullName } = this;

View File

@@ -33,7 +33,9 @@ export const useableColorways = Object.keys(theme.colors)
/** /**
* Generate a Tailwind classname for the font color based on the background color * Generate a Tailwind classname for the font color based on the background color
* @param bgColor the hex color of the background *
* @param bgColor - The hex color of the background
* @returns The Tailwind classname for the font color
*/ */
export function pickFontColor(bgColor: HexColor): 'text-white' | 'text-black' | 'text-theme-black' { export function pickFontColor(bgColor: HexColor): 'text-white' | 'text-black' | 'text-theme-black' {
const coefficients = [0.2126729, 0.7151522, 0.072175] as const; const coefficients = [0.2126729, 0.7151522, 0.072175] as const;
@@ -56,7 +58,11 @@ export function pickFontColor(bgColor: HexColor): 'text-white' | 'text-black' |
/** /**
* Get primary and secondary colors from a Tailwind colorway * Get primary and secondary colors from a Tailwind colorway
* @param colorway the Tailwind colorway ex. "emerald" *
* @param colorway - The Tailwind colorway ex. "emerald"
* @param index - The index of the primary color in the colorway
* @param offset - The offset to get the secondary color
* @returns The primary and secondary colors
*/ */
export function getCourseColors(colorway: TWColorway, index?: number, offset: number = 300): CourseColors { export function getCourseColors(colorway: TWColorway, index?: number, offset: number = 300): CourseColors {
if (index === undefined) { if (index === undefined) {
@@ -117,8 +123,12 @@ export function getColorwayFromColor(color: HexColor): TWColorway {
/** /**
* Get next unused color in a tailwind colorway for a given schedule * Get next unused color in a tailwind colorway for a given schedule
* @param schedule the schedule which the course is in *
* @param course the course to get the color for * @param schedule - The schedule which the course is in
* @param course - The course to get the color for
* @param index - The index of the primary color in the colorway
* @param offset - The offset to get the secondary color
* @returns The primary and secondary colors
*/ */
export function getUnusedColor( export function getUnusedColor(
schedule: Serialized<UserSchedule>, schedule: Serialized<UserSchedule>,
@@ -221,8 +231,9 @@ function rgbToSrgb(rgb: RGB): sRGB {
/** /**
* Convert an RGB color to the OKLab color space * Convert an RGB color to the OKLab color space
* @param rgb the RGB color *
* @returns the color in the OKLab color space * @param rgb - The RGB color
* @returns The color in the OKLab color space
*/ */
function srgbToOKlab([r, g, b]: sRGB): Lab { function srgbToOKlab([r, g, b]: sRGB): Lab {
let l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b; let l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;

View File

@@ -3,9 +3,10 @@ import MIMEType from '../types/MIMEType';
/** /**
* Downloads a blob by creating a temporary URL and triggering a download. * Downloads a blob by creating a temporary URL and triggering a download.
* @param blobPart The blob data to be downloaded. *
* @param type The MIME type of the blob. * @param blobPart - The blob data to be downloaded.
* @param fileName The name of the file to be downloaded. * @param type - The MIME type of the blob.
* @param fileName - The name of the file to be downloaded.
* @returns A promise that resolves when the download is successful, or rejects with an error if the download fails. * @returns A promise that resolves when the download is successful, or rejects with an error if the download fails.
*/ */
export function downloadBlob(blobPart: BlobPart, type: MIMETypeKey, fileName: string): Promise<void> { export function downloadBlob(blobPart: BlobPart, type: MIMETypeKey, fileName: string): Promise<void> {

View File

@@ -7,12 +7,17 @@ import ClosedIcon from '~icons/material-symbols/lock';
import WaitlistIcon from '~icons/material-symbols/timelapse'; import WaitlistIcon from '~icons/material-symbols/timelapse';
import CancelledIcon from '~icons/material-symbols/warning'; import CancelledIcon from '~icons/material-symbols/warning';
interface StatusIconProps extends SVGProps<SVGSVGElement> {
status: StatusType;
}
/** /**
* Get Icon component based on status * Get Icon component based on status
* @param props.status status *
* @returns the icon component * @param props - The props for the StatusIcon component
* @returns The rendered icon component
*/ */
export function StatusIcon(props: SVGProps<SVGSVGElement> & { status: StatusType }): JSX.Element | null { export function StatusIcon(props: StatusIconProps): JSX.Element | null {
const { status, ...rest } = props; const { status, ...rest } = props;
switch (status) { switch (status) {

View File

@@ -3,7 +3,7 @@ import { customAlphabet } from 'nanoid';
/** /**
* Generate secure URL-friendly unique ID. * Generate secure URL-friendly unique ID.
* *
* @param size Size of the ID. The default size is 12. * @param size - Size of the ID. The default size is 12.
* @returns A random string. * @returns A random string.
*/ */
export const generateRandomId = customAlphabet('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', 12); export const generateRandomId = customAlphabet('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', 12);

View File

@@ -1,6 +1,7 @@
/** /**
* Given a string, returns a string with the first letter capitalized. * Given a string, returns a string with the first letter capitalized.
* @input The string to capitalize. *
* @param input - The string to capitalize.
*/ */
export function capitalize(input: string): string { export function capitalize(input: string): string {
let capitalized = ''; let capitalized = '';
@@ -25,8 +26,9 @@ export function capitalize(input: string): string {
/** /**
* Given a string, returns a string with the first letter capitalized. * Given a string, returns a string with the first letter capitalized.
* @param input capitalize the first letter of this string *
* @returns the string with the first letter capitalized * @param input - Capitalize the first letter of this string
* @returns The string with the first letter capitalized
*/ */
export function capitalizeFirstLetter(input: string): string { export function capitalizeFirstLetter(input: string): string {
return input.charAt(0).toUpperCase() + input.slice(1).toLowerCase(); return input.charAt(0).toUpperCase() + input.slice(1).toLowerCase();
@@ -34,8 +36,9 @@ export function capitalizeFirstLetter(input: string): string {
/** /**
* Cuts the input string to the specified length and adds an ellipsis if the string is longer than the specified length. * Cuts the input string to the specified length and adds an ellipsis if the string is longer than the specified length.
* @param input The string to ellipsify. *
* @param length The length of the string to return. * @param input - The string to ellipsify.
* @param length - The length of the string to return.
* @returns The ellipsified string. * @returns The ellipsified string.
*/ */
export const ellipsify = (input: string, chars: number): string => { export const ellipsify = (input: string, chars: number): string => {

View File

@@ -6,6 +6,7 @@ export const DAY = 24 * HOUR;
/** /**
* Pauses the execution for the specified number of milliseconds. * Pauses the execution for the specified number of milliseconds.
*
* @param milliseconds - The number of milliseconds to sleep. * @param milliseconds - The number of milliseconds to sleep.
* @returns A promise that resolves after the specified number of milliseconds. * @returns A promise that resolves after the specified number of milliseconds.
*/ */
@@ -14,8 +15,8 @@ export const sleep = (milliseconds: number): Promise<void> => new Promise(resolv
/** /**
* Checks to see if expired by the time first stored and the time frame that it is stored for * Checks to see if expired by the time first stored and the time frame that it is stored for
* *
* @param time time it was stored * @param time - time it was stored
* @param threshold time frame it can be stored for * @param threshold - time frame it can be stored for
* @return true if expired, false if the time frame is still in range * @returns true if expired, false if the time frame is still in range
*/ */
export const didExpire = (time: number, threshold: number): boolean => time + threshold <= Date.now(); export const didExpire = (time: number, threshold: number): boolean => time + threshold <= Date.now();

View File

@@ -20,8 +20,8 @@ type CalendarBottomBarProps = {
/** /**
* Renders the bottom bar of the calendar component. * Renders the bottom bar of the calendar component.
* *
* @param {Object[]} courses - The list of courses to display in the calendar. * @param courses - The list of courses to display in the calendar.
* @returns {JSX.Element} The rendered bottom bar component. * @returns The rendered bottom bar component.
*/ */
export default function CalendarBottomBar({ courseCells, setCourse }: CalendarBottomBarProps): JSX.Element { export default function CalendarBottomBar({ courseCells, setCourse }: CalendarBottomBarProps): JSX.Element {
const asyncCourseCells = courseCells?.filter(block => block.async); const asyncCourseCells = courseCells?.filter(block => block.async);

View File

@@ -26,14 +26,12 @@ export interface CalendarCourseCellProps {
/** /**
* Renders a cell for a calendar course. * Renders a cell for a calendar course.
* *
* @component * @param courseDeptAndInstr - The course department and instructor.
* @param {CalendarCourseCellProps} props - The component props. * @param timeAndLocation - The time and location of the course.
* @param {string} props.courseDeptAndInstr - The course department and instructor. * @param status - The status of the course.
* @param {string} props.timeAndLocation - The time and location of the course. * @param colors - The colors for styling the cell.
* @param {StatusType} props.status - The status of the course. * @param className - Additional CSS class name for the cell.
* @param {Colors} props.colors - The colors for styling the cell. * @returns The rendered component.
* @param {string} props.className - Additional CSS class name for the cell.
* @returns {JSX.Element} The rendered component.
*/ */
export default function CalendarCourseCell({ export default function CalendarCourseCell({
courseDeptAndInstr, courseDeptAndInstr,

View File

@@ -15,12 +15,10 @@ interface ColorPatchProps {
/** /**
* Renders a color patch square used in the CalendarCourseCellColorPicker component. * Renders a color patch square used in the CalendarCourseCellColorPicker component.
* *
* @param {Object} props - The component props. * @param color - The color value (as a hex string with a hash prefix) to display in the patch.
* @param {string} props.color - The color value (as a hex string with a hash prefix) to display in the patch. * @param isSelected - Indicates whether the patch is selected.
* @param {boolean} props.isSelected - Indicates whether the patch is selected. * @param handleSetSelectedColor - Function from parent component to control selection state of a patch.
* @param {Function} props.handleSetSelectedColor - Function from parent component to control selection state of a patch. * @returns The rendered color patch button.
* color is a hex string with a hash prefix.
* @returns {JSX.Element} The rendered color patch button.
*/ */
export default function ColorPatch({ color, isSelected, handleSetSelectedColor }: ColorPatchProps): JSX.Element { export default function ColorPatch({ color, isSelected, handleSetSelectedColor }: ColorPatchProps): JSX.Element {
const handleClick = () => { const handleClick = () => {

View File

@@ -61,10 +61,14 @@ export interface CourseCellColorPickerProps {
} }
/** /**
* @param {CourseCellColorPickerProps} props - the props for the component * The CourseCellColorPicker component that displays a color palette with a list of color patches.
* @param {React.Dispatch<React.SetStateAction<string | null>>} props.setSelectedColor - set state function passed down from the parent component *
* @param {boolean} props.isInvertColorsToggled - boolean state passed down from the parent component that indicates whether the color picker is in invert colors mode * @remarks This component is available when a user hovers over a course cell in their calendar to
* @param {React.Dispatch<React.SetStateAction<boolean>>} props.setIsInvertColorsToggled - set state function passed down from the parent component to set invert colors mode * color for the course cell. The user can set any valid hex color they want.
*
* @param setSelectedColor - Set state function passed down from the parent component
* @param isInvertColorsToggled - Boolean state passed down from the parent component that indicates whether the color picker is in invert colors mode
* @param setIsInvertColorsToggled - Set state function passed down from the parent component to set invert colors mode
* that will be called when a color is selected. The user can set any valid hex color they want. * that will be called when a color is selected. The user can set any valid hex color they want.
* *
* @example * @example
@@ -80,9 +84,7 @@ export interface CourseCellColorPickerProps {
* ); * );
* ``` * ```
* *
* @returns {JSX.Element} - the color picker component that displays a color palette with a list of color patches. * @returns The color picker component that displays a color palette with a list of color patches.
* This component is available when a user hovers over a course cell in their calendar to
* color for the course cell. The user can set any valid hex color they want.
*/ */
export default function CourseCellColorPicker({ export default function CourseCellColorPicker({
setSelectedColor: setFinalColor, setSelectedColor: setFinalColor,

View File

@@ -14,11 +14,10 @@ export interface HexColorEditorProps {
/** /**
* Utility component to allow the user to enter a valid hex color code * Utility component to allow the user to enter a valid hex color code
* *
* @param {HexColorEditorProps} props - the props for the component * @param hexCode - The current hex color code displayed in this component. Note that this code does not
* @param {string} props.hexCode - the current hex color code displayed in this component. Note that this code does not
* include the leading '#' character since it is already included in the component. Passed down from the parent component. * include the leading '#' character since it is already included in the component. Passed down from the parent component.
* @param {React.Dispatch<React.SetStateAction<string>>} props.setHexCode - set state fn to control the hex color code from parent * @param setHexCode - Set state fn to control the hex color code from parent
* @returns {JSX.Element} - the hex color editor component * @returns The HexColorEditor component
*/ */
export default function HexColorEditor({ hexCode, setHexCode }: HexColorEditorProps): JSX.Element { export default function HexColorEditor({ hexCode, setHexCode }: HexColorEditorProps): JSX.Element {
const baseColor = React.useMemo(() => getThemeColorHexByName('ut-gray'), []); const baseColor = React.useMemo(() => getThemeColorHexByName('ut-gray'), []);

View File

@@ -43,7 +43,11 @@ function makeGridRow(row: number, cols: number): JSX.Element {
/** /**
* Grid of CalendarGridCell components forming the user's course schedule calendar view * Grid of CalendarGridCell components forming the user's course schedule calendar view
* @param props *
* @param courseCells - The courses to display on the calendar
* @param saturdayClass - Whether the user has a Saturday class
* @param setCourse - Function to set the course to display in the course details panel
* @returns The CalendarGrid component
*/ */
export default function CalendarGrid({ export default function CalendarGrid({
courseCells, courseCells,

View File

@@ -7,15 +7,18 @@ interface Props {
/** /**
* Component representing each 1 hour time block of a calendar * Component representing each 1 hour time block of a calendar
* @param props *
* @param row - The row of the cell
* @param col - The column of the cell
* @returns The CalendarCell component
*/ */
function CalendarCell(props: Props): JSX.Element { function CalendarCell({ row, col }: Props): JSX.Element {
return ( return (
<div <div
className='h-full w-full flex items-center border-b border-r border-gray-300' className='h-full w-full flex items-center border-b border-r border-gray-300'
style={{ style={{
gridColumn: props.col + 3, gridColumn: col + 3,
gridRow: `${2 * props.row + 2} / ${2 * props.row + 4}`, gridRow: `${2 * row + 2} / ${2 * row + 4}`,
}} }}
> >
<div className='h-0 w-full border-t border-gray-300/25' /> <div className='h-0 w-full border-t border-gray-300/25' />

View File

@@ -15,7 +15,9 @@ export interface ConflictsWithWarningProps {
* The ConflictsWithWarning component is used to display a warning message when a course conflicts * The ConflictsWithWarning component is used to display a warning message when a course conflicts
* with another course as part of the labels and details section * with another course as part of the labels and details section
* *
* @param props ConflictsWithWarningProps * @param className - The class name for the component
* @param conflicts - The courses that conflict with the current course
* @returns The ConflictsWithWarning component
*/ */
export default function ConflictsWithWarning({ className, conflicts }: ConflictsWithWarningProps): JSX.Element { export default function ConflictsWithWarning({ className, conflicts }: ConflictsWithWarningProps): JSX.Element {
return ( return (

View File

@@ -17,7 +17,9 @@ export interface CourseStatusProps {
/** /**
* The CourseStatus component as per the Labels and Details Figma section * The CourseStatus component as per the Labels and Details Figma section
* *
* @param props CourseStatusProps * @param status - The status of the course
* @param size - The size of the component
* @returns The CourseStatus component
*/ */
export default function CourseStatus({ status, size }: CourseStatusProps): JSX.Element { export default function CourseStatus({ status, size }: CourseStatusProps): JSX.Element {
const statusIconSizeClass = clsx({ const statusIconSizeClass = clsx({

View File

@@ -70,7 +70,9 @@ function Item<T>(props: {
* `List` is a functional component that displays a course meeting. * `List` is a functional component that displays a course meeting.
* *
* @example * @example
* ```
* <List draggableElements={elements} /> * <List draggableElements={elements} />
* ```
*/ */
function List<T>({ draggables, itemKey, children, onReordered, gap }: ListProps<T>): JSX.Element { function List<T>({ draggables, itemKey, children, onReordered, gap }: ListProps<T>): JSX.Element {
const [items, setItems] = useState(wrap(draggables, itemKey)); const [items, setItems] = useState(wrap(draggables, itemKey));

View File

@@ -2,10 +2,15 @@ import clsx from 'clsx';
import type { SVGProps } from 'react'; import type { SVGProps } from 'react';
import React from 'react'; import React from 'react';
interface LogoIconProps {
className?: string;
}
/** /**
* Renders the logo icon. * Renders the logo icon.
* @param {SVGProps<SVGSVGElement>} props - The SVG props. *
* @returns {JSX.Element} The rendered logo icon. * @param props - The SVG props.
* @returns The rendered logo icon.
*/ */
export function LogoIcon(props: SVGProps<SVGSVGElement>): JSX.Element { export function LogoIcon(props: SVGProps<SVGSVGElement>): JSX.Element {
return ( return (
@@ -20,11 +25,11 @@ export function LogoIcon(props: SVGProps<SVGSVGElement>): JSX.Element {
/** /**
* Renders the small logo. * Renders the small logo.
* @param {Object} props - The component props. *
* @param {string} props.className - The class name for the logo container. * @param className - The class name for the logo container.
* @returns {JSX.Element} The rendered small logo. * @returns The rendered small logo.
*/ */
export function SmallLogo({ className }: { className?: string }): JSX.Element { export function SmallLogo({ className }: LogoIconProps): JSX.Element {
return ( return (
<div className={clsx('flex items-center gap-2', className)}> <div className={clsx('flex items-center gap-2', className)}>
<LogoIcon /> <LogoIcon />
@@ -43,11 +48,11 @@ export function SmallLogo({ className }: { className?: string }): JSX.Element {
/** /**
* Renders the large logo. * Renders the large logo.
* @param {Object} props - The component props. *
* @param {string} props.className - The class name for the logo container. * @param className - The class name for the logo container.
* @returns {JSX.Element} The rendered large logo. * @returns The rendered large logo.
*/ */
export function LargeLogo({ className }: { className?: string }): JSX.Element { export function LargeLogo({ className }: LogoIconProps): JSX.Element {
return ( return (
<div className={clsx('flex items-center gap-2', className)}> <div className={clsx('flex items-center gap-2', className)}>
<LogoIcon className='h-12 w-12' /> <LogoIcon className='h-12 w-12' />

View File

@@ -25,7 +25,11 @@ export interface PopupCourseBlockProps {
/** /**
* The "course block" to be used in the extension popup. * The "course block" to be used in the extension popup.
* *
* @param props PopupCourseBlockProps * @param className - The class name to apply to the component.
* @param course - The course object to display.
* @param colors - The colors to use for the course block.
* @param dragHandleProps - The drag handle props for the course block.
* @returns The rendered PopupCourseBlock component.
*/ */
export default function PopupCourseBlock({ export default function PopupCourseBlock({
className, className,

View File

@@ -18,13 +18,15 @@ export interface PromptDialogProps {
/** /**
* A reusable dialog component that can be used to display a prompt to the user. * A reusable dialog component that can be used to display a prompt to the user.
* @param {PromptDialogProps} props.isOpen - Whether the dialog is open or not. *
* @param {Function} props.onClose - A function to call when the user exits the dialog. * @param isOpen - Whether the dialog is open or not.
* @param {React.ReactElement<typeof Text>} props.title - The title of the dialog. * @param onClose - A function to call when the user exits the dialog.
* @param {React.ReactElement<typeof Text>} props.content - The content of the dialog. * @param title - The title of the dialog.
* @param {React.ReactElement<typeof Button>[]} props.children - The buttons to display in the dialog. * @param content - The content of the dialog.
* @param children - The buttons to display in the dialog.
* @returns The rendered PromptDialog component.
*/ */
function PromptDialog({ isOpen, onClose, title, content, children }: PromptDialogProps) { function PromptDialog({ isOpen, onClose, title, content, children }: PromptDialogProps): JSX.Element {
return ( return (
<Transition appear show={isOpen} as={React.Fragment}> <Transition appear show={isOpen} as={React.Fragment}>
<Dialog as='div' onClose={onClose} className='relative z-50'> <Dialog as='div' onClose={onClose} className='relative z-50'>

View File

@@ -13,7 +13,10 @@ export interface ScheduleTotalHoursAndCoursesProps {
/** /**
* The ScheduleTotalHoursAndCourses as per the Labels and Details Figma section * The ScheduleTotalHoursAndCourses as per the Labels and Details Figma section
* *
* @param props ScheduleTotalHoursAndCoursesProps * @param scheduleName - The name of the schedule.
* @param totalHours - The total number of hours.
* @param totalCourses - The total number of courses.
* @returns The rendered ScheduleTotalHoursAndCourses component.
*/ */
export default function ScheduleTotalHoursAndCourses({ export default function ScheduleTotalHoursAndCourses({
scheduleName, scheduleName,

View File

@@ -9,11 +9,9 @@ type ToggleSwitchProps = {
/** /**
* A custom switch button component. * A custom switch button component.
* *
* @component * @param isChecked - The initial checked state of the switch button.
* @param {Object} props - The component props. * @param onChange - The callback function to be called when the switch button is toggled.
* @param {boolean} [props.isChecked=true] - The initial checked state of the switch button. * @returns The rendered SwitchButton component.
* @param {Function} props.onChange - The callback function to be called when the switch button is toggled.
* @returns {JSX.Element} The rendered SwitchButton component.
*/ */
const SwitchButton = ({ isChecked = true, onChange }: ToggleSwitchProps): JSX.Element => { const SwitchButton = ({ isChecked = true, onChange }: ToggleSwitchProps): JSX.Element => {
const [enabled, setEnabled] = useState(isChecked); const [enabled, setEnabled] = useState(isChecked);

View File

@@ -12,8 +12,7 @@ export type UpdateTextProps = {
* UpdateText component displays a message indicating that the extension has been updated * UpdateText component displays a message indicating that the extension has been updated
* and lists the unique course numbers from the old version. * and lists the unique course numbers from the old version.
* *
* @param props - The properties object. * @param courses - An array of course unique numbers to be displayed.
* @param props.courses - An array of course unique numbers to be displayed.
* @returns The rendered UpdateText component. * @returns The rendered UpdateText component.
*/ */
export default function UpdateText({ courses }: UpdateTextProps): JSX.Element { export default function UpdateText({ courses }: UpdateTextProps): JSX.Element {

View File

@@ -11,19 +11,17 @@ import HeadingAndActions from './HeadingAndActions';
/** /**
* Props for the CourseCatalogInjectedPopup component. * Props for the CourseCatalogInjectedPopup component.
*/ */
export type CourseCatalogInjectedPopupProps = DialogProps & { export interface CourseCatalogInjectedPopupProps extends DialogProps {
course: Course; course: Course;
}; }
/** /**
* CourseCatalogInjectedPopup component displays a popup with course details. * CourseCatalogInjectedPopup component displays a popup with course details.
* *
* @component * @param course - The course object containing course details.
* @param {CourseCatalogInjectedPopupProps} props - The component props. * @param activeSchedule - The active schedule object.
* @param {Course} props.course - The course object containing course details. * @param onClose - The function to close the popup.
* @param {Schedule} props.activeSchedule - The active schedule object. * @returns The CourseCatalogInjectedPopup component.
* @param {Function} props.onClose - The function to close the popup.
* @returns {JSX.Element} The CourseCatalogInjectedPopup component.
*/ */
function CourseCatalogInjectedPopup({ course, ...rest }: CourseCatalogInjectedPopupProps): JSX.Element { function CourseCatalogInjectedPopup({ course, ...rest }: CourseCatalogInjectedPopupProps): JSX.Element {
const emptyRef = React.useRef<HTMLDivElement>(null); const emptyRef = React.useRef<HTMLDivElement>(null);

View File

@@ -32,10 +32,8 @@ const fetchDescription = async (course: Course): Promise<string[]> => {
/** /**
* Renders the description component. * Renders the description component.
* *
* @component * @param course - The course for which to display the description.
* @param {DescriptionProps} props - The component props. * @returns The rendered description component.
* @param {Course} props.course - The course for which to display the description.
* @returns {JSX.Element} The rendered description component.
*/ */
export default function Description({ course }: DescriptionProps): JSX.Element { export default function Description({ course }: DescriptionProps): JSX.Element {
const [description, setDescription] = React.useState<string[]>([]); const [description, setDescription] = React.useState<string[]>([]);

View File

@@ -48,10 +48,8 @@ const GRADE_COLORS = {
/** /**
* Renders the grade distribution chart for a specific course. * Renders the grade distribution chart for a specific course.
* *
* @component * @param course - The course for which to display the grade distribution.
* @param {GradeDistributionProps} props - The component props. * @returns The grade distribution chart component.
* @param {Course} props.course - The course for which to display the grade distribution.
* @returns {JSX.Element} The grade distribution chart component.
*/ */
export default function GradeDistribution({ course }: GradeDistributionProps): JSX.Element { export default function GradeDistribution({ course }: GradeDistributionProps): JSX.Element {
const [semester, setSemester] = useState('Aggregate'); const [semester, setSemester] = useState('Aggregate');

View File

@@ -22,6 +22,14 @@ import Reviews from '~icons/material-symbols/reviews';
const { openNewTab, addCourse, removeCourse, openCESPage } = background; const { openNewTab, addCourse, removeCourse, openCESPage } = background;
/**
* Capitalizes the first letter of a string and converts the rest of the letters to lowercase.
*
* @param str - The string to be capitalized.
* @returns The capitalized string.
*/
const capitalizeString = (str: string) => str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
interface HeadingAndActionProps { interface HeadingAndActionProps {
/* The course to display */ /* The course to display */
course: Course; course: Course;
@@ -31,22 +39,13 @@ interface HeadingAndActionProps {
onClose: () => void; onClose: () => void;
} }
/**
* Capitalizes the first letter of a string and converts the rest of the letters to lowercase.
*
* @param str - The string to be capitalized.
* @returns The capitalized string.
*/
const capitalizeString = (str: string) => str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
/** /**
* Renders the heading component for the CoursePopup component. * Renders the heading component for the CoursePopup component.
* *
* @param {HeadingAndActionProps} props - The component props. * @param course - The course object containing course details.
* @param {Course} props.course - The course object containing course details. * @param activeSchedule - The active schedule object.
* @param {Schedule} props.activeSchedule - The active schedule object. * @param onClose - The function to close the popup.
* @param {Function} props.onClose - The function to close the popup. * @returns The rendered component.
* @returns {JSX.Element} The rendered component.
*/ */
export default function HeadingAndActions({ course, activeSchedule, onClose }: HeadingAndActionProps): JSX.Element { export default function HeadingAndActions({ course, activeSchedule, onClose }: HeadingAndActionProps): JSX.Element {
const { courseName, department, number: courseNumber, uniqueId, instructors, flags, schedule, core } = course; const { courseName, department, number: courseNumber, uniqueId, instructors, flags, schedule, core } = course;

View File

@@ -12,6 +12,7 @@ const RECRUIT_FROM_DEPARTMENTS = ['C S', 'ECE', 'MIS', 'CSE', 'EE', 'ITD', 'DES'
/** /**
* This adds a new column to the course catalog table header. * This adds a new column to the course catalog table header.
*
* @returns a react portal to the new column or null if the column has not been created yet. * @returns a react portal to the new column or null if the column has not been created yet.
*/ */
export default function RecruitmentBanner(): JSX.Element | null { export default function RecruitmentBanner(): JSX.Element | null {
@@ -53,7 +54,8 @@ export default function RecruitmentBanner(): JSX.Element | null {
/** /**
* Determines if recruitment can be done from the current department. * Determines if recruitment can be done from the current department.
* @returns {boolean} True if recruitment can be done from the current department, false otherwise. *
* @returns True if recruitment can be done from the current department, false otherwise.
*/ */
export const canRecruitFrom = (): boolean => { export const canRecruitFrom = (): boolean => {
const params = ['fos_fl', 'fos_cn']; const params = ['fos_fl', 'fos_cn'];

View File

@@ -1,4 +1,4 @@
import addCourse from '@pages/background/lib/addCourse'; // import addCourse from '@pages/background/lib/addCourse';
import { addCourseByURL } from '@pages/background/lib/addCourseByURL'; import { addCourseByURL } from '@pages/background/lib/addCourseByURL';
import { deleteAllSchedules } from '@pages/background/lib/deleteSchedule'; import { deleteAllSchedules } from '@pages/background/lib/deleteSchedule';
import { initSettings, OptionsStore } from '@shared/storage/OptionsStore'; import { initSettings, OptionsStore } from '@shared/storage/OptionsStore';
@@ -14,10 +14,10 @@ import SwitchButton from '@views/components/common/SwitchButton';
import Text from '@views/components/common/Text/Text'; import Text from '@views/components/common/Text/Text';
import useChangelog from '@views/hooks/useChangelog'; import useChangelog from '@views/hooks/useChangelog';
import useSchedules from '@views/hooks/useSchedules'; import useSchedules from '@views/hooks/useSchedules';
import { CourseCatalogScraper } from '@views/lib/CourseCatalogScraper'; // import { CourseCatalogScraper } from '@views/lib/CourseCatalogScraper';
import getCourseTableRows from '@views/lib/getCourseTableRows'; // import getCourseTableRows from '@views/lib/getCourseTableRows';
import { GitHubStatsService, LONGHORN_DEVELOPERS_ADMINS, LONGHORN_DEVELOPERS_SWE } from '@views/lib/getGitHubStats'; import { GitHubStatsService, LONGHORN_DEVELOPERS_ADMINS, LONGHORN_DEVELOPERS_SWE } from '@views/lib/getGitHubStats';
import { SiteSupport } from '@views/lib/getSiteSupport'; // import { SiteSupport } from '@views/lib/getSiteSupport';
import { getUpdatedAtDateTimeString } from '@views/lib/getUpdatedAtDateTimeString'; import { getUpdatedAtDateTimeString } from '@views/lib/getUpdatedAtDateTimeString';
import clsx from 'clsx'; import clsx from 'clsx';
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';

View File

@@ -21,26 +21,23 @@ export const SentryContext = createContext<[scope: Scope, client: Client]>(undef
*/ */
export const useSentryScope = () => useContext(SentryContext); export const useSentryScope = () => useContext(SentryContext);
interface SentryProviderProps {
children: React.ReactNode;
transactionName?: string;
fullInit?: boolean;
}
/** /**
* SentryProvider component initializes and provides Sentry error tracking context to its children. * SentryProvider component initializes and provides Sentry error tracking context to its children.
* It ensures that Sentry is not initialized more than once and configures the Sentry client and scope. * It ensures that Sentry is not initialized more than once and configures the Sentry client and scope.
* *
* @param props - The properties object. * @param children - The child components that will have access to the Sentry context.
* @param props.children - The child components that will have access to the Sentry context. * @param transactionName - Optional name for the Sentry transaction.
* @param props.transactionName - Optional name for the Sentry transaction. * @param fullInit - Flag to determine if full initialization of Sentry should be performed.
* @param props.fullInit - Flag to determine if full initialization of Sentry should be performed.
* *
* @returns The Sentry context provider wrapping the children components. * @returns The Sentry context provider wrapping the children components.
*/ */
export default function SentryProvider({ export default function SentryProvider({ children, transactionName, fullInit }: SentryProviderProps): JSX.Element {
children,
transactionName,
fullInit,
}: {
children: React.ReactNode;
transactionName?: string;
fullInit?: boolean;
}): JSX.Element {
// prevent accidentally initializing sentry twice // prevent accidentally initializing sentry twice
const parent = useSentryScope(); const parent = useSentryScope();

View File

@@ -12,7 +12,7 @@ const SCHEDULE_LIMIT = 10;
* If a new schedule can be created without exceeding the limit, the function returns true * If a new schedule can be created without exceeding the limit, the function returns true
* Otherwise, display a prompt explaining the limit, and returns false * Otherwise, display a prompt explaining the limit, and returns false
* *
* @returns a function, () => boolean * @returns a callback function that enforces the schedule limit via a dialog
*/ */
export function useEnforceScheduleLimit(): () => boolean { export function useEnforceScheduleLimit(): () => boolean {
const [, schedules] = useSchedules(); const [, schedules] = useSchedules();

View File

@@ -48,7 +48,8 @@ export interface FlattenedCourseSchedule {
/** /**
* Converts minutes to an index value. * Converts minutes to an index value.
* @param minutes The number of minutes. *
* @param minutes - The number of minutes.
* @returns The index value. * @returns The index value.
*/ */
export const convertMinutesToIndex = (minutes: number): number => Math.floor((minutes - 420) / 30); export const convertMinutesToIndex = (minutes: number): number => Math.floor((minutes - 420) / 30);

View File

@@ -2,7 +2,8 @@ import { useEffect } from 'react';
/** /**
* Hook to execute a callback when the user scrolls to the bottom of the page * Hook to execute a callback when the user scrolls to the bottom of the page
* @param callback the function to be called when the user scrolls to the bottom of the page *
* @param callback - the function to be called when the user scrolls to the bottom of the page
* @returns isLoading boolean to indicate if the callback is currently being executed * @returns isLoading boolean to indicate if the callback is currently being executed
*/ */

View File

@@ -2,8 +2,9 @@ import { useEffect } from 'react';
/** /**
* Hook that calls a callback when a key is pressed * Hook that calls a callback when a key is pressed
* @param key the key to listen for *
* @param callback the callback to call when the key is pressed * @param key - the key to listen for
* @param callback - the callback to call when the key is pressed
*/ */
export function useKeyPress(key: string, callback: (event: KeyboardEvent) => void): void { export function useKeyPress(key: string, callback: (event: KeyboardEvent) => void): void {
useEffect(() => { useEffect(() => {

View File

@@ -46,8 +46,9 @@ export class CourseCatalogScraper {
/** /**
* Pass in a list of HTMLtable rows and scrape every course from them * Pass in a list of HTMLtable rows and scrape every course from them
* @param rows the rows of the course catalog table *
* @param keepHeaders whether to keep the header rows (which contain the course name) in the output * @param rows - the rows of the course catalog table
* @param keepHeaders - whether to keep the header rows (which contain the course name) in the output
* @returns an array of course row objects (which contain courses corresponding to the htmltable row) * @returns an array of course row objects (which contain courses corresponding to the htmltable row)
*/ */
public scrape(rows: NodeListOf<HTMLTableRowElement> | HTMLTableRowElement[], keepHeaders = false): ScrapedRow[] { public scrape(rows: NodeListOf<HTMLTableRowElement> | HTMLTableRowElement[], keepHeaders = false): ScrapedRow[] {
@@ -109,8 +110,12 @@ export class CourseCatalogScraper {
/** /**
* Separate the course name into its department, number, and name * Separate the course name into its department, number, and name
* @example separateCourseName("CS 314H - Honors Discrete Structures") => ["Honors Discrete Structures", "CS", "314H"] *
* @param courseFullName the full name of the course (e.g. "CS 314H - Honors Discrete Structures") * @example
* ```
* separateCourseName("CS 314H - Honors Discrete Structures") => ["Honors Discrete Structures", "CS", "314H"]
* ```
* @param courseFullName - the full name of the course (e.g. "CS 314H - Honors Discrete Structures")
* @returns an array of the course name , department, and number * @returns an array of the course name , department, and number
*/ */
separateCourseName(courseFullName: string): [courseName: string, department: string, number: string] { separateCourseName(courseFullName: string): [courseName: string, department: string, number: string] {
@@ -124,8 +129,9 @@ export class CourseCatalogScraper {
/** /**
* Gets how many credit hours the course is worth * Gets how many credit hours the course is worth
* @param courseNumber the course number, CS 314H *
* @return the number of credit hours the course is worth * @param courseNumber - the course number, CS 314H
* @returns the number of credit hours the course is worth
*/ */
getCreditHours(courseNumber: string): number { getCreditHours(courseNumber: string): number {
let creditHours = Number(courseNumber.split('')[0]); let creditHours = Number(courseNumber.split('')[0]);
@@ -149,7 +155,8 @@ export class CourseCatalogScraper {
/** /**
* Scrape the Unique ID from the course catalog table row * Scrape the Unique ID from the course catalog table row
* @param row the row of the course catalog table *
* @param row - the row of the course catalog table
* @returns the uniqueid of the course as a number * @returns the uniqueid of the course as a number
*/ */
getUniqueId(row: HTMLTableRowElement): number { getUniqueId(row: HTMLTableRowElement): number {
@@ -162,7 +169,8 @@ export class CourseCatalogScraper {
/** /**
* Scrapes the individual URL for a given course that takes you to the course details page * Scrapes the individual URL for a given course that takes you to the course details page
* @param row the row of the course catalog table *
* @param row - the row of the course catalog table
* @returns the url of the course details page for the course in the row * @returns the url of the course details page for the course in the row
*/ */
getURL(row: HTMLTableRowElement): string { getURL(row: HTMLTableRowElement): string {
@@ -172,7 +180,8 @@ export class CourseCatalogScraper {
/** /**
* Scrape who is teaching the course from the course catalog table row with meta-data about their name * Scrape who is teaching the course from the course catalog table row with meta-data about their name
* @param row the row of the course catalog table *
* @param row - the row of the course catalog table
* @returns an array of instructors for the course * @returns an array of instructors for the course
*/ */
getInstructors(row: HTMLTableRowElement): Instructor[] { getInstructors(row: HTMLTableRowElement): Instructor[] {
@@ -197,7 +206,8 @@ export class CourseCatalogScraper {
/** /**
* Whether or not this is a header row for a course within the course catalog list (we can't scrape courses from header rows) * Whether or not this is a header row for a course within the course catalog list (we can't scrape courses from header rows)
* @param row the row of the course catalog table *
* @param row - the row of the course catalog table
* @returns true if this is a header row, false otherwise * @returns true if this is a header row, false otherwise
*/ */
isHeaderRow(row: HTMLTableRowElement): boolean { isHeaderRow(row: HTMLTableRowElement): boolean {
@@ -206,7 +216,8 @@ export class CourseCatalogScraper {
/** /**
* Scrape whether the class is being taught online, in person, or a hybrid of the two * Scrape whether the class is being taught online, in person, or a hybrid of the two
* @param row the row of the course catalog table *
* @param row - the row of the course catalog table
* @returns the instruction mode of the course * @returns the instruction mode of the course
*/ */
getInstructionMode(row: HTMLTableRowElement): InstructionMode { getInstructionMode(row: HTMLTableRowElement): InstructionMode {
@@ -223,7 +234,8 @@ export class CourseCatalogScraper {
/** /**
* Scrapes the description of the course from the course details page and separates it into an array of cleaned up lines * Scrapes the description of the course from the course details page and separates it into an array of cleaned up lines
* @param doc the document of the course details page to scrape *
* @param doc - the document of the course details page to scrape
* @returns an array of lines of the course description * @returns an array of lines of the course description
*/ */
getDescription(doc: Document): string[] { getDescription(doc: Document): string[] {
@@ -273,7 +285,8 @@ export class CourseCatalogScraper {
/** /**
* Get the full name of the course from the course catalog table row (e.g. "CS 314H - Honors Discrete Structures") * Get the full name of the course from the course catalog table row (e.g. "CS 314H - Honors Discrete Structures")
* @param row the row of the course catalog table *
* @param row - the row of the course catalog table
* @returns the full name of the course * @returns the full name of the course
*/ */
getFullName(row?: HTMLTableRowElement): string { getFullName(row?: HTMLTableRowElement): string {
@@ -286,7 +299,8 @@ export class CourseCatalogScraper {
/** /**
* When registration is open, the registration URL will show up in the course catalog table row as a link. This will scrape it from the row. * When registration is open, the registration URL will show up in the course catalog table row as a link. This will scrape it from the row.
* @param row the row of the course catalog table *
* @param row - the row of the course catalog table
* @returns the registration URL for the course if it is currently displayed, undefined otherwise * @returns the registration URL for the course if it is currently displayed, undefined otherwise
*/ */
getRegisterURL(row: HTMLTableRowElement): string | undefined { getRegisterURL(row: HTMLTableRowElement): string | undefined {
@@ -296,8 +310,9 @@ export class CourseCatalogScraper {
/** /**
* Scrapes whether the course is open, closed, waitlisted, or cancelled * Scrapes whether the course is open, closed, waitlisted, or cancelled
* @param row the row of the course catalog table *
* @returns * @param row - the row of the course catalog table
* @returns a tuple of the status of the course and whether the course is reserved
*/ */
getStatus(row: HTMLTableRowElement): [status: StatusType, isReserved: boolean] { getStatus(row: HTMLTableRowElement): [status: StatusType, isReserved: boolean] {
const div = row.querySelector(TableDataSelector.STATUS); const div = row.querySelector(TableDataSelector.STATUS);
@@ -327,7 +342,8 @@ export class CourseCatalogScraper {
/** /**
* At UT, some courses have certain "flags" which aid in graduation. This will scrape the flags from the course catalog table row. * At UT, some courses have certain "flags" which aid in graduation. This will scrape the flags from the course catalog table row.
* @param row *
* @param row - the row of the course catalog table
* @returns an array of flags for the course * @returns an array of flags for the course
*/ */
getFlags(row: HTMLTableRowElement): string[] { getFlags(row: HTMLTableRowElement): string[] {
@@ -337,7 +353,8 @@ export class CourseCatalogScraper {
/** /**
* Get the list of core curriculum requirements the course satisfies * Get the list of core curriculum requirements the course satisfies
* @param row *
* @param row - the row of the course catalog table
* @returns an array of core curriculum codes * @returns an array of core curriculum codes
*/ */
getCore(row: HTMLTableRowElement): string[] { getCore(row: HTMLTableRowElement): string[] {
@@ -352,7 +369,8 @@ export class CourseCatalogScraper {
/** /**
* This will scrape all the time information from the course catalog table row and return it as a CourseSchedule object, which represents all of the meeting timiestimes/places of the course. * This will scrape all the time information from the course catalog table row and return it as a CourseSchedule object, which represents all of the meeting timiestimes/places of the course.
* @param row the row of the course catalog table *
* @param row - the row of the course catalog table
* @returns a CourseSchedule object representing all of the meetings of the course * @returns a CourseSchedule object representing all of the meetings of the course
*/ */
getSchedule(row: HTMLTableRowElement): CourseSchedule { getSchedule(row: HTMLTableRowElement): CourseSchedule {

View File

@@ -12,8 +12,9 @@ type GradeDistributionParams = {
}; };
/** /**
* fetches the aggregate distribution of grades for a given course from the course db, and the semesters that we have data for * Fetches the aggregate distribution of grades for a given course from the course db, and the semesters that we have data for
* @param course the course to fetch the distribution for *
* @param course - the course to fetch the distribution for
* @returns a Distribution object containing the distribution of grades for the course, and * @returns a Distribution object containing the distribution of grades for the course, and
* an array of semesters that we have the distribution for * an array of semesters that we have the distribution for
*/ */
@@ -97,8 +98,9 @@ export async function queryAggregateDistribution(course: Course): Promise<[Distr
/** /**
* Creates a SQL query that we can execute on the database to get the distribution of grades for a given course in a given semester * Creates a SQL query that we can execute on the database to get the distribution of grades for a given course in a given semester
* @param course the course to fetch the distribution for *
* @param semester the semester to fetch the distribution for OR null if we want the aggregate distribution * @param course - the course to fetch the distribution for
* @param semester - the semester to fetch the distribution for OR null if we want the aggregate distribution
* @returns a SQL query string * @returns a SQL query string
*/ */
function generateQuery( function generateQuery(
@@ -131,9 +133,10 @@ function generateQuery(
} }
/** /**
* fetches the distribution of grades for a semester for a given course from the course db * Fetches the distribution of grades for a semester for a given course from the course db
* @param course the course to fetch the distribution for *
* @param semester the semester to fetch the distribution for * @param course - the course to fetch the distribution for
* @param semester - the semester to fetch the distribution for
* @returns a Distribution object containing the distribution of grades for the course * @returns a Distribution object containing the distribution of grades for the course
*/ */
export async function querySemesterDistribution(course: Course, semester: Semester): Promise<[Distribution, boolean]> { export async function querySemesterDistribution(course: Course, semester: Semester): Promise<[Distribution, boolean]> {

View File

@@ -2,7 +2,8 @@ const TABLE_ROW_SELECTOR = 'table tbody tr';
/** /**
* Returns an array of all the rows in the course table on the passed in document * Returns an array of all the rows in the course table on the passed in document
* @param doc the document to get the course table rows from *
* @param doc - the document to get the course table rows from
* @returns an array of all the rows in the course table on the passed in document * @returns an array of all the rows in the course table on the passed in document
*/ */
export default function getCourseTableRows(doc: Document): HTMLTableRowElement[] { export default function getCourseTableRows(doc: Document): HTMLTableRowElement[] {

View File

@@ -24,7 +24,8 @@ export type SiteSupportType = (typeof SiteSupport)[keyof typeof SiteSupport];
/** /**
* We use this function to determine what page the user is on, and then we can use that information to determine what to do * We use this function to determine what page the user is on, and then we can use that information to determine what to do
* @param url the url of the current page *
* @param url - the url of the current page
* @returns a list of page types that the current page is * @returns a list of page types that the current page is
*/ */
export default function getSiteSupport(url: string): SiteSupportType | null { export default function getSiteSupport(url: string): SiteSupportType | null {

View File

@@ -1,7 +1,8 @@
/** /**
* Returns a formatted string of the last time the schedule was updated.
* *
* @param updatedAt {number} - The time in milliseconds since the epoch when the schedule was last updated. * @param updatedAt - The time in milliseconds since the epoch when the schedule was last updated.
* @returns {string} - DateTime formatted as HH:MM AM/PM MM/DD/YYYY * @returns DateTime formatted as HH:MM AM/PM MM/DD/YYYY
*/ */
export function getUpdatedAtDateTimeString(updatedAt: number): string { export function getUpdatedAtDateTimeString(updatedAt: number): string {
const updatedAtDate = new Date(updatedAt); const updatedAtDate = new Date(updatedAt);

View File

@@ -25,6 +25,7 @@ let nextPageURL = getNextButton(document)?.href;
/** /**
* This will scrape the pagination buttons from the course list and use them to load the next page * This will scrape the pagination buttons from the course list and use them to load the next page
* and then return the table rows from the next page * and then return the table rows from the next page
*
* @returns a tuple of the current LoadStatus (whether are currently loading the next page, or if we have reached the end of the course catalog, * @returns a tuple of the current LoadStatus (whether are currently loading the next page, or if we have reached the end of the course catalog,
* or if there was an error loading the next page) and an array of the table rows from the next page (or an empty array * or if there was an error loading the next page) and an array of the table rows from the next page (or an empty array
* if we have reached the end of the course catalog * if we have reached the end of the course catalog
@@ -67,7 +68,8 @@ export async function loadNextCourseCatalogPage(): Promise<[AutoLoadStatusType,
/** /**
* Scrapes the next button from the document * Scrapes the next button from the document
* @param doc the document to get the next button from *
* @param doc - the document to get the next button from
* @returns the next button from the document * @returns the next button from the document
*/ */
export function getNextButton(doc: Document) { export function getNextButton(doc: Document) {
@@ -76,7 +78,8 @@ export function getNextButton(doc: Document) {
/** /**
* Removes the next and previous buttons from the document so that we don't load the same page twice * Removes the next and previous buttons from the document so that we don't load the same page twice
* @param doc the document to remove the next and previous buttons from *
* @param doc - the document to remove the next and previous buttons from
*/ */
export function removePaginationButtons(doc: Document) { export function removePaginationButtons(doc: Document) {
const nextButton = doc.querySelectorAll<HTMLAnchorElement>(NEXT_PAGE_BUTTON_SELECTOR); const nextButton = doc.querySelectorAll<HTMLAnchorElement>(NEXT_PAGE_BUTTON_SELECTOR);