Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add <Expandable /> permalinks #10622

Merged
merged 5 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/contributing/pages/components.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,25 @@ An Alert component with no level setting will render as a Note component.

See also the [Note component](#note).

## Expandable

Render an expandable section.

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

```markdown {tabTitle:Example}
<Expandable title="This is a title">
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @lizokm

This is some content
</Expandable>
```

Attributes:

- `title` (string)
- `permalink` (boolean) - optional: the title as a link and show it in the TOC.

## Code Blocks

Consecutive code blocks will be automatically collapsed into a tabulated container. This behavior is generally useful if you want to define an example in multiple languages:
Expand Down
22 changes: 11 additions & 11 deletions docs/platforms/javascript/common/session-replay/troubleshooting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,20 @@ excerpt: ""
description: "Troubleshooting Session Replay-specific Issues"
---

<Expandable title="My `canvas` elements aren't getting captured">
<Expandable title="My `canvas` elements aren't getting captured" permalink>

Canvas is supported in SDK versions >= `7.98.0`. Please see the <PlatformLink to="/session-replay/#canvas-recording">canvas setup documention</PlatformLink> to get started with canvas recordings.

If you are on a supported SDK version and your `canvas` elements still aren't getting captured, check if you have images or videos loaded from foreign origins inside your `canvas`. Any data loaded from an origin without CORS approval is not considered secure and will throw a `SecurityError` when trying to use the replay canvas integration. To fix this issue, set `crossorigin="anonymous"` to your images or videos. This will allow images that are loaded from foreign origins to be used in `canvas` as if they have been loaded from the current origin. See the [CORS documention](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image#security_and_tainted_canvases) for more information about cross-origin access.

</Expandable>

<Expandable title="Replay is slowing down my `canvas`">
<Expandable title="Replay is slowing down my `canvas`" permalink>

The integration needs to enable `preserveDrawingBuffer` to export images from 3D and WebGL canvases. This can negatively affect canvas performance. If your canvas application is impacted by enabling `preserveDrawingBuffer`, you'll need to <PlatformLink to="/session-replay/#canvas-recording-performance">enable manual snapshotting</PlatformLink> and call a `snapshot()` method inside of your re-paint loop.
</Expandable>

<Expandable title="My custom CSS/images/fonts/media aren't appearing when I view the Replay">
<Expandable title="My custom CSS/images/fonts/media aren't appearing when I view the Replay" permalink>

The replay 'video' is actually a video-like reproduction of the HTML on your website. This means that all the external resources your site uses (CSS/Images/Fonts), will be rendered by the corresponding &lt;style&gt;, &lt;img&gt; and &lt;video&gt; tags on your site. Add `sentry.io` to your CORS policy so the iframe hosted on sentry.io can fetch and display these resources.

Expand All @@ -46,7 +46,7 @@ Due to [browser limitations](https://developer.mozilla.org/en-US/docs/Web/SVG/El
</Alert>
</Expandable>

<Expandable title="Why can't I see the full HTTP request body or all the headers?">
<Expandable title="Why can't I see the full HTTP request body or all the headers?" permalink>

By default, Replay will capture basic information about all outgoing fetch and XHR requests in your application. This includes the URL, request and response body size, method, and status code.
The intention is to limit the chance of collecting private data. You can <PlatformLink to="/session-replay/configuration/">configure the SDK to capture bodies and additional headers</PlatformLink>.
Expand All @@ -57,7 +57,7 @@ More details about this feature can be found in the <PlatformLink to="/session-r

</Expandable>

<Expandable title="The SDK is slowing down my website.">
<Expandable title="The SDK is slowing down my website." permalink>

If you're experiencing slowdowns on your website, first make sure you're on the latest version of our SDK, which will have the most up-to-date bug fixes and performance improvements.

Expand All @@ -69,36 +69,36 @@ If you're having any problems with the latest SDK version, we want to hear about

</Expandable>

<Expandable title="Using Session Replay increases the bundle size of my application.">
<Expandable title="Using Session Replay increases the bundle size of my application." permalink>

Because of the complexity of the browser environment, there's a significant amount of code necessary in order for Session Replay to work. And while enabling Session Replay will add about 50 kb (gzipped) to your application bundle, we believe the benefits will outweigh the cost.

We're always working on ways to reduce the bundle size. Additionally, there are steps you can take in order to reduce the size of Session Replay based on your specific use case. See [Tree Shaking](../../configuration/tree-shaking/) for more information.

</Expandable>

<Expandable title='I see the message: "A large number of mutations was detected (N). This can slow down the Replay SDK and impact your customers."'>
<Expandable title='I see the message: "A large number of mutations was detected (N). This can slow down the Replay SDK and impact your customers."' permalink>

The Sentry SDK attempts to minimize potential [performance overhead](/product/explore/session-replay/performance-overhead/#how-is-session-replay-optimized) in a few different ways. For example, by keeping track of the number of DOM mutations that are happening then disabling recording when a large number of changes are detected. Many simultaneous mutations can slow down a web page whether Session Replay is installed or not, but when a large number of mutations happen, the Replay client can incur an additional slowdown because it records each change.

If you're seeing the "A large number of mutations was detected" message while watching a replay, it means that your page could be optimized. For example, a dropdown list with thousands of entries could be refactored so that rows are virtualized where only the visible rows are rendered in the DOM. Another potential solution is to paginate the results fetch more data as the user scrolls through it. The SDK has a <PlatformLink to="/session-replay/configuration/#mutation-limits">configuration</PlatformLink> that allows you to configure the limits before recording stops.
</Expandable>

<Expandable title="Text in the iframe isn't masked">
<Expandable title="Text in the iframe isn't masked" permalink>

Our masking logic doesn't run on iframe content that's provided using the `srcdoc` attribute, rather than loaded in via `src`.

To hide this content, block the iframe, as described in our Session Replay <PlatformLink to="/session-replay/privacy/#blocking">Privacy</PlatformLink> docs.

</Expandable>

<Expandable title="Replay on my browser extension doesn't work">
<Expandable title="Replay on my browser extension doesn't work" permalink>

This is not a supported use-case. The replay package is built to work on a website and not as an externally-loaded script via browser extension or other mechanism. In fact, Sentry's Session Replay product can help developers find out when a third-party Chrome extension causes otherwise hard to debug or reproduce issues with their website.

</Expandable>

<Expandable title="Response data for Apollo GraphQL Client network requests is not captured">
<Expandable title="Response data for Apollo GraphQL Client network requests is not captured" permalink>

Apollo Client sends an abort signal via `AbortController` whenever a query completes, to clean up and cancel all in-flight queries.
When this happens, the Replay can't capture the response body because the request is handled as aborted before Replay can access the response.
Expand All @@ -120,6 +120,6 @@ With this configuration, Replay is able to capture response bodies from Apollo C

</Expandable>

<Expandable title="`console` calls are triggering replays to record">
<Expandable title="`console` calls are triggering replays to record" permalink>
Using the [`captureConsoleIntegration`](https://docs.sentry.io/platforms/javascript/configuration/integrations/captureconsole/) can cause replays to record as if you have triggered an exception. You can use [`beforeErrorSampling`](https://docs.sentry.io/platforms/javascript/guides/sveltekit/session-replay/understanding-sessions/#ignore-certain-errors-for-error-sampling) to avoid this behavior.
</Expandable>
48 changes: 44 additions & 4 deletions src/components/expandable.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
'use client';

import {ReactNode, useState} from 'react';
import {ReactNode, useEffect, useState} from 'react';
import {ArrowDown} from 'react-feather';
import styled from '@emotion/styled';

type Props = {
children: ReactNode;
title: string;
permalink?: boolean;
};

type ExpandedProps = {
Expand Down Expand Up @@ -37,17 +38,56 @@ const ExpandableWrapper = styled.div`
padding: 0.5rem 1rem;
`;

export function Expandable({title, children}: Props) {
function slugify(str: string) {
return str
.toLowerCase()
.replace(/ /g, '-')
.replace(/[^a-z0-9-]/g, '');
}

const header = (title: string, permalink?: boolean) =>
permalink ? (
<h2 id={slugify(title)} className="!mb-0">
<a
href={'#' + slugify(title)}
className="!text-[1rem] !font-medium hover:!no-underline !text-darkPurple"
>
{title}
</a>
</h2>
) : (
title
);

export function Expandable({title, children, permalink}: Props) {
const [isExpanded, setIsExpanded] = useState(false);

useEffect(() => {
// if the url hash matches the title, expand the section
if (permalink && window.location.hash === `#${slugify(title)}`) {
setIsExpanded(true);
}
const onHashChange = () => {
if (window.location.hash === `#${slugify(title)}`) {
setIsExpanded(true);
}
};
// listen for hash changes and expand the section if the hash matches the title
window.addEventListener('hashchange', onHashChange);
return () => {
window.removeEventListener('hashchange', onHashChange);
};
}, []);

return (
<ExpandableWrapper>
<p
className="m-0 font-medium cursor-pointer relative pr-8"
className="m-0 font-medium cursor-pointer relative pr-8 select-none"
onClick={() => {
setIsExpanded(!isExpanded);
}}
>
{title}
{header(title, permalink)}
<ExpandedIndicator isExpanded={isExpanded} />
</p>
<ExpandableBody isExpanded={isExpanded}>{children}</ExpandableBody>
Expand Down
Loading