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