Compare commits

..

3 Commits

Author SHA1 Message Date
doprz
e4a368fbb6 fix: vitest path alias bug 2024-02-22 23:25:08 -06:00
doprz
29247d5dfa 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
2024-02-22 22:42:58 -06:00
Samuel Gunter
bb2efc0b46 feat: updated divider component (#99)
* feat: updated divider component

* refactor: inlined Divider's classes, simplified stories

* fix: style to unocss in story

* style: renamed variant to orientation for buttons

* docs: updated comments in Divider after prop name change
2024-02-22 21:24:41 -06:00
134 changed files with 1786 additions and 2597 deletions

View File

@@ -34,4 +34,4 @@
} }
] ]
] ]
} }

View File

@@ -11,8 +11,8 @@
"build": "tsc && vite build", "build": "tsc && vite build",
"prettier": "prettier src --check", "prettier": "prettier src --check",
"prettier:fix": "prettier src --write", "prettier:fix": "prettier src --write",
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "lint": "eslint src --ext ts,tsx --report-unused-disable-directives",
"lint:fix": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0 --fix", "lint:fix": "eslint src --ext ts,tsx --report-unused-disable-directives --fix",
"test": "vitest", "test": "vitest",
"test:ui": "vitest --ui", "test:ui": "vitest --ui",
"coverage": "vitest run --coverage", "coverage": "vitest run --coverage",
@@ -37,7 +37,6 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-devtools-core": "^5.0.0", "react-devtools-core": "^5.0.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-window": "^1.8.10",
"sass": "^1.71.1", "sass": "^1.71.1",
"sql.js": "1.10.2", "sql.js": "1.10.2",
"styled-components": "^6.1.8", "styled-components": "^6.1.8",

2681
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
import { defineManifest } from '@crxjs/vite-plugin'; import { defineManifest } from '@crxjs/vite-plugin';
import packageJson from '../package.json'; import packageJson from '../package.json';
// Convert from Semver (example: 0.1.0-beta6) // Convert from Semver (example: 0.1.0-beta6)

View File

@@ -1,5 +1,6 @@
import { BACKGROUND_MESSAGES } from '@shared/messages'; import type { BACKGROUND_MESSAGES } from '@shared/messages';
import { MessageListener } from 'chrome-extension-toolkit'; import { MessageListener } from 'chrome-extension-toolkit';
import onInstall from './events/onInstall'; import onInstall from './events/onInstall';
import onServiceWorkerAlive from './events/onServiceWorkerAlive'; import onServiceWorkerAlive from './events/onServiceWorkerAlive';
import onUpdate from './events/onUpdate'; import onUpdate from './events/onUpdate';

View File

