fix: injected course popup storybook

This commit is contained in:
Casey Charleston
2024-02-21 23:51:12 -06:00
parent e4ce051086
commit 5daccc8349
11 changed files with 1874 additions and 848 deletions

View File

@@ -37,6 +37,7 @@
"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",

2491
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
import { Course, Status } from '@shared/types/Course'; import { Course, Status } from '@shared/types/Course';
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 { 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 { CourseMeeting, DAY_MAP } from 'src/shared/types/CourseMeeting';
import { CourseSchedule } from 'src/shared/types/CourseSchedule'; import { CourseSchedule } from 'src/shared/types/CourseSchedule';

View File

@@ -1,9 +1,10 @@
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import type { Serialized } from 'chrome-extension-toolkit';
import { Course, Status } from 'src/shared/types/Course'; import { Course, Status } from 'src/shared/types/Course';
import { CourseMeeting, DAY_MAP } from 'src/shared/types/CourseMeeting'; import { CourseMeeting, DAY_MAP } from 'src/shared/types/CourseMeeting';
import { CourseSchedule } from 'src/shared/types/CourseSchedule'; import { CourseSchedule } from 'src/shared/types/CourseSchedule';
import Instructor from 'src/shared/types/Instructor'; import Instructor from 'src/shared/types/Instructor';
import { UserSchedule } from 'src/shared/types/UserSchedule';
import CourseCatalogInjectedPopup from 'src/views/components/injected/CourseCatalogInjectedPopup/CourseCatalogInjectedPopup'; import CourseCatalogInjectedPopup from 'src/views/components/injected/CourseCatalogInjectedPopup/CourseCatalogInjectedPopup';
const exampleCourse: Course = new Course({ const exampleCourse: Course = new Course({
@@ -51,11 +52,19 @@ 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' },
}, },
}; };
@@ -64,6 +73,7 @@ type Story = StoryObj<typeof CourseCatalogInjectedPopup>;
export const Default: Story = { export const Default: Story = {
args: { args: {
activeSchedule: exampleSchedule,
course: exampleCourse, course: exampleCourse,
}, },
}; };

View File

@@ -1,97 +1,154 @@
import React from 'react';
import { FaCalendarAlt, FaCog, FaRedo } from 'react-icons/fa'; // Added FaRedo for the refresh icon
import { StatusIcon } from '@shared/util/icons'; import { StatusIcon } from '@shared/util/icons';
import React from 'react';
// import { FaCalendarAlt, FaCog, FaRedo } from 'react-icons/fa'; // Added FaRedo for the refresh icon
import { Status } from 'src/shared/types/Course'; import { Status } from 'src/shared/types/Course';
import { test_colors } 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 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 PopupCourseBlock from './common/PopupCourseBlock/PopupCourseBlock';
import Text from './common/Text/Text'; import Text from './common/Text/Text';
import Divider from './common/Divider/Divider';
import logoImage from '../../assets/logo.png'; // Adjust the path as necessary
import List from './common/List/List'; // Ensure this path is correctly pointing to your List component
import useSchedules from '../hooks/useSchedules';
import { handleOpenCalendar } from './injected/CourseCatalogInjectedPopup/HeadingAndActions'; import { handleOpenCalendar } from './injected/CourseCatalogInjectedPopup/HeadingAndActions';
import { openTabFromContentScript } from '../lib/openNewTabFromContentScript';
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={test_colors[i]}
/>
)); ));
const handleOpenOptions = async () => { // Not sure if it's bad practice to export this const handleOpenOptions = async () => {
// Not sure if it's bad practice to export this
const url = chrome.runtime.getURL('/src/pages/options/index.html'); const url = chrome.runtime.getURL('/src/pages/options/index.html');
await openTabFromContentScript(url); await openTabFromContentScript(url);
}; };
return ( return (
<ExtensionRoot> <ExtensionRoot>
<div className="mx-auto max-w-sm rounded-lg bg-white p-4 shadow-md"> <div className='mx-auto max-w-sm rounded-lg bg-white p-4 shadow-md'>
<div className="mb-2 flex items-center justify-between bg-white"> <div className='mb-2 flex items-center justify-between bg-white'>
<div className="flex items-center"> <div className='flex items-center'>
<img src={logoImage} alt="Logo" style={{ width: '40px', height: '40px', marginRight: '8px' }} /> <img src={logoImage} alt='Logo' style={{ width: '40px', height: '40px', marginRight: '8px' }} />
<div> <div>
<Text as="div" variant="h1-course" style={{ color: '#bf5700', fontSize: '1.3rem' }}>UT Registration</Text> <Text as='div' variant='h1-course' style={{ color: '#bf5700', fontSize: '1.3rem' }}>
<Text as="div" variant="h1-course" style={{ color: '#f8971f', fontSize: '1.3rem' }}>Plus</Text> UT Registration
</Text>
<Text as='div' variant='h1-course' style={{ color: '#f8971f', fontSize: '1.3rem' }}>
Plus
</Text>
</div> </div>
</div> </div>
<div className="flex items-center"> <div className='flex items-center'>
<button style={{ backgroundColor: '#bf5700', borderRadius: '8px', padding: '8px' }} onClick={handleOpenCalendar}> <button
<FaCalendarAlt color="white" /> style={{ backgroundColor: '#bf5700', borderRadius: '8px', padding: '8px' }}
onClick={handleOpenCalendar}
>
{/* <FaCalendarAlt color='white' /> */}
</button> </button>
<button style={{ backgroundColor: 'white', marginLeft: '10px', borderRadius: '8px', padding: '8px', boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)' }} <button
onClick = {handleOpenOptions}> style={{
<FaCog color="#C05621" /> backgroundColor: 'white',
marginLeft: '10px',
borderRadius: '8px',
padding: '8px',
boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)',
}}
onClick={handleOpenOptions}
>
{/* <FaCog color='#C05621' /> */}
</button> </button>
</div> </div>
</div> </div>
<Divider color="#E2E8F0" type="solid" style={{ margin: '1rem 0' }} /> <Divider color='#E2E8F0' type='solid' style={{ margin: '1rem 0' }} />
<div className="mb-4 rounded-lg bg-white p-2 text-left shadow-inner" style={{ backgroundColor: 'white', border: '1px solid #FBD38D', borderRadius: '0.5rem' }}> <div
<Text as="div" variant="h2-course" style={{ color: '#DD6B20', fontSize: '1.2rem' }}>MAIN SCHEDULE:</Text> className='mb-4 rounded-lg bg-white p-2 text-left shadow-inner'
style={{ backgroundColor: 'white', border: '1px solid #FBD38D', borderRadius: '0.5rem' }}
>
<Text as='div' variant='h2-course' style={{ color: '#DD6B20', fontSize: '1.2rem' }}>
MAIN SCHEDULE:
</Text>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'start', color: '#333f48' }}> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'start', color: '#333f48' }}>
<Text as="div" variant="h1" style={{ fontSize: '1.2rem', fontWeight: 'bold', marginRight: '0.5rem' }}>22 HOURS</Text> <Text
<Text as="div" variant="h2-course" style={{ fontSize: '1.2rem' }}>8 Courses</Text> as='div'
variant='h1'
style={{ fontSize: '1.2rem', fontWeight: 'bold', marginRight: '0.5rem' }}
>
22 HOURS
</Text>
<Text as='div' variant='h2-course' style={{ fontSize: '1.2rem' }}>
8 Courses
</Text>
</div> </div>
</div> </div>
{/* Integrate the List component here */} {/* Integrate the List component here */}
{activeSchedule ? <List {activeSchedule ? (
draggableElements={draggableElements} <List
itemHeight={100} // Adjust based on your content size draggableElements={draggableElements}
listHeight={500} // Adjust based on total height you want for the list itemHeight={100} // Adjust based on your content size
listWidth={350} // Adjust based on your layout/design listHeight={500} // Adjust based on total height you want for the list
gap={12} // Spacing between items listWidth={350} // Adjust based on your layout/design
/> : null} gap={12} // Spacing between items
<div className="mt-4 flex justify-between border-t border-gray-200 p-4 text-xs"> />
<div className="flex items-center"> ) : null}
<div style={{ backgroundColor: '#6B7280', padding: '1px', borderRadius: '4px', marginRight: '3px', marginLeft: '8px' }}> <div className='mt-4 flex justify-between border-t border-gray-200 p-4 text-xs'>
<StatusIcon status={Status.WAITLISTED} className="h-5 w-5 text-white" /> <div className='flex items-center'>
<div
style={{
backgroundColor: '#6B7280',
padding: '1px',
borderRadius: '4px',
marginRight: '3px',
marginLeft: '8px',
}}
>
<StatusIcon status={Status.WAITLISTED} className='h-5 w-5 text-white' />
</div> </div>
<Text as="span" variant="mini">WAITLISTED</Text> <Text as='span' variant='mini'>
WAITLISTED
</Text>
</div> </div>
<div className="flex items-center"> <div className='flex items-center'>
<div style={{ backgroundColor: '#6B7280', padding: '1px', borderRadius: '4px', marginRight: '3px', marginLeft: '8px' }}> <div
<StatusIcon status={Status.CLOSED} className="h-5 w-5 text-white" /> style={{
backgroundColor: '#6B7280',
padding: '1px',
borderRadius: '4px',
marginRight: '3px',
marginLeft: '8px',
}}
>
<StatusIcon status={Status.CLOSED} className='h-5 w-5 text-white' />
</div> </div>
<Text as="span" variant="mini">CLOSED</Text> <Text as='span' variant='mini'>
CLOSED
</Text>
</div> </div>
<div className="flex items-center"> <div className='flex items-center'>
<div style={{ backgroundColor: '#6B7280', padding: '1px', borderRadius: '4px', marginRight: '3px', marginLeft: '8px' }}> <div
<StatusIcon status={Status.CANCELLED} className="h-5 w-5 text-white" /> style={{
backgroundColor: '#6B7280',
padding: '1px',
borderRadius: '4px',
marginRight: '3px',
marginLeft: '8px',
}}
>
<StatusIcon status={Status.CANCELLED} className='h-5 w-5 text-white' />
</div> </div>
<Text as="span" variant="mini">CANCELLED</Text> <Text as='span' variant='mini'>
CANCELLED
</Text>
</div> </div>
</div> </div>
<div className="mt-2 text-center text-xs"> <div className='mt-2 text-center text-xs'>
<div className="inline-flex items-center justify-center"> <div className='inline-flex items-center justify-center'>
<Text as="div" variant="mini">DATA UPDATED ON: 12:00 AM 02/01/2024</Text> <Text as='div' variant='mini'>
<FaRedo className="ml-2 h-4 w-4 text-gray-600" /> DATA UPDATED ON: 12:00 AM 02/01/2024
</Text>
{/* <FaRedo className='ml-2 h-4 w-4 text-gray-600' /> */}
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,5 +1,6 @@
import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd'; import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd';
import React, { ReactElement, useCallback, useState } from 'react'; import type { ReactElement } from 'react';
import React, { useCallback, useState } from 'react';
import { areEqual } from 'react-window'; import { areEqual } from 'react-window';
/* /*

View File

@@ -1,5 +1,7 @@
import clsx from 'clsx'; import clsx from 'clsx';
import React, { PropsWithChildren, useCallback } from 'react'; import type { PropsWithChildren } from 'react';
import React, { useCallback } from 'react';
import styles from './Popup.module.scss'; import styles from './Popup.module.scss';
interface Props { interface Props {
@@ -19,8 +21,6 @@ export default function Popup({ onClose, children, className, style, testId, ove
const containerRef = React.useRef<HTMLDivElement>(null); const containerRef = React.useRef<HTMLDivElement>(null);
const bodyRef = React.useRef<HTMLDivElement>(null); const bodyRef = React.useRef<HTMLDivElement>(null);
const handleClickOutside = useCallback( const handleClickOutside = useCallback(
(event: MouseEvent) => { (event: MouseEvent) => {
if (!bodyRef.current) return; if (!bodyRef.current) return;

View File

@@ -1,7 +1,8 @@
import Popup from '@views/components/common/Popup/Popup'; import Popup from '@views/components/common/Popup/Popup';
import React from 'react'; import React from 'react';
import { Course } from 'src/shared/types/Course'; import type { Course } from 'src/shared/types/Course';
import { UserSchedule } from 'src/shared/types/UserSchedule'; import type { UserSchedule } from 'src/shared/types/UserSchedule';
import Description from './Description'; import Description from './Description';
import GradeDistribution from './GradeDistribution'; import GradeDistribution from './GradeDistribution';
import HeadingAndActions from './HeadingAndActions'; import HeadingAndActions from './HeadingAndActions';

View File

@@ -1,5 +1,6 @@
import clsx from 'clsx'; import clsx from 'clsx';
import React from 'react'; import React from 'react';
import Text from '../../common/Text/Text'; import Text from '../../common/Text/Text';
interface DescriptionProps { interface DescriptionProps {

View File

@@ -3,8 +3,8 @@ import Text from '@views/components/common/Text/Text';
import Highcharts from 'highcharts'; import Highcharts from 'highcharts';
import HighchartsReact from 'highcharts-react-official'; import HighchartsReact from 'highcharts-react-official';
import React from 'react'; import React from 'react';
import { Course } from 'src/shared/types/Course'; import type { Course } from 'src/shared/types/Course';
import { Distribution, LetterGrade } from 'src/shared/types/Distribution'; import type { Distribution, LetterGrade } from 'src/shared/types/Distribution';
import { colors } from 'src/shared/util/themeColors'; import { colors } from 'src/shared/util/themeColors';
import { import {
NoDataError, NoDataError,

View File

@@ -1,20 +1,21 @@
import React, { useState } from 'react';
import { Button } from '@views/components/common/Button/Button'; import { Button } from '@views/components/common/Button/Button';
import { Chip, flagMap } from '@views/components/common/Chip/Chip'; import { Chip, flagMap } from '@views/components/common/Chip/Chip';
import Divider from '@views/components/common/Divider/Divider'; import Divider from '@views/components/common/Divider/Divider';
import Text from '@views/components/common/Text/Text'; import Text from '@views/components/common/Text/Text';
import React, { useState } from 'react';
import addCourse from 'src/pages/background/lib/addCourse'; import addCourse from 'src/pages/background/lib/addCourse';
import removeCourse from 'src/pages/background/lib/removeCourse'; import removeCourse from 'src/pages/background/lib/removeCourse';
import type { Course } from 'src/shared/types/Course';
import type { UserSchedule } from 'src/shared/types/UserSchedule';
import { openTabFromContentScript } from 'src/views/lib/openNewTabFromContentScript'; import { openTabFromContentScript } from 'src/views/lib/openNewTabFromContentScript';
import { Course } from 'src/shared/types/Course';
import { UserSchedule } from 'src/shared/types/UserSchedule';
import Add from '~icons/material-symbols/add'; import Add from '~icons/material-symbols/add';
import Remove from '~icons/material-symbols/remove';
import CalendarMonth from '~icons/material-symbols/calendar-month'; import CalendarMonth from '~icons/material-symbols/calendar-month';
import CloseIcon from '~icons/material-symbols/close'; import CloseIcon from '~icons/material-symbols/close';
import Copy from '~icons/material-symbols/content-copy'; import Copy from '~icons/material-symbols/content-copy';
import Description from '~icons/material-symbols/description'; import Description from '~icons/material-symbols/description';
import Mood from '~icons/material-symbols/mood'; import Mood from '~icons/material-symbols/mood';
import Remove from '~icons/material-symbols/remove';
import Reviews from '~icons/material-symbols/reviews'; import Reviews from '~icons/material-symbols/reviews';
interface HeadingAndActionProps { interface HeadingAndActionProps {
@@ -26,7 +27,8 @@ interface HeadingAndActionProps {
onClose: () => void; onClose: () => void;
} }
export const handleOpenCalendar = async () => { // Not sure if it's bad practice to export this export const handleOpenCalendar = async () => {
// Not sure if it's bad practice to export this
const url = chrome.runtime.getURL('calendar.html'); const url = chrome.runtime.getURL('calendar.html');
await openTabFromContentScript(url); await openTabFromContentScript(url);
}; };
@@ -41,7 +43,7 @@ const HeadingAndActions: React.FC<HeadingAndActionProps> = ({ course, onClose, a
const { courseName, department, number: courseNumber, uniqueId, instructors, flags, schedule } = course; const { courseName, department, number: courseNumber, uniqueId, instructors, flags, schedule } = course;
const [courseAdded, setCourseAdded] = useState<boolean>( const [courseAdded, setCourseAdded] = useState<boolean>(
activeSchedule.courses.some(course => course.uniqueId === uniqueId) activeSchedule.courses.some(course => course.uniqueId === uniqueId)
); );
const instructorString = instructors const instructorString = instructors
.map(instructor => { .map(instructor => {
@@ -74,8 +76,7 @@ const HeadingAndActions: React.FC<HeadingAndActionProps> = ({ course, onClose, a
const handleAddOrRemoveCourse = async () => { const handleAddOrRemoveCourse = async () => {
if (!courseAdded) { if (!courseAdded) {
await addCourse(activeSchedule.name, course); await addCourse(activeSchedule.name, course);
} } else {
else {
await removeCourse(activeSchedule.name, course); await removeCourse(activeSchedule.name, course);
} }
setCourseAdded(!courseAdded); setCourseAdded(!courseAdded);
@@ -137,7 +138,12 @@ const HeadingAndActions: React.FC<HeadingAndActionProps> = ({ course, onClose, a
<Button variant='outline' color='ut-orange' icon={Description} onClick={handleOpenPastSyllabi}> <Button variant='outline' color='ut-orange' icon={Description} onClick={handleOpenPastSyllabi}>
Past Syllabi Past Syllabi
</Button> </Button>
<Button variant='filled' color={!courseAdded ? 'ut-green' : 'ut-red'} icon={!courseAdded ? Add : Remove} onClick={handleAddOrRemoveCourse}> <Button
variant='filled'
color={!courseAdded ? 'ut-green' : 'ut-red'}
icon={!courseAdded ? Add : Remove}
onClick={handleAddOrRemoveCourse}
>
{!courseAdded ? 'Add Course' : 'Remove Course'} {!courseAdded ? 'Add Course' : 'Remove Course'}
</Button> </Button>
</div> </div>