Skip to content

Commit 8d528b1

Browse files
fix: List now keeps track of state when existing items are switched, while adding new items to the end
1 parent 0003585 commit 8d528b1

File tree

8 files changed

+65
-99
lines changed

8 files changed

+65
-99
lines changed
Lines changed: 16 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,11 @@
1-
<<<<<<< Updated upstream
2-
import { Meta, StoryObj } from '@storybook/react';
3-
=======
4-
import { Course, Status } from '@shared/types/Course';
5-
import { getCourseColors } from '@shared/util/colors';
6-
import type { Meta, StoryObj } from '@storybook/react';
7-
import type { CalendarGridCourse } from '@views/hooks/useFlattenedCourseSchedule';
8-
import type { Serialized } from 'chrome-extension-toolkit';
9-
import { CourseMeeting, DAY_MAP } from 'src/shared/types/CourseMeeting';
10-
import { CourseSchedule } from 'src/shared/types/CourseSchedule';
11-
import Instructor from 'src/shared/types/Instructor';
12-
import { UserSchedule } from 'src/shared/types/UserSchedule';
13-
>>>>>>> Stashed changes
14-
import CalendarGrid from 'src/views/components/calendar/CalendarGrid/CalendarGrid';
1+
import { Status } from '@shared/types/Course';
152
import { getCourseColors } from '@shared/util/colors';
163
import type { Meta, StoryObj } from '@storybook/react';
174
import CalendarGrid from '@views/components/calendar/CalendarGrid/CalendarGrid';
185
import type { CalendarGridCourse } from '@views/hooks/useFlattenedCourseSchedule';
196

