diff --git a/src/course-outline/section-card/SectionCard.jsx b/src/course-outline/section-card/SectionCard.jsx index 32d2cfb2c7..5d3b5fbb5a 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 a1fb451001..015da345f7 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,