Browse Source

ref(getting-started-docs): Migrate remix doc to sentry main repo (#52441)

Priscila Oliveira 1 year ago
parent
commit
77e2ad5acb

+ 9 - 9
static/app/components/onboarding/gettingStartedDoc/layout.tsx

@@ -5,7 +5,11 @@ import HookOrDefault from 'sentry/components/hookOrDefault';
 import ExternalLink from 'sentry/components/links/externalLink';
 import List from 'sentry/components/list';
 import ListItem from 'sentry/components/list/listItem';
-import {Step, StepType} from 'sentry/components/onboarding/gettingStartedDoc/step';
+import {
+  Step,
+  StepProps,
+  StepType,
+} from 'sentry/components/onboarding/gettingStartedDoc/step';
 import {
   ProductSelection,
   ProductSolution,
@@ -27,20 +31,17 @@ type NextStep = {
 
 type CurrentSteps = [
   {
-    code: string;
-    description: React.ReactNode;
+    configurations: StepProps['configurations'];
     language: string;
     type: StepType.INSTALL;
   },
   {
-    code: string;
-    description: React.ReactNode;
+    configurations: StepProps['configurations'];
     language: string;
     type: StepType.CONFIGURE;
   },
   {
-    code: string;
-    description: React.ReactNode;
+    configurations: StepProps['configurations'];
     language: string;
     type: StepType.VERIFY;
   }
@@ -74,8 +75,7 @@ export function Layout({steps, nextSteps, newOrg}: LayoutProps) {
           <Step
             key={step.type}
             type={step.type}
-            description={step.description}
-            code={step.code}
+            configurations={step.configurations}
             language={step.language}
           />
         ))}

+ 1 - 1
static/app/components/onboarding/gettingStartedDoc/sdkDocumentation.tsx

@@ -6,7 +6,7 @@ import {Organization, PlatformIntegration, Project, ProjectKey} from 'sentry/typ
 import {useApiQuery} from 'sentry/utils/queryClient';
 
 // Documents already migrated from sentry-docs to main sentry repository
-export const migratedDocs = ['javascript-react'];
+export const migratedDocs = ['javascript-react', 'javascript-remix'];
 
 type SdkDocumentationProps = {
   activeProductSelection: ProductSolution[];

+ 25 - 8
static/app/components/onboarding/gettingStartedDoc/step.tsx

@@ -1,3 +1,4 @@
+import styled from '@emotion/styled';
 import beautify from 'js-beautify';
 
 import {CodeSnippet} from 'sentry/components/codeSnippet';
@@ -15,7 +16,7 @@ export const StepTitle = {
   [StepType.VERIFY]: t('Verify'),
 };
 
-export type StepProps = {
+type Configuration = {
   /**
    * The code snippet to display
    */
@@ -24,6 +25,10 @@ export type StepProps = {
    * A brief description of the step
    */
   description: React.ReactNode;
+};
+
+export type StepProps = {
+  configurations: Configuration[];
   /**
    * The language of the selected platform (python, javascript, etc)
    */
@@ -34,16 +39,28 @@ export type StepProps = {
   type: StepType;
 };
 
-export function Step({type, description, language, code}: StepProps) {
+export function Step({type, configurations, language}: StepProps) {
   return (
     <div>
       <h4>{StepTitle[type]}</h4>
-      <p>{description}</p>
-      <CodeSnippet dark language={language}>
-        {language === 'javascript'
-          ? beautify.js(code, {indent_size: 2, e4x: true})
-          : beautify.html(code, {indent_size: 2})}
-      </CodeSnippet>
+      <Configurations>
+        {configurations.map((configuration, index) => (
+          <div key={index}>
+            <p>{configuration.description}</p>
+            <CodeSnippet dark language={language}>
+              {language === 'javascript'
+                ? beautify.js(configuration.code, {indent_size: 2, e4x: true})
+                : beautify.html(configuration.code, {indent_size: 2})}
+            </CodeSnippet>
+          </div>
+        ))}
+      </Configurations>
     </div>
   );
 }
+
+const Configurations = styled('div')`
+  display: flex;
+  flex-direction: column;
+  gap: 1rem;
+`;

+ 9 - 1
static/app/data/platforms.tsx

@@ -1,6 +1,7 @@
 import integrationDocsPlatforms from 'integration-docs-platforms';
 import sortBy from 'lodash/sortBy';
 
+import {migratedDocs} from 'sentry/components/onboarding/gettingStartedDoc/sdkDocumentation';
 import {t} from 'sentry/locale';
 import {PlatformIntegration} from 'sentry/types';
 
@@ -12,13 +13,20 @@ const migratedJavascriptPlatforms = {
   integrations: [
     ...(integrationDocsPlatforms.platforms
       .filter(platform => platform.id === 'javascript')?.[0]
-      ?.integrations?.filter(integration => integration.id !== 'javascript-react') ?? []),
+      ?.integrations?.filter(integration => !migratedDocs.includes(integration.id)) ??
+      []),
     {
       id: 'javascript-react',
       link: 'https://docs.sentry.io/platforms/javascript/guides/react/',
       name: 'React',
       type: 'framework',
     },
+    {
+      id: 'javascript-remix',
+      link: 'https://docs.sentry.io/platforms/javascript/guides/remix/',
+      name: 'Remix',
+      type: 'framework',
+    },
   ],
 };
 

+ 46 - 30
static/app/gettingStartedDocs/javascript/react.tsx

@@ -26,46 +26,62 @@ const performanceOtherConfig = `
 tracesSampleRate: 1.0, // Capture 100% of the transactions, reduce in production!
 `;
 
-export const steps = (sentryInitContent?: string): LayoutProps['steps'] => [
+export const steps = ({
+  sentryInitContent,
+}: {
+  sentryInitContent?: string;
+} = {}): LayoutProps['steps'] => [
   {
     language: 'bash',
     type: StepType.INSTALL,
-    description: t(
-      'Sentry captures data by using an SDK within your application’s runtime.'
-    ),
-    code: `
-    # Using yarn
-    yarn add @sentry/react
-
-    # Using npm
-    npm install --save @sentry/react
-    `,
+    configurations: [
+      {
+        description: t(
+          'Sentry captures data by using an SDK within your application’s runtime.'
+        ),
+        code: `
+        # Using yarn
+        yarn add @sentry/react
+
+        # Using npm
+        npm install --save @sentry/react
+        `,
+      },
+    ],
   },
   {
     language: 'javascript',
     type: StepType.CONFIGURE,
-    description: t(
-      "Initialize Sentry as early as possible in your application's lifecycle."
-    ),
-    code: `
-    Sentry.init({
-      ${sentryInitContent}
-    });
-
-    const container = document.getElementById(“app”);
-    const root = createRoot(container);
-    root.render(<App />)
-    `,
+    configurations: [
+      {
+        description: t(
+          "Initialize Sentry as early as possible in your application's lifecycle."
+        ),
+        code: `
+        Sentry.init({
+          ${sentryInitContent}
+        });
+
+        const container = document.getElementById(“app”);
+        const root = createRoot(container);
+        root.render(<App />)
+        `,
+      },
+    ],
   },
   {
     language: 'javascript',
     type: StepType.VERIFY,
-    description: t(
-      "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected."
-    ),
-    code: `
-    return <button onClick={() => methodDoesNotExist()}>Break the world</button>;
-    `,
+    configurations: [
+      {
+        description: t(
+          "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected."
+        ),
+        code: `
+        return <button onClick={() => methodDoesNotExist()}>Break the world</button>;
+        `,
+      },
+    ],
   },
 ];
 
@@ -152,7 +168,7 @@ export default function GettingStartedWithReact({
 
   return (
     <Layout
-      steps={steps(sentryInitContent.join('\n'))}
+      steps={steps({sentryInitContent: sentryInitContent.join('\n')})}
       nextSteps={nextStepDocs}
       newOrg={newOrg}
     />

+ 44 - 0
static/app/gettingStartedDocs/javascript/remix.spec.tsx

@@ -0,0 +1,44 @@
+import {render, screen} from 'sentry-test/reactTestingLibrary';
+
+import {StepTitle} from 'sentry/components/onboarding/gettingStartedDoc/step';
+import {ProductSolution} from 'sentry/components/onboarding/productSelection';
+
+import GettingStartedWithRemix, {nextSteps, steps} from './remix';
+
+describe('GettingStartedWithRemix', function () {
+  it('all products are selected', function () {
+    const {container} = render(
+      <GettingStartedWithRemix
+        dsn="test-dsn"
+        activeProductSelection={[
+          ProductSolution.PERFORMANCE_MONITORING,
+          ProductSolution.SESSION_REPLAY,
+        ]}
+      />
+    );
+
+    // Steps
+    for (const step of steps()) {
+      expect(
+        screen.getByRole('heading', {name: StepTitle[step.type]})
+      ).toBeInTheDocument();
+    }
+
+    // Next Steps
+    const filteredNextStepsLinks = nextSteps.filter(
+      nextStep =>
+        ![
+          ProductSolution.PERFORMANCE_MONITORING,
+          ProductSolution.SESSION_REPLAY,
+        ].includes(nextStep.id as ProductSolution)
+    );
+
+    for (const filteredNextStepsLink of filteredNextStepsLinks) {
+      expect(
+        screen.getByRole('link', {name: filteredNextStepsLink.name})
+      ).toBeInTheDocument();
+    }
+
+    expect(container).toSnapshot();
+  });
+});

+ 244 - 0
static/app/gettingStartedDocs/javascript/remix.tsx

@@ -0,0 +1,244 @@
+import ExternalLink from 'sentry/components/links/externalLink';
+import {Layout, LayoutProps} from 'sentry/components/onboarding/gettingStartedDoc/layout';
+import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/step';
+import {ProductSolution} from 'sentry/components/onboarding/productSelection';
+import {t, tct} from 'sentry/locale';
+
+// Configuration Start
+const replayIntegration = `
+new Sentry.Replay(),
+`;
+
+const replayOtherConfig = `
+// Session Replay
+replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
+replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
+`;
+
+const performanceIntegration = `
+new Sentry.BrowserTracing({
+  // Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled
+  tracePropagationTargets: ["localhost", "https:yourserver.io/api/"],
+  routingInstrumentation: Sentry.remixRouterInstrumentation(
+    useEffect,
+    useLocation,
+    useMatches
+  ),
+}),
+`;
+
+const performanceOtherConfig = `
+// Performance Monitoring
+tracesSampleRate: 1.0, // Capture 100% of the transactions, reduce in production!
+`;
+
+const prismaConfig = `
+new Sentry.Integrations.Prisma({ client: prisma })
+`;
+
+export const steps = ({
+  sentryInitContent,
+  sentryInitContentServer,
+}: {
+  sentryInitContent?: string;
+  sentryInitContentServer?: string[];
+} = {}): LayoutProps['steps'] => [
+  {
+    language: 'bash',
+    type: StepType.INSTALL,
+    configurations: [
+      {
+        description: t(
+          'Sentry captures data by using an SDK within your application’s runtime.'
+        ),
+        code: `
+        # Using yarn
+        yarn add @sentry/remix
+
+        # Using npm
+        npm install --save @sentry/remix
+        `,
+      },
+    ],
+  },
+  {
+    language: 'javascript',
+    type: StepType.CONFIGURE,
+    configurations: [
+      {
+        description: t(
+          'Import and initialize Sentry in your Remix entry points for both the client and server:'
+        ),
+        code: `
+        import { useLocation, useMatches } from "@remix-run/react";
+        import * as Sentry from "@sentry/remix";
+        import { useEffect } from "react";
+
+        Sentry.init({
+          ${sentryInitContent}
+        });
+        `,
+      },
+      {
+        description: tct(
+          `Initialize Sentry in your entry point for the server to capture exceptions and get performance metrics for your [action] and [loader] functions. You can also initialize Sentry's database integrations, such as Prisma, to get spans for your database calls:`,
+          {
+            action: (
+              <ExternalLink href="https://remix.run/docs/en/v1/api/conventions#action" />
+            ),
+            loader: (
+              <ExternalLink href="https://remix.run/docs/en/1.18.1/api/conventions#loader" />
+            ),
+          }
+        ),
+        code: `
+
+        ${
+          (sentryInitContentServer ?? []).length > 1
+            ? `import { prisma } from "~/db.server";`
+            : ''
+        }
+
+        import * as Sentry from "@sentry/remix";
+
+        Sentry.init({
+          ${sentryInitContentServer?.join('\n')}
+        });
+        `,
+      },
+      {
+        description: t(
+          'Lastly, wrap your Remix root with "withSentry" to catch React component errors and to get parameterized router transactions:'
+        ),
+        code: `
+import {
+  Links,
+  LiveReload,
+  Meta,
+  Outlet,
+  Scripts,
+  ScrollRestoration,
+} from "@remix-run/react";
+
+import { withSentry } from "@sentry/remix";
+
+function App() {
+  return (
+    <html>
+      <head>
+        <Meta />
+        <Links />
+      </head>
+      <body>
+        <Outlet />
+        <ScrollRestoration />
+        <Scripts />
+        <LiveReload />
+      </body>
+    </html>
+  );
+}
+
+export default withSentry(App);
+        `,
+      },
+    ],
+  },
+  {
+    language: 'javascript',
+    type: StepType.VERIFY,
+    configurations: [
+      {
+        description: t(
+          "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected."
+        ),
+        code: `
+        return <button onClick={() => methodDoesNotExist()}>Break the world</button>;
+        `,
+      },
+    ],
+  },
+];
+
+export const nextSteps = [
+  {
+    id: 'source-maps',
+    name: t('Source Maps'),
+    description: t('Learn how to enable readable stack traces in your Sentry errors.'),
+    link: 'https://docs.sentry.io/platforms/javascript/guides/remix/sourcemaps/',
+  },
+  {
+    id: 'performance-monitoring',
+    name: t('Performance Monitoring'),
+    description: t(
+      'Track down transactions to connect the dots between 10-second page loads and poor-performing API calls or slow database queries.'
+    ),
+    link: 'https://docs.sentry.io/platforms/javascript/guides/remix/performance/',
+  },
+  {
+    id: 'session-replay',
+    name: t('Session Replay'),
+    description: t(
+      'Get to the root cause of an error or latency issue faster by seeing all the technical details related to that issue in one visual replay on your web application.'
+    ),
+    link: 'https://docs.sentry.io/platforms/javascript/guides/remix/session-replay/',
+  },
+];
+// Configuration End
+
+type Props = {
+  activeProductSelection: ProductSolution[];
+  dsn: string;
+  newOrg?: boolean;
+};
+
+export default function GettingStartedWithRemix({
+  dsn,
+  activeProductSelection,
+  newOrg,
+}: Props) {
+  const integrations: string[] = [];
+  const otherConfigs: string[] = [];
+
+  let nextStepDocs = [...nextSteps];
+  const sentryInitContentServer: string[] = [`dsn: "${dsn}",`];
+
+  if (activeProductSelection.includes(ProductSolution.PERFORMANCE_MONITORING)) {
+    integrations.push(performanceIntegration.trim());
+    otherConfigs.push(performanceOtherConfig.trim());
+    sentryInitContentServer.push(prismaConfig.trim());
+    sentryInitContentServer.push(performanceOtherConfig.trim());
+    nextStepDocs = nextStepDocs.filter(
+      step => step.id !== ProductSolution.PERFORMANCE_MONITORING
+    );
+  }
+
+  if (activeProductSelection.includes(ProductSolution.SESSION_REPLAY)) {
+    integrations.push(replayIntegration.trim());
+    otherConfigs.push(replayOtherConfig.trim());
+    nextStepDocs = nextStepDocs.filter(
+      step => step.id !== ProductSolution.SESSION_REPLAY
+    );
+  }
+
+  let sentryInitContent: string[] = [`dsn: "${dsn}",`];
+
+  if (integrations.length > 0) {
+    sentryInitContent = sentryInitContent.concat('integrations: [', integrations, '],');
+  }
+
+  if (otherConfigs.length > 0) {
+    sentryInitContent = sentryInitContent.concat(otherConfigs);
+  }
+
+  return (
+    <Layout
+      steps={steps({
+        sentryInitContent: sentryInitContent.join('\n'),
+        sentryInitContentServer,
+      })}
+      nextSteps={nextStepDocs}
+      newOrg={newOrg}
+    />
+  );
+}