7+
import { ExampleCourse } from '../PopupCourseBlock.stories';
8+
209
const meta = {
2110
title: 'Components/Calendar/CalendarGrid',
2211
component: CalendarGrid,
@@ -30,62 +19,7 @@ const meta = {
3019
} satisfies Meta<typeof CalendarGrid>;
3120
export default meta;
3221

33-
<<<<<<< Updated upstream
3422
const testData: CalendarGridCourse[] = [
35-
=======
36-
const exampleCourse: Course = new Course({
37-
uniqueId: 50805,
38-
number: '314',
39-
fullName: 'C S 314 DATA STRUCTURES',
40-
courseName: 'DATA STRUCTURES',
41-
department: 'C S',
42-
creditHours: 3,
43-
status: Status.OPEN,
44-
instructors: [
45-
new Instructor({ fullName: 'SCOTT, MICHAEL', firstName: 'MICHAEL', lastName: 'SCOTT', middleInitial: 'D' }),
46-
],
47-
isReserved: true,
48-
description: [
49-
'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.',
50-
'Computer Science 314 and 314H may not both be counted.',
51-
'BVO 311C and 312H may not both be counted.',
52-
'Prerequisite: Computer Science 312 or 312H with a grade of at least C-.',
53-
'May be counted toward the Quantitative Reasoning flag requirement.',
54-
],
55-
schedule: new CourseSchedule({
56-
meetings: [
57-
new CourseMeeting({
58-
days: [DAY_MAP.T, DAY_MAP.TH],
59-
startTime: 480,
60-
endTime: 570,
61-
location: { building: 'UTC', room: '123' },
62-
}),
63-
new CourseMeeting({
64-
days: [DAY_MAP.TH],
65-
startTime: 570,
66-
endTime: 630,
67-
location: { building: 'JES', room: '123' },
68-
}),
69-
],
70-
}),
71-
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/12345/',
72-
flags: ['Writing', 'Independent Inquiry'],
73-
instructionMode: 'In Person',
74-
semester: {
75-
code: '12345',
76-
year: 2024,
77-
season: 'Spring',
78-
},
79-
});
80-
81-
const exampleSchedule = new UserSchedule({
82-
name: 'Example Schedule',
83-
courses: [exampleCourse],
84-
hours: 3,
85-
} as Serialized<UserSchedule>);
86-
87-
export const exampleCalendarGridCourses: CalendarGridCourse[] = [
88-
>>>>>>> Stashed changes
8923
{
9024
calendarGridPoint: {
9125
dayIndex: 4,
@@ -98,6 +32,7 @@ export const exampleCalendarGridCourses: CalendarGridCourse[] = [
9832
status: Status.OPEN,
9933
colors: getCourseColors('emerald', 500),
10034
},
35+
course: ExampleCourse,
10136
},
10237
{
10338
calendarGridPoint: {
@@ -111,6 +46,7 @@ export const exampleCalendarGridCourses: CalendarGridCourse[] = [
11146
status: Status.OPEN,
11247
colors: getCourseColors('emerald', 500),
11348
},
49+
course: ExampleCourse,
11450
},
11551
{
11652
calendarGridPoint: {
@@ -124,6 +60,7 @@ export const exampleCalendarGridCourses: CalendarGridCourse[] = [
12460
status: Status.CLOSED,
12561
colors: getCourseColors('emerald', 500),
12662
},
63+
course: ExampleCourse,
12764
},
12865
{
12966
calendarGridPoint: {
@@ -137,6 +74,7 @@ export const exampleCalendarGridCourses: CalendarGridCourse[] = [
13774
status: Status.OPEN,
13875
colors: getCourseColors('emerald', 500),
13976
},
77+
course: ExampleCourse,
14078
},
14179
{
14280
calendarGridPoint: {
@@ -150,6 +88,7 @@ export const exampleCalendarGridCourses: CalendarGridCourse[] = [
15088
status: Status.CLOSED,
15189
colors: getCourseColors('emerald', 500),
15290
},
91+
course: ExampleCourse,
15392
},
15493
{
15594
calendarGridPoint: {
@@ -163,27 +102,29 @@ export const exampleCalendarGridCourses: CalendarGridCourse[] = [
163102
status: Status.CLOSED,
164103
colors: getCourseColors('emerald', 500),
165104
},
105+
course: ExampleCourse,
166106
},
167107
{
168108
calendarGridPoint: {
169109
dayIndex: 1,
170-
startIndex: 11,
171-
endIndex: 13,
110+
startIndex: 10,
111+
endIndex: 12,
172112
},
173113
componentProps: {
174114
courseDeptAndInstr: 'Course 4',
175115
timeAndLocation: '10:00 AM - 11:00 AM, Room 102',
176116
status: Status.CLOSED,
177117
colors: getCourseColors('emerald', 500),
178118
},
119+
course: ExampleCourse,
179120
},
180-
]; */
121+
];
181122

182123
type Story = StoryObj<typeof meta>;
183124

184-
/* export const Default: Story = {
125+
export const Default: Story = {
185126
args: {
186127
saturdayClass: true,
187-
courseCells: exampleCalendarGridCourses,
128+
courseCells: testData,
188129
},
189-
}; */
130+
};

src/views/components/PopupMain.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ export default function PopupMain() {
3737
));
3838

3939
const handleOpenOptions = async () => {
40-
// Not sure if it's bad practice to export this
4140
const url = chrome.runtime.getURL('/src/pages/options/index.html');
4241
await openTabFromContentScript(url);
4342
};

src/views/components/calendar/Calendar/Calendar.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { CalendarSchedules } from '@views/components/calendar/CalendarSchedules/
55
import ImportantLinks from '@views/components/calendar/ImportantLinks';
66
import React from 'react';
77
import type { Course } from 'src/shared/types/Course';
8-
import { UserSchedule } from 'src/shared/types/UserSchedule';
98
import { ExampleCourse } from 'src/stories/components/PopupCourseBlock.stories';
109
import { useFlattenedCourseSchedule } from 'src/views/hooks/useFlattenedCourseSchedule';
1110

src/views/components/calendar/CalendarGrid/CalendarGrid.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,8 @@ function AccountForCourseConflicts({ courseCells, setCourse }: AccountForCourseC
158158
<div
159159
key={`${block}`}
160160
style={{
161-
gridColumn: `${block.calendarGridPoint.dayIndex + 1}`,
162-
gridRow: `${block.calendarGridPoint.startIndex + 1} / ${block.calendarGridPoint.endIndex + 1}`,
161+
gridColumn: `${block.calendarGridPoint.dayIndex + 2}`,
162+
gridRow: `${block.calendarGridPoint.startIndex} / ${block.calendarGridPoint.endIndex}`,
163163
width: `calc(100% / ${block.totalColumns})`,
164164
marginLeft: `calc(100% * ${(block.gridColumnStart - 1) / block.totalColumns})`,
165165
padding: '0px 10px 4px 0px',

src/views/components/calendar/CalendarHeader/CalenderHeader.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@ import ScheduleTotalHoursAndCourses from '@views/components/common/ScheduleTotal
66
import Text from '@views/components/common/Text/Text';
77
import React from 'react';
88
import calIcon from 'src/assets/logo.png';
9+
import { openTabFromContentScript } from 'src/views/lib/openNewTabFromContentScript';
910

1011
import MenuIcon from '~icons/material-symbols/menu';
1112
import RedoIcon from '~icons/material-symbols/redo';
1213
import SettingsIcon from '~icons/material-symbols/settings';
1314
import UndoIcon from '~icons/material-symbols/undo';
1415

15-
import handleOpenOptions from '../../PopupMain';
16-
import UndoIcon from '~icons/material-symbols/undo';
16+
const handleOpenOptions = async () => {
17+
const url = chrome.runtime.getURL('/src/pages/options/index.html');
18+
await openTabFromContentScript(url);
19+
};
1720

1821
const CalendarHeader = ( { totalHours, totalCourses, scheduleName } ) => (
1922
<div className='min-h-79px min-w-672px w-full flex px-0 py-15'>

src/views/components/calendar/CalendarSchedules/CalendarSchedules.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import type { UserSchedule } from '@shared/types/UserSchedule';
2-
import React, { useState, useEffect } from 'react';
3-
import useSchedules from 'src/views/hooks/useSchedules';
2+
import React, { useEffect,useState } from 'react';
43
import createSchedule from 'src/pages/background/lib/createSchedule';
4+
import switchSchedule from 'src/pages/background/lib/switchSchedule';
5+
import useSchedules from 'src/views/hooks/useSchedules';
6+
57
import AddSchedule from '~icons/material-symbols/add';
8+
69
import List from '../../common/List/List';
710
import ScheduleListItem from '../../common/ScheduleListItem/ScheduleListItem';
811
import Text from '../../common/Text/Text';
9-
import switchSchedule from 'src/pages/background/lib/switchSchedule';
1012

1113
export type Props = {
1214
style?: React.CSSProperties;
@@ -48,15 +50,13 @@ export function CalendarSchedules(props: Props) {
4850
switchSchedule(schedules[index].name);
4951
};
5052

51-
const scheduleComponents = schedules.map((schedule, index) => {
52-
return (
53+
const scheduleComponents = schedules.map((schedule, index) => (
5354
<ScheduleListItem
5455
active={index === activeScheduleIndex}
5556
name={schedule.name}
5657
onClick={() => selectItem(index)}
5758
/>
58-
);
59-
});
59+
));
6060

6161
return (
6262
<div style={{ ...props.style }} className='items-center'>

src/views/components/common/List/List.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd';
22
import type { ReactElement } from 'react';
3-
import React, { useCallback, useState } from 'react';
3+
import React, { useCallback, useEffect,useState } from 'react';
44

55
/*
66
* Ctrl + f dragHandleProps on PopupCourseBlock.tsx for example implementation of drag handle (two lines of code)
@@ -19,9 +19,9 @@ export interface ListProps {
1919
gap: number; // Impacts the spacing between items in the list
2020
}
2121

22-
function initial(draggableElements: any[] = []) {
22+
function initial(draggableElements: any[] = [], count: number) {
2323
return draggableElements.map((element, index) => ({
24-
id: `id:${index}`,
24+
id: `id:${index + count}`,
2525
content: element as ReactElement,
2626
}));
2727
}
@@ -83,10 +83,15 @@ const Row: React.FC<RowProps> = React.memo(({ data: { items, gap }, index, style
8383
* <List draggableElements={elements} />
8484
*/
8585
const List: React.FC<ListProps> = ({ draggableElements, itemHeight, listHeight, listWidth, gap = 12 }: ListProps) => {
86-
const [items, setItems] = useState(() => initial(draggableElements));
86+
const [items, setItems] = useState(() => initial(draggableElements, 0));
8787

8888
useEffect(() => {
89-
setItems(initial(draggableElements));
89+
setItems((prevItems) => {
90+
const prevItemIds = prevItems.map(item => item.id);
91+
const newElements = draggableElements.filter((_, index) => !prevItemIds.includes(`id:${index}`));
92+
const newItems = initial(newElements, prevItems.length);
93+
return [...prevItems, ...newItems];
94+
});
9095
}, [draggableElements]);
9196

9297
const onDragEnd = useCallback(

src/views/hooks/useFlattenedCourseSchedule.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { CalendarCourseCellProps } from '@views/components/calendar/CalendarCourseCell/CalendarCourseCell';
22
import type { Course, StatusType } from 'src/shared/types/Course';
3-
import type { CourseMeeting, TimeStringOptions } from 'src/shared/types/CourseMeeting';
3+
import type { CourseMeeting } from 'src/shared/types/CourseMeeting';
44
import type { UserSchedule } from 'src/shared/types/UserSchedule';
55

66
import useSchedules from './useSchedules';
@@ -113,7 +113,9 @@ function extractCourseInfo(course: Course) {
113113
return { status, courseDeptAndInstr, meetings, course };
114114
}
115115

116-
// Function to handle asynchronous (online) courses
116+
/**
117+
* Function to process each in-person class into its distinct meeting objects for calendar grid
118+
*/
117119
function processAsyncCourses({ courseDeptAndInstr, status, course }: { courseDeptAndInstr: string, status: StatusType, course: Course }) {
118120
return [{
119121
calendarGridPoint: {
@@ -133,7 +135,9 @@ function processAsyncCourses({ courseDeptAndInstr, status, course }: { courseDep
133135
}];
134136
}
135137

136-
// Function to process in-person meeting times and locations
138+
/**
139+
* Function to process each in-person class into its distinct meeting objects for calendar grid
140+
*/
137141
function processInPersonMeetings({ days, startTime, endTime, location }: CourseMeeting, { courseDeptAndInstr, status, course }) {
138142
const time = getTimeString({ separator: '-', capitalize: true }, startTime, endTime);
139143
const timeAndLocation = `${time} - ${location ? location.building : 'WB'}`;
@@ -156,7 +160,10 @@ function processInPersonMeetings({ days, startTime, endTime, location }: CourseM
156160
}));
157161
}
158162

159-
// Utility function to sort courses for the calendar grid
163+
164+
/**
165+
* Utility function to sort courses for the calendar grid
166+
*/
160167
function sortCourses(a, b) {
161168
if (a.calendarGridPoint.dayIndex !== b.calendarGridPoint.dayIndex) {
162169
return a.calendarGridPoint.dayIndex - b.calendarGridPoint.dayIndex;
@@ -168,7 +175,9 @@ function sortCourses(a, b) {
168175
}
169176

170177

171-
// Utility function also present in CourseMeeting object. Wasn't being found at runtime, so I copied it over.
178+
/**
179+
* Utility function also present in CourseMeeting object. Wasn't being found at runtime, so I copied it over.
180+
*/
172181
function getTimeString(options: TimeStringOptions, startTime: number, endTime: number): string {
173182
const startHour = Math.floor(startTime / 60);
174183
const startMinute = startTime % 60;
@@ -205,4 +214,14 @@ function getTimeString(options: TimeStringOptions, startTime: number, endTime: n
205214
}
206215

207216
return `${startTimeString} ${options.separator} ${endTimeString}`;
208-
}
217+
}
218+
219+
/**
220+
* Options to control the format of the time string
221+
*/
222+
type TimeStringOptions = {
223+
/** the separator between the start and end times */
224+
separator: string;
225+
/** capitalizes the AM/PM */
226+
capitalize?: boolean;
227+
};

0 commit comments

Comments
 (0)