Browse Source

feat(feedback): Add github feedback components (#57805)

Add pre-styled components to  ask users for feedback on GitHub.
Add `GithubFeedbackButton`
Add `GithubFeedbackTooltip`

- Closes https://github.com/getsentry/sentry/issues/57778
ArthurKnaus 1 year ago
parent
commit
e971b41c15

+ 46 - 0
static/app/components/githubFeedbackButton.spec.tsx

@@ -0,0 +1,46 @@
+import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
+
+import {GithubFeedbackButton} from 'sentry/components/githubFeedbackButton';
+
+describe('GithubFeedbackButton', function () {
+  it('renders', async function () {
+    render(<GithubFeedbackButton href="https://example.com/my-test-url" />);
+
+    const anchor = screen.getByRole<HTMLAnchorElement>('button', {
+      name: 'Give Feedback',
+    });
+
+    // Renders a link with given href
+    expect(anchor).toBeInTheDocument();
+    expect(anchor.tagName).toBe('A');
+    expect(anchor.href).toBe('https://example.com/my-test-url');
+
+    // Renders tooltip
+    await userEvent.hover(anchor);
+    expect(await screen.findByText('Give us feedback on GitHub')).toBeInTheDocument();
+  });
+
+  it('renders with custom label', function () {
+    render(
+      <GithubFeedbackButton href="https://example.com/my-test-url" label="My label" />
+    );
+
+    expect(
+      screen.getByRole<HTMLAnchorElement>('button', {
+        name: 'My label',
+      })
+    ).toBeInTheDocument();
+  });
+
+  it('renders without label', function () {
+    render(<GithubFeedbackButton href="https://example.com/my-test-url" label={null} />);
+
+    // Renders button without text content
+    const button = screen.getByRole<HTMLAnchorElement>('button', {
+      name: 'Give Feedback',
+    });
+    expect(button).toBeInTheDocument();
+    expect(button).toHaveTextContent('');
+    expect(button).toHaveAttribute('aria-label', 'Give Feedback');
+  });
+});

+ 29 - 0
static/app/components/githubFeedbackButton.tsx

@@ -0,0 +1,29 @@
+import {LinkButton, LinkButtonProps} from 'sentry/components/button';
+import {Tooltip} from 'sentry/components/tooltip';
+import {IconGithub} from 'sentry/icons';
+import {t} from 'sentry/locale';
+
+type GithubFeedbackButtonProps = Omit<LinkButtonProps, 'children' | 'aria-label'> & {
+  href: string;
+  ['aria-label']?: string;
+  label?: string | null;
+};
+
+export function GithubFeedbackButton({
+  label = t('Give Feedback'),
+  ...props
+}: GithubFeedbackButtonProps) {
+  return (
+    <Tooltip title={t('Give us feedback on GitHub')}>
+      <LinkButton
+        aria-label={label ?? t('Give Feedback')}
+        size="sm"
+        external
+        icon={<IconGithub />}
+        {...props}
+      >
+        {label}
+      </LinkButton>
+    </Tooltip>
+  );
+}

+ 27 - 0
static/app/components/githubFeedbackTooltip.spec.tsx

@@ -0,0 +1,27 @@
+import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
+
+import {GithubFeedbackTooltip} from 'sentry/components/githubFeedbackTooltip';
+
+describe('GithubFeedbackTooltip', function () {
+  it('renders', async function () {
+    render(
+      <GithubFeedbackTooltip
+        title="My custom title text"
+        href="https://example.com/my-test-url"
+      >
+        <span data-test-id="anchor" />
+      </GithubFeedbackTooltip>
+    );
+
+    const anchor = screen.getByTestId('anchor');
+    await userEvent.hover(anchor);
+
+    // Renders custom title text
+    expect(await screen.findByText('My custom title text')).toBeInTheDocument();
+
+    // Renders link with given href
+    const link = screen.getByRole<HTMLAnchorElement>('link', {name: 'GitHub'});
+    expect(link).toBeInTheDocument();
+    expect(link.href).toBe('https://example.com/my-test-url');
+  });
+});

+ 56 - 0
static/app/components/githubFeedbackTooltip.tsx

@@ -0,0 +1,56 @@
+import {Fragment} from 'react';
+import styled from '@emotion/styled';
+
+import ExternalLink from 'sentry/components/links/externalLink';
+import {Tooltip, TooltipProps} from 'sentry/components/tooltip';
+import {IconGithub} from 'sentry/icons';
+import {tct} from 'sentry/locale';
+import {space} from 'sentry/styles/space';
+
+type GithubFeedbackTooltipProps = TooltipProps & {
+  href: string;
+  title?: React.ReactNode;
+};
+
+export function GithubFeedbackTooltip({
+  href,
+  title,
+  ...props
+}: GithubFeedbackTooltipProps) {
+  return (
+    <Tooltip
+      isHoverable
+      title={
+        <Fragment>
+          {title}
+          <FeedbackLine hasTitle={!!title}>
+            {tct('Give us feedback on [githubLink]', {
+              githubLink: (
+                <GithubLink href={href}>
+                  GitHub <IconGithub size="xs" />
+                </GithubLink>
+              ),
+            })}
+          </FeedbackLine>
+        </Fragment>
+      }
+      {...props}
+    />
+  );
+}
+
+const FeedbackLine = styled('div')<{hasTitle: boolean}>`
+  ${p => p.hasTitle && `padding-top: ${space(1)};`}
+`;
+
+const GithubLink = styled(ExternalLink)`
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  gap: ${space(0.5)};
+  line-height: 0px;
+
+  & > svg {
+    margin-top: -1px;
+  }
+`;