Browse Source

feat(releases): Empty state releases landing page (#9937)

Dena Mwangi 6 years ago
parent
commit
7c23bb8021

+ 1 - 0
package.json

@@ -79,6 +79,7 @@
     "react-virtualized": "^9.20.1",
     "reflux": "0.4.1",
     "scroll-to-element": "^2.0.0",
+    "sentry-dreamy-components": "getsentry/dreamy-components",
     "sprintf-js": "1.0.3",
     "style-loader": "0.12.4",
     "svg-sprite-loader": "^3.4.1",

+ 0 - 2
src/sentry/static/sentry/app/views/projectReleases/index.jsx

@@ -18,7 +18,6 @@ import {Panel, PanelBody, PanelHeader} from 'app/components/panels';
 import EmptyStateWarning from 'app/components/emptyStateWarning';
 
 import ReleaseList from 'app/views/projectReleases/releaseList';
-
 import withEnvironmentInQueryString from 'app/utils/withEnvironmentInQueryString';
 
 const DEFAULT_QUERY = '';
@@ -171,7 +170,6 @@ const ProjectReleases = createReactClass({
           env: environment.displayName,
         })
       : t("There don't seem to be any releases yet.");
-
     return (
       <EmptyStateWarning>
         <p>{message}</p>

+ 100 - 0
src/sentry/static/sentry/app/views/projectReleases/releaseLanding.jsx

@@ -0,0 +1,100 @@
+import React from 'react';
+import {t} from 'app/locale';
+import styled from 'react-emotion';
+
+import {
+  BashCard,
+  Issues,
+  SuggestedAssignees,
+  Emails,
+  Contributors,
+} from 'sentry-dreamy-components';
+
+import ReleaseLandingCard from 'app/views/projectReleases/releaseLandingCard';
+import withApi from 'app/utils/withApi';
+
+const StyledSuggestedAssignees = styled(SuggestedAssignees)`
+  width: 150px;
+  height: 150px;
+  padding-left: 250px;
+`;
+
+const cards = [
+  {
+    title: t("You Haven't Set Up Releases!"),
+    message: t(
+      'Releases provide additional context, with rich commits, so you know which errors were addressed and which were introduced for the first time'
+    ),
+    component: Contributors,
+  },
+  {
+    title: t('Suspect Commits'),
+    message: t(
+      'Sentry suggests which commit caused an issue and who is likely responsible so you can triage'
+    ),
+    component: StyledSuggestedAssignees,
+  },
+  {
+    title: t('Release Stats'),
+    message: t(
+      'Set the commits in each release, and which issues were introduced or fixed in the release.'
+    ),
+    component: Issues,
+  },
+  {
+    title: t('Easy Resolution'),
+    message: t(
+      'Automatically resolve issues by including the issue number in your commit message.'
+    ),
+    component: BashCard,
+  },
+  {
+    title: t('Deploy Emails'),
+    message: t('Receive email notifications when your code gets deployed.'),
+    component: Emails,
+  },
+];
+
+const ReleaseLanding = withApi(
+  class ReleaseLanding extends React.Component {
+    constructor(props) {
+      super(props);
+      this.state = {
+        stepId: 0,
+      };
+    }
+
+    handleClick = () => {
+      let {stepId} = this.state;
+
+      if (stepId >= cards.length - 1) return;
+      this.setState(state => ({
+        stepId: state.stepId + 1,
+      }));
+    };
+
+    getCard = stepId => {
+      return cards[stepId];
+    };
+
+    render() {
+      let {stepId} = this.state;
+      let card = this.getCard(stepId);
+
+      return (
+        <div className="container">
+          <div className="row">
+            <ReleaseLandingCard
+              onClick={this.handleClick}
+              card={card}
+              step={stepId}
+              cardsLength={cards.length}
+            />
+          </div>
+        </div>
+      );
+    }
+  }
+);
+
+export default ReleaseLanding;

+ 71 - 0
src/sentry/static/sentry/app/views/projectReleases/releaseLandingCard.jsx

@@ -0,0 +1,71 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import styled from 'react-emotion';
+import {Box} from 'grid-emotion';
+import {t} from 'app/locale';
+
+import Button from 'app/components/button';
+
+class ReleaseLandingCard extends React.Component {
+  static propTypes = {
+    card: PropTypes.object.isRequired,
+    cardsLength: PropTypes.number.isRequired,
+    step: PropTypes.number.isRequired,
+    onClick: PropTypes.func.isRequired,
+  };
+
+  getMessage = () => {
+    let {cardsLength, step} = this.props;
+    if (step == 0) {
+      return t('Tell Me More');
+    } else if (step < cardsLength - 1) {
+      return t('Next');
+    } else {
+      return t('See Docs for Setup');
+    }
+  };
+
+  render() {
+    let {card, cardsLength, step} = this.props;
+    let CardComponent = card.component;
+    let finalStep = step === cardsLength - 1;
+    return (
+      <div className="row">
+        <div className="col-md-6">
+          <StyledBox>
+            <CardComponent />
+          </StyledBox>
+        </div>
+
+        <div className="col-md-6">
+          <StyledBox>
+            <h3>{card.title}</h3>
+            <p>{card.message}</p>
+            {finalStep ? (
+              <StyledButton
+                href={'https://docs.sentry.io/learn/releases/'}
+                onClick={this.props.onClick}
+              >
+                {t(this.getMessage())}
+              </StyledButton>
+            ) : (
+              <StyledButton onClick={this.props.onClick}>
+                {this.getMessage()}
+              </StyledButton>
+            )}
+          </StyledBox>
+        </div>
+      </div>
+    );
+  }
+}
+
+const StyledBox = styled(Box)`
+  padding: 80px;
+  align-items: center;
+`;
+
+const StyledButton = styled(Button)`
+  align-items: left;
+`;
+export default ReleaseLandingCard;

+ 27 - 0
tests/js/spec/views/releasesLanding.spec.jsx

@@ -0,0 +1,27 @@
+import React from 'react';
+import {mount} from 'enzyme';
+
+import ReleaseLanding from 'app/views/projectReleases/releaseLanding';
+
+describe('ProjectReleasesLanding', function() {
+  let wrapper;
+
+  beforeEach(function() {
+    wrapper = mount(<ReleaseLanding />, TestStubs.routerContext());
+  });
+
+  it('renders first step', function() {
+    expect(wrapper.find('Contributors')).toHaveLength(1);
+  });
+
+  it('renders next steps', function() {
+    wrapper.find('Button').simulate('click');
+    expect(wrapper.find('SuggestedAssignees')).toHaveLength(1);
+    wrapper.find('Button').simulate('click');
+    expect(wrapper.find('Issues')).toHaveLength(1);
+    wrapper.find('Button').simulate('click');
+    expect(wrapper.find('BashCard')).toHaveLength(1);
+    wrapper.find('Button').simulate('click');
+    expect(wrapper.find('Emails')).toHaveLength(1);
+  });
+});