diff --git a/desktop/app/src/PluginContainer.tsx b/desktop/app/src/PluginContainer.tsx index 288cd17db..a43efbb6b 100644 --- a/desktop/app/src/PluginContainer.tsx +++ b/desktop/app/src/PluginContainer.tsx @@ -45,7 +45,7 @@ import {activateMenuItems} from './MenuBar'; import {Message} from './reducers/pluginMessageQueue'; import {Idler} from './utils/Idler'; import {processMessageQueue} from './utils/messageQueue'; -import {ToggleButton, SmallText} from './ui'; +import {ToggleButton, SmallText, Layout} from './ui'; import {SandyPluginRenderer} from 'flipper-plugin'; import {isDevicePluginDefinition} from './utils/pluginUtils'; import ArchivedDevice from './devices/ArchivedDevice'; @@ -67,7 +67,6 @@ const Waiting = styled(FlexColumn)({ width: '100%', height: '100%', flexGrow: 1, - background: colors.light02, alignItems: 'center', justifyContent: 'center', textAlign: 'center', @@ -95,6 +94,7 @@ const ProgressBarBar = styled.div<{progress: number}>(({progress}) => ({ type OwnProps = { logger: Logger; + isSandy?: boolean; }; type StateFromProps = { @@ -362,6 +362,7 @@ class PluginContainer extends PureComponent { isArchivedDevice, selectedApp, settingsState, + isSandy, } = this.props; if (!activePlugin || !target || !pluginKey) { console.warn(`No selected plugin. Rendering empty!`); @@ -429,7 +430,17 @@ class PluginContainer extends PureComponent { }; pluginElement = React.createElement(activePlugin, props); } - return ( + return isSandy ? ( + + + {pluginElement} + + + + ) : ( document.getElementById('detailsSidebar'), []); -type DispatchFromProps = { - toggleRightSidebarAvailable: (visible?: boolean) => any; -}; - -type Props = OwnProps & StateFromProps & DispatchFromProps; -class DetailSidebar extends React.Component { - componentDidMount() { - this.updateSidebarAvailablility(); + if (!reduxContext || !domNode) { + // For unit tests, make sure to render elements inline + return
{children}
; } - componentDidUpdate() { - this.updateSidebarAvailablility(); - } + const isSandy = useIsSandy(); + const dispatch = useDispatch(); + const {rightSidebarAvailable, rightSidebarVisible} = useStore((state) => { + const {rightSidebarAvailable, rightSidebarVisible} = state.application; + return {rightSidebarAvailable, rightSidebarVisible}; + }); - updateSidebarAvailablility() { - const available = Boolean(this.props.children); - if (available !== this.props.rightSidebarAvailable) { - this.props.toggleRightSidebarAvailable(available); - } - } + useEffect( + function updateSidebarAvailablility() { + const available = Boolean(children); + if (available !== rightSidebarAvailable) { + dispatch(toggleRightSidebarAvailable(available)); + } + }, + [children, rightSidebarAvailable, dispatch], + ); - render() { - const domNode = document.getElementById('detailsSidebar'); - return ( - this.props.children && - this.props.rightSidebarVisible && + return ( + (children && + rightSidebarVisible && domNode && ReactDOM.createPortal( - {this.props.children} + minWidth={minWidth} + width={width || 300} + position="right" + gutter={isSandy}> + {isSandy ? {children} : children} , domNode, - ) - ); - } + )) || + null + ); } - -export default connect( - ({application: {rightSidebarVisible, rightSidebarAvailable}}) => ({ - rightSidebarVisible, - rightSidebarAvailable, - }), - { - toggleRightSidebarAvailable, - }, -)(DetailSidebar); diff --git a/desktop/app/src/chrome/mainsidebar/MainSidebarUtilsSection.tsx b/desktop/app/src/chrome/mainsidebar/MainSidebarUtilsSection.tsx index 5c5ac8314..6543f739f 100644 --- a/desktop/app/src/chrome/mainsidebar/MainSidebarUtilsSection.tsx +++ b/desktop/app/src/chrome/mainsidebar/MainSidebarUtilsSection.tsx @@ -19,7 +19,6 @@ import {StaticView, setStaticView} from '../../reducers/connections'; import {setActiveSheet} from '../../reducers/application'; import UserAccount from '../UserAccount'; import SupportRequestFormV2 from '../../fb-stubs/SupportRequestFormV2'; -import WatchTools from '../../fb-stubs/WatchTools'; import { isStaticViewActive, PluginIcon, @@ -32,6 +31,7 @@ import {ConsoleLogs, errorCounterAtom} from '../ConsoleLogs'; import {useValue} from 'flipper-plugin'; import {colors} from '../../ui'; import GK from '../../fb-stubs/GK'; +import WatchTools from '../../fb-stubs/WatchTools'; type OwnProps = {}; diff --git a/desktop/app/src/sandy-chrome/ContentContainer.tsx b/desktop/app/src/sandy-chrome/ContentContainer.tsx new file mode 100644 index 000000000..bddaaabea --- /dev/null +++ b/desktop/app/src/sandy-chrome/ContentContainer.tsx @@ -0,0 +1,19 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {Layout, styled} from '../ui'; +import {theme} from './theme'; + +export const ContentContainer = styled(Layout.Container)({ + overflow: 'hidden', + background: theme.backgroundDefault, + border: `1px solid ${theme.dividerColor}`, + borderRadius: theme.containerBorderRadius, + boxShadow: `0px 0px 5px rgba(0, 0, 0, 0.05), 0px 0px 1px rgba(0, 0, 0, 0.05)`, +}); diff --git a/desktop/app/src/sandy-chrome/LeftRail.tsx b/desktop/app/src/sandy-chrome/LeftRail.tsx index 60f430cc2..9d2291c97 100644 --- a/desktop/app/src/sandy-chrome/LeftRail.tsx +++ b/desktop/app/src/sandy-chrome/LeftRail.tsx @@ -8,7 +8,6 @@ */ import React, {cloneElement, useState, useCallback, useMemo} from 'react'; -import {styled, Layout} from '../ui'; import {Button, Divider, Badge, Tooltip, Avatar, Popover} from 'antd'; import { MobileFilled, @@ -23,7 +22,10 @@ import { } from '@ant-design/icons'; import {SidebarLeft, SidebarRight} from './SandyIcons'; import {useDispatch, useStore} from '../utils/useStore'; -import {toggleLeftSidebarVisible} from '../reducers/application'; +import { + toggleLeftSidebarVisible, + toggleRightSidebarVisible, +} from '../reducers/application'; import {theme} from './theme'; import SetupDoctorScreen, {checkHasNewProblem} from './SetupDoctorScreen'; import SettingsSheet from '../chrome/SettingsSheet'; @@ -34,6 +36,8 @@ import {ToplevelProps} from './SandyApp'; import {useValue} from 'flipper-plugin'; import {logout} from '../reducers/user'; import config from '../fb-stubs/config'; +import Layout from '../ui/components/Layout'; +import styled from '@emotion/styled'; const LeftRailButtonElem = styled(Button)<{kind?: 'small'}>(({kind}) => ({ width: kind === 'small' ? 32 : 36, @@ -52,11 +56,13 @@ function LeftRailButton({ count, title, onClick, + disabled, }: { icon?: React.ReactElement; small?: boolean; toggled?: boolean; - selected?: boolean; // TODO: make sure only one element can be selected + selected?: boolean; + disabled?: boolean; count?: number | true; title: string; onClick?: React.MouseEventHandler; @@ -78,6 +84,7 @@ function LeftRailButton({ type={selected ? 'primary' : 'ghost'} icon={iconElement} onClick={onClick} + disabled={disabled} style={{ color: toggled ? theme.primaryColor : undefined, background: toggled ? theme.backgroundWash : undefined, @@ -99,9 +106,9 @@ export function LeftRail({ setToplevelSelection, }: ToplevelProps) { return ( - + - + } title="App Inspect" @@ -118,7 +125,7 @@ export function LeftRail({ setToplevelSelection={setToplevelSelection} /> - + @@ -127,11 +134,7 @@ export function LeftRail({ small title="Feedback / Bug Reporter" /> - } - small - title="Right Sidebar Toggle" - /> + {config.showLogin && } @@ -141,12 +144,11 @@ export function LeftRail({ } function LeftSidebarToggleButton() { + const dispatch = useDispatch(); const mainMenuVisible = useStore( (state) => state.application.leftSidebarVisible, ); - const dispatch = useDispatch(); - return ( } @@ -160,6 +162,29 @@ function LeftSidebarToggleButton() { ); } +function RightSidebarToggleButton() { + const dispatch = useDispatch(); + const rightSidebarAvailable = useStore( + (state) => state.application.rightSidebarAvailable, + ); + const rightSidebarVisible = useStore( + (state) => state.application.rightSidebarVisible, + ); + + return ( + } + small + title="Right Sidebar Toggle" + toggled={rightSidebarVisible} + disabled={!rightSidebarAvailable} + onClick={() => { + dispatch(toggleRightSidebarVisible()); + }} + /> + ); +} + function DebugLogsButton({ toplevelSelection, setToplevelSelection, diff --git a/desktop/app/src/sandy-chrome/SandyApp.tsx b/desktop/app/src/sandy-chrome/SandyApp.tsx index 5dfe97a81..accc8731b 100644 --- a/desktop/app/src/sandy-chrome/SandyApp.tsx +++ b/desktop/app/src/sandy-chrome/SandyApp.tsx @@ -23,6 +23,7 @@ import {setStaticView} from '../reducers/connections'; import {toggleLeftSidebarVisible} from '../reducers/application'; import {AppInspect} from './appinspect/AppInspect'; import PluginContainer from '../PluginContainer'; +import {ContentContainer} from './ContentContainer'; export type ToplevelNavItem = 'appinspect' | 'flipperlogs' | undefined; export type ToplevelProps = { @@ -95,25 +96,15 @@ export function SandyApp({logger}: {logger: Logger}) { - - {staticView ? ( - React.createElement(staticView, { + {staticView ? ( + + {React.createElement(staticView, { logger: logger, - }) - ) : ( - - )} - - - - + })} - + ) : ( + + )} @@ -121,19 +112,7 @@ export function SandyApp({logger}: {logger: Logger}) { ); } -const MainContainer = styled(Layout.Right)({ +const MainContainer = styled(Layout.Container)({ background: theme.backgroundWash, + padding: `${theme.space.large}px ${theme.space.large}px ${theme.space.large}px 0`, }); - -export const ContentContainer = styled(Layout.Container)({ - background: theme.backgroundDefault, - border: `1px solid ${theme.dividerColor}`, - borderRadius: theme.containerBorderRadius, - boxShadow: `0px 0px 5px rgba(0, 0, 0, 0.05), 0px 0px 1px rgba(0, 0, 0, 0.05)`, - marginTop: theme.space.large, - marginBottom: theme.space.large, -}); - -function RightMenu() { - return
RightMenu
; -} diff --git a/desktop/app/src/ui/components/Layout.tsx b/desktop/app/src/ui/components/Layout.tsx index 35fe4392c..bb2a969b1 100644 --- a/desktop/app/src/ui/components/Layout.tsx +++ b/desktop/app/src/ui/components/Layout.tsx @@ -45,6 +45,7 @@ const Container = styled.div( height, ...rest }) => ({ + boxSizing: 'border-box', minWidth: `0`, // ensures the Container can shrink smaller than it's largest width, height, @@ -64,6 +65,7 @@ const Container = styled.div( ); const ScrollParent = styled.div({ + boxSizing: 'border-box', flex: 1, position: 'relative', overflow: 'auto', @@ -141,11 +143,7 @@ const Layout = { let [child1, child2] = props.children; if (props.scrollable) child2 = {child2}; return ( - + {child1} {child2} @@ -157,11 +155,7 @@ const Layout = { let [child1, child2] = props.children; if (props.scrollable) child1 = {child1}; return ( - + {child1} {child2} @@ -173,7 +167,7 @@ const Layout = { let [child1, child2] = props.children; if (props.scrollable) child2 = {child2}; return ( - + {child1} {child2} @@ -185,7 +179,7 @@ const Layout = { let [child1, child2] = props.children; if (props.scrollable) child1 = {child1}; return ( - + {child1} {child2} @@ -202,23 +196,25 @@ Object.keys(Layout).forEach((key) => { }); const SandySplitContainer = styled.div<{ - flex1: number; - flex2: number; + grow: 1 | 2; center?: boolean; flexDirection: CSSProperties['flexDirection']; }>((props) => ({ + boxSizing: 'border-box', display: 'flex', flex: 1, flexDirection: props.flexDirection, alignItems: props.center ? 'center' : 'stretch', + overflow: 'hidden', '> :first-child': { - flexGrow: props.flex1, - flexShrink: props.flex1, + flex: props.grow === 1 ? growStyle : fixedStyle, }, '> :last-child': { - flexGrow: props.flex2, - flexShrink: props.flex2, + flex: props.grow === 2 ? growStyle : fixedStyle, }, })); +const fixedStyle = `0 0 auto`; +const growStyle = `1 0 0`; + export default Layout; diff --git a/desktop/plugins/seamammals/src/__tests__/seamammals.spec.tsx b/desktop/plugins/seamammals/src/__tests__/seamammals.spec.tsx index 9b35a2e84..7979c18d1 100644 --- a/desktop/plugins/seamammals/src/__tests__/seamammals.spec.tsx +++ b/desktop/plugins/seamammals/src/__tests__/seamammals.spec.tsx @@ -7,20 +7,12 @@ * @format */ -import * as React from 'react'; -import * as Flipper from 'flipper'; // eslint-disable-next-line import {act} from '@testing-library/react'; { // These mocks are needed because seammammals still uses Flipper in its UI implementation, // so we need to mock some things - - // @ts-ignore - jest.spyOn(Flipper.DetailSidebar, 'type').mockImplementation((props) => { - return
{props.children}
; - }); - const origRequestIdleCallback = window.requestIdleCallback; const origCancelIdleCallback = window.cancelIdleCallback; // @ts-ignore