diff --git a/src/shared/types/Course.ts b/src/shared/types/Course.ts
index 075c2268..4a4098d6 100644
--- a/src/shared/types/Course.ts
+++ b/src/shared/types/Course.ts
@@ -82,5 +82,5 @@ export class Course {
*/
export type ScrapedRow = {
element: HTMLTableRowElement;
- course: Course;
+ course: Course | null;
};
diff --git a/src/views/components/CourseCatalogMain.tsx b/src/views/components/CourseCatalogMain.tsx
index 0a54eaa9..e1c58a97 100644
--- a/src/views/components/CourseCatalogMain.tsx
+++ b/src/views/components/CourseCatalogMain.tsx
@@ -6,6 +6,7 @@ import getCourseTableRows from '../lib/getCourseTableRows';
import { SiteSupport } from '../lib/getSiteSupport';
import { populateSearchInputs } from '../lib/populateSearchInputs';
import ExtensionRoot from './common/ExtensionRoot/ExtensionRoot';
+import Text from './common/Text/Text';
import AutoLoad from './injected/AutoLoad/AutoLoad';
import CoursePanel from './injected/CoursePanel/CoursePanel';
import TableHead from './injected/TableHead';
@@ -53,15 +54,21 @@ export default function CourseCatalogMain({ support }: Props) {
return (
Plus
- {rows.map(row => (
-
- ))}
+ {rows.map(row => {
+ if (!row.course) {
+ // TODO: handle the course section headers
+ return null;
+ }
+ return (
+
+ );
+ })}
{selectedCourse && }
diff --git a/src/views/components/common/Spinner/Spinner.module.scss b/src/views/components/common/Spinner/Spinner.module.scss
index 6267bb9b..9ad9a85b 100644
--- a/src/views/components/common/Spinner/Spinner.module.scss
+++ b/src/views/components/common/Spinner/Spinner.module.scss
@@ -3,9 +3,9 @@
$spinner-border-width: 10px;
.spinner {
- border: 1px solid $CHARCOAL;
+ border: 1px solid $charcoal;
border-width: $spinner-border-width;
- border-top: $spinner-border-width solid $TANGERINE;
+ border-top: $spinner-border-width solid $tangerine;
margin: 0 auto 15px auto;
border-radius: 50%;
width: 60px;
diff --git a/src/views/components/common/Text/Text.module.scss b/src/views/components/common/Text/Text.module.scss
new file mode 100644
index 00000000..3408a54f
--- /dev/null
+++ b/src/views/components/common/Text/Text.module.scss
@@ -0,0 +1,3 @@
+.text {
+ font-family: 'Inter', sans-serif;
+}
diff --git a/src/views/components/common/Text/Text.tsx b/src/views/components/common/Text/Text.tsx
new file mode 100644
index 00000000..f304e031
--- /dev/null
+++ b/src/views/components/common/Text/Text.tsx
@@ -0,0 +1,40 @@
+import classNames from 'classnames';
+import React, { PropsWithChildren } from 'react';
+import colors, { ISassColors } from 'src/views/styles/colors.module.scss';
+import fonts, { ISizes, IWeights } from 'src/views/styles/fonts.module.scss';
+import styles from './Text.module.scss';
+
+type Props = {
+ color?: keyof ISassColors & 'black';
+ weight: keyof IWeights;
+ size: keyof ISizes;
+ span?: boolean;
+ className?: string;
+ onClick?: () => void;
+ align?: React.CSSProperties['textAlign'];
+ style?: React.CSSProperties;
+};
+
+/**
+ * A reusable Text component with props that build on top of the design system for the extension
+ */
+export default function Text(props: PropsWithChildren) {
+ const style = props.style || {};
+
+ style.textAlign ??= props.align;
+ style.color ??= colors?.[props.color ?? 'charcoal'];
+ style.fontSize ??= fonts?.[props.size ?? 'medium'];
+ style.fontWeight ??= fonts?.[props.weight ?? 'bold'];
+
+ if (props.span) {
+
+ {props.children}
+ ;
+ }
+
+ return (
+
+ {props.children}
+
+ );
+}
diff --git a/src/views/components/injected/AutoLoad/AutoLoad.tsx b/src/views/components/injected/AutoLoad/AutoLoad.tsx
index 30fe8727..9f5be0d9 100644
--- a/src/views/components/injected/AutoLoad/AutoLoad.tsx
+++ b/src/views/components/injected/AutoLoad/AutoLoad.tsx
@@ -42,7 +42,7 @@ export default function AutoLoad({ addRows }: Props) {
}
// scrape the courses from the page
const ccs = new CourseCatalogScraper(SiteSupport.COURSE_CATALOG_LIST);
- const scrapedRows = ccs.scrape(nextRows);
+ const scrapedRows = ccs.scrape(nextRows, true);
// add the scraped courses to the current page
addRows(scrapedRows);
diff --git a/src/views/index.tsx b/src/views/index.tsx
index 6b4baaf1..6c947b65 100644
--- a/src/views/index.tsx
+++ b/src/views/index.tsx
@@ -34,7 +34,7 @@ onContextInvalidated(() => {
div.id = 'context-invalidated-container';
document.body.appendChild(div);
render(
- ,
+ ,
div
);
});
diff --git a/src/views/lib/CourseCatalogScraper.ts b/src/views/lib/CourseCatalogScraper.ts
index 6a79ad10..20d9a68e 100644
--- a/src/views/lib/CourseCatalogScraper.ts
+++ b/src/views/lib/CourseCatalogScraper.ts
@@ -39,9 +39,10 @@ export class CourseCatalogScraper {
/**
* Pass in a list of HTMLtable rows and scrape every course from them
* @param rows the rows of the course catalog table
+ * @param keepHeaders whether to keep the header rows (which contain the course name) in the output
* @returns an array of course row objects (which contain courses corresponding to the htmltable row)
*/
- public scrape(rows: NodeListOf | HTMLTableRowElement[]): ScrapedRow[] {
+ public scrape(rows: NodeListOf | HTMLTableRowElement[], keepHeaders = false): ScrapedRow[] {
const courses: ScrapedRow[] = [];
let fullName = this.getFullName();
@@ -49,6 +50,12 @@ export class CourseCatalogScraper {
rows.forEach(row => {
if (this.isHeaderRow(row)) {
fullName = this.getFullName(row);
+ if (keepHeaders) {
+ courses.push({
+ element: row,
+ course: null,
+ });
+ }
return;
}
// we are now ready to build the course object
diff --git a/src/views/styles/base.module.scss b/src/views/styles/base.module.scss
index a013ea4e..b9e73c7d 100644
--- a/src/views/styles/base.module.scss
+++ b/src/views/styles/base.module.scss
@@ -1,3 +1,4 @@
@import './colors.module.scss';
@import './fonts.module.scss';
@import './elevation.module.scss';
+@import './utils.module.scss';
diff --git a/src/views/styles/colors.module.scss b/src/views/styles/colors.module.scss
index a2ccb0bf..52408efa 100644
--- a/src/views/styles/colors.module.scss
+++ b/src/views/styles/colors.module.scss
@@ -1,25 +1,25 @@
-$BURNT_ORANGE: #bf5700;
-$CHARCOAL: #333f48;
-$WHITE: #ffffff;
-$TANGERINE: #f8971f;
-$SUNSHINE: #ffd600;
-$CACTUS: #a6cd57;
-$TURTLE_POND: #579d42;
-$TURQUOISE: #00a9b7;
-$BLUEBONNET: #005f86;
-$SHADE: #9cadb7;
-$LIMESTONE: #d6d2c4;
+$burnt_orange: #bf5700;
+$charcoal: #333f48;
+$white: #ffffff;
+$tangerine: #f8971f;
+$sunshine: #ffd600;
+$cactus: #a6cd57;
+$turtle_pond: #579d42;
+$turquoise: #00a9b7;
+$bluebonnet: #005f86;
+$shade: #9cadb7;
+$limestone: #d6d2c4;
:export {
- BURNT_ORANGE: $BURNT_ORANGE;
- CHARCOAL: $CHARCOAL;
- WHITE: $WHITE;
- TANGERINE: $TANGERINE;
- SUNSHINE: $SUNSHINE;
- CACTUS: $CACTUS;
- TURTLE_POND: $TURTLE_POND;
- TURQUOISE: $TURQUOISE;
- BLUEBONNET: $BLUEBONNET;
- SHADE: $SHADE;
- LIMESTONE: $LIMESTONE;
+ burnt_orange: $burnt_orange;
+ charcoal: $charcoal;
+ white: $white;
+ tangerine: $tangerine;
+ sunshine: $sunshine;
+ cactus: $cactus;
+ turtle_pond: $turtle_pond;
+ turquoise: $turquoise;
+ bluebonnet: $bluebonnet;
+ shade: $shade;
+ limestone: $limestone;
}
diff --git a/src/views/styles/colors.module.scss.d.ts b/src/views/styles/colors.module.scss.d.ts
index 6c0f096a..2a0007de 100644
--- a/src/views/styles/colors.module.scss.d.ts
+++ b/src/views/styles/colors.module.scss.d.ts
@@ -3,17 +3,17 @@
* when we import them into ts/tsx files
*/
export interface ISassColors {
- BURNT_ORANGE: string;
- CHARCOAL: string;
- WHITE: string;
- TANGERINE: string;
- SUNSHINE: string;
- CACTUS: string;
- TURTLE_POND: string;
- TURQUOISE: string;
- BLUEBONNET: string;
- SHADE: string;
- LIMESTONE: string;
+ burnt_orange: string;
+ charcoal: string;
+ white: string;
+ tangerine: string;
+ sunshine: string;
+ cactus: string;
+ turtle_pond: string;
+ turquoise: string;
+ bluebonnet: string;
+ shade: string;
+ limestone: string;
}
declare const colors: ISassColors;
diff --git a/src/views/styles/fonts.module.scss b/src/views/styles/fonts.module.scss
index bf85a8c1..5f9d6e24 100644
--- a/src/views/styles/fonts.module.scss
+++ b/src/views/styles/fonts.module.scss
@@ -7,3 +7,32 @@
font-weight: #{$weights};
}
}
+
+$light: 300;
+$regular: 400;
+$medium: 500;
+$semi_bold: 600;
+$bold: 700;
+$black: 900;
+
+$x_small: 8px;
+$small: 12px;
+$medium: 16px;
+$large: 24px;
+$x_large: 32px;
+$xx_large: 48px;
+
+:export {
+ light: $light;
+ regular: $regular;
+ medium: $medium;
+ semi_bold: $semi_bold;
+ bold: $bold;
+ black: $black;
+ x_small: $x_small;
+ small: $small;
+ medium: $medium;
+ large: $large;
+ x_large: $x_large;
+ xx_large: $xx_large;
+}
diff --git a/src/views/styles/fonts.module.scss.d.ts b/src/views/styles/fonts.module.scss.d.ts
new file mode 100644
index 00000000..1f814beb
--- /dev/null
+++ b/src/views/styles/fonts.module.scss.d.ts
@@ -0,0 +1,26 @@
+export interface IWeights {
+ light: number;
+ regular: number;
+ medium: number;
+ bold: number;
+ semi_bold: number;
+ black: number;
+}
+
+export interface ISizes {
+ x_small: number;
+ small: number;
+ medium: number;
+ large: number;
+ x_large: number;
+ xx_large: number;
+}
+
+/**
+ * This is a file that we need to create to tell typescript what the shape of the css modules is
+ * when we import them into ts/tsx files
+ */
+export type IFonts = IWeights & ISizes;
+
+declare const fonts: IFonts;
+export default fonts;
diff --git a/src/views/styles/utils.module.scss b/src/views/styles/utils.module.scss
new file mode 100644
index 00000000..e69de29b