77import * as React from 'react' ;
88import classNames from 'classnames' ;
99import flow from 'lodash/flow' ;
10+ import getProp from 'lodash/get' ;
1011import uniqueid from 'lodash/uniqueId' ;
1112import { withRouter } from 'react-router-dom' ;
1213import type { Location , RouterHistory } from 'react-router-dom' ;
@@ -48,76 +49,51 @@ type Props = {
4849
4950type State = {
5051 isDirty : boolean ,
51- isOpen : boolean , // Local isOpen state consists of stored forced state (if any) and responsive adjustments
5252} ;
5353
5454export const SIDEBAR_FORCE_KEY : 'bcs.force' = 'bcs.force' ;
5555export const SIDEBAR_FORCE_VALUE_CLOSED : 'closed' = 'closed' ;
5656export const SIDEBAR_FORCE_VALUE_OPEN : 'open' = 'open' ;
5757
5858class Sidebar extends React . Component < Props , State > {
59+ static defaultProps = {
60+ isLarge : true ,
61+ isLoading : false ,
62+ } ;
63+
5964 id : string = uniqueid ( 'bcs_' ) ;
6065
6166 props : Props ;
6267
63- state : State ;
68+ state : State = {
69+ isDirty : false ,
70+ } ;
6471
6572 store : LocalStore = new LocalStore ( ) ;
6673
67- static defaultProps = {
68- isLarge : true ,
69- isLoading : false ,
70- } ;
71-
7274 constructor ( props : Props ) {
7375 super ( props ) ;
7476
75- const { isLarge } = this . props ;
76-
77- this . state = {
78- isDirty : false ,
79- isOpen : this . isForcedSet ( ) ? this . isForcedOpen ( ) : ! ! isLarge ,
80- } ;
77+ this . setForcedByLocation ( ) ;
8178 }
8279
8380 componentDidUpdate ( prevProps : Props ) : void {
84- const { fileId , history , isLarge , location } : Props = this . props ;
85- const { fileId : prevFileId , isLarge : prevIsLarge , location : prevLocation } : Props = prevProps ;
86- const { isDirty, isOpen } : State = this . state ;
87- const { state : locationState = { } } = location ;
88- const isForcedSet = this . isForcedSet ( ) ;
81+ const { fileId , history , location } : Props = this . props ;
82+ const { fileId : prevFileId , location : prevLocation } : Props = prevProps ;
83+ const { isDirty } : State = this . state ;
8984
90- // User navigated to a different file without ever navigating to a tab
85+ // User navigated to a different file without ever navigating the sidebar
9186 if ( ! isDirty && fileId !== prevFileId && location . pathname !== '/' ) {
9287 history . replace ( { pathname : '/' , state : { silent : true } } ) ;
9388 }
9489
95- // User navigated to a different route or tab for the first time this session
96- if ( ! isDirty && location !== prevLocation && ! locationState . silent ) {
90+ // User navigated or toggled the sidebar intentionally, internally or externally
91+ if ( location !== prevLocation && ! this . getLocationState ( 'silent' ) ) {
92+ this . setForcedByLocation ( ) ;
9793 this . setState ( { isDirty : true } ) ;
9894 }
99-
100- // User resized their viewport without ever toggling the sidebar open/closed
101- if ( ! isForcedSet && isLarge !== prevIsLarge && isLarge !== isOpen ) {
102- this . setState ( { isOpen : isLarge } ) ;
103- }
10495 }
10596
106- /**
107- * Handle sidebar navigation events
108- *
109- * @param {SyntheticEvent } event - The event
110- * @param {NavigateOptions } options - The navigation options
111- * @return {void }
112- */
113- handleNavigation = ( event : SyntheticEvent < > , { isToggle } : NavigateOptions ) : void => {
114- const { isOpen } : State = this . state ;
115-
116- // Persist user preference for all future sessions in this browser
117- this . isForced ( isToggle ? ! isOpen : true ) ;
118- this . setState ( { isOpen : this . isForcedOpen ( ) } ) ;
119- } ;
120-
12197 /**
12298 * Handle version history click
12399 *
@@ -136,6 +112,22 @@ class Sidebar extends React.Component<Props, State> {
136112 history . push ( `${ history . location . pathname } /versions${ fileVersionSlug } ` ) ;
137113 } ;
138114
115+ /**
116+ * Getter for location state properties.
117+ *
118+ * NOTE: Each location on the history stack has its own optional state object that is wholly separate from
119+ * this component's internal state. Values on the location state object can persist even between refreshes
120+ * when using certain history contexts, such as BrowserHistory.
121+ *
122+ * @param key - Optionally get a specific key value from state
123+ * @returns {any } - The location state or state key value
124+ */
125+ getLocationState ( key ? : string ) : any {
126+ const { location } = this . props ;
127+ const { state : locationState = { } } = location ;
128+ return getProp ( locationState , key ) ;
129+ }
130+
139131 /**
140132 * Getter/setter for sidebar forced state
141133 *
@@ -155,7 +147,7 @@ class Sidebar extends React.Component<Props, State> {
155147 * @returns {boolean } - True if the sidebar has been forced open
156148 */
157149 isForcedOpen ( ) : boolean {
158- return this . isForced ( ) !== SIDEBAR_FORCE_VALUE_CLOSED ;
150+ return this . isForced ( ) === SIDEBAR_FORCE_VALUE_OPEN ;
159151 }
160152
161153 /**
@@ -166,6 +158,17 @@ class Sidebar extends React.Component<Props, State> {
166158 return this . isForced ( ) !== null ;
167159 }
168160
161+ /**
162+ * Helper to set the local store open state based on the location open state, if defined
163+ */
164+ setForcedByLocation ( ) : void {
165+ const isLocationOpen : ?boolean = this . getLocationState ( 'open' ) ;
166+
167+ if ( isLocationOpen !== undefined && isLocationOpen !== null ) {
168+ this . isForced ( isLocationOpen ) ;
169+ }
170+ }
171+
169172 render ( ) {
170173 const {
171174 activitySidebarProps,
@@ -179,13 +182,14 @@ class Sidebar extends React.Component<Props, State> {
179182 getPreview,
180183 getViewer,
181184 hasAdditionalTabs,
185+ isLarge,
182186 isLoading,
183187 metadataEditors,
184188 metadataSidebarProps,
185189 onVersionChange,
186190 } : Props = this . props ;
187191
188- const { isOpen } = this . state ;
192+ const isOpen = this . isForcedSet ( ) ? this . isForcedOpen ( ) : ! ! isLarge ;
189193 const hasActivity = SidebarUtils . canHaveActivitySidebar ( this . props ) ;
190194 const hasDetails = SidebarUtils . canHaveDetailsSidebar ( this . props ) ;
191195 const hasMetadata = SidebarUtils . shouldRenderMetadataSidebar ( this . props , metadataEditors ) ;
@@ -213,7 +217,6 @@ class Sidebar extends React.Component<Props, State> {
213217 hasMetadata = { hasMetadata }
214218 hasSkills = { hasSkills }
215219 isOpen = { isOpen }
216- onNavigate = { this . handleNavigation }
217220 />
218221 < SidebarPanels
219222 activitySidebarProps = { activitySidebarProps }
0 commit comments