@@ -1,5 +1,5 @@
import BrowserActionMessages from '@shared/messages/BrowserActionMessages'; import type BrowserActionMessages from '@shared/messages/BrowserActionMessages';
import { MessageHandler } from 'chrome-extension-toolkit'; import type { MessageHandler } from 'chrome-extension-toolkit';
const browserActionHandler: MessageHandler<BrowserActionMessages> = { const browserActionHandler: MessageHandler<BrowserActionMessages> = {
disableBrowserAction({ sender, sendResponse }) { disableBrowserAction({ sender, sendResponse }) {

View File

@@ -1,6 +1,6 @@
import HotReloadingMessages from '@shared/messages/HotReloadingMessages'; import type HotReloadingMessages from '@shared/messages/HotReloadingMessages';
import { DevStore } from '@shared/storage/DevStore'; import { DevStore } from '@shared/storage/DevStore';
import { MessageHandler } from 'chrome-extension-toolkit'; import type { MessageHandler } from 'chrome-extension-toolkit';
const hotReloadingHandler: MessageHandler<HotReloadingMessages> = { const hotReloadingHandler: MessageHandler<HotReloadingMessages> = {
async reloadExtension({ sendResponse }) { async reloadExtension({ sendResponse }) {

View File

@@ -1,5 +1,6 @@
import TabManagementMessages from '@shared/messages/TabManagementMessages'; import type TabManagementMessages from '@shared/messages/TabManagementMessages';
import { MessageHandler } from 'chrome-extension-toolkit'; import type { MessageHandler } from 'chrome-extension-toolkit';
import openNewTab from '../util/openNewTab'; import openNewTab from '../util/openNewTab';
const tabManagementHandler: MessageHandler<TabManagementMessages> = { const tabManagementHandler: MessageHandler<TabManagementMessages> = {

View File

@@ -1,6 +1,7 @@
import { UserScheduleMessages } from '@shared/messages/UserScheduleMessages'; import type { UserScheduleMessages } from '@shared/messages/UserScheduleMessages';
import { Course } from '@shared/types/Course'; import { Course } from '@shared/types/Course';
import { MessageHandler } from 'chrome-extension-toolkit'; import type { MessageHandler } from 'chrome-extension-toolkit';
import addCourse from '../lib/addCourse'; import addCourse from '../lib/addCourse';
import clearCourses from '../lib/clearCourses'; import clearCourses from '../lib/clearCourses';
import createSchedule from '../lib/createSchedule'; import createSchedule from '../lib/createSchedule';

View File

@@ -1,5 +1,5 @@
import { UserScheduleStore } from '@shared/storage/UserScheduleStore'; import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
import { Course } from '@shared/types/Course'; import type { Course } from '@shared/types/Course';
/** /**
* *

View File

@@ -1,5 +1,10 @@
import { UserScheduleStore } from '@shared/storage/UserScheduleStore'; import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
/**
* Clears the courses for a given schedule.
* @param scheduleName - The name of the schedule.
* @throws Error if the schedule does not exist.
*/
export default async function clearCourses(scheduleName: string): Promise<void> { export default async function clearCourses(scheduleName: string): Promise<void> {
const schedules = await UserScheduleStore.get('schedules'); const schedules = await UserScheduleStore.get('schedules');
const schedule = schedules.find(schedule => schedule.name === scheduleName); const schedule = schedules.find(schedule => schedule.name === scheduleName);

View File

@@ -14,7 +14,7 @@ export default async function createSchedule(scheduleName: string): Promise<stri
schedules.push({ schedules.push({
name: scheduleName, name: scheduleName,
courses: [], courses: [],
hours: 0 hours: 0,
}); });
await UserScheduleStore.set('schedules', schedules); await UserScheduleStore.set('schedules', schedules);

View File

@@ -1,5 +1,11 @@
import { UserScheduleStore } from '@shared/storage/UserScheduleStore'; import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
/**
* Deletes a schedule with the specified name.
*
* @param scheduleName - The name of the schedule to delete.
* @returns A promise that resolves to a string if there is an error, or undefined if the schedule is deleted successfully.
*/
export default async function deleteSchedule(scheduleName: string): Promise<string | undefined> { export default async function deleteSchedule(scheduleName: string): Promise<string | undefined> {
const [schedules, activeIndex] = await Promise.all([ const [schedules, activeIndex] = await Promise.all([
UserScheduleStore.get('schedules'), UserScheduleStore.get('schedules'),

View File

@@ -1,5 +1,5 @@
import { UserScheduleStore } from '@shared/storage/UserScheduleStore'; import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
import { Course } from '@shared/types/Course'; import type { Course } from '@shared/types/Course';
/** /**
* *

View File

@@ -1,5 +1,11 @@
import { UserScheduleStore } from '@shared/storage/UserScheduleStore'; import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
/**
* Renames a schedule with the specified name to a new name.
* @param scheduleName - The name of the schedule to be renamed.
* @param newName - The new name for the schedule.
* @returns A promise that resolves to a string if there is an error, or undefined if the schedule is renamed successfully.
*/
export default async function renameSchedule(scheduleName: string, newName: string): Promise<string | undefined> { export default async function renameSchedule(scheduleName: string, newName: string): Promise<string | undefined> {
const schedules = await UserScheduleStore.get('schedules'); const schedules = await UserScheduleStore.get('schedules');
const scheduleIndex = schedules.findIndex(schedule => schedule.name === scheduleName); const scheduleIndex = schedules.findIndex(schedule => schedule.name === scheduleName);

View File

@@ -1,5 +1,11 @@
import { UserScheduleStore } from '@shared/storage/UserScheduleStore'; import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
/**
* Switches the active schedule to the specified schedule name.
* Throws an error if the schedule does not exist.
* @param scheduleName - The name of the schedule to switch to.
* @returns A Promise that resolves when the active schedule is successfully switched.
*/
export default async function switchSchedule(scheduleName: string): Promise<void> { export default async function switchSchedule(scheduleName: string): Promise<void> {
const schedules = await UserScheduleStore.get('schedules'); const schedules = await UserScheduleStore.get('schedules');

View File

@@ -1,5 +1,5 @@
import React from 'react';
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot'; import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
import React from 'react';
import { Calendar } from 'src/views/components/calendar/Calendar/Calendar'; import { Calendar } from 'src/views/components/calendar/Calendar/Calendar';
/** /**

View File

@@ -1,18 +1,16 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<title>Calendar</title>
</head>
<head> <body>
<meta charset="utf-8" /> <noscript>You need to enable JavaScript to run this app.</noscript>
<meta name="viewport" content="width=device-width, initial-scale=1" /> <div id="root"></div>
<meta name="theme-color" content="#000000" />
<title>Calendar</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="./index.tsx" type="module"></script>
</body>
<script src="./index.tsx" type="module"></script>
</body>
</html> </html>

View File

@@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import CalendarMain from './CalendarMain'; import CalendarMain from './CalendarMain';
createRoot(document.getElementById('root')).render(<CalendarMain />); createRoot(document.getElementById('root')).render(<CalendarMain />);

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import CourseCatalogMain from '@views/components/CourseCatalogMain'; import CourseCatalogMain from '@views/components/CourseCatalogMain';
import getSiteSupport, { SiteSupport } from '@views/lib/getSiteSupport'; import getSiteSupport, { SiteSupport } from '@views/lib/getSiteSupport';
import React from 'react';
import { createRoot } from 'react-dom/client';
const support = getSiteSupport(window.location.href); const support = getSiteSupport(window.location.href);

View File

@@ -1,5 +1,5 @@
import React from 'react';
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot'; import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
import React from 'react';
/** /**
* *

View File

@@ -1,18 +1,16 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<title>Debug</title>
</head>
<head> <body>
<meta charset="utf-8" /> <noscript>You need to enable JavaScript to run this app.</noscript>
<meta name="viewport" content="width=device-width, initial-scale=1" /> <div id="root"></div>
<meta name="theme-color" content="#000000" />
<title>Debug</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="./index.tsx" type="module"></script>
</body>
<script src="./index.tsx" type="module"></script>
</body>
</html> </html>

View File

@@ -1,5 +1,5 @@
import React from 'react';
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot'; import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
import React from 'react';
/** /**
* *

View File

@@ -1,18 +1,16 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<title>Popup</title>
</head>
<head> <body>
<meta charset="utf-8" /> <noscript>You need to enable JavaScript to run this app.</noscript>
<meta name="viewport" content="width=device-width, initial-scale=1" /> <div id="root"></div>
<meta name="theme-color" content="#000000" />
<title>Popup</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="./index.tsx" type="module"></script>
</body>
<script src="./index.tsx" type="module"></script>
</body>
</html> </html>

View File

@@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import App from './App'; import App from './App';
createRoot(document.getElementById('root')).render(<App />); createRoot(document.getElementById('root')).render(<App />);

View File

@@ -1,18 +1,16 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<title>Popup</title>
</head>
<head> <body>
<meta charset="utf-8" /> <noscript>You need to enable JavaScript to run this app.</noscript>
<meta name="viewport" content="width=device-width, initial-scale=1" /> <div id="root"></div>
<meta name="theme-color" content="#000000" />
<title>Popup</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="./index.tsx" type="module"></script>
</body>
<script src="./index.tsx" type="module"></script>
</body>
</html> </html>

View File

@@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import PopupMain from '../../views/components/PopupMain'; import PopupMain from '../../views/components/PopupMain';
createRoot(document.getElementById('root')).render(<PopupMain />); createRoot(document.getElementById('root')).render(<PopupMain />);

View File

@@ -1,3 +1,4 @@
/* eslint-disable jsdoc/require-jsdoc */
export default interface BrowserActionMessages { export default interface BrowserActionMessages {
/** make it so that clicking the browser action will open the popup.html */ /** make it so that clicking the browser action will open the popup.html */
enableBrowserAction: () => void; enableBrowserAction: () => void;

View File

@@ -1,3 +1,4 @@
/* eslint-disable jsdoc/require-jsdoc */
export default interface HotReloadingMessages { export default interface HotReloadingMessages {
reloadExtension: () => void; reloadExtension: () => void;
} }

View File

@@ -1,5 +1,8 @@
import { Course } from '../types/Course'; import type { Course } from '@shared/types/Course';
/**
* Represents a collection of user schedule messages.
*/
export interface UserScheduleMessages { export interface UserScheduleMessages {
/** /**
* Add a course to a schedule * Add a course to a schedule

View File

@@ -1,8 +1,9 @@
import { createMessenger } from 'chrome-extension-toolkit'; import { createMessenger } from 'chrome-extension-toolkit';
import BrowserActionMessages from './BrowserActionMessages';
import TabManagementMessages from './TabManagementMessages'; import type BrowserActionMessages from './BrowserActionMessages';
import TAB_MESSAGES from './TabMessages'; import type TabManagementMessages from './TabManagementMessages';
import { UserScheduleMessages } from './UserScheduleMessages'; import type TAB_MESSAGES from './TabMessages';
import type { UserScheduleMessages } from './UserScheduleMessages';
/** /**
* This is a type with all the message definitions that can be sent TO the background script * This is a type with all the message definitions that can be sent TO the background script

View File

@@ -15,6 +15,4 @@ export const ExtensionStore = createLocalStore<IExtensionStore>({
lastUpdate: Date.now(), lastUpdate: Date.now(),
}); });
debugStore({ ExtensionStore }); debugStore({ ExtensionStore });

View File

@@ -1,6 +1,6 @@
/* eslint-disable max-classes-per-file */ import type { Serialized } from 'chrome-extension-toolkit';
import { Serialized } from 'chrome-extension-toolkit';
import { CourseMeeting } from './CourseMeeting'; import type { CourseMeeting } from './CourseMeeting';
import { CourseSchedule } from './CourseSchedule'; import { CourseSchedule } from './CourseSchedule';
import Instructor from './Instructor'; import Instructor from './Instructor';
@@ -12,12 +12,17 @@ export type InstructionMode = 'Online' | 'In Person' | 'Hybrid';
/** /**
* The status of a course (e.g. open, closed, waitlisted, cancelled) * The status of a course (e.g. open, closed, waitlisted, cancelled)
*/ */
export enum Status { export const Status = {
OPEN = 'OPEN', OPEN: 'OPEN',
CLOSED = 'CLOSED', CLOSED: 'CLOSED',
WAITLISTED = 'WAITLISTED', WAITLISTED: 'WAITLISTED',
CANCELLED = 'CANCELLED', CANCELLED: 'CANCELLED',
} } as const;
/**
* Represents the type of status for a course.
*/
export type StatusType = (typeof Status)[keyof typeof Status];
/** /**
* Represents a semester, with the year and the season for when a course is offered * Represents a semester, with the year and the season for when a course is offered
@@ -49,7 +54,7 @@ export class Course {
/** The number of credits that a course is worth */ /** The number of credits that a course is worth */
creditHours: number; creditHours: number;
/** Is the course open, closed, waitlisted, or cancelled? */ /** Is the course open, closed, waitlisted, or cancelled? */
status: Status; status: StatusType;
/** all the people that are teaching this course, and some metadata about their names */ /** all the people that are teaching this course, and some metadata about their names */
instructors: Instructor[]; instructors: Instructor[];
/** Some courses at UT are reserved for certain groups of people or people within a certain major, which makes it difficult for people outside of that group to register for the course. */ /** Some courses at UT are reserved for certain groups of people or people within a certain major, which makes it difficult for people outside of that group to register for the course. */

View File

@@ -1,4 +1,4 @@
import { Serialized } from 'chrome-extension-toolkit'; import type { Serialized } from 'chrome-extension-toolkit';
/** /**
* a map of the days of the week that a class is taught, and the corresponding abbreviation * a map of the days of the week that a class is taught, and the corresponding abbreviation

View File

@@ -1,5 +1,7 @@
import { Serialized } from 'chrome-extension-toolkit'; import type { Serialized } from 'chrome-extension-toolkit';
import { CourseMeeting, Day, DAY_MAP } from './CourseMeeting';
import type { Day } from './CourseMeeting';
import { CourseMeeting, DAY_MAP } from './CourseMeeting';
/** /**
* This represents the schedule for a course, which includes all the meeting times for the course, as well as helper functions for parsing, serializing, and deserializing the schedule * This represents the schedule for a course, which includes all the meeting times for the course, as well as helper functions for parsing, serializing, and deserializing the schedule

View File

@@ -1,5 +1,5 @@
/* eslint-disable max-classes-per-file */
import { PointOptionsObject } from 'highcharts'; import { PointOptionsObject } from 'highcharts';
import { Semester } from './Course'; import { Semester } from './Course';
/** /**
* Each of the possible letter grades that can be given in a course * Each of the possible letter grades that can be given in a course

View File

@@ -1,4 +1,5 @@
import { Serialized } from 'chrome-extension-toolkit'; import type { Serialized } from 'chrome-extension-toolkit';
import { capitalize } from '../util/string'; import { capitalize } from '../util/string';
/** /**

View File

@@ -1,4 +1,5 @@
import { Serialized } from 'chrome-extension-toolkit'; import type { Serialized } from 'chrome-extension-toolkit';
import { Course } from './Course'; import { Course } from './Course';
/** /**
@@ -21,4 +22,4 @@ export class UserSchedule {
containsCourse(course: Course): boolean { containsCourse(course: Course): boolean {
return this.courses.some(c => c.uniqueId === course.uniqueId); return this.courses.some(c => c.uniqueId === course.uniqueId);
} }
} }

View File

@@ -1,11 +1,19 @@
import { theme } from 'unocss/preset-mini'; import { theme } from 'unocss/preset-mini';
/**
* Represents the colors for a course.
*/
export interface CourseColors { export interface CourseColors {
primaryColor: string; primaryColor: string;
secondaryColor: string; secondaryColor: string;
} }
// calculates luminance of a hex string /**
* Calculates the luminance of a given hexadecimal color.
*
* @param hex - The hexadecimal color value.
* @returns The luminance value between 0 and 1.
*/
export function getLuminance(hex: string): number { export function getLuminance(hex: string): number {
let r = parseInt(hex.substring(1, 3), 16); let r = parseInt(hex.substring(1, 3), 16);
let g = parseInt(hex.substring(3, 5), 16); let g = parseInt(hex.substring(3, 5), 16);

View File

@@ -1,15 +1,18 @@
import React, { SVGProps } from 'react'; import type { StatusType } from '@shared/types/Course';
import { Status } from '@shared/types/Course';
import type { SVGProps } from 'react';
import React from 'react';
import ClosedIcon from '~icons/material-symbols/lock'; 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';
import { Status } from '../types/Course';
/** /**
* Get Icon component based on status * Get Icon component based on status
* @param props.status status * @param props.status status
* @returns React.ReactElement - the icon component * @returns React.ReactElement - the icon component
*/ */
export function StatusIcon(props: SVGProps<SVGSVGElement> & { status: Status }): React.ReactElement { export function StatusIcon(props: SVGProps<SVGSVGElement> & { status: StatusType }): React.ReactElement {
const { status, ...rest } = props; const { status, ...rest } = props;
switch (props.status) { switch (props.status) {

View File

@@ -18,6 +18,8 @@ export function capitalize(input: string): string {
} }
capitalized += ' '; capitalized += ' ';
} }
capitalized = capitalized.trim(); // Remove extra space
return capitalized; return capitalized;
} }
@@ -31,7 +33,7 @@ export function capitalizeFirstLetter(input: string): string {
} }
/** /**
* Cuts the * 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 input The string to ellipsify.
* @param length The length of the string to return. * @param length The length of the string to return.
* @returns The ellipsified string. * @returns The ellipsified string.

View File

@@ -1,17 +1,17 @@
import { describe, expect, it } from 'vitest'; import { describe, expect, it } from 'vitest';
import { capitalize } from '../string'; import { capitalize, capitalizeFirstLetter, ellipsify } from '../string';
// TODO: Fix `string.ts` and `string.test.ts` to make the tests pass // TODO: Fix `string.ts` and `string.test.ts` to make the tests pass
// `capitalize` is adding an extra space at the end of the word. // `capitalize` is adding an extra space at the end of the word.
describe('capitalize', () => { describe('capitalize', () => {
it('should capitalize the first letter of each word', () => { it('should capitalize the first letter of each word', () => {
// Debug // Debug
const word = 'hello world'; // const word = 'hello world';
const capitalized = capitalize(word); // const capitalized = capitalize(word);
console.log(capitalize(word)); // console.log(capitalize(word));
console.log(capitalized.length); // console.log(capitalized.length);
console.log(capitalized.split('')); // console.log(capitalized.split(''));
// Test case 1: Single word // Test case 1: Single word
expect(capitalize('hello')).toBe('Hello'); expect(capitalize('hello')).toBe('Hello');
@@ -25,15 +25,40 @@ describe('capitalize', () => {
// Test case 4: Words with hyphens and spaces // Test case 4: Words with hyphens and spaces
expect(capitalize('hello-world test')).toBe('Hello-World Test'); expect(capitalize('hello-world test')).toBe('Hello-World Test');
}); });
});
it('should not change the capitalization of the remaining letters', () => { describe('capitalizeFirstLetter', () => {
// Test case 1: All lowercase it('should return a string with the first letter capitalized', () => {
expect(capitalize('hello')).toBe('Hello'); // Test case 1: Single word
expect(capitalizeFirstLetter('hello')).toBe('Hello');
// Test case 2: All uppercase // Test case 2: Word with all lowercase letters
expect(capitalize('WORLD')).toBe('WORLD'); expect(capitalizeFirstLetter('world')).toBe('World');
// Test case 3: Mixed case // Test case 3: Word with all uppercase letters
expect(capitalize('HeLLo WoRLd')).toBe('Hello World'); expect(capitalizeFirstLetter('EXAMPLE')).toBe('Example');
// Test case 4: Word with mixed case letters
expect(capitalizeFirstLetter('tEsT')).toBe('Test');
});
it('should handle empty string input', () => {
expect(capitalizeFirstLetter('')).toBe('');
});
});
describe('ellipsify', () => {
it('should add ellipsis if the input string exceeds the specified character limit', () => {
// Test case 1: Input string is shorter than the character limit
expect(ellipsify('Hello', 10)).toBe('Hello');
// Test case 2: Input string is equal to the character limit
expect(ellipsify('Hello World', 11)).toBe('Hello World');
// Test case 3: Input string is longer than the character limit
expect(ellipsify('Hello World', 5)).toBe('Hello...');
// Test case 4: Input string is empty
expect(ellipsify('', 5)).toBe('');
}); });
}); });

View File

@@ -0,0 +1,51 @@
import { describe, expect, it } from 'vitest';
import { getThemeColorHexByName, getThemeColorRgbByName, hexToRgb } from '../themeColors';
describe('hexToRgb', () => {
it('should convert hex color to RGB', () => {
expect(hexToRgb('#BF5700')).toEqual([191, 87, 0]);
expect(hexToRgb('#333F48')).toEqual([51, 63, 72]);
expect(hexToRgb('#f8971f')).toEqual([248, 151, 31]);
expect(hexToRgb('#ffd600')).toEqual([255, 214, 0]);
expect(hexToRgb('#a6cd57')).toEqual([166, 205, 87]);
expect(hexToRgb('#579d42')).toEqual([87, 157, 66]);
expect(hexToRgb('#00a9b7')).toEqual([0, 169, 183]);
expect(hexToRgb('#005f86')).toEqual([0, 95, 134]);
expect(hexToRgb('#9cadb7')).toEqual([156, 173, 183]);
expect(hexToRgb('#d6d2c4')).toEqual([214, 210, 196]);
expect(hexToRgb('#95a5a6')).toEqual([149, 165, 166]);
expect(hexToRgb('#B91C1C')).toEqual([185, 28, 28]);
expect(hexToRgb('#af2e2d')).toEqual([175, 46, 45]);
expect(hexToRgb('#1a2024')).toEqual([26, 32, 36]);
expect(hexToRgb('#22c55e')).toEqual([34, 197, 94]);
expect(hexToRgb('#a3e635')).toEqual([163, 230, 53]);
expect(hexToRgb('#84CC16')).toEqual([132, 204, 22]);
expect(hexToRgb('#FDE047')).toEqual([253, 224, 71]);
expect(hexToRgb('#FACC15')).toEqual([250, 204, 21]);
expect(hexToRgb('#F59E0B')).toEqual([245, 158, 11]);
expect(hexToRgb('#FB923C')).toEqual([251, 146, 60]);
expect(hexToRgb('#F97316')).toEqual([249, 115, 22]);
expect(hexToRgb('#EA580C')).toEqual([234, 88, 12]);
expect(hexToRgb('#DC2626')).toEqual([220, 38, 38]);
expect(hexToRgb('#B91C1C')).toEqual([185, 28, 28]);
});
});
describe('getThemeColorHexByName', () => {
it('should return the hex color value by name', () => {
expect(getThemeColorHexByName('ut-burntorange')).toEqual('#BF5700');
expect(getThemeColorHexByName('ut-offwhite')).toEqual('#D6D2C4');
expect(getThemeColorHexByName('ut-black')).toEqual('#333F48');
// Add more test cases for other theme color names
});
});
describe('getThemeColorRgbByName', () => {
it('should return the RGB color value by name', () => {
expect(getThemeColorRgbByName('ut-burntorange')).toEqual([191, 87, 0]);
expect(getThemeColorRgbByName('ut-offwhite')).toEqual([214, 210, 196]);
expect(getThemeColorRgbByName('ut-black')).toEqual([51, 63, 72]);
// Add more test cases for other theme color names
});
});

View File

@@ -2,24 +2,24 @@ export const colors = {
ut: { ut: {
burntorange: '#BF5700', burntorange: '#BF5700',
black: '#333F48', black: '#333F48',
orange: '#f8971f', orange: '#F8971F',
yellow: '#ffd600', yellow: '#FFD600',
lightgreen: '#a6cd57', lightgreen: '#A6CD57',
green: '#579d42', green: '#579D42',
teal: '#00a9b7', teal: '#00A9B7',
blue: '#005f86', blue: '#005F86',
gray: '#9cadb7', gray: '#9CADB7',
offwhite: '#d6d2c4', offwhite: '#D6D2C4',
concrete: '#95a5a6', concrete: '#95A5A6',
red: '#B91C1C' // Not sure if this should be here, but it's used for remove course, and add course is ut-green red: '#B91C1C', // Not sure if this should be here, but it's used for remove course, and add course is ut-green
}, },
theme: { theme: {
red: '#af2e2d', red: '#AF2E2D',
black: '#1a2024', black: '#1A2024',
}, },
gradeDistribution: { gradeDistribution: {
a: '#22c55e', a: '#22C55E',
aminus: '#a3e635', aminus: '#A3E635',
bplus: '#84CC16', bplus: '#84CC16',
b: '#FDE047', b: '#FDE047',
bminus: '#FACC15', bminus: '#FACC15',
@@ -31,7 +31,7 @@ export const colors = {
dminus: '#B91C1C', dminus: '#B91C1C',
f: '#B91C1C', f: '#B91C1C',
}, },
} as const; } as const satisfies Record<string, Record<string, string>>;
type NestedKeys<T> = { type NestedKeys<T> = {
[K in keyof T]: T[K] extends Record<string, any> ? `${string & K}-${string & keyof T[K]}` : never; [K in keyof T]: T[K] extends Record<string, any> ? `${string & K}-${string & keyof T[K]}` : never;
@@ -42,6 +42,10 @@ type NestedKeys<T> = {
*/ */
export type ThemeColor = NestedKeys<typeof colors>; export type ThemeColor = NestedKeys<typeof colors>;
/**
* Flattened colors object.
* @type {Record<ThemeColor, string>}
*/
export const colorsFlattened = Object.entries(colors).reduce( export const colorsFlattened = Object.entries(colors).reduce(
(acc, [prefix, group]) => { (acc, [prefix, group]) => {
for (const [name, hex] of Object.entries(group)) { for (const [name, hex] of Object.entries(group)) {
@@ -52,9 +56,18 @@ export const colorsFlattened = Object.entries(colors).reduce(
{} as Record<ThemeColor, string> {} as Record<ThemeColor, string>
); );
const hexToRgb = (hex: string) => /**
* Converts a hexadecimal color code to an RGB color array.
* @param hex The hexadecimal color code to convert.
* @returns An array representing the RGB color values.
*/
export const hexToRgb = (hex: string) =>
hex.match(/[0-9a-f]{2}/gi).map(partialHex => parseInt(partialHex, 16)) as [number, number, number]; hex.match(/[0-9a-f]{2}/gi).map(partialHex => parseInt(partialHex, 16)) as [number, number, number];
/**
* Represents the flattened RGB values of the colors.
* @type {Record<ThemeColor, ReturnType<typeof hexToRgb>>}
*/
const colorsFlattenedRgb = Object.fromEntries( const colorsFlattenedRgb = Object.fromEntries(
Object.entries(colorsFlattened).map(([name, hex]) => [name, hexToRgb(hex)]) Object.entries(colorsFlattened).map(([name, hex]) => [name, hexToRgb(hex)])
) as Record<ThemeColor, ReturnType<typeof hexToRgb>>; ) as Record<ThemeColor, ReturnType<typeof hexToRgb>>;

View File

@@ -5,7 +5,9 @@ export const HOUR = 60 * MINUTE;
export const DAY = 24 * HOUR; export const DAY = 24 * HOUR;
/** /**
* * Pauses the execution for the specified number of milliseconds.
* @param milliseconds - The number of milliseconds to sleep.
* @returns A promise that resolves after the specified number of milliseconds.
*/ */
export const sleep = (milliseconds: number): Promise<void> => new Promise(resolve => setTimeout(resolve, milliseconds)); export const sleep = (milliseconds: number): Promise<void> => new Promise(resolve => setTimeout(resolve, milliseconds));

View File

@@ -1,7 +1,8 @@
import { colorsFlattened } from '@shared/util/themeColors';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import { Button } from '@views/components/common/Button/Button';
import React from 'react'; import React from 'react';
import { colorsFlattened } from 'src/shared/util/themeColors';
import { Button } from 'src/views/components/common/Button/Button';
import AddIcon from '~icons/material-symbols/add'; import AddIcon from '~icons/material-symbols/add';
import CalendarMonthIcon from '~icons/material-symbols/calendar-month'; import CalendarMonthIcon from '~icons/material-symbols/calendar-month';
import DescriptionIcon from '~icons/material-symbols/description'; import DescriptionIcon from '~icons/material-symbols/description';
@@ -135,7 +136,7 @@ export const CourseCatalogActionButtons: Story = {
<Button {...props} variant='outline' color='ut-teal' icon={HappyFaceIcon}> <Button {...props} variant='outline' color='ut-teal' icon={HappyFaceIcon}>
CES CES
</Button> </Button>
<Button {...props} variant='outline' color='ut-yellow' icon={DescriptionIcon}> <Button {...props} variant='outline' color='ut-orange' icon={DescriptionIcon}>
Past Syllabi Past Syllabi
</Button> </Button>
<Button {...props} variant='filled' color='ut-green' icon={AddIcon}> <Button {...props} variant='filled' color='ut-green' icon={AddIcon}>

View File

@@ -1,20 +1,20 @@
import Card from 'src/views/components/common/Card/Card';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import Card from '@views/components/common/Card/Card';
import React from 'react'; import React from 'react';
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
const meta = { const meta = {
title: 'Components/Common/Card', title: 'Components/Common/Card',
component: Card, component: Card,
parameters: { parameters: {
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
layout: 'centered', layout: 'centered',
}, },
args: { args: {
children: <div>Hello</div>, children: <div>Hello</div>,
}, },
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
tags: ['autodocs'], tags: ['autodocs'],
} satisfies Meta<typeof Card>; } satisfies Meta<typeof Card>;
export default meta; export default meta;

View File

@@ -1,5 +1,5 @@
import { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import { Chip } from 'src/views/components/common/Chip/Chip'; import { Chip } from '@views/components/common/Chip/Chip';
const meta = { const meta = {
title: 'Components/Common/Chip', title: 'Components/Common/Chip',
@@ -20,4 +20,4 @@ export const Default: Story = {
args: { args: {
label: 'QR', label: 'QR',
}, },
}; };

View File

@@ -1,8 +1,8 @@
import { Meta, StoryObj } from '@storybook/react'; import { Course, Status } from '@shared/types/Course';
import { CourseMeeting } from '@shared/types/CourseMeeting';
import Instructor from '@shared/types/Instructor';
import type { Meta, StoryObj } from '@storybook/react';
import ConflictsWithWarning from '@views/components/common/ConflictsWithWarning/ConflictsWithWarning'; import ConflictsWithWarning from '@views/components/common/ConflictsWithWarning/ConflictsWithWarning';
import { Course, Status } from 'src/shared/types/Course';
import { CourseMeeting } from 'src/shared/types/CourseMeeting';
import Instructor from 'src/shared/types/Instructor';
export const ExampleCourse: Course = new Course({ export const ExampleCourse: Course = new Course({
courseName: 'ELEMS OF COMPTRS/PROGRAMMNG-WB', courseName: 'ELEMS OF COMPTRS/PROGRAMMNG-WB',

View File

@@ -1,8 +1,7 @@
import React from 'react';
import { Status } from '@shared/types/Course'; import { Status } from '@shared/types/Course';
import { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import CourseStatus from '@views/components/common/CourseStatus/CourseStatus'; import CourseStatus from '@views/components/common/CourseStatus/CourseStatus';
import React from 'react';
const meta = { const meta = {
title: 'Components/Common/CourseStatus', title: 'Components/Common/CourseStatus',
@@ -40,7 +39,7 @@ export const Default: Story = {};
export const Variants: Story = { export const Variants: Story = {
render: args => ( render: args => (
<div className='flex flex-col gap-4 items-center'> <div className='flex flex-col items-center gap-4'>
<CourseStatus {...args} size='small' /> <CourseStatus {...args} size='small' />
<CourseStatus {...args} size='mini' /> <CourseStatus {...args} size='mini' />
</div> </div>

View File

@@ -1,18 +0,0 @@
import Divider from 'src/views/components/common/Divider/Divider';
import type { Meta, StoryObj } from '@storybook/react';
const meta = {
title: 'Components/Common/Divider',
component: Divider,
tags: ['autodocs'],
argTypes: {
color: {
control: 'color',
},
},
} satisfies Meta<typeof Divider>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {};

View File

@@ -0,0 +1,78 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from '@views/components/common/Button/Button';
import Divider from '@views/components/common/Divider/Divider';
import React from 'react';
import AddIcon from '~icons/material-symbols/add';
import CalendarMonthIcon from '~icons/material-symbols/calendar-month';
import DescriptionIcon from '~icons/material-symbols/description';
import HappyFaceIcon from '~icons/material-symbols/mood';
import ReviewsIcon from '~icons/material-symbols/reviews';
const meta = {
title: 'Components/Common/Divider',
component: Divider,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
} satisfies Meta<typeof Divider>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Vertical: Story = {
args: {
size: '2.5rem',
orientation: 'vertical',
},
render: props => <Divider {...props} />,
};
export const Horizontal: Story = {
args: {
size: '2.5rem',
orientation: 'horizontal',
},
render: props => <Divider {...props} />,
};
export const IGotHorizontalIGotVerticalWhatYouWant: Story = {
args: {
size: '2.5rem',
orientation: 'vertical',
},
render: props => (
<div className='grid grid-cols-7 grid-rows-3 items-center justify-items-center gap-3.75'>
{Array.from({ length: 21 }).map((_, i) => (
<Divider {...props} orientation={i % 2 === 0 ? 'horizontal' : 'vertical'} />
))}
</div>
),
};
export const CourseCatalogActionButtons: Story = {
args: {
size: '1.75rem',
orientation: 'vertical',
},
render: props => (
<div className='flex items-center gap-3.75'>
<Button variant='filled' color='ut-burntorange' icon={CalendarMonthIcon} />
<Divider {...props} />
<Button variant='outline' color='ut-blue' icon={ReviewsIcon}>
RateMyProf
</Button>
<Button variant='outline' color='ut-teal' icon={HappyFaceIcon}>
CES
</Button>
<Button variant='outline' color='ut-orange' icon={DescriptionIcon}>
Past Syllabi
</Button>
<Button variant='filled' color='ut-green' icon={AddIcon}>
Add Course
</Button>
</div>
),
};

View File

@@ -1,13 +1,13 @@
import { Course, Status } from '@shared/types/Course'; import { Course, Status } from '@shared/types/Course';
import { CourseMeeting, DAY_MAP } from '@shared/types/CourseMeeting';
import { CourseSchedule } from '@shared/types/CourseSchedule';
import Instructor from '@shared/types/Instructor';
import { UserSchedule } from '@shared/types/UserSchedule'; import { UserSchedule } from '@shared/types/UserSchedule';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import Dropdown from '@views/components/common/Dropdown/Dropdown';
import ScheduleListItem from '@views/components/common/ScheduleListItem/ScheduleListItem';
import type { Serialized } from 'chrome-extension-toolkit'; import type { Serialized } from 'chrome-extension-toolkit';
import React from 'react'; import React from 'react';
import { CourseMeeting, DAY_MAP } from 'src/shared/types/CourseMeeting';
import { CourseSchedule } from 'src/shared/types/CourseSchedule';
import Instructor from 'src/shared/types/Instructor';
import Dropdown from 'src/views/components/common/Dropdown/Dropdown';
import ScheduleListItem from 'src/views/components/common/ScheduleListItem/ScheduleListItem';
const meta: Meta<typeof Dropdown> = { const meta: Meta<typeof Dropdown> = {
title: 'Components/Common/Dropdown', title: 'Components/Common/Dropdown',

View File

@@ -1,5 +1,5 @@
import { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import ImportantLinks from 'src/views/components/calendar/ImportantLinks'; import ImportantLinks from '@views/components/calendar/ImportantLinks';
const meta = { const meta = {
title: 'Components/Common/ImportantLinks', title: 'Components/Common/ImportantLinks',

View File

@@ -1,5 +1,5 @@
import { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import { InfoCard } from 'src/views/components/common/InfoCard/InfoCard'; import { InfoCard } from '@views/components/common/InfoCard/InfoCard';
const meta = { const meta = {
title: 'Components/Common/InfoCard', title: 'Components/Common/InfoCard',
@@ -22,4 +22,4 @@ export const Default: Story = {
titleText: 'WAITLIST SIZE', titleText: 'WAITLIST SIZE',
bodyText: '14 Students', bodyText: '14 Students',
}, },
}; };

View File

@@ -1,21 +1,21 @@
import Link from 'src/views/components/common/Link/Link';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import Link from '@views/components/common/Link/Link';
const meta = { const meta = {
title: 'Components/Common/Link', title: 'Components/Common/Link',
component: Link, component: Link,
parameters: { parameters: {
layout: 'centered', layout: 'centered',
}, },
tags: ['autodocs'], tags: ['autodocs'],
argTypes: { argTypes: {
color: { color: {
control: 'color', control: 'color',
},
},
args: {
children: 'Link',
}, },
},
args: {
children: 'Link',
},
} satisfies Meta<typeof Link>; } satisfies Meta<typeof Link>;
export default meta; export default meta;

View File

@@ -1,15 +1,22 @@
import { Course, Status } from '@shared/types/Course'; import { Course, Status } from '@shared/types/Course';
import { CourseMeeting } from '@shared/types/CourseMeeting'; import { CourseMeeting } from '@shared/types/CourseMeeting';
import Instructor from '@shared/types/Instructor'; import Instructor from '@shared/types/Instructor';
import { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import List from '@views/components/common/List/List'; import List from '@views/components/common/List/List';
import PopupCourseBlock from '@views/components/common/PopupCourseBlock/PopupCourseBlock'; import PopupCourseBlock from '@views/components/common/PopupCourseBlock/PopupCourseBlock';
import React from 'react'; import React from 'react';
import { test_colors } from './PopupCourseBlock.stories';
import { TestColors } from './PopupCourseBlock.stories';
const numberOfCourses = 5; const numberOfCourses = 5;
export const generateCourses = count => { /**
* Generates an array of courses.
*
* @param count - The number of courses to generate.
* @returns An array of generated courses.
*/
export const GenerateCourses = count => {
const courses = []; const courses = [];
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
@@ -61,10 +68,10 @@ export const generateCourses = count => {
return courses; return courses;
}; };
const exampleCourses = generateCourses(numberOfCourses); const exampleCourses = GenerateCourses(numberOfCourses);
const generateCourseBlocks = (exampleCourses, colors) => const generateCourseBlocks = (exampleCourses, colors) =>
exampleCourses.map((course, i) => <PopupCourseBlock key={course.uniqueId} course={course} colors={colors[i]} />); exampleCourses.map((course, i) => <PopupCourseBlock key={course.uniqueId} course={course} colors={colors[i]} />);
export const exampleCourseBlocks = generateCourseBlocks(exampleCourses, test_colors); export const ExampleCourseBlocks = generateCourseBlocks(exampleCourses, TestColors);
const meta = { const meta = {
title: 'Components/Common/List', title: 'Components/Common/List',
@@ -87,7 +94,7 @@ type Story = StoryObj<typeof meta>;
export const Default: Story = { export const Default: Story = {
args: { args: {
draggableElements: exampleCourseBlocks, draggableElements: ExampleCourseBlocks,
itemHeight: 55, itemHeight: 55,
listHeight: 300, listHeight: 300,
listWidth: 300, listWidth: 300,

View File

@@ -1,13 +1,19 @@
import { Course, Status } from '@shared/types/Course';
import { CourseMeeting } from '@shared/types/CourseMeeting';
import Instructor from '@shared/types/Instructor';
import { getCourseColors } from '@shared/util/colors';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import PopupCourseBlock from '@views/components/common/PopupCourseBlock/PopupCourseBlock'; import PopupCourseBlock from '@views/components/common/PopupCourseBlock/PopupCourseBlock';
import React from 'react'; import React from 'react';
import { Course, Status } from 'src/shared/types/Course';
import { CourseMeeting } from 'src/shared/types/CourseMeeting';
import Instructor from 'src/shared/types/Instructor';
import { getCourseColors } from 'src/shared/util/colors';
import { theme } from 'unocss/preset-mini'; import { theme } from 'unocss/preset-mini';
export const exampleCourse: Course = new Course({ /**
* Represents an example course.
*
* @remarks
* This is a sample course object that provides information about a specific course.
*/
export const ExampleCourse: Course = new Course({
courseName: 'ELEMS OF COMPTRS/PROGRAMMNG-WB', courseName: 'ELEMS OF COMPTRS/PROGRAMMNG-WB',
creditHours: 3, creditHours: 3,
department: 'C S', department: 'C S',
@@ -62,7 +68,7 @@ const meta = {
// More on argTypes: https://storybook.js.org/docs/api/argtypes // More on argTypes: https://storybook.js.org/docs/api/argtypes
args: { args: {
colors: getCourseColors('emerald'), colors: getCourseColors('emerald'),
course: exampleCourse, course: ExampleCourse,
}, },
argTypes: { argTypes: {
colors: { colors: {
@@ -87,15 +93,15 @@ export const Default: Story = {
export const Variants: Story = { export const Variants: Story = {
render: props => ( render: props => (
<div className='grid grid-cols-2 max-w-2xl w-90vw gap-x-4 gap-y-2'> <div className='grid grid-cols-2 max-w-2xl w-90vw gap-x-4 gap-y-2'>
<PopupCourseBlock {...props} course={new Course({ ...exampleCourse, status: Status.OPEN })} /> <PopupCourseBlock {...props} course={new Course({ ...ExampleCourse, status: Status.OPEN })} />
<PopupCourseBlock {...props} course={new Course({ ...exampleCourse, status: Status.CLOSED })} /> <PopupCourseBlock {...props} course={new Course({ ...ExampleCourse, status: Status.CLOSED })} />
<PopupCourseBlock {...props} course={new Course({ ...exampleCourse, status: Status.WAITLISTED })} /> <PopupCourseBlock {...props} course={new Course({ ...ExampleCourse, status: Status.WAITLISTED })} />
<PopupCourseBlock {...props} course={new Course({ ...exampleCourse, status: Status.CANCELLED })} /> <PopupCourseBlock {...props} course={new Course({ ...ExampleCourse, status: Status.CANCELLED })} />
</div> </div>
), ),
}; };
export const test_colors = Object.keys(theme.colors) export const TestColors = Object.keys(theme.colors)
// check that the color is a colorway (is an object) // check that the color is a colorway (is an object)
.filter(color => typeof theme.colors[color] === 'object') .filter(color => typeof theme.colors[color] === 'object')
.slice(0, 17) .slice(0, 17)
@@ -104,8 +110,8 @@ export const test_colors = Object.keys(theme.colors)
export const AllColors: Story = { export const AllColors: Story = {
render: props => ( render: props => (
<div className='grid grid-flow-col grid-cols-2 grid-rows-9 max-w-2xl w-90vw gap-x-4 gap-y-2'> <div className='grid grid-flow-col grid-cols-2 grid-rows-9 max-w-2xl w-90vw gap-x-4 gap-y-2'>
{test_colors.map((color, i) => ( {TestColors.map((color, i) => (
<PopupCourseBlock key={color.primaryColor} course={exampleCourse} colors={color} /> <PopupCourseBlock key={color.primaryColor} course={ExampleCourse} colors={color} />
))} ))}
</div> </div>
), ),

View File

@@ -1,4 +1,4 @@
import { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import PopupMain from '@views/components/PopupMain'; import PopupMain from '@views/components/PopupMain';
const meta = { const meta = {
@@ -8,16 +8,12 @@ const meta = {
layout: 'centered', layout: 'centered',
}, },
tags: ['autodocs'], tags: ['autodocs'],
argTypes: { argTypes: {},
},
} satisfies Meta<typeof PopupMain>; } satisfies Meta<typeof PopupMain>;
export default meta; export default meta;
type Story = StoryObj<typeof meta>; type Story = StoryObj<typeof meta>;
export const Default: Story = { export const Default: Story = {
args: { args: {},
};
},
};

View File

@@ -1,4 +1,4 @@
import { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import ScheduleTotalHoursAndCourses from '@views/components/common/ScheduleTotalHoursAndCourses/ScheduleTotalHoursAndCourses'; import ScheduleTotalHoursAndCourses from '@views/components/common/ScheduleTotalHoursAndCourses/ScheduleTotalHoursAndCourses';
const meta = { const meta = {
@@ -11,7 +11,7 @@ const meta = {
argTypes: { argTypes: {
scheduleName: { control: 'text' }, scheduleName: { control: 'text' },
totalHours: { control: 'number' }, totalHours: { control: 'number' },
totalCourses: { control: 'number' } totalCourses: { control: 'number' },
}, },
} satisfies Meta<typeof ScheduleTotalHoursAndCourses>; } satisfies Meta<typeof ScheduleTotalHoursAndCourses>;
export default meta; export default meta;
@@ -22,6 +22,6 @@ export const Default: Story = {
args: { args: {
scheduleName: 'SCHEDULE', scheduleName: 'SCHEDULE',
totalHours: 22, totalHours: 22,
totalCourses: 8 totalCourses: 8,
}, },
}; };

View File

@@ -1,5 +1,6 @@
/* eslint-disable jsdoc/require-jsdoc */
import ScheduleListItem from '@views/components/common/ScheduleListItem/ScheduleListItem';
import React from 'react'; import React from 'react';
import ScheduleListItem from 'src/views/components/common/ScheduleListItem/ScheduleListItem';
export default { export default {
title: 'Components/Common/ScheduleListItem', title: 'Components/Common/ScheduleListItem',
@@ -14,21 +15,21 @@ export default {
}, },
}; };
export const Default = (args) => <ScheduleListItem {...args} />; export const Default = args => <ScheduleListItem {...args} />;
Default.args = { Default.args = {
name: 'My Schedule', name: 'My Schedule',
active: true, active: true,
}; };
export const Active = (args) => <ScheduleListItem {...args} />; export const Active = args => <ScheduleListItem {...args} />;
Active.args = { Active.args = {
name: 'My Schedule', name: 'My Schedule',
active: true, active: true,
}; };
export const Inactive = (args) => <ScheduleListItem {...args} />; export const Inactive = args => <ScheduleListItem {...args} />;
Inactive.args = { Inactive.args = {
name: 'My Schedule', name: 'My Schedule',

View File

@@ -1,5 +1,5 @@
import { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import Settings from 'src/views/components/Settings'; import Settings from '@views/components/Settings';
const meta = { const meta = {
title: 'Components/Common/Settings', title: 'Components/Common/Settings',

View File

@@ -1,11 +1,11 @@
import Spinner from 'src/views/components/common/Spinner/Spinner';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import Spinner from '@views/components/common/Spinner/Spinner';
const meta = { const meta = {
title: 'Components/Common/Spinner', title: 'Components/Common/Spinner',
component: Spinner, component: Spinner,
tags: ['autodocs'], tags: ['autodocs'],
argTypes: {}, argTypes: {},
} satisfies Meta<typeof Spinner>; } satisfies Meta<typeof Spinner>;
export default meta; export default meta;

View File

@@ -1,7 +1,6 @@
import { Button } from 'src/views/components/common/Button/Button';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import Text from '@views/components/common/Text/Text';
import React from 'react'; import React from 'react';
import Text from '../../views/components/common/Text/Text';
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
const meta = { const meta = {

View File

@@ -1,5 +1,5 @@
import { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import { Calendar } from 'src/views/components/calendar/Calendar/Calendar'; import { Calendar } from '@views/components/calendar/Calendar/Calendar';
const meta = { const meta = {
title: 'Components/Calendar/Calendar', title: 'Components/Calendar/Calendar',
@@ -8,16 +8,12 @@ const meta = {
layout: 'centered', layout: 'centered',
}, },
tags: ['autodocs'], tags: ['autodocs'],
argTypes: { argTypes: {},
},
} satisfies Meta<typeof Calendar>; } satisfies Meta<typeof Calendar>;
export default meta; export default meta;
type Story = StoryObj<typeof meta>; type Story = StoryObj<typeof meta>;
export const Default: Story = { export const Default: Story = {
args: { args: {},
};
},
};

View File

@@ -1,9 +1,9 @@
import React from 'react';
import { Meta, StoryObj } from '@storybook/react';
import { Course, Status } from '@shared/types/Course'; import { Course, Status } from '@shared/types/Course';
import Instructor from '@shared/types/Instructor'; import Instructor from '@shared/types/Instructor';
import { CalendarBottomBar } from 'src/views/components/calendar/CalendarBottomBar/CalendarBottomBar'; import { getCourseColors } from '@shared/util/colors';
import { getCourseColors } from '../../../shared/util/colors'; import type { Meta, StoryObj } from '@storybook/react';
import { CalendarBottomBar } from '@views/components/calendar/CalendarBottomBar/CalendarBottomBar';
import React from 'react';
const exampleGovCourse: Course = new Course({ const exampleGovCourse: Course = new Course({
courseName: 'Nope', courseName: 'Nope',

View File

@@ -1,9 +1,9 @@
import { Meta, StoryObj } from '@storybook/react';
import { Course, Status } from '@shared/types/Course'; import { Course, Status } from '@shared/types/Course';
import { CourseMeeting, DAY_MAP } from '@shared/types/CourseMeeting'; import { CourseMeeting, DAY_MAP } from '@shared/types/CourseMeeting';
import { CourseSchedule } from '@shared/types/CourseSchedule'; import { CourseSchedule } from '@shared/types/CourseSchedule';
import Instructor from '@shared/types/Instructor'; import Instructor from '@shared/types/Instructor';
import CalendarCourse from 'src/views/components/calendar/CalendarCourseBlock/CalendarCourseMeeting'; import type { Meta, StoryObj } from '@storybook/react';
import CalendarCourse from '@views/components/calendar/CalendarCourseBlock/CalendarCourseMeeting';
const meta = { const meta = {
title: 'Components/Calendar/CalendarCourseMeeting', title: 'Components/Calendar/CalendarCourseMeeting',

View File

@@ -1,9 +1,10 @@
import { Course, Status } from '@shared/types/Course'; import { Course, Status } from '@shared/types/Course';
import { getCourseColors } from '@shared/util/colors'; import { getCourseColors } from '@shared/util/colors';
import { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import CalendarCourseCell from 'src/views/components/calendar/CalendarCourseCell/CalendarCourseCell'; import CalendarCourseCell from '@views/components/calendar/CalendarCourseCell/CalendarCourseCell';
import React from 'react'; import React from 'react';
import { exampleCourse } from '../PopupCourseBlock.stories';
import { ExampleCourse } from '../PopupCourseBlock.stories';
const meta = { const meta = {
title: 'Components/Calendar/CalendarCourseCell', title: 'Components/Calendar/CalendarCourseCell',
@@ -25,10 +26,10 @@ const meta = {
</div> </div>
), ),
args: { args: {
courseDeptAndInstr: exampleCourse.department, courseDeptAndInstr: ExampleCourse.department,
className: exampleCourse.number, className: ExampleCourse.number,
status: exampleCourse.status, status: ExampleCourse.status,
timeAndLocation: exampleCourse.schedule.meetings[0].getTimeString({ separator: '-' }), timeAndLocation: ExampleCourse.schedule.meetings[0].getTimeString({ separator: '-' }),
colors: getCourseColors('emerald', 500), colors: getCourseColors('emerald', 500),
}, },

View File

@@ -1,13 +1,8 @@
import { Course, Status } from '@shared/types/Course'; import { Status } from '@shared/types/Course';
import { getCourseColors } from '@shared/util/colors'; import { getCourseColors } from '@shared/util/colors';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import CalendarGrid from '@views/components/calendar/CalendarGrid/CalendarGrid';
import type { CalendarGridCourse } from '@views/hooks/useFlattenedCourseSchedule'; import type { CalendarGridCourse } from '@views/hooks/useFlattenedCourseSchedule';
import { Serialized } from 'chrome-extension-toolkit';
import { CourseMeeting, DAY_MAP } from 'src/shared/types/CourseMeeting';
import { CourseSchedule } from 'src/shared/types/CourseSchedule';
import Instructor from 'src/shared/types/Instructor';
import { UserSchedule } from 'src/shared/types/UserSchedule';
import CalendarGrid from 'src/views/components/calendar/CalendarGrid/CalendarGrid';
const meta = { const meta = {
title: 'Components/Calendar/CalendarGrid', title: 'Components/Calendar/CalendarGrid',
@@ -22,57 +17,6 @@ const meta = {
} satisfies Meta<typeof CalendarGrid>; } satisfies Meta<typeof CalendarGrid>;
export default meta; export default meta;
const exampleCourse: Course = new Course({
uniqueId: 50805,
number: '314',
fullName: 'C S 314 DATA STRUCTURES',
courseName: 'DATA STRUCTURES',
department: 'C S',
creditHours: 3,
status: Status.OPEN,
instructors: [
new Instructor({ fullName: 'SCOTT, MICHAEL', firstName: 'MICHAEL', lastName: 'SCOTT', middleInitial: 'D' }),
],
isReserved: true,
description: [
'Second part of a two-part sequence in programming. Introduction to specifications, simple unit testing, and debugging; building and using canonical data structures; algorithm analysis and reasoning techniques such as assertions and invariants.',
'Computer Science 314 and 314H may not both be counted.',
'BVO 311C and 312H may not both be counted.',
'Prerequisite: Computer Science 312 or 312H with a grade of at least C-.',
'May be counted toward the Quantitative Reasoning flag requirement.',
],
schedule: new CourseSchedule({
meetings: [
new CourseMeeting({
days: [DAY_MAP.T, DAY_MAP.TH],
startTime: 480,
endTime: 570,
location: { building: 'UTC', room: '123' },
}),
new CourseMeeting({
days: [DAY_MAP.TH],
startTime: 570,
endTime: 630,
location: { building: 'JES', room: '123' },
}),
],
}),
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/12345/',
flags: ['Writing', 'Independent Inquiry'],
instructionMode: 'In Person',
semester: {
code: '12345',
year: 2024,
season: 'Spring',
},
});
const exampleSchedule = new UserSchedule({
name: 'Example Schedule',
courses: [exampleCourse],
hours: 3,
} as Serialized<UserSchedule>);
const testData: CalendarGridCourse[] = [ const testData: CalendarGridCourse[] = [
{ {
calendarGridPoint: { calendarGridPoint: {
@@ -86,11 +30,6 @@ const testData: CalendarGridCourse[] = [
status: Status.OPEN, status: Status.OPEN,
colors: getCourseColors('emerald', 500), colors: getCourseColors('emerald', 500),
}, },
popupProps: {
course: exampleCourse,
activeSchedule: exampleSchedule,
onClose: () => {},
},
}, },
{ {
calendarGridPoint: { calendarGridPoint: {
@@ -104,11 +43,6 @@ const testData: CalendarGridCourse[] = [
status: Status.OPEN, status: Status.OPEN,
colors: getCourseColors('emerald', 500), colors: getCourseColors('emerald', 500),
}, },
popupProps: {
course: exampleCourse,
activeSchedule: exampleSchedule,
onClose: () => {},
},
}, },
{ {
calendarGridPoint: { calendarGridPoint: {
@@ -122,11 +56,6 @@ const testData: CalendarGridCourse[] = [
status: Status.CLOSED, status: Status.CLOSED,
colors: getCourseColors('emerald', 500), colors: getCourseColors('emerald', 500),
}, },
popupProps: {
course: exampleCourse,
activeSchedule: exampleSchedule,
onClose: () => {},
},
}, },
{ {
calendarGridPoint: { calendarGridPoint: {
@@ -140,11 +69,6 @@ const testData: CalendarGridCourse[] = [
status: Status.OPEN, status: Status.OPEN,
colors: getCourseColors('emerald', 500), colors: getCourseColors('emerald', 500),
}, },
popupProps: {
course: exampleCourse,
activeSchedule: exampleSchedule,
onClose: () => {},
},
}, },
{ {
calendarGridPoint: { calendarGridPoint: {
@@ -158,11 +82,6 @@ const testData: CalendarGridCourse[] = [
status: Status.CLOSED, status: Status.CLOSED,
colors: getCourseColors('emerald', 500), colors: getCourseColors('emerald', 500),
}, },
popupProps: {
course: exampleCourse,
activeSchedule: exampleSchedule,
onClose: () => {},
},
}, },
{ {
calendarGridPoint: { calendarGridPoint: {
@@ -176,11 +95,6 @@ const testData: CalendarGridCourse[] = [
status: Status.CLOSED, status: Status.CLOSED,
colors: getCourseColors('emerald', 500), colors: getCourseColors('emerald', 500),
}, },
popupProps: {
course: exampleCourse,
activeSchedule: exampleSchedule,
onClose: () => {},
},
}, },
{ {
calendarGridPoint: { calendarGridPoint: {
@@ -194,11 +108,6 @@ const testData: CalendarGridCourse[] = [
status: Status.CLOSED, status: Status.CLOSED,
colors: getCourseColors('emerald', 500), colors: getCourseColors('emerald', 500),
}, },
popupProps: {
course: exampleCourse,
activeSchedule: exampleSchedule,
onClose: () => {},
},
}, },
]; ];

View File

@@ -1,7 +1,6 @@
// Calendar.stories.tsx // Calendar.stories.tsx
import React from 'react';
import CalendarCell from 'src/views/components/calendar/CalendarGridCell/CalendarGridCell';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import CalendarCell from '@views/components/calendar/CalendarGridCell/CalendarGridCell';
const meta = { const meta = {
title: 'Components/Calendar/CalendarGridCell', title: 'Components/Calendar/CalendarGridCell',

View File

@@ -1,6 +1,5 @@
import React from 'react'; import type { Meta, StoryObj } from '@storybook/react';
import { Meta, StoryObj } from '@storybook/react'; import CalendarHeader from '@views/components/calendar/CalendarHeader/CalenderHeader';
import CalendarHeader from 'src/views/components/calendar/CalendarHeader/CalenderHeader';
const meta = { const meta = {
title: 'Components/Calendar/CalendarHeader', title: 'Components/Calendar/CalendarHeader',

View File

@@ -1,11 +1,11 @@
import { Course, Status } from '@shared/types/Course'; import { Course, Status } from '@shared/types/Course';
import { CourseMeeting, DAY_MAP } from '@shared/types/CourseMeeting';
import { CourseSchedule } from '@shared/types/CourseSchedule';
import Instructor from '@shared/types/Instructor';
import { UserSchedule } from '@shared/types/UserSchedule'; import { UserSchedule } from '@shared/types/UserSchedule';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import { CalendarSchedules } from '@views/components/calendar/CalendarSchedules/CalendarSchedules';
import React from 'react'; import React from 'react';
import { CourseMeeting, DAY_MAP } from 'src/shared/types/CourseMeeting';
import { CourseSchedule } from 'src/shared/types/CourseSchedule';
import Instructor from 'src/shared/types/Instructor';
import { CalendarSchedules } from 'src/views/components/calendar/CalendarSchedules/CalendarSchedules';
const meta = { const meta = {
title: 'Components/Calendar/CalendarSchedules', title: 'Components/Calendar/CalendarSchedules',
@@ -17,7 +17,6 @@ const meta = {
argTypes: { argTypes: {
dummySchedules: { control: 'object' }, dummySchedules: { control: 'object' },
dummyActiveIndex: { control: 'number' }, dummyActiveIndex: { control: 'number' },
}, },
render: (args: any) => ( render: (args: any) => (
<div> <div>
@@ -141,6 +140,5 @@ export const Default: Story = {
args: { args: {
dummySchedules: schedules, dummySchedules: schedules,
dummyActiveIndex: 0, dummyActiveIndex: 0,
}, },
}; };

View File

@@ -1,11 +1,9 @@
import { Course, Status } from '@shared/types/Course';
import { CourseMeeting, DAY_MAP } from '@shared/types/CourseMeeting';
import { CourseSchedule } from '@shared/types/CourseSchedule';
import Instructor from '@shared/types/Instructor';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import type { Serialized } from 'chrome-extension-toolkit'; import CourseCatalogInjectedPopup from '@views/components/injected/CourseCatalogInjectedPopup/CourseCatalogInjectedPopup';
import { Course, Status } from 'src/shared/types/Course';
import { CourseMeeting, DAY_MAP } from 'src/shared/types/CourseMeeting';
import { CourseSchedule } from 'src/shared/types/CourseSchedule';
import Instructor from 'src/shared/types/Instructor';
import { UserSchedule } from 'src/shared/types/UserSchedule';
import CourseCatalogInjectedPopup from 'src/views/components/injected/CourseCatalogInjectedPopup/CourseCatalogInjectedPopup';
const exampleCourse: Course = new Course({ const exampleCourse: Course = new Course({
uniqueId: 50805, uniqueId: 50805,
@@ -52,19 +50,11 @@ const exampleCourse: Course = new Course({
}, },
}); });
const exampleSchedule = new UserSchedule({
name: 'Example Schedule',
courses: [exampleCourse],
hours: 3,
} as Serialized<UserSchedule>);
const meta: Meta<typeof CourseCatalogInjectedPopup> = { const meta: Meta<typeof CourseCatalogInjectedPopup> = {
title: 'Components/Injected/CourseCatalogInjectedPopup', title: 'Components/Injected/CourseCatalogInjectedPopup',
component: CourseCatalogInjectedPopup, component: CourseCatalogInjectedPopup,
argTypes: { argTypes: {
onClose: { action: 'onClose' }, onClose: { action: 'onClose' },
activeSchedule: { control: 'object' },
course: { control: 'object' },
}, },
}; };
@@ -73,7 +63,6 @@ type Story = StoryObj<typeof CourseCatalogInjectedPopup>;
export const Default: Story = { export const Default: Story = {
args: { args: {
activeSchedule: exampleSchedule,
course: exampleCourse, course: exampleCourse,
}, },
}; };

View File

@@ -1,9 +1,9 @@
import { Course, Status } from 'src/shared/types/Course'; import { Course, Status } from '@shared/types/Course';
import { CourseMeeting } from 'src/shared/types/CourseMeeting'; import { CourseMeeting } from '@shared/types/CourseMeeting';
import { UserSchedule } from 'src/shared/types/UserSchedule'; import Instructor from '@shared/types/Instructor';
import CoursePopup from 'src/views/components/injected/CoursePopupOld/CoursePopup'; import { UserSchedule } from '@shared/types/UserSchedule';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import Instructor from 'src/shared/types/Instructor'; import CoursePopup from '@views/components/injected/CoursePopupOld/CoursePopup';
const exampleCourse: Course = new Course({ const exampleCourse: Course = new Course({
courseName: 'ELEMS OF COMPTRS/PROGRAMMNG-WB', courseName: 'ELEMS OF COMPTRS/PROGRAMMNG-WB',

View File

@@ -2,13 +2,7 @@
"extends": "../tsconfig.json", "extends": "../tsconfig.json",
"compilerOptions": { "compilerOptions": {
"composite": true, "composite": true,
"lib": [ "lib": ["DOM", "es2021"],
"DOM", "types": ["chrome", "node"]
"es2021" }
],
"types": [
"chrome",
"node",
],
},
} }

View File

@@ -1,19 +1,20 @@
import { Course, ScrapedRow } from '@shared/types/Course'; import type { Course, ScrapedRow } from '@shared/types/Course';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useKeyPress } from '../hooks/useKeyPress'; import { useKeyPress } from '../hooks/useKeyPress';
import useSchedules from '../hooks/useSchedules'; import useSchedules from '../hooks/useSchedules';
import { CourseCatalogScraper } from '../lib/CourseCatalogScraper'; import { CourseCatalogScraper } from '../lib/CourseCatalogScraper';
import getCourseTableRows from '../lib/getCourseTableRows'; import getCourseTableRows from '../lib/getCourseTableRows';
import { SiteSupport } from '../lib/getSiteSupport'; import type { SiteSupport } from '../lib/getSiteSupport';
import { populateSearchInputs } from '../lib/populateSearchInputs'; import { populateSearchInputs } from '../lib/populateSearchInputs';
import ExtensionRoot from './common/ExtensionRoot/ExtensionRoot'; import ExtensionRoot from './common/ExtensionRoot/ExtensionRoot';
import AutoLoad from './injected/AutoLoad/AutoLoad'; import AutoLoad from './injected/AutoLoad/AutoLoad';
import CourseCatalogInjectedPopup from './injected/CourseCatalogInjectedPopup/CourseCatalogInjectedPopup';
import CoursePopup from './injected/CoursePopupOld/CoursePopup'; import CoursePopup from './injected/CoursePopupOld/CoursePopup';
import RecruitmentBanner from './injected/RecruitmentBanner/RecruitmentBanner'; import RecruitmentBanner from './injected/RecruitmentBanner/RecruitmentBanner';
import TableHead from './injected/TableHead'; import TableHead from './injected/TableHead';
import TableRow from './injected/TableRow/TableRow'; import TableRow from './injected/TableRow/TableRow';
import TableSubheading from './injected/TableSubheading/TableSubheading'; import TableSubheading from './injected/TableSubheading/TableSubheading';
import CourseCatalogInjectedPopup from './injected/CourseCatalogInjectedPopup/CourseCatalogInjectedPopup';
interface Props { interface Props {
support: SiteSupport.COURSE_CATALOG_DETAILS | SiteSupport.COURSE_CATALOG_LIST; support: SiteSupport.COURSE_CATALOG_DETAILS | SiteSupport.COURSE_CATALOG_LIST;

View File

@@ -1,23 +1,27 @@
import logoImage from '@assets/logo.png'; // Adjust the path as necessary
import { Status } from '@shared/types/Course';
import { StatusIcon } from '@shared/util/icons'; import { StatusIcon } from '@shared/util/icons';
import Divider from '@views/components/common/Divider/Divider';
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
import List from '@views/components/common/List/List'; // Ensure this path is correctly pointing to your List component
import PopupCourseBlock from '@views/components/common/PopupCourseBlock/PopupCourseBlock';
import Text from '@views/components/common/Text/Text';
import { handleOpenCalendar } from '@views/components/injected/CourseCatalogInjectedPopup/HeadingAndActions';
import useSchedules from '@views/hooks/useSchedules';
import { openTabFromContentScript } from '@views/lib/openNewTabFromContentScript';
import React from 'react'; import React from 'react';
// import { FaCalendarAlt, FaCog, FaRedo } from 'react-icons/fa'; // Added FaRedo for the refresh icon import { FaCalendarAlt, FaCog, FaRedo } from 'react-icons/fa'; // Added FaRedo for the refresh icon
import { Status } from 'src/shared/types/Course'; import { TestColors } from 'src/stories/components/PopupCourseBlock.stories';
import { test_colors } from 'src/stories/components/PopupCourseBlock.stories';
import logoImage from '../../assets/logo.png'; // Adjust the path as necessary
import useSchedules from '../hooks/useSchedules';
import { openTabFromContentScript } from '../lib/openNewTabFromContentScript';
import Divider from './common/Divider/Divider';
import ExtensionRoot from './common/ExtensionRoot/ExtensionRoot';
import List from './common/List/List'; // Ensure this path is correctly pointing to your List component
import PopupCourseBlock from './common/PopupCourseBlock/PopupCourseBlock';
import Text from './common/Text/Text';
import { handleOpenCalendar } from './injected/CourseCatalogInjectedPopup/HeadingAndActions';
/**
* Renders the main popup component.
* This component displays the main schedule, courses, and options buttons.
*/
export default function PopupMain() { export default function PopupMain() {
const [activeSchedule] = useSchedules(); const [activeSchedule] = useSchedules();
const draggableElements = activeSchedule?.courses.map((course, i) => ( const draggableElements = activeSchedule?.courses.map((course, i) => (
<PopupCourseBlock key={course.uniqueId} course={course} colors={test_colors[i]} /> <PopupCourseBlock key={course.uniqueId} course={course} colors={TestColors[i]} />
)); ));
const handleOpenOptions = async () => { const handleOpenOptions = async () => {
@@ -46,7 +50,7 @@ export default function PopupMain() {
style={{ backgroundColor: '#bf5700', borderRadius: '8px', padding: '8px' }} style={{ backgroundColor: '#bf5700', borderRadius: '8px', padding: '8px' }}
onClick={handleOpenCalendar} onClick={handleOpenCalendar}
> >
{/* <FaCalendarAlt color='white' /> */} <FaCalendarAlt color='white' />
</button> </button>
<button <button
style={{ style={{
@@ -58,7 +62,7 @@ export default function PopupMain() {
}} }}
onClick={handleOpenOptions} onClick={handleOpenOptions}
> >
{/* <FaCog color='#C05621' /> */} <FaCog color='#C05621' />
</button> </button>
</div> </div>
</div> </div>
@@ -148,7 +152,7 @@ export default function PopupMain() {
<Text as='div' variant='mini'> <Text as='div' variant='mini'>
DATA UPDATED ON: 12:00 AM 02/01/2024 DATA UPDATED ON: 12:00 AM 02/01/2024
</Text> </Text>
{/* <FaRedo className='ml-2 h-4 w-4 text-gray-600' /> */} <FaRedo className='ml-2 h-4 w-4 text-gray-600' />
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,9 +1,10 @@
import React from 'react'; import React from 'react';
import CalendarHeader from 'src/views/components/calendar/CalendarHeader/CalenderHeader'; import CalendarHeader from 'src/views/components/calendar/CalendarHeader/CalenderHeader';
import { CalendarBottomBar } from '../CalendarBottomBar/CalendarBottomBar'; import { CalendarBottomBar } from '../CalendarBottomBar/CalendarBottomBar';
import CalendarGrid from '../CalendarGrid/CalendarGrid';
import { CalendarSchedules } from '../CalendarSchedules/CalendarSchedules'; import { CalendarSchedules } from '../CalendarSchedules/CalendarSchedules';
import ImportantLinks from '../ImportantLinks'; import ImportantLinks from '../ImportantLinks';
import CalendarGrid from '../CalendarGrid/CalendarGrid';
export const flags = ['WR', 'QR', 'GC', 'CD', 'E', 'II']; export const flags = ['WR', 'QR', 'GC', 'CD', 'E', 'II'];

View File

@@ -1,10 +1,13 @@
import React from 'react';
import clsx from 'clsx'; import clsx from 'clsx';
import Text from '../../common/Text/Text'; import React from 'react';
import CalendarCourseBlock, { CalendarCourseCellProps } from '../CalendarCourseCell/CalendarCourseCell';
import { Button } from '../../common/Button/Button';
import ImageIcon from '~icons/material-symbols/image';
import CalendarMonthIcon from '~icons/material-symbols/calendar-month'; import CalendarMonthIcon from '~icons/material-symbols/calendar-month';
import ImageIcon from '~icons/material-symbols/image';
import { Button } from '../../common/Button/Button';
import Text from '../../common/Text/Text';
import type { CalendarCourseCellProps } from '../CalendarCourseCell/CalendarCourseCell';
import CalendarCourseBlock from '../CalendarCourseCell/CalendarCourseCell';
type CalendarBottomBarProps = { type CalendarBottomBarProps = {
courses?: CalendarCourseCellProps[]; courses?: CalendarCourseCellProps[];

View File

@@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import { Course } from 'src/shared/types/Course'; import type { Course } from 'src/shared/types/Course';
import { CourseMeeting } from 'src/shared/types/CourseMeeting'; import type { CourseMeeting } from 'src/shared/types/CourseMeeting';
import styles from './CalendarCourseMeeting.module.scss'; import styles from './CalendarCourseMeeting.module.scss';
/** /**
@@ -26,6 +27,8 @@ export interface CalendarCourseMeetingProps {
const CalendarCourseMeeting: React.FC<CalendarCourseMeetingProps> = ({ const CalendarCourseMeeting: React.FC<CalendarCourseMeetingProps> = ({
course, course,
meetingIdx, meetingIdx,
color,
rightIcon,
}: CalendarCourseMeetingProps) => { }: CalendarCourseMeetingProps) => {
let meeting: CourseMeeting | null = meetingIdx !== undefined ? course.schedule.meetings[meetingIdx] : null; let meeting: CourseMeeting | null = meetingIdx !== undefined ? course.schedule.meetings[meetingIdx] : null;
return ( return (

View File

@@ -1,4 +1,5 @@
import { Status } from '@shared/types/Course'; import { Status } from '@shared/types/Course';
import Text from '@views/components/common/Text/Text';
import clsx from 'clsx'; import clsx from 'clsx';
import React from 'react'; import React from 'react';
import type { CourseColors } from 'src/shared/util/colors'; import type { CourseColors } from 'src/shared/util/colors';
@@ -8,26 +9,36 @@ 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';
import Text from '../../common/Text/Text'; /**
* Props for the CalendarCourseCell component.
*/
export interface CalendarCourseCellProps { export interface CalendarCourseCellProps {
courseDeptAndInstr: string; courseDeptAndInstr: string;
timeAndLocation?: string; timeAndLocation?: string;
status: Status; status: Status;
colors: CourseColors; colors: CourseColors;
className?: string; className?: string;
popup?: any;
} }
/**
* Renders a cell for a calendar course.
*
* @component
* @param {CalendarCourseCellProps} props - The component props.
* @param {string} props.courseDeptAndInstr - The course department and instructor.
* @param {string} props.timeAndLocation - The time and location of the course.
* @param {Status} props.status - The status of the course.
* @param {Colors} props.colors - The colors for styling the cell.
* @param {string} props.className - Additional CSS class name for the cell.
* @returns {JSX.Element} The rendered component.
*/
const CalendarCourseCell: React.FC<CalendarCourseCellProps> = ({ const CalendarCourseCell: React.FC<CalendarCourseCellProps> = ({
courseDeptAndInstr, courseDeptAndInstr,
timeAndLocation, timeAndLocation,
status, status,
colors, colors,
className, className,
popup,
}: CalendarCourseCellProps) => { }: CalendarCourseCellProps) => {
const [showPopup, setShowPopup] = React.useState(false);
let rightIcon: React.ReactNode | null = null; let rightIcon: React.ReactNode | null = null;
if (status === Status.WAITLISTED) { if (status === Status.WAITLISTED) {
rightIcon = <WaitlistIcon className='h-5 w-5' />; rightIcon = <WaitlistIcon className='h-5 w-5' />;
@@ -37,26 +48,15 @@ const CalendarCourseCell: React.FC<CalendarCourseCellProps> = ({
rightIcon = <CancelledIcon className='h-5 w-5' />; rightIcon = <CancelledIcon className='h-5 w-5' />;
} }
// popup.onClose = () => setShowPopup(false);
const handleClick = () => {
setShowPopup(true);
};
// whiteText based on secondaryColor // whiteText based on secondaryColor
const fontColor = pickFontColor(colors.primaryColor); const fontColor = pickFontColor(colors.primaryColor);
return ( return (
<div <div
className={clsx( className={clsx('h-full w-full flex justify-center rounded p-2 overflow-x-hidden', fontColor, className)}
'h-full w-full flex justify-center rounded p-2 overflow-x-hidden cursor-pointer',
fontColor,
className
)}
style={{ style={{
backgroundColor: colors.primaryColor, backgroundColor: colors.primaryColor,
}} }}
onClick={handleClick}
> >
<div className='flex flex-1 flex-col gap-1'> <div className='flex flex-1 flex-col gap-1'>
<Text <Text
@@ -83,7 +83,6 @@ const CalendarCourseCell: React.FC<CalendarCourseCellProps> = ({
{rightIcon} {rightIcon}
</div> </div>
)} )}
<div>{showPopup ? popup : null}</div>
</div> </div>
); );
}; };

View File

@@ -1,8 +1,7 @@
import type { CalendarGridCourse } from '@views/hooks/useFlattenedCourseSchedule';
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
// import html2canvas from 'html2canvas'; // import html2canvas from 'html2canvas';
import { DAY_MAP } from 'src/shared/types/CourseMeeting'; import { DAY_MAP } from 'src/shared/types/CourseMeeting';
import CourseCatalogInjectedPopup from 'src/views/components/injected/CourseCatalogInjectedPopup/CourseCatalogInjectedPopup';
import type { CalendarGridCourse } from 'src/views/hooks/useFlattenedCourseSchedule';
import CalendarCourseCell from '../CalendarCourseCell/CalendarCourseCell'; import CalendarCourseCell from '../CalendarCourseCell/CalendarCourseCell';
/* import calIcon from 'src/assets/icons/cal.svg'; /* import calIcon from 'src/assets/icons/cal.svg';
@@ -11,7 +10,6 @@ import pngIcon from 'src/assets/icons/png.svg';
import CalendarCell from '../CalendarGridCell/CalendarGridCell'; import CalendarCell from '../CalendarGridCell/CalendarGridCell';
import styles from './CalendarGrid.module.scss'; import styles from './CalendarGrid.module.scss';
/* const daysOfWeek = Object.keys(DAY_MAP).filter(key => !['S', 'SU'].includes(key)); /* const daysOfWeek = Object.keys(DAY_MAP).filter(key => !['S', 'SU'].includes(key));
const hoursOfDay = Array.from({ length: 14 }, (_, index) => index + 8); const hoursOfDay = Array.from({ length: 14 }, (_, index) => index + 8);
const grid = []; const grid = [];
@@ -110,7 +108,7 @@ function CalendarGrid({ courseCells, saturdayClass }: React.PropsWithChildren<Pr
newGrid.push(row); newGrid.push(row);
} }
setGrid(newGrid); setGrid(newGrid);
}, []); }, [hoursOfDay]);
return ( return (
<div className={styles.calendarGrid}> <div className={styles.calendarGrid}>
@@ -206,7 +204,6 @@ function AccountForCourseConflicts({ courseCells }: AccountForCourseConflictsPro
timeAndLocation={block.componentProps.timeAndLocation} timeAndLocation={block.componentProps.timeAndLocation}
status={block.componentProps.status} status={block.componentProps.status}
colors={block.componentProps.colors} colors={block.componentProps.colors}
popup={<CourseCatalogInjectedPopup course={block.popupProps.course} activeSchedule={block.popupProps.activeSchedule} onClose={block.popupProps.onClose} />}
/> />
</div> </div>
)); ));

View File

@@ -1,16 +1,21 @@
import React from 'react';
import { Status } from '@shared/types/Course'; import { Status } from '@shared/types/Course';
import Divider from '../../common/Divider/Divider'; import { Button } from '@views/components/common/Button/Button';
import { Button } from '../../common/Button/Button'; import CourseStatus from '@views/components/common/CourseStatus/CourseStatus';
import Text from '../../common/Text/Text'; import Divider from '@views/components/common/Divider/Divider';
import MenuIcon from '~icons/material-symbols/menu'; import ScheduleTotalHoursAndCourses from '@views/components/common/ScheduleTotalHoursAndCourses/ScheduleTotalHoursAndCourses';
import UndoIcon from '~icons/material-symbols/undo'; import Text from '@views/components/common/Text/Text';
import RedoIcon from '~icons/material-symbols/redo'; import React from 'react';
import SettingsIcon from '~icons/material-symbols/settings';
import ScheduleTotalHoursAndCourses from '../../common/ScheduleTotalHoursAndCourses/ScheduleTotalHoursAndCourses';
import CourseStatus from '../../common/CourseStatus/CourseStatus';
import calIcon from 'src/assets/logo.png'; import calIcon from 'src/assets/logo.png';
import MenuIcon from '~icons/material-symbols/menu';
import RedoIcon from '~icons/material-symbols/redo';
import SettingsIcon from '~icons/material-symbols/settings';
import UndoIcon from '~icons/material-symbols/undo';
/**
* Renders the header component for the calendar.
* @returns The CalendarHeader component.
*/
const CalendarHeader = () => ( const CalendarHeader = () => (
<div className='min-h-79px min-w-672px flex px-0 py-15'> <div className='min-h-79px min-w-672px flex px-0 py-15'>
<div className='flex flex-row gap-20'> <div className='flex flex-row gap-20'>
@@ -18,9 +23,9 @@ const CalendarHeader = () => (
<div className='flex gap-1'> <div className='flex gap-1'>
<Button variant='single' icon={MenuIcon} color='ut-gray' /> <Button variant='single' icon={MenuIcon} color='ut-gray' />
<div className='flex items-center'> <div className='flex items-center'>
<img src={calIcon} className='min-w-[48px] max-w-[48px]' alt='UT Registration Plus Logo' /> <img src={calIcon} className='max-w-[48px] min-w-[48px]' alt='UT Registration Plus Logo' />
<div className='flex flex-col whitespace-nowrap'> <div className='flex flex-col whitespace-nowrap'>
<Text className='leading-trim text-cap font-roboto text-base text-ut-burntorange font-medium'> <Text className='leading-trim font-roboto text-cap text-base text-ut-burntorange font-medium'>
UT Registration UT Registration
</Text> </Text>
<Text className='leading-trim text-cap font-roboto text-base text-ut-orange font-medium'> <Text className='leading-trim text-cap font-roboto text-base text-ut-orange font-medium'>

View File

@@ -1,16 +1,26 @@
import { UserSchedule } from '@shared/types/UserSchedule'; import type { UserSchedule } from '@shared/types/UserSchedule';
import List from '@views/components/common/List/List';
import ScheduleListItem from '@views/components/common/ScheduleListItem/ScheduleListItem';
import Text from '@views/components/common/Text/Text';
import React, { useState } from 'react'; import React, { useState } from 'react';
import AddSchedule from '~icons/material-symbols/add';
import List from '../../common/List/List';
import ScheduleListItem from '../../common/ScheduleListItem/ScheduleListItem';
import Text from '../../common/Text/Text';
import AddSchedule from '~icons/material-symbols/add';
/**
* Props for the CalendarSchedules component.
*/
export type Props = { export type Props = {
style?: React.CSSProperties; style?: React.CSSProperties;
dummySchedules?: UserSchedule[]; dummySchedules?: UserSchedule[];
dummyActiveIndex?: number; dummyActiveIndex?: number;
}; };
/**
* Renders a component that displays a list of schedules.
*
* @param props - The component props.
* @returns The rendered component.
*/
export function CalendarSchedules(props: Props) { export function CalendarSchedules(props: Props) {
const [activeScheduleIndex, setActiveScheduleIndex] = useState(props.dummyActiveIndex || 0); const [activeScheduleIndex, setActiveScheduleIndex] = useState(props.dummyActiveIndex || 0);
const [schedules, setSchedules] = useState(props.dummySchedules || []); const [schedules, setSchedules] = useState(props.dummySchedules || []);

View File

@@ -1,8 +1,10 @@
import React from 'react';
import clsx from 'clsx'; import clsx from 'clsx';
import Text from '../common/Text/Text'; import React from 'react';
import OutwardArrowIcon from '~icons/material-symbols/arrow-outward'; import OutwardArrowIcon from '~icons/material-symbols/arrow-outward';
import Text from '../common/Text/Text';
type Props = { type Props = {
className?: string; className?: string;
}; };

View File

@@ -1,7 +1,10 @@
import clsx from 'clsx'; import clsx from 'clsx';
import React from 'react'; import React from 'react';
import type IconComponent from '~icons/material-symbols'; import type IconComponent from '~icons/material-symbols';
import { ThemeColor, getThemeColorHexByName, getThemeColorRgbByName } from '../../../../shared/util/themeColors';
import type { ThemeColor } from '../../../../shared/util/themeColors';
import { getThemeColorHexByName, getThemeColorRgbByName } from '../../../../shared/util/themeColors';
import Text from '../Text/Text'; import Text from '../Text/Text';
interface Props { interface Props {

View File

@@ -1,7 +1,11 @@
import clsx from 'clsx'; import clsx from 'clsx';
import React from 'react'; import React from 'react';
import styles from './Card.module.scss'; import styles from './Card.module.scss';
/**
* Props for the Card component.
*/
export type Props = { export type Props = {
style?: React.CSSProperties; style?: React.CSSProperties;
className?: string; className?: string;

View File

@@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
import Text from '../Text/Text'; import Text from '../Text/Text';
/** /**

View File

@@ -1,6 +1,7 @@
import React from 'react';
import { Course } from 'src/shared/types/Course';
import clsx from 'clsx'; import clsx from 'clsx';
import React from 'react';
import type { Course } from 'src/shared/types/Course';
import Text from '../Text/Text'; import Text from '../Text/Text';
/** /**
@@ -28,9 +29,7 @@ export default function ConflictsWithWarning({ className, conflicts }: Conflicts
> >
<div>Conflicts With:</div> <div>Conflicts With:</div>
{conflicts.map(course => ( {conflicts.map(course => (
<div> <div>{`${course.department} ${course.number} (${course.uniqueId})`}</div>
{`${course.department} ${course.number} (${course.uniqueId})`}
</div>
))} ))}
</Text> </Text>
); );

View File

@@ -1,7 +1,8 @@
import { Status } from '@shared/types/Course'; import type { Status } from '@shared/types/Course';
import { StatusIcon } from '@shared/util/icons'; import { StatusIcon } from '@shared/util/icons';
import clsx from 'clsx'; import clsx from 'clsx';
import React from 'react'; import React from 'react';
import Text from '../Text/Text'; import Text from '../Text/Text';
type SizeType = 'small' | 'mini'; type SizeType = 'small' | 'mini';

View File

@@ -1,5 +0,0 @@
@use 'src/views/styles/colors.module.scss';
hr {
border: 1px solid colors.$limestone;
}

View File

@@ -1,25 +1,48 @@
import type { Color } from '@views/styles/colors.module.scss';
import clsx from 'clsx'; import clsx from 'clsx';
import React from 'react'; import React from 'react';
import { Color } from '@views/styles/colors.module.scss';
import styles from './Divider.module.scss';
export type Props = { /**
color?: Color | React.CSSProperties['borderColor']; * Props for the Divider component
type?: 'solid' | 'dashed' | 'dotted'; *
style?: React.CSSProperties; * @param orientation - Orientation of the divider (horizontal or vertical)
* @param size - Size of the divider (forwards to width or height in CSS)
* @param className - Additional classes to be added to the divider
* @param testId - Test id for the divider
*/
export type DividerProps = {
orientation: 'horizontal' | 'vertical';
size: React.CSSProperties['width' | 'height'];
className?: string; className?: string;
testId?: string; testId?: string;
}; };
/** /**
* This is a reusable divider component that can be used to separate content * This is a reusable divider component that can be used to separate content
*
* @returns A divider component
*
* @example
* ```tsx
* <Divider size="2.5rem" orientation="vertical" />
* ```
*
* @example
* ```tsx
* <Divider size="19px" orientation="horizontal" />
* ```
*/ */
export default function Divider(props: Props) { export default function Divider({ className, testId, size, orientation }: DividerProps) {
const style = { const style: React.CSSProperties =
...props.style, orientation === 'horizontal'
borderColor: props.color, ? { width: size, borderBottomWidth: '1px' }
borderStyle: props.type, : { height: size, borderRightWidth: '1px' };
};
return <hr data-testid={props.testId} style={style} className={clsx(styles.divider, props.className)} />; return (
<div
style={style}
data-testid={testId}
className={clsx('border-solid border-ut-offwhite w-0 h-0', className)}
/>
);
} }

View File

@@ -1,12 +1,16 @@
import { Disclosure, Transition } from '@headlessui/react'; import { Disclosure, Transition } from '@headlessui/react';
import { UserSchedule } from '@shared/types/UserSchedule'; import type { UserSchedule } from '@shared/types/UserSchedule';
import List from '@views/components/common/List/List';
import Text from '@views/components/common/Text/Text';
import React from 'react'; import React from 'react';
import userScheduleHandler from 'src/pages/background/handler/userScheduleHandler'; import userScheduleHandler from 'src/pages/background/handler/userScheduleHandler';
import DropdownArrowDown from '~icons/material-symbols/arrow-drop-down'; import DropdownArrowDown from '~icons/material-symbols/arrow-drop-down';
import DropdownArrowUp from '~icons/material-symbols/arrow-drop-up'; import DropdownArrowUp from '~icons/material-symbols/arrow-drop-up';
import List from '../List/List';
import Text from '../Text/Text';
/**
* Props for the Dropdown component.
*/
export type Props = { export type Props = {
style?: React.CSSProperties; style?: React.CSSProperties;
// Dummy value solely for storybook // Dummy value solely for storybook
@@ -62,7 +66,7 @@ export default function Dropdown(props: Props) {
<Disclosure.Button> <Disclosure.Button>
<div className='flex items-center border-none bg-white p-3 text-left'> <div className='flex items-center border-none bg-white p-3 text-left'>
<div className='flex-1'> <div className='flex-1'>
<Text as='div' variant='h4' className='text-ut-burntorange mb-1 w-100%'> <Text as='div' variant='h4' className='mb-1 w-100% text-ut-burntorange'>
MAIN SCHEDULE: MAIN SCHEDULE:
</Text> </Text>
<div> <div>
@@ -74,7 +78,7 @@ export default function Dropdown(props: Props) {
</Text> </Text>
</div> </div>
</div> </div>
<Text className='text-ut-burntorange text-2xl font-normal'> <Text className='text-2xl text-ut-burntorange font-normal'>
{expanded ? <DropdownArrowDown /> : <DropdownArrowUp />} {expanded ? <DropdownArrowDown /> : <DropdownArrowUp />}
</Text> </Text>
</div> </div>

View File

@@ -1,9 +1,10 @@
import React from 'react';
import styles from './ExtensionRoot.module.scss';
import '@unocss/reset/tailwind-compat.css'; import '@unocss/reset/tailwind-compat.css';
import 'uno.css'; import 'uno.css';
import React from 'react';
import styles from './ExtensionRoot.module.scss';
interface Props { interface Props {
testId?: string; testId?: string;
} }

View File

@@ -1,9 +1,12 @@
import type { Color } from '@views/styles/colors.module.scss';
import colors from '@views/styles/colors.module.scss';
import type { Size } from '@views/styles/fonts.module.scss';
import fonts from '@views/styles/fonts.module.scss';
import clsx from 'clsx'; import clsx from 'clsx';
import React from 'react'; import React from 'react';
import colors, { Color } from '@views/styles/colors.module.scss';
import fonts, { Size } from '@views/styles/fonts.module.scss';
import styles from './Icon.module.scss'; import styles from './Icon.module.scss';
import { MaterialIconCode } from './MaterialIcons'; import type { MaterialIconCode } from './MaterialIcons';
/** /**
* *

View File

@@ -2194,4 +2194,7 @@ const icons = [
'zoom_out_map', 'zoom_out_map',
] as const; ] as const;
export type MaterialIconCode = typeof icons[number]; /**
* Represents a type that corresponds to a material icon code.
*/
export type MaterialIconCode = (typeof icons)[number];

View File

@@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
import Text from '../Text/Text'; import Text from '../Text/Text';
interface Props { interface Props {
@@ -10,31 +11,35 @@ interface Props {
* A maybe reusable InfoCard component that follows the design system of the extension. * A maybe reusable InfoCard component that follows the design system of the extension.
* @returns * @returns
*/ */
export function InfoCard({ export function InfoCard({ titleText, bodyText }: React.PropsWithChildren<Props>): JSX.Element {
titleText,
bodyText
}: React.PropsWithChildren<Props>): JSX.Element {
return ( return (
<div <div
className = 'w-50 flex flex-col items-start justify-center border rounded p-4' className='w-50 flex flex-col items-start justify-center border rounded p-4'
style = {{ style={{
border: "1px solid #D6D2C4", border: '1px solid #D6D2C4',
background: "#FFF" // White background: '#FFF', // White
}}> }}
<div className="flex flex-col items-start self-stretch gap-1.5"> >
<Text variant = "h4" as = 'span' <div className='flex flex-col items-start self-stretch gap-1.5'>
style = {{ <Text
color: '#F8971F', // Orange variant='h4'
}}> as='span'
{titleText} style={{
</Text> color: '#F8971F', // Orange
<Text variant = "small" as = 'span' }}
style = {{ >
color: '#333F48', // Black {titleText}
}}> </Text>
{bodyText} <Text
</Text> variant='small'
</ div> as='span'
style={{
color: '#333F48', // Black
}}
>
{bodyText}
</Text>
</div>
</div> </div>
); );
} }

View File

@@ -1,7 +1,10 @@
import { background } from '@shared/messages'; import { background } from '@shared/messages';
import clsx from 'clsx'; import clsx from 'clsx';
import React, { PropsWithChildren } from 'react'; import type { PropsWithChildren } from 'react';
import Text, { TextProps } from '../Text/Text'; import React from 'react';
import type { TextProps } from '../Text/Text';
import Text from '../Text/Text';
import styles from './Link.module.scss'; import styles from './Link.module.scss';
type Props = Omit<TextProps, 'span'> & { type Props = Omit<TextProps, 'span'> & {

View File

@@ -1,9 +1,13 @@
import { Course, Status } from '@shared/types/Course'; import type { Course } from '@shared/types/Course';
import { CourseColors, pickFontColor } from '@shared/util/colors'; import { Status } from '@shared/types/Course';
import type { CourseColors } from '@shared/util/colors';
import { pickFontColor } from '@shared/util/colors';
import { StatusIcon } from '@shared/util/icons'; import { StatusIcon } from '@shared/util/icons';
import clsx from 'clsx'; import clsx from 'clsx';
import React from 'react'; import React from 'react';
import DragIndicatorIcon from '~icons/material-symbols/drag-indicator'; import DragIndicatorIcon from '~icons/material-symbols/drag-indicator';
import Text from '../Text/Text'; import Text from '../Text/Text';
/** /**

View File

@@ -1,8 +1,12 @@
import Text from '@views/components/common/Text/Text';
import clsx from 'clsx'; import clsx from 'clsx';
import React from 'react'; import React from 'react';
import DragIndicatorIcon from '~icons/material-symbols/drag-indicator';
import Text from '../Text/Text';
import DragIndicatorIcon from '~icons/material-symbols/drag-indicator';
/**
* Props for the ScheduleListItem component.
*/
export type Props = { export type Props = {
style?: React.CSSProperties; style?: React.CSSProperties;
active?: boolean; active?: boolean;
@@ -18,11 +22,11 @@ export default function ScheduleListItem(props: Props) {
console.log(props); console.log(props);
return ( return (
<div style={{ ...props.style }} className='items-center'> <div style={{ ...props.style }} className='items-center'>
<li className='text-ut-burntorange w-100% flex cursor-pointer items-center self-stretch justify-left'> <li className='w-100% flex cursor-pointer items-center self-stretch justify-left text-ut-burntorange'>
<div className='group flex justify-center'> <div className='group flex justify-center'>
<div <div
className='flex cursor-move items-center self-stretch rounded rounded-r-0' className='flex cursor-move items-center self-stretch rounded rounded-r-0'
{...dragHandleProps} {...dragHandleProps}
> >
<DragIndicatorIcon className='h-6 w-6 cursor-move text-zinc-300 btn-transition -ml-1.5 hover:text-zinc-400' /> <DragIndicatorIcon className='h-6 w-6 cursor-move text-zinc-300 btn-transition -ml-1.5 hover:text-zinc-400' />
</div> </div>

Some files were not shown because too many files have changed in this diff Show More