Skip to content

Commit 46f6353

Browse files
committed
feat: Improve Expandable style
For this, I extracted the `Alert` component stuff into a more generic `Callout` component, which the `Alert` extends. The `Expandable` can now use the same basis, ensuring we have the same style etc, and just pass some more stuff into it. For this, the `Callout` component also allows to pass `id` and `titleOnClick` attributes which we need for the Expandable.
1 parent fdb5228 commit 46f6353

File tree

5 files changed

+143
-65
lines changed

5 files changed

+143
-65
lines changed

docs/contributing/pages/components.mdx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,36 @@ Render an expandable section.
9494

9595
<Expandable title="This is a title">This is some content</Expandable>
9696

97+
<Expandable permalink title="With Permalink">This is some content</Expandable>
98+
99+
<Expandable title="Warning" level="warning">This is some warning content</Expandable>
100+
101+
<Expandable title="Success" level="success">This is some success content</Expandable>
102+
97103
```markdown {tabTitle:Example}
98104
<Expandable title="This is a title">
99105
This is some content
100106
</Expandable>
101107
```
102108

109+
```markdown {tabTitle:Permalink}
110+
<Expandable permalink title="With Permalink">
111+
This is some content
112+
</Expandable>
113+
```
114+
115+
```markdown {tabTitle:Warning}
116+
<Expandable level="warning" title="This is a title">
117+
This is some content
118+
</Expandable>
119+
```
120+
121+
```markdown {tabTitle:Success}
122+
<Expandable level="success" title="This is a title">
123+
This is some content
124+
</Expandable>
125+
```
126+
103127
Attributes:
104128

105129
- `title` (string)

