created reusable text component, and setup typing for it automatically. also fixed bugs with autoload and scraper so that it would handle appending course name headers
This commit is contained in:
@@ -82,5 +82,5 @@ export class Course {
|
|||||||
*/
|
*/
|
||||||
export type ScrapedRow = {
|
export type ScrapedRow = {
|
||||||
element: HTMLTableRowElement;
|
element: HTMLTableRowElement;
|
||||||
course: Course;
|
course: Course | null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import getCourseTableRows from '../lib/getCourseTableRows';
|
|||||||
import { SiteSupport } from '../lib/getSiteSupport';
|
import { 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 Text from './common/Text/Text';
|
||||||
import AutoLoad from './injected/AutoLoad/AutoLoad';
|
import AutoLoad from './injected/AutoLoad/AutoLoad';
|
||||||
import CoursePanel from './injected/CoursePanel/CoursePanel';
|
import CoursePanel from './injected/CoursePanel/CoursePanel';
|
||||||
import TableHead from './injected/TableHead';
|
import TableHead from './injected/TableHead';
|
||||||
@@ -53,15 +54,21 @@ export default function CourseCatalogMain({ support }: Props) {
|
|||||||
return (
|
return (
|
||||||
<ExtensionRoot>
|
<ExtensionRoot>
|
||||||
<TableHead>Plus</TableHead>
|
<TableHead>Plus</TableHead>
|
||||||
{rows.map(row => (
|
{rows.map(row => {
|
||||||
<TableRow
|
if (!row.course) {
|
||||||
element={row.element}
|
// TODO: handle the course section headers
|
||||||
course={row.course}
|
return null;
|
||||||
isSelected={row.course.uniqueId === selectedCourse?.uniqueId}
|
}
|
||||||
support={support}
|
return (
|
||||||
onClick={handleRowButtonClick(row.course)}
|
<TableRow
|
||||||
/>
|
element={row.element}
|
||||||
))}
|
course={row.course}
|
||||||
|
isSelected={row.course.uniqueId === selectedCourse?.uniqueId}
|
||||||
|
support={support}
|
||||||
|
onClick={handleRowButtonClick(row.course)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
{selectedCourse && <CoursePanel course={selectedCourse} onClose={handleClearSelectedCourse} />}
|
{selectedCourse && <CoursePanel course={selectedCourse} onClose={handleClearSelectedCourse} />}
|
||||||
<AutoLoad addRows={addRows} />
|
<AutoLoad addRows={addRows} />
|
||||||
</ExtensionRoot>
|
</ExtensionRoot>
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
$spinner-border-width: 10px;
|
$spinner-border-width: 10px;
|
||||||
|
|
||||||
.spinner {
|
.spinner {
|
||||||
border: 1px solid $CHARCOAL;
|
border: 1px solid $charcoal;
|
||||||
border-width: $spinner-border-width;
|
border-width: $spinner-border-width;
|
||||||
border-top: $spinner-border-width solid $TANGERINE;
|
border-top: $spinner-border-width solid $tangerine;
|
||||||
margin: 0 auto 15px auto;
|
margin: 0 auto 15px auto;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
width: 60px;
|
width: 60px;
|
||||||
|
|||||||
3
src/views/components/common/Text/Text.module.scss
Normal file
3
src/views/components/common/Text/Text.module.scss
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.text {
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
}
|
||||||
40
src/views/components/common/Text/Text.tsx
Normal file
40
src/views/components/common/Text/Text.tsx
Normal file
@@ -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<Props>) {
|
||||||
|
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) {
|
||||||
|
<span className={classNames(styles.text, props.className)} style={style} onClick={props.onClick}>
|
||||||
|
{props.children}
|
||||||
|
</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames(styles.text, props.className)} style={style} onClick={props.onClick}>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -42,7 +42,7 @@ export default function AutoLoad({ addRows }: Props) {
|
|||||||
}
|
}
|
||||||
// scrape the courses from the page
|
// scrape the courses from the page
|
||||||
const ccs = new CourseCatalogScraper(SiteSupport.COURSE_CATALOG_LIST);
|
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
|
// add the scraped courses to the current page
|
||||||
addRows(scrapedRows);
|
addRows(scrapedRows);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ onContextInvalidated(() => {
|
|||||||
div.id = 'context-invalidated-container';
|
div.id = 'context-invalidated-container';
|
||||||
document.body.appendChild(div);
|
document.body.appendChild(div);
|
||||||
render(
|
render(
|
||||||
<ContextInvalidated fontFamily='monospace' color={colors.WHITE} backgroundColor={colors.BURNT_ORANGE} />,
|
<ContextInvalidated fontFamily='monospace' color={colors.white} backgroundColor={colors.burnt_orange} />,
|
||||||
div
|
div
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -39,9 +39,10 @@ export class CourseCatalogScraper {
|
|||||||
/**
|
/**
|
||||||
* Pass in a list of HTMLtable rows and scrape every course from them
|
* Pass in a list of HTMLtable rows and scrape every course from them
|
||||||
* @param rows the rows of the course catalog table
|
* @param rows the rows of the course catalog table
|
||||||
|
* @param keepHeaders whether to keep the header rows (which contain the course name) in the output
|
||||||
* @returns an array of course row objects (which contain courses corresponding to the htmltable row)
|
* @returns an array of course row objects (which contain courses corresponding to the htmltable row)
|
||||||
*/
|
*/
|
||||||
public scrape(rows: NodeListOf<HTMLTableRowElement> | HTMLTableRowElement[]): ScrapedRow[] {
|
public scrape(rows: NodeListOf<HTMLTableRowElement> | HTMLTableRowElement[], keepHeaders = false): ScrapedRow[] {
|
||||||
const courses: ScrapedRow[] = [];
|
const courses: ScrapedRow[] = [];
|
||||||
|
|
||||||
let fullName = this.getFullName();
|
let fullName = this.getFullName();
|
||||||
@@ -49,6 +50,12 @@ export class CourseCatalogScraper {
|
|||||||
rows.forEach(row => {
|
rows.forEach(row => {
|
||||||
if (this.isHeaderRow(row)) {
|
if (this.isHeaderRow(row)) {
|
||||||
fullName = this.getFullName(row);
|
fullName = this.getFullName(row);
|
||||||
|
if (keepHeaders) {
|
||||||
|
courses.push({
|
||||||
|
element: row,
|
||||||
|
course: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// we are now ready to build the course object
|
// we are now ready to build the course object
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
@import './colors.module.scss';
|
@import './colors.module.scss';
|
||||||
@import './fonts.module.scss';
|
@import './fonts.module.scss';
|
||||||
@import './elevation.module.scss';
|
@import './elevation.module.scss';
|
||||||
|
@import './utils.module.scss';
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
$BURNT_ORANGE: #bf5700;
|
$burnt_orange: #bf5700;
|
||||||
$CHARCOAL: #333f48;
|
$charcoal: #333f48;
|
||||||
$WHITE: #ffffff;
|
$white: #ffffff;
|
||||||
$TANGERINE: #f8971f;
|
$tangerine: #f8971f;
|
||||||
$SUNSHINE: #ffd600;
|
$sunshine: #ffd600;
|
||||||
$CACTUS: #a6cd57;
|
$cactus: #a6cd57;
|
||||||
$TURTLE_POND: #579d42;
|
$turtle_pond: #579d42;
|
||||||
$TURQUOISE: #00a9b7;
|
$turquoise: #00a9b7;
|
||||||
$BLUEBONNET: #005f86;
|
$bluebonnet: #005f86;
|
||||||
$SHADE: #9cadb7;
|
$shade: #9cadb7;
|
||||||
$LIMESTONE: #d6d2c4;
|
$limestone: #d6d2c4;
|
||||||
|
|
||||||
:export {
|
:export {
|
||||||
BURNT_ORANGE: $BURNT_ORANGE;
|
burnt_orange: $burnt_orange;
|
||||||
CHARCOAL: $CHARCOAL;
|
charcoal: $charcoal;
|
||||||
WHITE: $WHITE;
|
white: $white;
|
||||||
TANGERINE: $TANGERINE;
|
tangerine: $tangerine;
|
||||||
SUNSHINE: $SUNSHINE;
|
sunshine: $sunshine;
|
||||||
CACTUS: $CACTUS;
|
cactus: $cactus;
|
||||||
TURTLE_POND: $TURTLE_POND;
|
turtle_pond: $turtle_pond;
|
||||||
TURQUOISE: $TURQUOISE;
|
turquoise: $turquoise;
|
||||||
BLUEBONNET: $BLUEBONNET;
|
bluebonnet: $bluebonnet;
|
||||||
SHADE: $SHADE;
|
shade: $shade;
|
||||||
LIMESTONE: $LIMESTONE;
|
limestone: $limestone;
|
||||||
}
|
}
|
||||||
|
|||||||
22
src/views/styles/colors.module.scss.d.ts
vendored
22
src/views/styles/colors.module.scss.d.ts
vendored
@@ -3,17 +3,17 @@
|
|||||||
* when we import them into ts/tsx files
|
* when we import them into ts/tsx files
|
||||||
*/
|
*/
|
||||||
export interface ISassColors {
|
export interface ISassColors {
|
||||||
BURNT_ORANGE: string;
|
burnt_orange: string;
|
||||||
CHARCOAL: string;
|
charcoal: string;
|
||||||
WHITE: string;
|
white: string;
|
||||||
TANGERINE: string;
|
tangerine: string;
|
||||||
SUNSHINE: string;
|
sunshine: string;
|
||||||
CACTUS: string;
|
cactus: string;
|
||||||
TURTLE_POND: string;
|
turtle_pond: string;
|
||||||
TURQUOISE: string;
|
turquoise: string;
|
||||||
BLUEBONNET: string;
|
bluebonnet: string;
|
||||||
SHADE: string;
|
shade: string;
|
||||||
LIMESTONE: string;
|
limestone: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare const colors: ISassColors;
|
declare const colors: ISassColors;
|
||||||
|
|||||||
@@ -7,3 +7,32 @@
|
|||||||
font-weight: #{$weights};
|
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;
|
||||||
|
}
|
||||||
|
|||||||
26
src/views/styles/fonts.module.scss.d.ts
vendored
Normal file
26
src/views/styles/fonts.module.scss.d.ts
vendored
Normal file
@@ -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;
|
||||||
0
src/views/styles/utils.module.scss
Normal file
0
src/views/styles/utils.module.scss
Normal file
Reference in New Issue
Block a user