diff --git a/package.json b/package.json index e091457a9c..954f6ae29a 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,6 @@ "react-dom": "^18.3.1", "react-intersection-observer": "^10.0.3", "react-redux": "^9.2.0", - "react-splitter-layout": "^4.0.0", "redux": "^5.0.1", "redux-logger": "^3.0.6", "redux-thunk": "^3.1.0", diff --git a/res/css/global.css b/res/css/global.css index 5353f161f0..6f8247df9c 100644 --- a/res/css/global.css +++ b/res/css/global.css @@ -184,15 +184,3 @@ a:active { .colored-border.ellipsis { opacity: 0; } - -.splitter-layout.splitter-layout > .layout-splitter { - background-color: var(--wide-splitter-color); -} - -.splitter-layout.splitter-layout > .layout-splitter:hover { - background-color: var(--wide-splitter-hover-color); -} - -.splitter-layout.splitter-layout.layout-changing > .layout-splitter { - background-color: var(--wide-splitter-pressed-color); -} diff --git a/src/components/app/BottomBox.css b/src/components/app/BottomBox.css index dc80d0345b..29e7afb40c 100644 --- a/src/components/app/BottomBox.css +++ b/src/components/app/BottomBox.css @@ -2,13 +2,21 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +.bottom-box { + display: flex; + min-height: 0; + flex-flow: row nowrap; +} + .bottom-box-pane { --internal-sourceview-background-color: var(--grey-20); --internal-close-icon: url(../../../res/img/svg/close-dark.svg); --internal-assembly-icon: url(../../../res/img/svg/asm-icon.svg); display: flex; - height: 100%; /* direct child of SplitterLayout */ + overflow: hidden; + min-width: 0; + flex: 1; flex-flow: column nowrap; color: var(--base-foreground-color); } @@ -23,7 +31,7 @@ background: var(--internal-sourceview-background-color); } -.bottom-box .layout-splitter { +.bottom-box .resizableWithSplitterSplitter { position: relative; /* containing block for absolute ::before */ width: 1px; border: none; @@ -31,7 +39,7 @@ } /* Provide 3px extra grabbable surface on each side of the splitter */ -.bottom-box .layout-splitter::before { +.bottom-box .resizableWithSplitterSplitter::before { position: absolute; z-index: var(--z-bottom-box); display: block; @@ -44,6 +52,7 @@ overflow: hidden; height: 24px; flex-flow: row; + flex-shrink: 0; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--panel-border-color); diff --git a/src/components/app/BottomBox.tsx b/src/components/app/BottomBox.tsx index 96558f3b9c..0534b6c43f 100644 --- a/src/components/app/BottomBox.tsx +++ b/src/components/app/BottomBox.tsx @@ -3,11 +3,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import React from 'react'; -import SplitterLayout from 'react-splitter-layout'; import classNames from 'classnames'; import { SourceView } from '../shared/SourceView'; import { AssemblyView } from '../shared/AssemblyView'; +import { ResizableWithSplitter } from '../shared/ResizableWithSplitter'; import { AssemblyViewToggleButton } from './AssemblyViewToggleButton'; import { AssemblyViewNativeSymbolNavigator } from './AssemblyViewNativeSymbolNavigator'; import { IonGraphView } from '../shared/IonGraphView'; @@ -217,43 +217,47 @@ class BottomBoxImpl extends React.PureComponent { return (
- -
-
-

{path ?? '(no source file)'}

- {assemblyViewIsOpen ? null : trailingHeaderButtons} -
-
- {displayIonGraph ? ( - - ) : null} - {displaySourceView ? ( - - ) : null} - {sourceViewCode !== undefined && - sourceViewCode.type === 'LOADING' ? ( - - ) : null} - {sourceViewCode !== undefined && - sourceViewCode.type === 'ERROR' ? ( - - ) : null} -
+
+
+

{path ?? '(no source file)'}

+ {assemblyViewIsOpen ? null : trailingHeaderButtons} +
+
+ {displayIonGraph ? ( + + ) : null} + {displaySourceView ? ( + + ) : null} + {sourceViewCode !== undefined && + sourceViewCode.type === 'LOADING' ? ( + + ) : null} + {sourceViewCode !== undefined && sourceViewCode.type === 'ERROR' ? ( + + ) : null}
+
- {assemblyViewIsOpen ? ( + {assemblyViewIsOpen ? ( +

@@ -289,8 +293,8 @@ class BottomBoxImpl extends React.PureComponent { ) : null}

- ) : null} - +
+ ) : null}
); } diff --git a/src/components/app/Details.css b/src/components/app/Details.css index ed1cafd550..43a051246c 100644 --- a/src/components/app/Details.css +++ b/src/components/app/Details.css @@ -3,8 +3,11 @@ --internal-sidebar-expand-icon: url(../../../res/img/svg/pane-expand.svg); display: flex; + + /* If .Details is squished to a very small size between the sidebar and/or bottom bar, + don't spill out our contents over those other elements. */ overflow: clip; - flex: auto; + flex: 1; flex-direction: column; } diff --git a/src/components/app/DetailsContainer.css b/src/components/app/DetailsContainer.css index 21bcae24d1..78bd724119 100644 --- a/src/components/app/DetailsContainer.css +++ b/src/components/app/DetailsContainer.css @@ -1,26 +1,29 @@ -.DetailsContainer .layout-pane > * { - width: 100%; - height: 100%; +.DetailsContainer { + position: relative; + z-index: 0; + display: flex; box-sizing: border-box; + flex: 1; + flex-flow: row nowrap; + contain: size; } -.DetailsContainer .layout-pane:not(.layout-pane-primary) { - max-width: 600px; +.DetailsContainer .resizableWithSplitterInner > * { + min-width: 0; + flex: 1; } -/* overriding defaults from splitter-layout */ -.DetailsContainer { - /* SplitterLayout injects position: absolute, this conflicts with our using - * Flex Layout. */ - position: unset; +.DetailsContainerResizableSidebarWrapper { + max-width: 600px; } -.DetailsContainer.splitter-layout > .layout-splitter { +/* overriding defaults from ResizableWithSplitter.css */ +.DetailsContainer .resizableWithSplitterSplitter { border-top: 1px solid var(--panel-border-color); border-left: 1px solid var(--panel-border-color); - background: var(--panel-background-color); + background: var(--panel-background-color); /* Same background as sidebars */ } -.DetailsContainer.splitter-layout > .layout-splitter:hover { - background: var(--panel-background-color); +.DetailsContainer .resizableWithSplitterSplitter:hover { + background: var(--panel-background-color); /* same as the border above */ } diff --git a/src/components/app/DetailsContainer.tsx b/src/components/app/DetailsContainer.tsx index b1fe74f00d..879af498da 100644 --- a/src/components/app/DetailsContainer.tsx +++ b/src/components/app/DetailsContainer.tsx @@ -1,10 +1,9 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -// React imported for JSX -import SplitterLayout from 'react-splitter-layout'; import { Details } from './Details'; +import { ResizableWithSplitter } from 'firefox-profiler/components/shared/ResizableWithSplitter'; import { selectSidebar } from 'firefox-profiler/components/sidebar'; import { getSelectedTab } from 'firefox-profiler/selectors/url-state'; @@ -27,14 +26,20 @@ function DetailsContainerImpl({ selectedTab, isSidebarOpen }: Props) { const Sidebar = selectSidebar(selectedTab); return ( - +
- {Sidebar && isSidebarOpen ? : null} - + {Sidebar && isSidebarOpen ? ( + + + + ) : null} +
); } diff --git a/src/components/app/ProfileViewer.css b/src/components/app/ProfileViewer.css index 1fdf52b78f..447647b6a1 100644 --- a/src/components/app/ProfileViewer.css +++ b/src/components/app/ProfileViewer.css @@ -47,6 +47,10 @@ } .profileViewer { + /* Create a stacking context, so that the KeyboardShortcut can overlay the profile + viewer. */ + position: relative; + z-index: 0; display: flex; min-width: 0; /* This allows Flexible Layout to shrink this further than its min-content */ flex: 1; @@ -109,21 +113,6 @@ } } -.profileViewerSplitter { - /* Position relative for layout. Don't set z-index here to avoid creating a - stacking context that would trap the context menu below the screenshot hover. - This allows both the screenshot hover and context menu to be compared - at the .profileViewer level, ensuring the context menu appears on top. */ - position: relative; -} - -.profileViewerSplitter > .layout-pane:not(.layout-pane-primary) { - display: flex; - overflow: hidden; - max-height: var(--profile-viewer-splitter-max-height); - flex-direction: column; -} - .profileViewerTopBar { display: flex; height: 24px; diff --git a/src/components/app/ProfileViewer.tsx b/src/components/app/ProfileViewer.tsx index bc140e3af4..71d835d377 100644 --- a/src/components/app/ProfileViewer.tsx +++ b/src/components/app/ProfileViewer.tsx @@ -5,6 +5,7 @@ import { PureComponent } from 'react'; import explicitConnect from 'firefox-profiler/utils/connect'; +import { ResizableWithSplitter } from 'firefox-profiler/components/shared/ResizableWithSplitter'; import { DetailsContainer } from './DetailsContainer'; import { SourceCodeFetcher } from './SourceCodeFetcher'; import { AssemblyCodeFetcher } from './AssemblyCodeFetcher'; @@ -20,8 +21,6 @@ import { KeyboardShortcut } from './KeyboardShortcut'; import { returnToZipFileList } from 'firefox-profiler/actions/zipped-profiles'; import { Timeline } from 'firefox-profiler/components/timeline'; import { getHasZipFile } from 'firefox-profiler/selectors/zipped-profiles'; -import SplitterLayout from 'react-splitter-layout'; -import { getTimelineHeight } from 'firefox-profiler/selectors/app'; import { getIsBottomBoxOpen } from 'firefox-profiler/selectors/url-state'; import { getUploadProgress, @@ -35,14 +34,13 @@ import { BackgroundImageStyleDef } from 'firefox-profiler/components/shared/Styl import classNames from 'classnames'; import { DebugWarning } from 'firefox-profiler/components/app/DebugWarning'; -import type { CssPixels, IconsWithClassNames } from 'firefox-profiler/types'; +import type { IconsWithClassNames } from 'firefox-profiler/types'; import type { ConnectedProps } from 'firefox-profiler/utils/connect'; import './ProfileViewer.css'; type StateProps = { readonly hasZipFile: boolean; - readonly timelineHeight: CssPixels | null; readonly uploadProgress: number; readonly isUploading: boolean; readonly isHidingStaleProfile: boolean; @@ -62,7 +60,6 @@ class ProfileViewerImpl extends PureComponent { const { hasZipFile, returnToZipFileList, - timelineHeight, isUploading, uploadProgress, isHidingStaleProfile, @@ -92,13 +89,6 @@ class ProfileViewerImpl extends PureComponent { hasSanitizedProfile && !isHidingStaleProfile, profileViewerFadeOut: isHidingStaleProfile, })} - style={ - timelineHeight === null - ? {} - : ({ - '--profile-viewer-splitter-max-height': `${timelineHeight}px`, - } as React.CSSProperties) - } >
{hasZipFile ? ( @@ -125,28 +115,25 @@ class ProfileViewerImpl extends PureComponent { /> ) : null}
- - + + {isBottomBoxOpen ? ( + - - {isBottomBoxOpen ? : null} - - + + + ) : null}
@@ -163,7 +150,6 @@ class ProfileViewerImpl extends PureComponent { export const ProfileViewer = explicitConnect<{}, StateProps, DispatchProps>({ mapStateToProps: (state) => ({ hasZipFile: getHasZipFile(state), - timelineHeight: getTimelineHeight(state), uploadProgress: getUploadProgress(state), isUploading: getUploadPhase(state) === 'uploading', isHidingStaleProfile: getIsHidingStaleProfile(state), diff --git a/src/components/shared/ResizableWithSplitter.css b/src/components/shared/ResizableWithSplitter.css new file mode 100644 index 0000000000..af0ed0918d --- /dev/null +++ b/src/components/shared/ResizableWithSplitter.css @@ -0,0 +1,34 @@ +.resizableWithSplitterOuter, +.resizableWithSplitterInner { + display: flex; + min-width: 0; + min-height: 0; + flex-direction: inherit; +} + +.resizableWithSplitterInner { + flex: 1; +} + +.resizableWithSplitterSplitter { + flex-shrink: 0; + background-color: var(--wide-splitter-color); +} + +.resizableWithSplitterSplitter:hover { + background-color: var(--wide-splitter-hover-color); +} + +.resizableWithSplitterSplitter.dragging { + background-color: var(--wide-splitter-pressed-color); +} + +.resizableWithSplitterSplitter.resizesWidth { + width: 5px; + cursor: col-resize; +} + +.resizableWithSplitterSplitter.resizesHeight { + height: 5px; + cursor: row-resize; +} diff --git a/src/components/shared/ResizableWithSplitter.tsx b/src/components/shared/ResizableWithSplitter.tsx new file mode 100644 index 0000000000..abbb1f89c9 --- /dev/null +++ b/src/components/shared/ResizableWithSplitter.tsx @@ -0,0 +1,159 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import * as React from 'react'; +import classNames from 'classnames'; + +import type { CssPixels } from '../../types/units'; + +import './ResizableWithSplitter.css'; + +type Props = { + // If 'start', the splitter is placed before the resized element, otherwise after. + splitterPosition: 'start' | 'end'; + // The CSS property which gets changed if the splitter is dragged. + controlledProperty: + | 'width' + | 'height' + | 'min-width' + | 'min-height' + | 'max-width' + | 'max-height'; + // The initial size, as a valid CSS length. For example "200px" or "30%". + // This prop is read only once, during the initial render. + initialSize: string; + // True if the value for controlledProperty should be set as a percentage, + // false if it should be set in CSS "px". + percent: boolean; + // An extra className for the outer div. + className?: string; + // The sized contents. These are placed inside an element with the className + // "resizableWithSplitterInner" and should have a non-zero CSS "flex" value + // set on them, so that they resize when "resizableWithSplitterInner" resizes. + children: React.ReactNode; +}; + +type DragState = { + outerDim: CssPixels; + parentDim: CssPixels; + isWidth: boolean; + splitterPosition: 'start' | 'end'; + percent: boolean; + startX: number; + startY: number; +}; + +export function ResizableWithSplitter({ + splitterPosition, + controlledProperty, + initialSize, + percent, + className, + children, +}: Props) { + const [size, setSize] = React.useState(initialSize); + const [dragging, setDragging] = React.useState(false); + const dragState = React.useRef(null); + const isWidth = controlledProperty.endsWith('width'); + const orientClassName = isWidth ? 'resizesWidth' : 'resizesHeight'; + + const onPointerDown = React.useCallback( + (e: React.PointerEvent) => { + if (e.button !== 0) { + return; + } + e.stopPropagation(); + e.preventDefault(); + e.currentTarget.setPointerCapture(e.pointerId); + const outer = e.currentTarget.parentElement; + const parent = outer?.parentElement; + if (!outer || !parent) { + return; + } + const outerRect = outer.getBoundingClientRect(); + const parentRect = parent.getBoundingClientRect(); + dragState.current = { + outerDim: isWidth ? outerRect.width : outerRect.height, + parentDim: isWidth ? parentRect.width : parentRect.height, + isWidth, + splitterPosition, + percent, + startX: e.pageX, + startY: e.pageY, + }; + setDragging(true); + }, + [isWidth, splitterPosition, percent] + ); + + const applyDrag = React.useCallback( + (ds: DragState, pageX: number, pageY: number) => { + const delta = ds.isWidth ? pageX - ds.startX : pageY - ds.startY; + const adjusted = Math.max( + 0, + ds.splitterPosition === 'end' + ? ds.outerDim + delta + : ds.outerDim - delta + ); + setSize( + ds.percent + ? `${((adjusted / ds.parentDim) * 100).toFixed(2)}%` + : `${adjusted}px` + ); + }, + [] + ); + + const onPointerMove = React.useCallback( + (e: React.PointerEvent) => { + const ds = dragState.current; + if (!ds) { + return; + } + applyDrag(ds, e.pageX, e.pageY); + }, + [applyDrag] + ); + + const onPointerUpOrCancel = React.useCallback( + (e: React.PointerEvent) => { + const ds = dragState.current; + if (!ds) { + return; + } + applyDrag(ds, e.pageX, e.pageY); + dragState.current = null; + setDragging(false); + }, + [applyDrag] + ); + + const splitter = ( +
+ ); + + return ( +
+ {splitterPosition === 'start' ? splitter : null} +
{children}
+ {splitterPosition === 'end' ? splitter : null} +
+ ); +} diff --git a/src/components/timeline/OverflowEdgeIndicator.css b/src/components/timeline/OverflowEdgeIndicator.css index e4aa0a11b5..7725c056ac 100644 --- a/src/components/timeline/OverflowEdgeIndicator.css +++ b/src/components/timeline/OverflowEdgeIndicator.css @@ -6,6 +6,7 @@ position: relative; display: flex; width: calc(100% + var(--vertical-scrollbar-reserved-width)); + min-height: 0; flex: 1; flex-direction: column; } diff --git a/src/components/timeline/OverflowEdgeIndicator.tsx b/src/components/timeline/OverflowEdgeIndicator.tsx index b73d562f21..293d7bb292 100644 --- a/src/components/timeline/OverflowEdgeIndicator.tsx +++ b/src/components/timeline/OverflowEdgeIndicator.tsx @@ -112,10 +112,7 @@ class OverflowEdgeIndicator extends React.PureComponent {
diff --git a/src/components/timeline/Selection.css b/src/components/timeline/Selection.css index 47c1bbb72c..9d749915a1 100644 --- a/src/components/timeline/Selection.css +++ b/src/components/timeline/Selection.css @@ -27,6 +27,7 @@ position: relative; display: flex; width: var(--content-width); + min-height: 0; flex: 1; flex-direction: column; margin-left: var(--thread-label-column-width); diff --git a/src/components/timeline/index.css b/src/components/timeline/index.css index 5abd986b91..caa50149b2 100644 --- a/src/components/timeline/index.css +++ b/src/components/timeline/index.css @@ -2,16 +2,15 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -.timelineOverflowEdgeIndicatorScrollbox { +.timelineOverflowEdgeIndicator > .overflowEdgeIndicatorScrollbox { --internal-background-stop1-color: #eee; --internal-background-stop2-color: var(--grey-30); - position: absolute; + position: relative; /* if the platform scrollbar ends up being larger than --vertical-scrollbar-reserved-width (which is unexpected), make sure there is no horizontal scrollbar */ overflow: hidden auto; width: 100%; - height: 100%; padding-left: var(--thread-label-column-width); margin-right: calc(var(--vertical-scrollbar-reserved-width) * -1); margin-left: calc(var(--thread-label-column-width) * -1); @@ -125,7 +124,6 @@ /* Moving the timeline 1 pixel below the timeline header. This way, the first * visible track's top border will be hidden as expected. */ margin-top: -1px; - margin-bottom: -1px; } @media (forced-colors: active) { @@ -150,7 +148,7 @@ } :root.dark-mode { - .timelineOverflowEdgeIndicatorScrollbox { + .timelineOverflowEdgeIndicator > .overflowEdgeIndicatorScrollbox { --internal-background-stop1-color: var(--grey-80); --internal-background-stop2-color: var(--grey-70); } diff --git a/src/index.tsx b/src/index.tsx index 30658b2910..45a56f62c5 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -12,7 +12,6 @@ import '../res/css/style.css'; import '../res/css/global.css'; import '../res/css/categories.css'; import '../res/css/network.css'; -import 'react-splitter-layout/lib/index.css'; import 'devtools-reps/reps.css'; // React imported for JSX in Root component diff --git a/src/selectors/app.tsx b/src/selectors/app.tsx index ec78760d84..0501ee6608 100644 --- a/src/selectors/app.tsx +++ b/src/selectors/app.tsx @@ -3,28 +3,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { createSelector } from 'reselect'; -import { - getDataSource, - getSelectedTab, - getHiddenGlobalTracks, - getHiddenLocalTracksByPid, -} from './url-state'; -import { getGlobalTracks, getLocalTracksByPid } from './profile'; +import { getDataSource, getSelectedTab } from './url-state'; import { getZipFileState } from './zipped-profiles'; -import { assertExhaustiveCheck, ensureExists } from '../utils/types'; -import { - FULL_TRACK_SCREENSHOT_HEIGHT, - TRACK_NETWORK_HEIGHT, - TRACK_MEMORY_HEIGHT, - TRACK_BANDWIDTH_HEIGHT, - TRACK_IPC_HEIGHT, - TRACK_PROCESS_BLANK_HEIGHT, - TIMELINE_RULER_HEIGHT, - TRACK_VISUAL_PROGRESS_HEIGHT, - TRACK_EVENT_DELAY_HEIGHT, - TRACK_PROCESS_CPU_HEIGHT, - TRACK_MARKER_HEIGHT, -} from '../app-logic/constants'; import type { AppState, @@ -35,7 +15,6 @@ import type { ThreadsKey, ExperimentalFlags, UploadedProfileInformation, - Pid, } from 'firefox-profiler/types'; import type { TabSlug } from 'firefox-profiler/app-logic/tabs-handling'; import type { @@ -88,131 +67,6 @@ export const getCurrentProfileUploadedInformation: Selector< UploadedProfileInformation | null > = (state) => getApp(state).currentProfileUploadedInformation; -/** - * This selector takes all of the tracks, and deduces the height in CssPixels - * of the timeline. This is here to calculate the max-height of the timeline - * for the splitter component. - * - * The height of the component is determined by the sizing of each track in the list. - * Most sizes are pretty static, and are set through values in the component. The only - * tricky value to determine is the thread track. These values get reported to the store - * and get added in here. - */ -export const getTimelineHeight: Selector = createSelector( - getGlobalTracks, - getLocalTracksByPid, - getHiddenGlobalTracks, - getHiddenLocalTracksByPid, - getTrackThreadHeights, - ( - globalTracks, - localTracksByPid, - hiddenGlobalTracks, - hiddenLocalTracksByPid, - trackThreadHeights - ) => { - let height = TIMELINE_RULER_HEIGHT; - const border = 1; - for (const [trackIndex, globalTrack] of globalTracks.entries()) { - if (!hiddenGlobalTracks.has(trackIndex)) { - switch (globalTrack.type) { - case 'screenshots': - height += FULL_TRACK_SCREENSHOT_HEIGHT + border; - break; - case 'visual-progress': - case 'perceptual-visual-progress': - case 'contentful-visual-progress': - height += TRACK_VISUAL_PROGRESS_HEIGHT; - break; - case 'process': { - // The thread tracks have enough complexity that it warrants measuring - // them rather than statically using a value like the other tracks. - const { mainThreadIndex } = globalTrack; - if (mainThreadIndex === null) { - height += TRACK_PROCESS_BLANK_HEIGHT + border; - } else { - const trackThreadHeight = trackThreadHeights[mainThreadIndex]; - if (trackThreadHeight === undefined) { - // The height isn't computed yet, return. - return null; - } - height += trackThreadHeight + border; - } - break; - } - default: - throw assertExhaustiveCheck(globalTrack); - } - } - } - - // Figure out which PIDs are hidden. - const hiddenPids = new Set(); - for (const trackIndex of hiddenGlobalTracks) { - const globalTrack = globalTracks[trackIndex]; - if (globalTrack.type === 'process') { - hiddenPids.add(globalTrack.pid); - } - } - - for (const [pid, localTracks] of localTracksByPid) { - if (hiddenPids.has(pid)) { - // This track is hidden already. - continue; - } - for (const [trackIndex, localTrack] of localTracks.entries()) { - const hiddenLocalTracks = ensureExists( - hiddenLocalTracksByPid.get(pid), - 'Could not look up the hidden local tracks from the given PID' - ); - if (!hiddenLocalTracks.has(trackIndex)) { - switch (localTrack.type) { - case 'thread': - { - // The thread tracks have enough complexity that it warrants measuring - // them rather than statically using a value like the other tracks. - const trackThreadHeight = - trackThreadHeights[localTrack.threadIndex]; - if (trackThreadHeight === undefined) { - // The height isn't computed yet, return. - return null; - } - height += trackThreadHeight + border; - } - - break; - case 'network': - height += TRACK_NETWORK_HEIGHT + border; - break; - case 'memory': - height += TRACK_MEMORY_HEIGHT + border; - break; - case 'bandwidth': - height += TRACK_BANDWIDTH_HEIGHT + border; - break; - case 'event-delay': - height += TRACK_EVENT_DELAY_HEIGHT + border; - break; - case 'ipc': - height += TRACK_IPC_HEIGHT + border; - break; - case 'process-cpu': - case 'power': - height += TRACK_PROCESS_CPU_HEIGHT + border; - break; - case 'marker': - height += TRACK_MARKER_HEIGHT + border; - break; - default: - throw assertExhaustiveCheck(localTrack); - } - } - } - } - return height; - } -); - /** * This selector lets us know if it is safe to load a new profile. If * the app is already busy loading a profile, this selector returns diff --git a/src/test/components/ProfileViewer.test.tsx b/src/test/components/ProfileViewer.test.tsx deleted file mode 100644 index e5fba9e71f..0000000000 --- a/src/test/components/ProfileViewer.test.tsx +++ /dev/null @@ -1,71 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import ReactDOM from 'react-dom'; -import { Provider } from 'react-redux'; - -import { render } from 'firefox-profiler/test/fixtures/testing-library'; -import { ProfileViewer } from 'firefox-profiler/components/app/ProfileViewer'; -import { getTimelineHeight } from 'firefox-profiler/selectors/app'; -import { updateUrlState } from 'firefox-profiler/actions/app'; -import { viewProfile } from 'firefox-profiler/actions/receive-profile'; -import { stateFromLocation } from 'firefox-profiler/app-logic/url-handling'; - -import { blankStore } from '../fixtures/stores'; -import { getProfileWithNiceTracks } from '../fixtures/profiles/tracks'; -import { autoMockCanvasContext } from '../fixtures/mocks/canvas-context'; -import { mockRaf } from '../fixtures/mocks/request-animation-frame'; -import { - autoMockElementSize, - getElementWithFixedSize, -} from '../fixtures/mocks/element-size'; -import { autoMockIntersectionObserver } from '../fixtures/mocks/intersection-observer'; - -describe('ProfileViewer', function () { - autoMockCanvasContext(); - autoMockElementSize({ width: 200, height: 300 }); - autoMockIntersectionObserver(); - - beforeEach(() => { - jest - .spyOn(ReactDOM, 'findDOMNode') - .mockImplementation(() => - getElementWithFixedSize({ width: 300, height: 300 }) - ); - }); - - function setup() { - // WithSize uses requestAnimationFrame - const flushRafCalls = mockRaf(); - - const store = blankStore(); - store.dispatch( - updateUrlState( - stateFromLocation({ - pathname: '/from-browser', - search: '', - hash: '', - }) - ) - ); - store.dispatch(viewProfile(getProfileWithNiceTracks())); - - const renderResult = render( - - - - ); - - // Flushing the requestAnimationFrame calls so we can see the actual height of tracks. - flushRafCalls(); - - return { ...renderResult, ...store }; - } - - it('calculates the full timeline height correctly', () => { - const { getState } = setup(); - - // Note: You should update this total height if you changed the height calculation algorithm. - expect(getTimelineHeight(getState())).toBe(1224); - }); -}); diff --git a/src/test/components/__snapshots__/Timeline.test.tsx.snap b/src/test/components/__snapshots__/Timeline.test.tsx.snap index 4c3ac88f95..204cca2f27 100644 --- a/src/test/components/__snapshots__/Timeline.test.tsx.snap +++ b/src/test/components/__snapshots__/Timeline.test.tsx.snap @@ -51,7 +51,7 @@ exports[`TimelineSelection does not crash when there are no samples or markers 1 class="overflowEdgeIndicatorEdge leftEdge" />