docs/platforms/javascript/common/troubleshooting/index.mdx

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -137,14 +137,11 @@ You can work around our CDN bundles being blocked by [using our NPM packages](#u
137137

138138
<PlatformSection supported={["javascript.nextjs"]}>
139139

140-
<Note>
141-
The Sentry Next.js SDK has a separate option to make setting up tunnels very
142-
straight-forward, allowing you to skip the setup below. See
143-
<PlatformLink to="/manual-setup/#configure-tunneling-to-avoid-ad-blockers">
144-
Configure Tunneling to avoid Ad-Blockers
145-
</PlatformLink>
146-
to learn how to set up tunneling on Next.js.
147-
</Note>
140+
The Sentry Next.js SDK has a separate option to make setting up tunnels very
141+
straight-forward, allowing you to skip the setup below. See <PlatformLink to="/manual-setup/#configure-tunneling-to-avoid-ad-blockers">
142+
Configure Tunneling to avoid Ad-Blockers</PlatformLink> to learn how to set up tunneling on Next.js.
143+
144+
If you do not want to configure `tunnelRoute`, you can follow the guide below.
148145

149146
</PlatformSection>
150147

@@ -543,7 +540,7 @@ shamefully-hoist=true
543540
<PlatformSection supported={['javascript.nextjs']}>
544541
<Expandable permalink title="Out of Memory (OOM) errors during build">
545542
The problem here is related to memory consumption during the build process, especially when generating source maps. Here are some potential solutions and workarounds:
546-
543+
547544
- Update your `@sentry/nextjs` package to the latest version.
548545
- Increase Node.js memory limit: You can try increasing the memory limit for Node.js during the build process. Add this to your build command: `NODE_OPTIONS="--max-old-space-size=8192" next build`. This flag will increase the memory available to the node process to 8 GB. We have found that Next.js consumes around 4 GB in most cases. Decrease the size depending on your memory availability.
549546
- Disable source maps entirely: As a last resort, you can disable source map generation completely:

src/components/callout/index.tsx

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {ForwardRefExoticComponent, ReactNode} from 'react';
1+
import {ForwardRefExoticComponent, MouseEventHandler, ReactNode} from 'react';
22

33
// explicitly not usig CSS modules here
44
// because there's some prerendered content that depends on these exact class names
@@ -7,17 +7,70 @@ import './styles.scss';
77
type CalloutProps = {
88
Icon: ForwardRefExoticComponent<any>;
99
children?: ReactNode;
10+
/** If defined, the title of the callout will receive this ID and render a link to this ID. */
11+
id?: string;
1012
level?: 'info' | 'warning' | 'success';
1113
role?: string;
1214
title?: string;
15+
titleOnClick?: MouseEventHandler;
1316
};
1417

15-
export function Callout({title, children, level = 'info', Icon, role}: CalloutProps) {
18+
function Header({
19+
title,
20+
id,
21+
onClick,
22+
}: {
23+
title: string;
24+
id?: string;
25+
onClick?: MouseEventHandler;
26+
}) {
27+
if (!id) {
28+
return (
29+
<h5
30+
className="callout-header"
31+
onClick={onClick}
32+
role={onClick ? 'button' : undefined}
33+
>
34+
{title}
35+
</h5>
36+
);
37+
}
38+
39+
// We want to avoid actually triggering the link
40+
const wrappedOnClick = onClick
41+
? (event: React.MouseEvent) => {
42+
event.preventDefault();
43+
onClick(event);
44+
}
45+
: undefined;
46+
47+
return (
48+
<h5 className="callout-header" id={id}>
49+
<a href={'#' + id} onClick={wrappedOnClick}>
50+
{title}
51+
</a>
52+
</h5>
53+
);
54+
}
55+
56+
export function Callout({
57+
title,
58+
children,
59+
level = 'info',
60+
Icon,
61+
role,
62+
id,
63+
titleOnClick,
64+
}: CalloutProps) {
1665
return (
1766
<div className={`callout ${'callout-' + level}`} role={role}>
18-
<Icon className="callout-icon" />
67+
<Icon
68+
className="callout-icon"
69+
onClick={titleOnClick}
70+
role={titleOnClick ? 'button' : undefined}
71+
/>
1972
<div className="callout-content">
20-
{title && <h5 className="callout-header">{title}</h5>}
73+
{title && <Header title={title} id={id} onClick={titleOnClick} />}
2174
<div className="callout-body content-flush-bottom">{children}</div>
2275
</div>
2376
</div>

src/components/callout/styles.scss

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,29 @@
3131
.callout-header {
3232
font-weight: 500;
3333
color: inherit;
34+
35+
& a {
36+
color: inherit;
37+
}
38+
39+
&[role="button"] {
40+
cursor: pointer;
41+
}
3442
}
3543
}
3644

3745
.callout-icon {
3846
flex: 1em 0 0;
3947
height: 1.75em;
4048
color: var(--callout-highlight-color);
49+
50+
&[role="button"] {
51+
cursor: pointer;
52+
}
53+
}
54+
55+
.callout-content {
56+
min-width: 0;
4157
}
4258

4359
.callout-info {

src/components/expandable.tsx

Lines changed: 40 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,74 @@
11
'use client';
22

33
import {ReactNode, useEffect, useState} from 'react';
4-
import {ArrowDown} from 'react-feather';
5-
import styled from '@emotion/styled';
4+
import {ChevronDownIcon, ChevronRightIcon} from '@radix-ui/react-icons';
5+
6+
import {Callout} from './callout';
67

78
type Props = {
89
children: ReactNode;
910
title: string;
11+
level?: 'info' | 'warning' | 'success';
1012
permalink?: boolean;
1113
};
1214

13-
const Arrow = styled(({...props}) => <ArrowDown {...props} />)<{className: string}>`
14-
user-select: none;
15-
transition: transform 200ms ease-in-out;
16-
stroke-width: 3px;
17-
position: absolute;
18-
right: 0;
19-
top: 4px;
20-
`;
21-
22-
const Details = styled.details`
23-
background: var(--accent-2);
24-
border-color: var(--accent-12);
25-
border-left: 3px solid var(--accent-12);
26-
margin-bottom: 1rem;
27-
padding: 0.5rem 1rem;
28-
h2 {
29-
margin-top: 0;
30-
}
31-
&[open] .expandable-arrow {
32-
transform: rotate(180deg);
33-
}
34-
`;
35-
3615
function slugify(str: string) {
3716
return str
3817
.toLowerCase()
3918
.replace(/ /g, '-')
4019
.replace(/[^a-z0-9-]/g, '');
4120
}
4221

43-
const header = (title: string, permalink?: boolean) =>
44-
permalink ? (
45-
<h2 id={slugify(title)} className="!mb-0">
46-
<a
47-
href={'#' + slugify(title)}
48-
className="w-full !text-[1rem] !font-medium hover:!no-underline !text-[var(--foreground)]"
49-
>
50-
{title}
51-
</a>
52-
</h2>
53-
) : (
54-
title
55-
);
22+
export function Expandable({title, level, children, permalink}: Props) {
23+
const id = permalink ? slugify(title) : undefined;
5624

57-
export function Expandable({title, children, permalink}: Props) {
58-
const [isExpanded, setIsExpanded] = useState(false);
25+
const defaultIsExpanded = id && window.location.hash === `#${id}`;
26+
const [isExpanded, setIsExpanded] = useState(defaultIsExpanded);
5927

28+
// Ensure we scroll to the element if the URL hash matches
6029
useEffect(() => {
61-
// if the url hash matches the title, expand the section
62-
if (permalink && window.location.hash === `#${slugify(title)}`) {
63-
setIsExpanded(true);
30+
if (id && window.location.hash === `#${id}`) {
31+
document.querySelector(`#${id}`)?.scrollIntoView();
6432
}
33+
34+
// When the hash changes (e.g. when the back/forward browser buttons are used),
35+
// we want to ensure to jump to the correct section
6536
const onHashChange = () => {
66-
if (window.location.hash === `#${slugify(title)}`) {
37+
if (window.location.hash === `#${id}`) {
6738
setIsExpanded(true);
39+
document.querySelector(`#${id}`)?.scrollIntoView();
6840
}
6941
};
7042
// listen for hash changes and expand the section if the hash matches the title
7143
window.addEventListener('hashchange', onHashChange);
7244
return () => {
7345
window.removeEventListener('hashchange', onHashChange);
7446
};
75-
}, [title, permalink]);
47+
}, [id]);
48+
49+
function toggleIsExpanded() {
50+
const newVal = !isExpanded;
51+
52+
if (id) {
53+
if (newVal) {
54+
window.history.pushState({}, '', `#${id}`);
55+
} else {
56+
window.history.pushState({}, '', '#');
57+
}
58+
}
59+
60+
setIsExpanded(newVal);
61+
}
7662

7763
return (
78-
<Details open={isExpanded}>
79-
<summary className="m-0 font-medium cursor-pointer relative pr-8 select-none appearance-none list-none">
80-
{header(title, permalink)}
81-
<Arrow className="expandable-arrow" />
82-
</summary>
83-
{children}
84-
</Details>
64+
<Callout
65+
level={level}
66+
title={title}
67+
Icon={isExpanded ? ChevronDownIcon : ChevronRightIcon}
68+
id={id}
69+
titleOnClick={toggleIsExpanded}
70+
>
71+
{isExpanded ? children : undefined}
72+
</Callout>
8573
);
8674
}

0 commit comments

Comments
 (0)