diff --git a/src/course-outline/section-card/SectionCard.jsx b/src/course-outline/section-card/SectionCard.jsx index 4eea3175d9..f8d11a4cd0 100644 --- a/src/course-outline/section-card/SectionCard.jsx +++ b/src/course-outline/section-card/SectionCard.jsx @@ -45,7 +45,33 @@ const SectionCard = ({ const [searchParams] = useSearchParams(); const locatorId = searchParams.get('show'); const isScrolledToElement = locatorId === section.id; - const [isExpanded, setIsExpanded] = useState(locatorId ? !!locatorId : isSectionsExpanded); + + // Expand the section if a search result should be shown/scrolled to + const containsSearchResult = () => { + if (locatorId) { + const subsections = section.childInfo?.children; + if (subsections) { + for (let i = 0; i < subsections.length; i++) { + const subsection = subsections[i]; + + // Check if the search result is one of the subsections + const matchedSubsection = subsection.id === locatorId; + if (matchedSubsection) { + return true; + } + + // Check if the search result is one of the units + const matchedUnit = !!subsection.childInfo?.children?.filter((child) => child.id === locatorId).length; + if (matchedUnit) { + return true; + } + } + } + } + + return false; + }; + const [isExpanded, setIsExpanded] = useState(containsSearchResult() || isSectionsExpanded); const [isFormOpen, openForm, closeForm] = useToggle(false); const namePrefix = 'section'; @@ -83,8 +109,8 @@ const SectionCard = ({ useEffect(() => { // If the locatorId is set/changed, we need to make sure that the section is expanded - // in order to scroll to search result, otherwise leave it as is. - setIsExpanded((prevState) => (locatorId ? !!locatorId : prevState)); + // if it contains the result, in order to scroll to it + setIsExpanded((prevState) => containsSearchResult() || prevState); }, [locatorId, setIsExpanded]); // re-create actions object for customizations @@ -261,6 +287,20 @@ SectionCard.propTypes = { duplicable: PropTypes.bool.isRequired, }).isRequired, isHeaderVisible: PropTypes.bool, + childInfo: PropTypes.shape({ + children: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.string.isRequired, + childInfo: PropTypes.shape({ + children: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.string.isRequired, + }), + ).isRequired, + }).isRequired, + }), + ).isRequired, + }).isRequired, }).isRequired, isSelfPaced: PropTypes.bool.isRequired, isCustomRelativeDatesActive: PropTypes.bool.isRequired, diff --git a/src/course-outline/subsection-card/SubsectionCard.jsx b/src/course-outline/subsection-card/SubsectionCard.jsx index b798fa5b99..9f134fdbe1 100644 --- a/src/course-outline/subsection-card/SubsectionCard.jsx +++ b/src/course-outline/subsection-card/SubsectionCard.jsx @@ -73,7 +73,14 @@ const SubsectionCard = ({ actions.allowMoveDown = !isEmpty(moveDownDetails); // Expand the subsection if a search result should be shown/scrolled to - const [isExpanded, setIsExpanded] = useState(locatorId ? !!locatorId : !isHeaderVisible); + const containsSearchResult = () => { + if (locatorId) { + return !!subsection.childInfo?.children?.filter((child) => child.id === locatorId).length; + } + + return false; + }; + const [isExpanded, setIsExpanded] = useState(containsSearchResult() || !isHeaderVisible); const subsectionStatus = getItemStatus({ published, visibilityState, @@ -141,8 +148,8 @@ const SubsectionCard = ({ useEffect(() => { // If the locatorId is set/changed, we need to make sure that the subsection is expanded - // in order to scroll to search result - setIsExpanded((prevState) => (locatorId ? !!locatorId : prevState)); + // if it contains the result, in order to scroll to it + setIsExpanded((prevState) => (containsSearchResult() || prevState)); }, [locatorId, setIsExpanded]); useEffect(() => { @@ -273,6 +280,13 @@ SubsectionCard.propTypes = { duplicable: PropTypes.bool.isRequired, }).isRequired, isHeaderVisible: PropTypes.bool, + childInfo: PropTypes.shape({ + children: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.string.isRequired, + }), + ).isRequired, + }).isRequired, }).isRequired, children: PropTypes.node, isSelfPaced: PropTypes.bool.isRequired, diff --git a/src/course-outline/subsection-card/SubsectionCard.test.jsx b/src/course-outline/subsection-card/SubsectionCard.test.jsx index 167be766a9..2aebb09dfb 100644 --- a/src/course-outline/subsection-card/SubsectionCard.test.jsx +++ b/src/course-outline/subsection-card/SubsectionCard.test.jsx @@ -32,13 +32,8 @@ const clipboardBroadcastChannelMock = { global.BroadcastChannel = jest.fn(() => clipboardBroadcastChannelMock); -const section = { - id: '123', - displayName: 'Section Name', - published: true, - visibilityState: 'live', - hasChanges: false, - highlights: ['highlight 1', 'highlight 2'], +const unit = { + id: 'unit-1', }; const subsection = { @@ -56,6 +51,25 @@ const subsection = { }, isHeaderVisible: true, releasedToStudents: true, + childInfo: { + children: [{ + id: unit.id, + }], + }, +}; + +const section = { + id: '123', + displayName: 'Section Name', + published: true, + visibilityState: 'live', + hasChanges: false, + highlights: ['highlight 1', 'highlight 2'], + childInfo: { + children: [{ + id: subsection.id, + }], + }, }; const onEditSubectionSubmit = jest.fn(); @@ -227,12 +241,22 @@ describe('', () => { expect(await findByText(cardHeaderMessages.statusBadgeDraft.defaultMessage)).toBeInTheDocument(); }); - it('check extended section when URL has a "show" param', async () => { - const { findByTestId } = renderComponent(null, `?show=${section.id}`); + it('check extended subsection when URL "show" param', async () => { + const { findByTestId } = renderComponent(null, `?show=${unit.id}`); const cardUnits = await findByTestId('subsection-card__units'); const newUnitButton = await findByTestId('new-unit-button'); expect(cardUnits).toBeInTheDocument(); expect(newUnitButton).toBeInTheDocument(); }); + + it('check not extended subsection when URL "show" param not in subsection', async () => { + const randomId = 'random-id'; + const { queryByTestId } = renderComponent(null, `?show=${randomId}`); + + const cardUnits = await queryByTestId('subsection-card__units'); + const newUnitButton = await queryByTestId('new-unit-button'); + expect(cardUnits).toBeNull(); + expect(newUnitButton).toBeNull(); + }); });