From 261d2f2e84a09480111c5e8874aeeab2610853ec Mon Sep 17 00:00:00 2001 From: Razboy20 Date: Sat, 9 Mar 2024 23:16:56 -0600 Subject: [PATCH] refactor: match dropdown to figma & fix issues (#142) --- src/stories/components/Dropdown.stories.tsx | 186 ++++++------------ .../components/common/Dropdown/Dropdown.tsx | 106 ---------- .../ScheduleDropdown/ScheduleDropdown.tsx | 61 ++++++ .../ScheduleListItem/ScheduleListItem.tsx | 28 +-- 4 files changed, 137 insertions(+), 244 deletions(-) delete mode 100644 src/views/components/common/Dropdown/Dropdown.tsx create mode 100644 src/views/components/common/ScheduleDropdown/ScheduleDropdown.tsx diff --git a/src/stories/components/Dropdown.stories.tsx b/src/stories/components/Dropdown.stories.tsx index 5ed23eda..212557e0 100644 --- a/src/stories/components/Dropdown.stories.tsx +++ b/src/stories/components/Dropdown.stories.tsx @@ -1,144 +1,78 @@ -import { Course, Status } from '@shared/types/Course'; -import { CourseMeeting, DAY_MAP } from '@shared/types/CourseMeeting'; -import { CourseSchedule } from '@shared/types/CourseSchedule'; -import Instructor from '@shared/types/Instructor'; +import { UserScheduleStore } from '@shared/storage/UserScheduleStore'; import { UserSchedule } from '@shared/types/UserSchedule'; import type { Meta, StoryObj } from '@storybook/react'; -import Dropdown from '@views/components/common/Dropdown/Dropdown'; +import List from '@views/components/common/List/List'; +import ScheduleDropdown from '@views/components/common/ScheduleDropdown/ScheduleDropdown'; import ScheduleListItem from '@views/components/common/ScheduleListItem/ScheduleListItem'; +import useSchedules, { switchSchedule } from '@views/hooks/useSchedules'; import type { Serialized } from 'chrome-extension-toolkit'; import React from 'react'; -const meta: Meta = { +import { exampleSchedule } from '../injected/mocked'; + +const schedules: UserSchedule[] = new Array(10).fill(exampleSchedule).map( + (schedule: UserSchedule, index) => + new UserSchedule({ + ...schedule, + name: `Schedule ${index + 1}`, + }) +); + +UserScheduleStore.set( + 'schedules', + schedules.reduce((acc, schedule) => { + acc.push(schedule); + return acc; + }, [] as Serialized[]) +); + +UserScheduleStore.set('activeIndex', 0); + +const meta: Meta = { title: 'Components/Common/Dropdown', - component: Dropdown, + component: ScheduleDropdown, parameters: { layout: 'centered', }, tags: ['autodocs'], argTypes: { - dummySchedules: { control: 'object' }, - dummyActiveIndex: { control: 'number' }, - scheduleComponents: { control: 'object' }, + defaultOpen: { + control: { + type: 'boolean', + }, + }, + children: { + control: { + type: 'node', + }, + }, }, render: (args: any) => (
- + + { + const [activeSchedule] = useSchedules(); + return ( + { + switchSchedule(schedule.name); + }} + /> + ); + })} + gap={10} + /> +
), -} satisfies Meta; +} satisfies Meta; export default meta; type Story = StoryObj; -const schedules = [ - new UserSchedule({ - courses: [ - new Course({ - uniqueId: 123, - number: '311C', - fullName: "311C - Bevo's Default Course", - courseName: "Bevo's Default Course", - department: 'BVO', - creditHours: 3, - status: Status.WAITLISTED, - instructors: [new Instructor({ firstName: '', lastName: 'Bevo', fullName: 'Bevo' })], - isReserved: false, - url: '', - flags: [], - schedule: new CourseSchedule({ - meetings: [ - new CourseMeeting({ - days: [DAY_MAP.M, DAY_MAP.W, DAY_MAP.F], - startTime: 480, - endTime: 570, - location: { - building: 'UTC', - room: '123', - }, - }), - ], - }), - instructionMode: 'In Person', - semester: { - year: 2024, - season: 'Fall', - }, - }), - ], - name: 'Main Schedule', - hours: 0, - } as Serialized), - new UserSchedule({ - courses: [ - new Course({ - uniqueId: 123, - number: '311C', - fullName: "311C - Bevo's Default Course", - courseName: "Bevo's Default Course", - department: 'BVO', - creditHours: 3, - status: Status.WAITLISTED, - instructors: [new Instructor({ firstName: '', lastName: 'Bevo', fullName: 'Bevo' })], - isReserved: false, - url: '', - flags: [], - schedule: new CourseSchedule({ - meetings: [ - new CourseMeeting({ - days: [DAY_MAP.M, DAY_MAP.W, DAY_MAP.F], - startTime: 480, - endTime: 570, - location: { - building: 'UTC', - room: '123', - }, - }), - ], - }), - instructionMode: 'In Person', - semester: { - year: 2024, - season: 'Fall', - }, - }), - new Course({ - uniqueId: 123, - number: '311C', - fullName: "311C - Bevo's Default Course", - courseName: "Bevo's Default Course", - department: 'BVO', - creditHours: 3, - status: Status.WAITLISTED, - instructors: [new Instructor({ firstName: '', lastName: 'Bevo', fullName: 'Bevo' })], - isReserved: false, - url: '', - flags: [], - schedule: new CourseSchedule({ - meetings: [ - new CourseMeeting({ - days: [DAY_MAP.M, DAY_MAP.W, DAY_MAP.F], - startTime: 480, - endTime: 570, - location: { - building: 'UTC', - room: '123', - }, - }), - ], - }), - instructionMode: 'In Person', - semester: { - year: 2024, - season: 'Fall', - }, - }), - ], - name: 'Backup #3', - hours: 0, - } as Serialized), -]; - export const Hidden: Story = { parameters: { design: { @@ -148,10 +82,12 @@ export const Hidden: Story = { }, args: { - dummySchedules: schedules, - dummyActiveIndex: 0, - scheduleComponents: schedules.map((schedule, index) => ( - - )), + defaultOpen: false, + }, +}; + +export const Visible: Story = { + args: { + defaultOpen: true, }, }; diff --git a/src/views/components/common/Dropdown/Dropdown.tsx b/src/views/components/common/Dropdown/Dropdown.tsx deleted file mode 100644 index 625505b0..00000000 --- a/src/views/components/common/Dropdown/Dropdown.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import { Disclosure, Transition } from '@headlessui/react'; -import userScheduleHandler from '@pages/background/handler/userScheduleHandler'; -import type { UserSchedule } from '@shared/types/UserSchedule'; -import List from '@views/components/common/List/List'; -import Text from '@views/components/common/Text/Text'; -import React from 'react'; - -import DropdownArrowDown from '~icons/material-symbols/arrow-drop-down'; -import DropdownArrowUp from '~icons/material-symbols/arrow-drop-up'; - -/** - * Props for the Dropdown component. - */ -export type Props = { - style?: React.CSSProperties; - // Dummy value solely for storybook - dummySchedules?: UserSchedule[]; - dummyActiveIndex?: number; - dummyActiveSchedule?: UserSchedule; - scheduleComponents?: any[]; -}; - -/** - * This is a reusable dropdown component that can be used to toggle the visiblity of information - */ -export default function Dropdown(props: Props) { - // Expand/Hide state for dropdown - let [expanded, toggle] = React.useState(false); - let [activeScheduleIndex, select] = React.useState(props.dummyActiveIndex); - let [activeSchedule, selectFrom] = React.useState(props.dummyActiveSchedule); - let [scheduleComponents, setScheduleComponents] = React.useState(props.scheduleComponents); - - const schedules = props.dummySchedules; - if (schedules == null) { - // TODO - // if no dummy values passed in - // useSchedules hook here - } - - const toggleSwitch = () => { - toggle(!expanded); - }; - - // TODO - // WIP function to swap schedules. Prefer to use the hook when in production - const switchSchedule = (index: number) => { - const scheduleToSwitchTo = schedules[index]; - - select(index); - selectFrom(scheduleToSwitchTo); - if (scheduleToSwitchTo != null && scheduleToSwitchTo.name != null) { - userScheduleHandler.switchSchedule({ - data: { - scheduleName: scheduleToSwitchTo.name, - }, - sender: null, - sendResponse: null, - }); - } - }; - - return ( -
- - -
-
- - MAIN SCHEDULE: - -
- - {activeSchedule ? activeSchedule.hours : 0} HOURS - - - {activeSchedule ? activeSchedule.courses.length : 0} Courses - -
-
- - {expanded ? : } - -
-
- - - - - - -
-
- ); -} diff --git a/src/views/components/common/ScheduleDropdown/ScheduleDropdown.tsx b/src/views/components/common/ScheduleDropdown/ScheduleDropdown.tsx new file mode 100644 index 00000000..14a36a43 --- /dev/null +++ b/src/views/components/common/ScheduleDropdown/ScheduleDropdown.tsx @@ -0,0 +1,61 @@ +import { Disclosure, Transition } from '@headlessui/react'; +import Text from '@views/components/common/Text/Text'; +import useSchedules from '@views/hooks/useSchedules'; +import React from 'react'; + +import DropdownArrowDown from '~icons/material-symbols/arrow-drop-down'; +import DropdownArrowUp from '~icons/material-symbols/arrow-drop-up'; + +/** + * Props for the Dropdown component. + */ +export type Props = { + defaultOpen?: boolean; + children: React.ReactNode; +}; + +/** + * This is a reusable dropdown component that can be used to toggle the visiblity of information + */ +export default function ScheduleDropdown(props: Props) { + const [activeSchedule] = useSchedules(); + + return ( +
+ + {({ open }) => ( + <> + +
+ + {(activeSchedule ? activeSchedule.name : 'Schedule').toUpperCase()}: + +

+ + {activeSchedule ? activeSchedule.hours : 0} HOURS + + + {activeSchedule ? activeSchedule.courses.length : 0} Courses + +

+
+ + {open ? : } + +
+ + + {props.children} + + + )} +
+
+ ); +} diff --git a/src/views/components/common/ScheduleListItem/ScheduleListItem.tsx b/src/views/components/common/ScheduleListItem/ScheduleListItem.tsx index 3a7d9c5d..ad5733a2 100644 --- a/src/views/components/common/ScheduleListItem/ScheduleListItem.tsx +++ b/src/views/components/common/ScheduleListItem/ScheduleListItem.tsx @@ -1,6 +1,7 @@ import Text from '@views/components/common/Text/Text'; +import useSchedules from '@views/hooks/useSchedules'; import clsx from 'clsx'; -import React from 'react'; +import React, { useMemo } from 'react'; import DragIndicatorIcon from '~icons/material-symbols/drag-indicator'; @@ -11,34 +12,35 @@ export type Props = { style?: React.CSSProperties; active?: boolean; name: string; - dragHandleProps?: any; - onClick?: (index) => void; + dragHandleProps?: Omit, 'className'>; + onClick?: React.DOMAttributes['onClick']; }; /** * This is a reusable dropdown component that can be used to toggle the visiblity of information */ -export default function ScheduleListItem({ style, active, name, dragHandleProps, onClick }: Props): JSX.Element { +export default function ScheduleListItem({ style, name, dragHandleProps, onClick }: Props): JSX.Element { + const [activeSchedule] = useSchedules(); + + const isActive = useMemo(() => activeSchedule?.name === name, [activeSchedule, name]); + return ( -
+
  • -
    +
    -
    -
    +
    +