From 63c1cc57e0ef04082abc710bf7b071bcf6cc1786 Mon Sep 17 00:00:00 2001 From: Zhao Wei Liew Date: Fri, 3 Jan 2025 10:08:13 +0800 Subject: [PATCH] fix: website: today: Hide "hidden" courses in Today page (#3927) --- website/src/selectors/timetables.ts | 8 + .../TodayContainer/TodayContainer.test.tsx | 163 ++++++------------ .../today/TodayContainer/TodayContainer.tsx | 22 ++- 3 files changed, 72 insertions(+), 121 deletions(-) diff --git a/website/src/selectors/timetables.ts b/website/src/selectors/timetables.ts index 75a5d719a9..7cc0736626 100644 --- a/website/src/selectors/timetables.ts +++ b/website/src/selectors/timetables.ts @@ -38,3 +38,11 @@ export const getSemesterTimetableColors = createSelector( (colors) => (semester: Semester | null) => semester === null ? EMPTY_OBJECT : colors[semester] ?? EMPTY_OBJECT, ); + +/** + * Extract hidden courses for a specific semester. + */ +export const getSemesterTimetableHidden = createSelector( + ({ timetables }: State) => timetables.hidden, + (hidden) => (semester: Semester | null) => semester === null ? [] : hidden[semester] ?? [], +); diff --git a/website/src/views/today/TodayContainer/TodayContainer.test.tsx b/website/src/views/today/TodayContainer/TodayContainer.test.tsx index 9b285516a2..de45e24af4 100644 --- a/website/src/views/today/TodayContainer/TodayContainer.test.tsx +++ b/website/src/views/today/TodayContainer/TodayContainer.test.tsx @@ -6,6 +6,7 @@ import { waitFor } from 'test-utils/async'; import { EVEN_WEEK, EVERY_WEEK } from 'test-utils/timetable'; import { captureException } from 'utils/error'; +import { State } from 'types/state'; import { Props, DaySection, TodayContainerComponent, mapStateToProps } from './TodayContainer'; import forecasts from './__mocks__/forecasts.json'; import DayEvents from '../DayEvents'; @@ -137,7 +138,7 @@ describe(TodayContainerComponent, () => { const componentProps: Props = { colors: COLORS, matchBreakpoint: false, - timetableWithLessons: LESSONS, + visibleTimetableWithLessons: LESSONS, ...props, }; @@ -165,7 +166,7 @@ describe(TodayContainerComponent, () => { const wrapper = make({ currentTime: now, - timetableWithLessons: {}, + visibleTimetableWithLessons: {}, }); expect(getLessons(wrapper)).toHaveLength(0); @@ -206,13 +207,13 @@ describe(TodayContainerComponent, () => { const now = new Date('2016-08-22T18:00:00+08:00'); make({ currentTime: now }); - expect(mockWeather.twoHour).toBeCalled(); - expect(mockWeather.tomorrow).toBeCalled(); - expect(mockWeather.fourDay).toBeCalled(); + expect(mockWeather.twoHour).toHaveBeenCalled(); + expect(mockWeather.tomorrow).toHaveBeenCalled(); + expect(mockWeather.fourDay).toHaveBeenCalled(); await waitFor(() => mockCaptureException.mock.calls.length > 0); - expect(mockCaptureException).toBeCalled(); + expect(mockCaptureException).toHaveBeenCalled(); }); test('should show icons for the next four days', async () => { @@ -251,152 +252,86 @@ describe(TodayContainerComponent, () => { }); describe(mapStateToProps, () => { + const state = { + moduleBank: { modules: {} }, + timetables: { + lessons: { + [1]: { + CS3216: {}, + }, + [2]: { + CS1010S: {}, + GEX10105: {}, + }, + [3]: { + CS1231: {}, + }, + [4]: { + CS2040: {}, + }, + }, + colors: { + [1]: COLORS, + [2]: COLORS, + [3]: COLORS, + [4]: COLORS, + }, + hidden: { [2]: ['GEX1015'] }, + }, + } as any as State; + test('should use correct semester (test 1, special case)', () => { // On week -1 of sem 2 the semester should be 2, not 1 - const ownProps: any = { + const ownProps = { // Week -1 of sem 2 of AY2018/2019 currentTime: new Date('2019-01-09T00:00:00.000Z'), }; - const state: any = { - moduleBank: { modules: {} }, - timetables: { - lessons: { - [1]: { - CS3216: {}, - }, - [2]: { - CS1010S: {}, - }, - }, - colors: { - [1]: COLORS, - [2]: COLORS, - }, - }, - }; - // Should return sem 2 timetable, not sem 1 - expect(mapStateToProps(state, ownProps).timetableWithLessons).toHaveProperty('CS1010S'); + expect(mapStateToProps(state, ownProps).visibleTimetableWithLessons).toHaveProperty('CS1010S'); + // Should hide "hidden" courses + expect(mapStateToProps(state, ownProps).visibleTimetableWithLessons).not.toHaveProperty( + 'GEX1015', + ); }); -}); -describe(mapStateToProps, () => { test('should use correct semester (test 2)', () => { // On week -1 of orientation week, it should be special term II - const ownProps: any = { + const ownProps = { currentTime: new Date('2024-08-04T00:00:00.000Z'), }; - const state: any = { - moduleBank: { modules: {} }, - timetables: { - lessons: { - [4]: { - CS3216: {}, - }, - [1]: { - CS1010S: {}, - }, - }, - colors: { - [4]: COLORS, - [1]: COLORS, - }, - }, - }; - // Should return special term II timetable, not sem 1 - expect(mapStateToProps(state, ownProps).timetableWithLessons).toHaveProperty('CS3216'); + expect(mapStateToProps(state, ownProps).visibleTimetableWithLessons).toHaveProperty('CS2040'); }); -}); -describe(mapStateToProps, () => { test('should use correct semester (test 3)', () => { // On orientation week, it should be sem1 - const ownProps: any = { + const ownProps = { currentTime: new Date('2024-08-05T00:00:00.000Z'), }; - const state: any = { - moduleBank: { modules: {} }, - timetables: { - lessons: { - [4]: { - CS3216: {}, - }, - [1]: { - CS1010S: {}, - }, - }, - colors: { - [4]: COLORS, - [1]: COLORS, - }, - }, - }; - // Should return sem1 timetable - expect(mapStateToProps(state, ownProps).timetableWithLessons).toHaveProperty('CS1010S'); + expect(mapStateToProps(state, ownProps).visibleTimetableWithLessons).toHaveProperty('CS3216'); }); -}); -describe(mapStateToProps, () => { test('should use correct semester (test 4)', () => { // On week -1 of special term I, it should be sem2 - const ownProps: any = { + const ownProps = { currentTime: new Date('2025-05-11T00:00:00.000Z'), }; - const state: any = { - moduleBank: { modules: {} }, - timetables: { - lessons: { - [2]: { - CS3216: {}, - }, - [3]: { - CS1010S: {}, - }, - }, - colors: { - [2]: COLORS, - [3]: COLORS, - }, - }, - }; - // Should return sem2 timetable - expect(mapStateToProps(state, ownProps).timetableWithLessons).toHaveProperty('CS3216'); + expect(mapStateToProps(state, ownProps).visibleTimetableWithLessons).toHaveProperty('CS1010S'); }); -}); -describe(mapStateToProps, () => { test('should use correct semester (test 5)', () => { // On week -1 of special term II, it should be special term I - const ownProps: any = { + const ownProps = { currentTime: new Date('2025-06-22T00:00:00.000Z'), }; - const state: any = { - moduleBank: { modules: {} }, - timetables: { - lessons: { - [3]: { - CS3216: {}, - }, - [4]: { - CS1010S: {}, - }, - }, - colors: { - [3]: COLORS, - [4]: COLORS, - }, - }, - }; - // Should return special term I timetable - expect(mapStateToProps(state, ownProps).timetableWithLessons).toHaveProperty('CS3216'); + expect(mapStateToProps(state, ownProps).visibleTimetableWithLessons).toHaveProperty('CS1231'); }); }); diff --git a/website/src/views/today/TodayContainer/TodayContainer.tsx b/website/src/views/today/TodayContainer/TodayContainer.tsx index bda50f0534..31d96bd951 100644 --- a/website/src/views/today/TodayContainer/TodayContainer.tsx +++ b/website/src/views/today/TodayContainer/TodayContainer.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { connect } from 'react-redux'; -import { get, minBy, range } from 'lodash'; +import { get, minBy, range, omit } from 'lodash'; import NUSModerator, { AcadWeekInfo } from 'nusmoderator'; import classnames from 'classnames'; import { @@ -28,7 +28,11 @@ import { } from 'utils/timetables'; import { captureException } from 'utils/error'; import Title from 'views/components/Title'; -import { getSemesterTimetableColors, getSemesterTimetableLessons } from 'selectors/timetables'; +import { + getSemesterTimetableColors, + getSemesterTimetableHidden, + getSemesterTimetableLessons, +} from 'selectors/timetables'; import ExternalLink from 'views/components/ExternalLink'; import * as weatherAPI from 'apis/weather'; import config from 'config'; @@ -61,7 +65,7 @@ export type OwnProps = TimerData; export type Props = OwnProps & Readonly<{ - timetableWithLessons: SemTimetableConfigWithLessons; + visibleTimetableWithLessons: SemTimetableConfigWithLessons; colors: ColorMapping; matchBreakpoint: boolean; }>; @@ -174,9 +178,9 @@ export class TodayContainerComponent extends React.PureComponent { }; groupLessons() { - const { colors, currentTime } = this.props; + const { colors, currentTime, visibleTimetableWithLessons } = this.props; - const timetableLessons: Lesson[] = timetableLessonsArray(this.props.timetableWithLessons); + const timetableLessons: Lesson[] = timetableLessonsArray(visibleTimetableWithLessons); // Inject color into module const coloredTimetableLessons = timetableLessons.map( @@ -363,11 +367,15 @@ export const mapStateToProps = (state: StoreState, ownProps: OwnProps) => { const semester = semesterNameMap[weekInfo.sem]; const timetable = getSemesterTimetableLessons(state)(semester); const colors = getSemesterTimetableColors(state)(semester); - const timetableWithLessons = hydrateSemTimetableWithLessons(timetable, modules, semester); + const hidden = getSemesterTimetableHidden(state)(semester); + const visibleTimetableWithLessons = omit( + hydrateSemTimetableWithLessons(timetable, modules, semester), + hidden, + ); return { colors, - timetableWithLessons, + visibleTimetableWithLessons, }; };