diff --git a/static/app/components/demo/demoHeader.tsx b/static/app/components/demo/demoHeader.tsx
index 3be364240fc74a..8b305e7b16964f 100644
--- a/static/app/components/demo/demoHeader.tsx
+++ b/static/app/components/demo/demoHeader.tsx
@@ -95,7 +95,13 @@ const Wrapper = styled('div')<{collapsed: boolean}>`
white-space: nowrap;
gap: ${space(4)};
- margin-left: calc(-1 * ${p => p.theme.sidebar.width});
+ margin-left: calc(
+ -1 * ${p => (p.collapsed ? p.theme.sidebar.collapsedWidth : p.theme.sidebar.expandedWidth)}
+ );
+
+ .sidebar-v2 & {
+ margin-left: calc(-1 * ${p => p.theme.sidebar.v2_width});
+ }
position: fixed;
width: 100%;
diff --git a/static/app/components/sidebar/index.tsx b/static/app/components/sidebar/index.tsx
index 0b0b61aa12241d..90e11181b8e4d8 100644
--- a/static/app/components/sidebar/index.tsx
+++ b/static/app/components/sidebar/index.tsx
@@ -2,6 +2,7 @@ import {Fragment, useCallback, useContext, useEffect} from 'react';
import {css} from '@emotion/react';
import styled from '@emotion/styled';
+import {hideSidebar, showSidebar} from 'sentry/actionCreators/preferences';
import Feature from 'sentry/components/acl/feature';
import GuideAnchor from 'sentry/components/assistant/guideAnchor';
import {Chevron} from 'sentry/components/chevron';
@@ -35,6 +36,7 @@ import {t} from 'sentry/locale';
import ConfigStore from 'sentry/stores/configStore';
import DemoWalkthroughStore from 'sentry/stores/demoWalkthroughStore';
import HookStore from 'sentry/stores/hookStore';
+import PreferencesStore from 'sentry/stores/preferencesStore';
import SidebarPanelStore from 'sentry/stores/sidebarPanelStore';
import {useLegacyStore} from 'sentry/stores/useLegacyStore';
import {space} from 'sentry/styles/space';
@@ -113,10 +115,12 @@ function useOpenOnboardingSidebar(organization?: Organization) {
function Sidebar() {
const location = useLocation();
+ const preferences = useLegacyStore(PreferencesStore);
const activePanel = useLegacyStore(SidebarPanelStore);
const organization = useOrganization({allowNull: true});
const {shouldAccordionFloat} = useContext(ExpandedContext);
- const hasNewNav = true;
+ const hasNewNav = organization?.features.includes('navigation-sidebar-v2') ?? false;
+ const collapsed = hasNewNav ? true : !!preferences.collapsed;
const hasOrganization = !!organization;
const isSelfHostedErrorsOnly = ConfigStore.get('isSelfHostedErrorsOnly');
@@ -130,7 +134,7 @@ function Sidebar() {
hasPanel,
organization,
hasNewNav,
- collapsed: true,
+ collapsed,
};
// Avoid showing superuser UI on self-hosted instances
const showSuperuserWarning = () => {
@@ -144,7 +148,13 @@ function Sidebar() {
useOpenOnboardingSidebar();
- const toggleCollapse = useCallback(() => {}, []);
+ const toggleCollapse = useCallback(() => {
+ if (collapsed) {
+ showSidebar();
+ } else {
+ hideSidebar();
+ }
+ }, [collapsed]);
// Close panel on any navigation
useEffect(() => void hidePanel(), [location?.pathname]);
@@ -165,17 +175,30 @@ function Sidebar() {
});
}, [location?.hash]);
+ // Add sidebar collapse classname to body
+ useEffect(() => {
+ const bcl = document.body.classList;
+
+ if (collapsed) {
+ bcl.add('collapsed');
+ } else {
+ bcl.remove('collapsed');
+ }
+
+ return () => bcl.remove('collapsed');
+ }, [collapsed]);
+
// Add sidebar hasNewNav classname to body
useEffect(() => {
const bcl = document.body.classList;
if (hasNewNav) {
- bcl.add('hasNewNav');
+ bcl.add('sidebar-v2');
} else {
- bcl.remove('hasNewNav');
+ bcl.remove('sidebar-v2');
}
- return () => bcl.remove('hasNewNav');
+ return () => bcl.remove('sidebar-v2');
}, [hasNewNav]);
const sidebarAnchor = isDemoWalkthrough() ? (
@@ -217,7 +240,7 @@ function Sidebar() {
>
}
+ icon={}
label={{t('Discover')}}
to={getDiscoverLandingUrl(organization)}
id="discover-v2"
@@ -236,7 +259,7 @@ function Sidebar() {
}
to={`/organizations/${organization.slug}/${moduleURLBuilder('db')}/`}
id="performance-database"
- icon={}
+ icon={}
/>
);
@@ -250,7 +273,7 @@ function Sidebar() {
}
to={`/organizations/${organization.slug}/${moduleURLBuilder('http')}/`}
id="performance-http"
- icon={}
+ icon={}
/>
);
@@ -264,7 +287,7 @@ function Sidebar() {
}
to={`/organizations/${organization.slug}/${moduleURLBuilder('cache')}/`}
id="performance-cache"
- icon={}
+ icon={}
/>
);
@@ -278,7 +301,7 @@ function Sidebar() {
}
to={`/organizations/${organization.slug}/${moduleURLBuilder('vital')}/`}
id="performance-webvitals"
- icon={}
+ icon={}
/>
);
@@ -292,7 +315,7 @@ function Sidebar() {
}
to={`/organizations/${organization.slug}/${moduleURLBuilder('queue')}/`}
id="performance-queues"
- icon={}
+ icon={}
/>
);
@@ -313,7 +336,7 @@ function Sidebar() {
label={MODULE_TITLES.screen_load}
to={`/organizations/${organization.slug}/${moduleURLBuilder('screen_load')}/`}
id="performance-mobile-screens"
- icon={}
+ icon={}
/>
);
@@ -325,7 +348,7 @@ function Sidebar() {
label={MODULE_TITLES.app_start}
to={`/organizations/${organization.slug}/${moduleURLBuilder('app_start')}/`}
id="performance-mobile-app-startup"
- icon={}
+ icon={}
/>
);
@@ -341,7 +364,7 @@ function Sidebar() {
label={MODULE_TITLES['mobile-ui']}
to={`/organizations/${organization.slug}/${moduleURLBuilder('mobile-ui')}/`}
id="performance-mobile-ui"
- icon={}
+ icon={}
isAlpha
/>
@@ -358,7 +381,7 @@ function Sidebar() {
label={MODULE_TITLES['mobile-screens']}
to={`/organizations/${organization.slug}/${moduleURLBuilder('mobile-screens')}/`}
id="performance-mobile-screens"
- icon={}
+ icon={}
/>
);
@@ -370,7 +393,7 @@ function Sidebar() {
label={{MODULE_TITLES.resource}}
to={`/organizations/${organization.slug}/${moduleURLBuilder('resource')}/`}
id="performance-browser-resources"
- icon={}
+ icon={}
/>
);
@@ -382,7 +405,7 @@ function Sidebar() {
label={{t('Traces')}}
to={`/organizations/${organization.slug}/traces/`}
id="performance-trace-explorer"
- icon={}
+ icon={}
isBeta
/>
@@ -392,7 +415,7 @@ function Sidebar() {
}
+ icon={}
label={MODULE_TITLES.ai}
to={`/organizations/${organization.slug}/${moduleURLBuilder('ai')}/`}
id="llm-monitoring"
@@ -484,7 +507,7 @@ function Sidebar() {
>
}
+ icon={}
label={t('Replays')}
to={`/organizations/${organization.slug}/replays/`}
id="replays"
@@ -497,7 +520,7 @@ function Sidebar() {
const metrics = hasOrganization && hasCustomMetrics(organization) && (
}
+ icon={}
label={t('Metrics')}
to={metricsPath}
search={location?.pathname === normalizeUrl(metricsPath) ? location.search : ''}
@@ -534,7 +557,7 @@ function Sidebar() {
}
+ icon={}
label={t('Profiles')}
to={`/organizations/${organization.slug}/profiling/`}
id="profiling"
@@ -605,17 +628,19 @@ function Sidebar() {
);
- const collapsed = true;
-
return (
-
+
-
+
{showSuperuserWarning() && !isExcludedOrg() && (
@@ -630,8 +655,25 @@ function Sidebar() {
{projects}
- {!isSelfHostedErrorsOnly && (
-
+ {!isSelfHostedErrorsOnly && !hasNewNav && (
+
+
+ {explore}
+ {insights}
+
+
+ {performance}
+ {feedback}
+ {monitors}
+ {alerts}
+ {dashboards}
+ {releases}
+
+
+ )}
+
+ {!isSelfHostedErrorsOnly && hasNewNav && (
+
{explore}
{insights}
{performance}
@@ -781,8 +823,15 @@ export const SidebarWrapper = styled('nav')<{collapsed: boolean; hasNewNav?: boo
background: ${p => p.theme.sidebarGradient};
color: ${p => p.theme.sidebar.color};
line-height: 1;
- padding: 16px 8px;
- width: ${p => p.theme.sidebar.width};
+ padding: 12px 0 2px; /* Allows for 32px avatars */
+ width: ${p =>
+ p.theme.sidebar[
+ p.hasNewNav
+ ? 'semiCollapsedWidth'
+ : p.collapsed
+ ? 'collapsedWidth'
+ : 'expandedWidth'
+ ]};
position: fixed;
top: ${p => (ConfigStore.get('demoMode') ? p.theme.demo.headerSize : 0)};
left: 0;
@@ -792,6 +841,11 @@ export const SidebarWrapper = styled('nav')<{collapsed: boolean; hasNewNav?: boo
border-right: solid 1px ${p => p.theme.sidebarBorder};
${responsiveFlex};
+ .sidebar-v2 & {
+ padding: ${space(1)} ${space(0.75)};
+ width: ${p => p.theme.sidebar.v2_width};
+ }
+
@media (max-width: ${p => p.theme.breakpoints.medium}) {
top: 0;
left: 0;
diff --git a/static/app/components/sidebar/sidebarItem.tsx b/static/app/components/sidebar/sidebarItem.tsx
index 2d78dbebf1e8f7..e34aa95c218074 100644
--- a/static/app/components/sidebar/sidebarItem.tsx
+++ b/static/app/components/sidebar/sidebarItem.tsx
@@ -387,19 +387,44 @@ const StyledSidebarItem = styled(Link, {
shouldForwardProp: p => typeof p === 'string' && isPropValid(p),
})`
display: flex;
- align-self: center;
- justify-content: center;
color: ${p => (p.isInFloatingAccordion ? p.theme.gray400 : 'inherit')};
position: relative;
cursor: pointer;
- font-size: 11px;
- height: 52px;
- width: 100%;
+ font-size: 15px;
+ height: ${p => (p.isInFloatingAccordion ? '35px' : p.hasNewNav ? '40px' : '30px')};
flex-shrink: 0;
- flex-grow: 1;
border-radius: ${p => p.theme.borderRadius};
transition: none;
+ ${p =>
+ !p.hasNewNav &&
+ css`
+ &:before {
+ display: block;
+ content: '';
+ position: absolute;
+ top: 4px;
+ left: calc(-${space(2)} - 1px);
+ bottom: 6px;
+ width: 5px;
+ border-radius: 0 3px 3px 0;
+ background-color: transparent;
+ transition: 0.15s background-color linear;
+ }
+ `}
+
+ @media (max-width: ${p => p.theme.breakpoints.medium}) {
+ &:before {
+ top: auto;
+ left: 5px;
+ bottom: -12px;
+ height: 5px;
+ width: auto;
+ right: 5px;
+ border-radius: 3px 3px 0 0;
+ }
+ }
+
&:hover,
&:focus-visible {
${p => {
@@ -424,6 +449,15 @@ const StyledSidebarItem = styled(Link, {
box-shadow: inset 0 0 0 2px ${p => p.theme.purple300};
}
+ .sidebar-v2 & {
+ font-size: 11px;
+ align-self: center;
+ justify-content: center;
+ height: 52px;
+ width: 100%;
+ flex-grow: 1;
+ }
+
${getActiveStyle};
`;
@@ -524,7 +558,12 @@ const CollapsedFeatureBadge = styled(FeatureBadge)`
`;
const StyledInteractionStateLayer = styled(InteractionStateLayer)`
- height: 53px;
- width: 58px;
- border-radius: ${p => p.theme.borderRadius};
+ height: ${16 * 2 + 40}px;
+ width: 70px;
+
+ .sidebar-v2 & {
+ height: 53px;
+ width: 58px;
+ border-radius: ${p => p.theme.borderRadius};
+ }
`;
diff --git a/static/app/components/sidebar/sidebarPanel.tsx b/static/app/components/sidebar/sidebarPanel.tsx
index e5e8f8357fc8cf..2f154f813dc27a 100644
--- a/static/app/components/sidebar/sidebarPanel.tsx
+++ b/static/app/components/sidebar/sidebarPanel.tsx
@@ -35,7 +35,13 @@ const PanelContainer = styled('div')`
: css`
width: 460px;
top: 0;
- left: ${p.theme.sidebar.width};
+ left: ${p.collapsed
+ ? p.theme.sidebar.collapsedWidth
+ : p.theme.sidebar.expandedWidth};
+
+ .sidebar-v2 & {
+ left: ${p.theme.sidebar.v2_width};
+ }
`};
`;
diff --git a/static/app/utils/replays/hooks/useReplayLayout.tsx b/static/app/utils/replays/hooks/useReplayLayout.tsx
index 5abe5b8320c129..2e22d2ed630d60 100644
--- a/static/app/utils/replays/hooks/useReplayLayout.tsx
+++ b/static/app/utils/replays/hooks/useReplayLayout.tsx
@@ -68,8 +68,9 @@ function isLayout(val: string): val is LayoutKey {
function useReplayLayout() {
const collapsed = !!useLegacyStore(PreferencesStore).collapsed;
- const defaultLayout = getDefaultLayout(collapsed);
const organization = useOrganization();
+ const hasNewNav = organization?.features.includes('navigation-sidebar-v2') ?? false;
+ const defaultLayout = getDefaultLayout(collapsed, hasNewNav);
const {getParamValue, setParamValue} = useUrlParams('l_page', defaultLayout);
diff --git a/static/app/utils/theme.tsx b/static/app/utils/theme.tsx
index 3b5ea26d34928a..843801555090a0 100644
--- a/static/app/utils/theme.tsx
+++ b/static/app/utils/theme.tsx
@@ -806,8 +806,10 @@ const commonTheme = {
badgeSize: '22px',
smallBadgeSize: '11px',
collapsedWidth: '70px',
- width: '80px',
- panelWidth: '150px',
+ semiCollapsedWidth: '100px',
+ expandedWidth: '220px',
+ v2_width: '80px',
+ v2_panelWidth: '150px',
mobileHeightNumber: 54,
mobileHeight: '54px',
menuSpacing: '15px',
diff --git a/static/app/views/replays/detail/layout/utils.tsx b/static/app/views/replays/detail/layout/utils.tsx
index 4700a9d4c4982f..4610fdfa0e0391 100644
--- a/static/app/views/replays/detail/layout/utils.tsx
+++ b/static/app/views/replays/detail/layout/utils.tsx
@@ -1,10 +1,14 @@
import {LayoutKey} from 'sentry/utils/replays/hooks/useReplayLayout';
import theme from 'sentry/utils/theme';
-export const getDefaultLayout = (_collapsed: boolean): LayoutKey => {
+export const getDefaultLayout = (collapsed: boolean, hasNewNav: boolean): LayoutKey => {
const {innerWidth, innerHeight} = window;
- const sidebarWidth = parseInt(theme.sidebar.width, 10);
+ let width = collapsed ? theme.sidebar.collapsedWidth : theme.sidebar.expandedWidth;
+ if (hasNewNav) {
+ width = theme.sidebar.v2_width;
+ }
+ const sidebarWidth = parseInt(width, 10);
const mediumScreenWidth = parseInt(theme.breakpoints.medium, 10);
diff --git a/static/less/layout.less b/static/less/layout.less
index f42b2d1f8ad130..5dcb6a6b8b71d3 100644
--- a/static/less/layout.less
+++ b/static/less/layout.less
@@ -8,7 +8,7 @@ body {
padding-left: @sidebar-collapsed-width;
}
- &.hasNewNav {
+ &.sidebar-v2 {
padding-left: @sidebar-v2-width;
}
}
@@ -44,7 +44,7 @@ body.narrow {
padding-left: @sidebar-collapsed-width;
}
- &.hasNewNav {
+ &.sidebar-v2 {
padding-left: calc(@sidebar-v2-width + @sidebar-v2-panel-width);
&.collapsed {