1- import { useState , useEffect , useRef } from "preact/compat" ;
1+ import { useState , useEffect , useRef , useCallback } from "preact/compat" ;
22import PropTypes from "prop-types" ;
33import * as S from "./styles" ;
44import { Nav } from "../Nav" ;
@@ -52,7 +52,62 @@ const Header = ({
5252 setSrollPosition ( position ) ;
5353 } ;
5454
55+ const killEvent = useCallback ( ( e ) => {
56+ e . preventDefault ( ) ;
57+ e . stopPropagation ( ) ;
58+ e . stopImmediatePropagation ( ) ;
59+ } , [ ] ) ;
60+
61+ /**
62+ * This function solves a bug where all onclick functions are overwritten
63+ * with the first declared onclick function. This is known to occur
64+ * when inserting the header component in a Lightning Web Component on
65+ * Salesforce and may occur in other scenarios as well.
66+ *
67+ * When this function overwrites all other onclick functions, it then
68+ * checks the event's target's data-onclick-identifier attribute to determine
69+ * what was clicked on and intiates the correct process.
70+ *
71+ * When the bug does not occur, this function should never be called.
72+ */
73+ const onClickCallbackOverride = useCallback ( ( e ) => {
74+ let whatWasClicked = e . target ;
75+ let limit = 100 ; // prevent infinite loop
76+
77+ // Climb DOM tree until someone has an identifier
78+ while ( limit > 0 && whatWasClicked . dataset . onclickIdentifier === undefined ) {
79+ whatWasClicked = whatWasClicked . parentNode ;
80+ limit -- ;
81+ }
5582
83+ // Action depends on what was clicked
84+ const identifier = whatWasClicked . dataset . onclickIdentifier ;
85+ if ( identifier === "universal-search-bar" ) {
86+ setSearchOpen ( true ) ;
87+ } else if ( identifier === "mobile-dropdown" ) {
88+ toggle ( ) ;
89+ } else if ( identifier . includes ( "toggle-dropdown." ) ) {
90+ setSearchOpen ( false ) ;
91+ clearSearchBar ( whatWasClicked ) ;
92+ toggleNavDropdown ( whatWasClicked )
93+ } else if ( identifier === "leave-open" ) {
94+ killEvent ( ) ;
95+ }
96+ } , [ ] ) ;
97+ const toggleNavDropdown = ( clickedDOM ) => {
98+ navRef . current . forceToggle ( clickedDOM ) ;
99+ }
100+ const clearSearchBar = ( domElement ) => {
101+ let searchUp = domElement ;
102+ let limit = 100 ; // prevent infinite loop
103+ while ( limit > 0 && searchUp . dataset . onclickIdentifier !== 'top-of-header' ) {
104+ limit -- ;
105+ searchUp = searchUp . parentNode
106+ }
107+ if ( searchUp . querySelector ( '[data-onclick-identifier = "universal-search-bar"]' ) . querySelector ( "input" ) . value . length > 0 ) {
108+ searchUp . querySelector ( '[data-onclick-identifier = "universal-search-bar"]' ) . querySelector ( "input" ) . value = "" ;
109+ }
110+ }
56111
57112 // Attach scroll event lister which will update the scrollPosition state
58113 // when window scrolled
@@ -68,6 +123,7 @@ const Header = ({
68123 const universalRef = useRef ( null ) ;
69124 const logoRef = useRef ( null ) ;
70125 const titleRef = useRef ( null ) ;
126+ const navRef = useRef ( null ) ;
71127
72128 // Calculate the mobile nav menu max-height every time the mobile nav is opened
73129 // or the viewport changes size
@@ -94,7 +150,9 @@ const Header = ({
94150 ? "scrolled"
95151 : ""
96152 }
153+ data-onclick-identifier = "top-of-header"
97154 >
155+ < div onmousedown = { killEvent } onclick = { onClickCallbackOverride } data-onclick-identifier = "no-action" > </ div >
98156 < S . UniversalNav open = { mobileOpen } ref = { universalRef } { ...{ searchOpen } } >
99157 < S . UniversalNavLinks >
100158 < a href = "https://www.asu.edu/" > ASU home</ a >
@@ -115,6 +173,7 @@ const Header = ({
115173 } }
116174 mobileOpen = { mobileOpen }
117175 logo = { < Logo { ...logo } ref = { logoRef } /> }
176+ data-onclick-identifier = "mobile-dropdown"
118177 >
119178 { props . dangerouslyGenerateStub ? (
120179 < div id = "asu-generated-stub" />
@@ -137,6 +196,7 @@ const Header = ({
137196 breakpoint,
138197 expandOnHover
139198 } }
199+ ref = { navRef }
140200 />
141201 </ >
142202 ) }
0 commit comments