Skip to content

Commit 0da4d0f

Browse files
natemoo-reandrewshie-sentry
authored andcommitted
feat(stories): bring back the sidebar on landing page (#95882)
Landing page should have the sidebar visible [See Preview Deployment](https://sentry-git-stories-nmlanding-sidebar.sentry.dev/stories)
1 parent 3c36227 commit 0da4d0f

File tree

3 files changed

+146
-87
lines changed

3 files changed

+146
-87
lines changed

static/app/stories/view/index.tsx

Lines changed: 89 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import {Fragment, type PropsWithChildren} from 'react';
2+
import {css, Global, useTheme} from '@emotion/react';
13
import styled from '@emotion/styled';
24

35
import {Alert} from 'sentry/components/core/alert';
@@ -12,6 +14,7 @@ import RouteAnalyticsContextProvider from 'sentry/views/routeAnalyticsContextPro
1214
import {StoryLanding} from './landing';
1315
import {StoryExports} from './storyExports';
1416
import {StoryHeader} from './storyHeader';
17+
import {useStoryDarkModeTheme} from './useStoriesDarkMode';
1518
import {useStoriesLoader} from './useStoriesLoader';
1619

1720
export default function Stories() {
@@ -25,18 +28,11 @@ function isLandingPage(location: ReturnType<typeof useLocation>) {
2528

2629
function StoriesLanding() {
2730
return (
28-
<RouteAnalyticsContextProvider>
29-
<OrganizationContainer>
30-
<Layout style={{gridTemplateColumns: 'auto'}}>
31-
<HeaderContainer>
32-
<StoryHeader />
33-
</HeaderContainer>
34-
<StoryMainContainer style={{gridColumn: '1 / -1'}}>
35-
<StoryLanding />
36-
</StoryMainContainer>
37-
</Layout>
38-
</OrganizationContainer>
39-
</RouteAnalyticsContextProvider>
31+
<StoriesLayout>
32+
<StoryMainContainer>
33+
<StoryLanding />
34+
</StoryMainContainer>
35+
</StoriesLayout>
4036
);
4137
}
4238

@@ -47,44 +43,90 @@ function StoryDetail() {
4743
const story = useStoriesLoader({files});
4844

4945
return (
50-
<RouteAnalyticsContextProvider>
51-
<OrganizationContainer>
52-
<Layout>
53-
<HeaderContainer>
54-
<StoryHeader />
55-
</HeaderContainer>
56-
57-
<StorySidebar />
58-
59-
{story.isLoading ? (
60-
<VerticalScroll>
61-
<LoadingIndicator />
62-
</VerticalScroll>
63-
) : story.isError ? (
64-
<VerticalScroll>
65-
<Alert.Container>
66-
<Alert type="error" showIcon>
67-
<strong>{story.error.name}:</strong> {story.error.message}
68-
</Alert>
69-
</Alert.Container>
70-
</VerticalScroll>
71-
) : story.isSuccess ? (
72-
<StoryMainContainer>
73-
{story.data.map(s => {
74-
return <StoryExports key={s.filename} story={s} />;
75-
})}
76-
</StoryMainContainer>
77-
) : (
78-
<VerticalScroll>
79-
<strong>The file you selected does not export a story.</strong>
80-
</VerticalScroll>
81-
)}
82-
</Layout>
83-
</OrganizationContainer>
84-
</RouteAnalyticsContextProvider>
46+
<StoriesLayout>
47+
{story.isLoading ? (
48+
<VerticalScroll>
49+
<LoadingIndicator />
50+
</VerticalScroll>
51+
) : story.isError ? (
52+
<VerticalScroll>
53+
<Alert.Container>
54+
<Alert type="error" showIcon>
55+
<strong>{story.error.name}:</strong> {story.error.message}
56+
</Alert>
57+
</Alert.Container>
58+
</VerticalScroll>
59+
) : story.isSuccess ? (
60+
<StoryMainContainer>
61+
{story.data.map(s => {
62+
return <StoryExports key={s.filename} story={s} />;
63+
})}
64+
</StoryMainContainer>
65+
) : (
66+
<VerticalScroll>
67+
<strong>The file you selected does not export a story.</strong>
68+
</VerticalScroll>
69+
)}
70+
</StoriesLayout>
8571
);
8672
}
8773

74+
function StoriesLayout(props: PropsWithChildren) {
75+
return (
76+
<Fragment>
77+
<GlobalStoryStyles />
78+
<RouteAnalyticsContextProvider>
79+
<OrganizationContainer>
80+
<Layout>
81+
<HeaderContainer>
82+
<StoryHeader />
83+
</HeaderContainer>
84+
85+
<StorySidebar />
86+
87+
{props.children}
88+
</Layout>
89+
</OrganizationContainer>
90+
</RouteAnalyticsContextProvider>
91+
</Fragment>
92+
);
93+
}
94+
95+
function GlobalStoryStyles() {
96+
const theme = useTheme();
97+
const darkTheme = useStoryDarkModeTheme();
98+
const location = useLocation();
99+
const isIndex = isLandingPage(location);
100+
const styles = css`
101+
/* match body background with header story styles */
102+
body {
103+
background-color: ${isIndex
104+
? darkTheme.tokens.background.secondary
105+
: theme.tokens.background.secondary};
106+
}
107+
/* fixed position color block to match overscroll color to story background */
108+
body::after {
109+
content: '';
110+
display: block;
111+
position: fixed;
112+
inset: 0;
113+
top: unset;
114+
background-color: ${theme.tokens.background.primary};
115+
height: 50vh;
116+
z-index: -1;
117+
pointer-events: none;
118+
}
119+
/* adjust position of global .messages-container element */
120+
.messages-container {
121+
margin-top: 52px;
122+
margin-left: 256px;
123+
z-index: ${theme.zIndex.header};
124+
background: ${theme.tokens.background.primary};
125+
}
126+
`;
127+
return <Global key="stories" styles={styles} />;
128+
}
129+
88130
const Layout = styled('div')`
89131
background: ${p => p.theme.tokens.background.primary};
90132
--stories-grid-space: 0;

static/app/stories/view/landing/index.tsx

Lines changed: 25 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type {PropsWithChildren} from 'react';
2-
import {Fragment, useMemo} from 'react';
3-
import {ThemeProvider, useTheme} from '@emotion/react';
2+
import {Fragment} from 'react';
3+
import {useTheme} from '@emotion/react';
44
import styled from '@emotion/styled';
55

66
import performanceWaitingForSpan from 'sentry-images/spot/performance-waiting-for-span.svg';
@@ -11,12 +11,8 @@ import {LinkButton} from 'sentry/components/core/button/linkButton';
1111
import {Flex} from 'sentry/components/core/layout';
1212
import {Link} from 'sentry/components/core/link';
1313
import {IconOpen} from 'sentry/icons';
14+
import {StoryDarkModeProvider} from 'sentry/stories/view/useStoriesDarkMode';
1415
import {space} from 'sentry/styles/space';
15-
import type {Theme} from 'sentry/utils/theme';
16-
// we need the hero to always use values from the dark theme
17-
// eslint-disable-next-line no-restricted-imports
18-
import {darkTheme} from 'sentry/utils/theme';
19-
import {DO_NOT_USE_darkChonkTheme} from 'sentry/utils/theme/theme.chonk';
2016

2117
import {Colors, Icons, Typography} from './figures';
2218

@@ -49,7 +45,7 @@ const frontmatter = {
4945
export function StoryLanding() {
5046
return (
5147
<Fragment>
52-
<AlwaysDarkThemeProvider>
48+
<StoryDarkModeProvider>
5349
<Hero>
5450
<Container>
5551
<Flex direction="column" gap={space(3)}>
@@ -74,7 +70,7 @@ export function StoryLanding() {
7470
/>
7571
</Container>
7672
</Hero>
77-
</AlwaysDarkThemeProvider>
73+
</StoryDarkModeProvider>
7874

7975
<Container>
8076
<Flex as="section" direction="column" gap={space(4)} flex={1}>
@@ -132,17 +128,6 @@ function Border() {
132128
);
133129
}
134130

135-
function AlwaysDarkThemeProvider(props: PropsWithChildren) {
136-
const theme = useTheme();
137-
138-
const localThemeValue = useMemo(
139-
() => (theme.isChonk ? DO_NOT_USE_darkChonkTheme : darkTheme),
140-
[theme]
141-
);
142-
143-
return <ThemeProvider theme={localThemeValue as Theme}>{props.children}</ThemeProvider>;
144-
}
145-
146131
const TitleEmphasis = styled('em')`
147132
font-style: normal;
148133
display: inline-block;
@@ -151,13 +136,13 @@ const TitleEmphasis = styled('em')`
151136
`;
152137

153138
const Hero = styled('div')`
154-
width: 100vw;
155-
padding: 48px 16px;
139+
padding: 48px 0;
156140
gap: ${space(4)};
157141
display: flex;
158142
align-items: center;
159-
background: ${p => p.theme.tokens.background.tertiary};
143+
background: ${p => p.theme.tokens.background.secondary};
160144
color: ${p => p.theme.tokens.content.primary};
145+
border-bottom: 1px solid ${p => p.theme.tokens.border.primary};
161146
162147
h1 {
163148
font-size: 36px;
@@ -174,15 +159,13 @@ const Hero = styled('div')`
174159
min-width: 320px;
175160
height: auto;
176161
}
177-
178-
@media (min-width: ${p => p.theme.breakpoints.md}) {
179-
padding: 48px 92px;
180-
}
181162
`;
182163

183164
const Container = styled('div')`
184-
max-width: 1134px;
185-
width: calc(100vw - 32px);
165+
max-width: 1080px;
166+
width: 100%;
167+
flex-grow: 1;
168+
flex-shrink: 1;
186169
margin-inline: auto;
187170
display: flex;
188171
flex-direction: column;
@@ -198,13 +181,9 @@ const Container = styled('div')`
198181
`;
199182

200183
const CardGrid = styled('div')`
201-
display: grid;
202-
grid-template-columns: minmax(0, 1fr);
184+
display: flex;
185+
flex-flow: row wrap;
203186
gap: ${space(2)};
204-
205-
@media (min-width: ${p => p.theme.breakpoints.md}) {
206-
grid-template-columns: repeat(2, minmax(0, 1fr));
207-
}
208187
`;
209188

210189
interface CardProps {
@@ -224,12 +203,13 @@ const CardLink = styled(Link)`
224203
color: ${p => p.theme.tokens.content.primary};
225204
display: flex;
226205
flex-direction: column;
227-
width: 100%;
228-
height: 256px;
206+
flex-grow: 1;
207+
width: calc(100% * 3 / 5);
208+
aspect-ratio: 2/1;
229209
padding: ${space(2)};
230210
border: 1px solid ${p => p.theme.tokens.border.muted};
231211
border-radius: ${p => p.theme.borderRadius};
232-
transition: initial 80ms ease-out;
212+
transition: all 80ms ease-out;
233213
transition-property: background-color, color, border-color;
234214
235215
&:hover,
@@ -245,15 +225,20 @@ const CardLink = styled(Link)`
245225
max-width: 509px;
246226
max-height: 170px;
247227
}
228+
229+
@media screen and (min-width: ${p => p.theme.breakpoints.md}) {
230+
max-width: calc(50% - 32px);
231+
}
248232
`;
249233

250234
const CardTitle = styled('span')`
251235
margin: 0;
252-
margin-top: ${space(1)};
236+
margin-top: auto;
237+
margin-bottom: ${space(2)};
238+
padding: ${space(1)} ${space(2)};
253239
width: 100%;
254240
height: 24px;
255241
font-size: 24px;
256-
padding: ${space(1)} ${space(2)};
257242
font-weight: ${p => p.theme.fontWeight.bold};
258243
color: currentColor;
259244
`;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import type {PropsWithChildren} from 'react';
2+
import {type Theme, ThemeProvider, useTheme} from '@emotion/react';
3+
4+
// these utils are for stories that have forced dark mode
5+
// which is a very specific sanctioned use case
6+
// eslint-disable-next-line no-restricted-imports
7+
import {darkTheme} from 'sentry/utils/theme';
8+
import {DO_NOT_USE_darkChonkTheme} from 'sentry/utils/theme/theme.chonk';
9+
10+
/**
11+
* Access the raw values from the dark theme
12+
*
13+
* ⚠️ DO NOT USE OUTSIDE OF STORIES
14+
*/
15+
export const useStoryDarkModeTheme = (): Theme => {
16+
const theme = useTheme();
17+
if (theme.isChonk) {
18+
return DO_NOT_USE_darkChonkTheme as any;
19+
}
20+
return darkTheme;
21+
};
22+
23+
/**
24+
* Forces all `children` to be rendered in dark mode,
25+
* regardless of user preferences.
26+
*
27+
* ⚠️ DO NOT USE OUTSIDE OF STORIES
28+
*/
29+
export function StoryDarkModeProvider(props: PropsWithChildren) {
30+
const theme = useStoryDarkModeTheme();
31+
return <ThemeProvider theme={theme}>{props.children}</ThemeProvider>;
32+
}

0 commit comments

Comments
 (0)