Browse Source

ref(ui) Use ButtonBar component in issue details (#17478)

Remove the LESS powered button bar and buttons and use the newer styled
components. I'm going to continue replacing button bars in small chunks
over the next few weeks.
Mark Story 5 years ago
parent
commit
11553c48ba

+ 7 - 3
docs-ui/components/button.stories.js

@@ -221,8 +221,10 @@ storiesOf('UI|Buttons', module)
         <Section>
           <h3>Merged Buttons</h3>
           <ButtonBar merged>
-            <Button>Left Button</Button>
-            <Button priority="primary">Right Button</Button>
+            <Button priority="primary" className="active">
+              Left Button
+            </Button>
+            <Button>Right Button</Button>
           </ButtonBar>
         </Section>
 
@@ -230,7 +232,9 @@ storiesOf('UI|Buttons', module)
           <h3>Multiple Merged Buttons</h3>
           <ButtonBar merged>
             <Button>First Button</Button>
-            <Button>Second Button</Button>
+            <Button priority="primary" className="active">
+              Second Button
+            </Button>
             <Button>Third Button</Button>
             <Button>Fourth Button</Button>
           </ButtonBar>

+ 13 - 2
src/sentry/static/sentry/app/components/buttonBar.tsx

@@ -27,6 +27,16 @@ const ButtonGrid = styled('div')<{gap: ValidSize; merged: boolean}>`
   ${p =>
     p.merged &&
     `
+    & > button,
+    & > a {
+      position: relative;
+    }
+
+    /* Raised buttons show borders on both sides. Useful to create pill bars */
+    & > .active {
+      z-index: 2;
+    }
+
     /* First button is square on the right side */
     & > button:first-child:not(:last-child),
     & > a:first-child:not(:last-child) {
@@ -42,13 +52,13 @@ const ButtonGrid = styled('div')<{gap: ValidSize; merged: boolean}>`
     /* Middle buttons only need one border so we don't get a double line */
     & > a:first-child + a:not(:last-child),
     & > button:first-child + button:not(:last-child) {
-      border-left: 0;
+      margin-left: -1px;
     }
 
     /* Middle buttons only need one border so we don't get a double line */
     & > button:not(:last-child):not(:first-child) + button,
     & > a:not(:last-child):not(:first-child) + a {
-      border-left: 0;
+      margin-left: -1px;
     }
 
     /* Last button is square on the left side */
@@ -56,6 +66,7 @@ const ButtonGrid = styled('div')<{gap: ValidSize; merged: boolean}>`
     & > a:last-child:not(:first-child) {
       border-top-left-radius: 0;
       border-bottom-left-radius: 0;
+      margin-left: -1px;
     }
   `}
 `;

+ 16 - 9
src/sentry/static/sentry/app/components/events/eventDataSection.tsx

@@ -5,6 +5,8 @@ import styled from '@emotion/styled';
 import {t} from 'app/locale';
 import {callIfFunction} from 'app/utils/callIfFunction';
 import {DataSection} from 'app/components/events/styles';
+import Button from 'app/components/button';
+import ButtonBar from 'app/components/buttonBar';
 import space from 'app/styles/space';
 
 const defaultProps = {
@@ -77,20 +79,24 @@ class EventDataSection extends React.Component<Props> {
             </Permalink>
             {titleNode}
             {type === 'extra' && (
-              <div className="btn-group">
-                <a
-                  className={(!raw ? 'active' : '') + ' btn btn-default btn-sm'}
+              <ButtonBar merged>
+                <Button
+                  className={!raw ? 'active' : ''}
+                  priority={!raw ? 'primary' : 'default'}
+                  size="xsmall"
                   onClick={() => callIfFunction(toggleRaw, false)}
                 >
                   {t('Formatted')}
-                </a>
-                <a
-                  className={(raw ? 'active' : '') + ' btn btn-default btn-sm'}
+                </Button>
+                <Button
+                  className={raw ? 'active' : ''}
+                  priority={raw ? 'primary' : 'default'}
+                  size="xsmall"
                   onClick={() => callIfFunction(toggleRaw, true)}
                 >
                   {t('Raw')}
-                </a>
-              </div>
+                </Button>
+              </ButtonBar>
             )}
             {actions && <ActionContainer>{actions}</ActionContainer>}
           </SectionHeader>
@@ -116,6 +122,7 @@ const SectionHeader = styled('div')`
   display: flex;
   justify-content: space-between;
   position: relative;
+  margin-bottom: ${space(3)};
 
   & h3,
   & h3 a {
@@ -130,8 +137,8 @@ const SectionHeader = styled('div')`
     font-weight: 600;
     line-height: 1.2;
     padding: ${space(0.75)} 0;
+    margin-bottom: 0;
     text-transform: uppercase;
-    margin-bottom: 20px;
   }
 
   & small {

+ 31 - 32
src/sentry/static/sentry/app/components/events/interfaces/crashHeader.jsx

@@ -2,6 +2,8 @@ import PropTypes from 'prop-types';
 import React from 'react';
 import styled from '@emotion/styled';
 
+import Button from 'app/components/button';
+import ButtonBar from 'app/components/buttonBar';
 import Tooltip from 'app/components/tooltip';
 import {t} from 'app/locale';
 import GuideAnchor from 'app/components/assistant/guideAnchor';
@@ -126,55 +128,55 @@ class CrashHeader extends React.Component {
           {titleNode}
         </TitleInfo>
         <ButtonGroupWrapper>
-          <ButtonGroup className="btn-group">
+          <ButtonGroup merged>
             {this.hasSystemFrames() && (
-              <a
-                className={
-                  (stackView === 'app' ? 'active' : '') + ' btn btn-default btn-sm'
-                }
+              <Button
+                className={stackView === 'app' ? 'active' : ''}
+                priority={stackView === 'app' ? 'primary' : 'default'}
+                size="xsmall"
                 onClick={() => this.setStackView('app')}
               >
                 {t('App Only')}
-              </a>
+              </Button>
             )}
-            <a
-              className={
-                (stackView === 'full' ? 'active' : '') + ' btn btn-default btn-sm'
-              }
+            <Button
+              className={stackView === 'full' ? 'active' : ''}
+              priority={stackView === 'full' ? 'primary' : 'default'}
+              size="xsmall"
               onClick={() => this.setStackView('full')}
             >
               {t('Full')}
-            </a>
-            <a
-              className={
-                (stackView === 'raw' ? 'active' : '') + ' btn btn-default btn-sm'
-              }
+            </Button>
+            <Button
+              className={stackView === 'raw' ? 'active' : ''}
+              priority={stackView === 'raw' ? 'primary' : 'default'}
               onClick={() => this.setStackView('raw')}
+              size="xsmall"
             >
               {t('Raw')}
-            </a>
+            </Button>
           </ButtonGroup>
           {this.hasMinified() && (
-            <ButtonGroup className="btn-group">
+            <ButtonGroup merged>
               {[
-                <a
+                <Button
                   key="original"
-                  className={
-                    (stackType === 'original' ? 'active' : '') + ' btn btn-default btn-sm'
-                  }
+                  className={stackType === 'original' ? 'active' : ''}
+                  priority={stackType === 'original' ? 'primary' : 'default'}
+                  size="xsmall"
                   onClick={() => this.setStackType('original')}
                 >
                   {this.getOriginalButtonLabel()}
-                </a>,
-                <a
+                </Button>,
+                <Button
                   key="minified"
-                  className={
-                    (stackType === 'minified' ? 'active' : '') + ' btn btn-default btn-sm'
-                  }
+                  className={stackType === 'minified' ? 'active' : ''}
+                  priority={stackType === 'minified' ? 'primary' : 'default'}
+                  size="xsmall"
                   onClick={() => this.setStackType('minified')}
                 >
                   {this.getMinifiedButtonLabel()}
-                </a>,
+                </Button>,
               ]}
             </ButtonGroup>
           )}
@@ -189,7 +191,6 @@ export default CrashHeader;
 const Wrapper = styled('div')`
   display: flex;
   justify-content: space-between;
-  margin-bottom: ${space(3)};
   flex-direction: column;
   flex-wrap: wrap;
   width: 100%;
@@ -211,9 +212,10 @@ const TitleInfo = styled('div')`
   }
 `;
 
-const ButtonGroup = styled('div')`
+const ButtonGroup = styled(ButtonBar)`
   padding-top: ${space(1.5)};
   padding-bottom: ${space(1.5)};
+  padding-right: ${space(1)};
 `;
 
 const ButtonGroupWrapper = styled('div')`
@@ -221,9 +223,6 @@ const ButtonGroupWrapper = styled('div')`
   flex-direction: column;
   flex-wrap: wrap;
   margin-right: -${space(1)};
-  > .btn-group {
-    padding-right: ${space(1)};
-  }
   @media (min-width: ${props => props.theme.breakpoints[1]}) {
     flex-direction: row;
   }

+ 19 - 11
src/sentry/static/sentry/app/components/events/interfaces/csp.jsx

@@ -2,6 +2,8 @@ import PropTypes from 'prop-types';
 import React from 'react';
 
 import SentryTypes from 'app/sentryTypes';
+import ButtonBar from 'app/components/buttonBar';
+import Button from 'app/components/button';
 import EventDataSection from 'app/components/events/eventDataSection';
 import CSPContent from 'app/components/events/interfaces/cspContent';
 import CSPHelp from 'app/components/events/interfaces/cspHelp';
@@ -50,26 +52,32 @@ export default class CspInterface extends React.Component {
 
     const title = (
       <div>
-        <div className="btn-group">
-          <a
-            className={(view === 'report' ? 'active' : '') + ' btn btn-default btn-sm'}
+        <ButtonBar merged>
+          <Button
+            className={view === 'report' ? 'active' : ''}
+            priority={view === 'report' ? 'primary' : 'default'}
+            size="xsmall"
             onClick={this.toggleView.bind(this, 'report')}
           >
             {t('Report')}
-          </a>
-          <a
-            className={(view === 'raw' ? 'active' : '') + ' btn btn-default btn-sm'}
+          </Button>
+          <Button
+            className={view === 'raw' ? 'active' : ''}
+            priority={view === 'raw' ? 'primary' : 'default'}
+            size="xsmall"
             onClick={this.toggleView.bind(this, 'raw')}
           >
             {t('Raw')}
-          </a>
-          <a
-            className={(view === 'help' ? 'active' : '') + ' btn btn-default btn-sm'}
+          </Button>
+          <Button
+            className={view === 'help' ? 'active' : ''}
+            priority={view === 'help' ? 'primary' : 'default'}
+            size="xsmall"
             onClick={this.toggleView.bind(this, 'help')}
           >
             {t('Help')}
-          </a>
-        </div>
+          </Button>
+        </ButtonBar>
         <h3>{t('CSP Report')}</h3>
       </div>
     );

+ 14 - 8
src/sentry/static/sentry/app/components/events/interfaces/generic.jsx

@@ -2,6 +2,8 @@ import PropTypes from 'prop-types';
 import React, {Component} from 'react';
 
 import SentryTypes from 'app/sentryTypes';
+import ButtonBar from 'app/components/buttonBar';
+import Button from 'app/components/button';
 import EventDataSection from 'app/components/events/eventDataSection';
 import KeyValueList from 'app/components/events/interfaces/keyValueList/keyValueList';
 import {t} from 'app/locale';
@@ -44,20 +46,24 @@ export default class GenericInterface extends Component {
 
     const title = (
       <div>
-        <div className="btn-group">
-          <a
-            className={(view === 'report' ? 'active' : '') + ' btn btn-default btn-sm'}
+        <ButtonBar merged>
+          <Button
+            className={view === 'report' ? 'active' : ''}
+            priority={view === 'report' ? 'primary' : 'default'}
+            size="xsmall"
             onClick={this.toggleView.bind(this, 'report')}
           >
             {t('Report')}
-          </a>
-          <a
-            className={(view === 'raw' ? 'active' : '') + ' btn btn-default btn-sm'}
+          </Button>
+          <Button
+            className={view === 'raw' ? 'active' : ''}
+            priority={view === 'raw' ? 'primary' : 'default'}
+            size="xsmall"
             onClick={this.toggleView.bind(this, 'raw')}
           >
             {t('Raw')}
-          </a>
-        </div>
+          </Button>
+        </ButtonBar>
         <h3>{t('Report')}</h3>
       </div>
     );

+ 19 - 9
src/sentry/static/sentry/app/components/events/interfaces/request.jsx

@@ -4,6 +4,8 @@ import styled from '@emotion/styled';
 
 import EventDataSection from 'app/components/events/eventDataSection';
 import SentryTypes from 'app/sentryTypes';
+import Button from 'app/components/button';
+import ButtonBar from 'app/components/buttonBar';
 import RichHttpContent from 'app/components/events/interfaces/richHttpContent/richHttpContent';
 import {getFullUrl, getCurlCommand} from 'app/components/events/interfaces/utils';
 import {isUrl} from 'app/utils';
@@ -64,21 +66,25 @@ class RequestInterface extends React.Component {
     let actions;
     if (!this.isPartial() && fullUrl) {
       actions = (
-        <div key="view-buttons" className="btn-group">
-          <a
-            className={(view === 'formatted' ? 'active' : '') + ' btn btn-default btn-sm'}
+        <ButtonBar key="view-buttons" merged>
+          <Button
+            className={view === 'formatted' ? 'active' : ''}
+            priority={view === 'formatted' ? 'primary' : 'default'}
+            size="xsmall"
             onClick={this.toggleView.bind(this, 'formatted')}
           >
             {/* Translators: this means "formatted" rendering (fancy tables) */
             t('Formatted')}
-          </a>
-          <a
-            className={(view === 'curl' ? 'active' : '') + ' btn btn-default btn-sm'}
+          </Button>
+          <MonoButton
+            className={view === 'curl' ? 'active' : ''}
+            priority={view === 'curl' ? 'primary' : 'default'}
+            size="xsmall"
             onClick={this.toggleView.bind(this, 'curl')}
           >
-            <code>{'curl'}</code>
-          </a>
-        </div>
+            curl
+          </MonoButton>
+        </ButtonBar>
       );
     }
 
@@ -147,4 +153,8 @@ const StyledIconOpen = styled(IconOpen)`
   }
 `;
 
+const MonoButton = styled(Button)`
+  font-family: ${p => p.theme.text.familyMono};
+`;
+
 export default RequestInterface;

+ 1 - 1
tests/acceptance/test_issue_details.py

@@ -104,7 +104,7 @@ class IssueDetailsTest(AcceptanceTestCase, SnubaTestCase):
         self.page.visit_issue(self.org.slug, event.group.id)
         self.browser.snapshot("issue details javascript - event details")
 
-        self.browser.find_element_by_xpath("//a//code[contains(text(), 'curl')]").click()
+        self.browser.find_element_by_xpath("//button//span[contains(text(), 'curl')]").click()
         self.browser.snapshot("issue details javascript - event details - curl command")
 
     def test_rust_event(self):

+ 26 - 18
tests/js/spec/components/__snapshots__/toggleRawEventData.spec.jsx.snap

@@ -18,22 +18,26 @@ exports[`EventDataSection renders formatted 1`] = `
     <h3>
       Additional Data
     </h3>
-    <div
-      className="btn-group"
+    <ButtonBar
+      merged={true}
     >
-      <a
-        className="active btn btn-default btn-sm"
+      <forwardRef<Button>
+        className="active"
         onClick={[Function]}
+        priority="primary"
+        size="xsmall"
       >
         Formatted
-      </a>
-      <a
-        className=" btn btn-default btn-sm"
+      </forwardRef<Button>>
+      <forwardRef<Button>
+        className=""
         onClick={[Function]}
+        priority="default"
+        size="xsmall"
       >
         Raw
-      </a>
-    </div>
+      </forwardRef<Button>>
+    </ButtonBar>
   </SectionHeader>
   <SectionContents />
 </DataSection>
@@ -57,22 +61,26 @@ exports[`EventDataSection renders raw 1`] = `
     <h3>
       Additional Data
     </h3>
-    <div
-      className="btn-group"
+    <ButtonBar
+      merged={true}
     >
-      <a
-        className=" btn btn-default btn-sm"
+      <forwardRef<Button>
+        className=""
         onClick={[Function]}
+        priority="default"
+        size="xsmall"
       >
         Formatted
-      </a>
-      <a
-        className="active btn btn-default btn-sm"
+      </forwardRef<Button>>
+      <forwardRef<Button>
+        className="active"
         onClick={[Function]}
+        priority="primary"
+        size="xsmall"
       >
         Raw
-      </a>
-    </div>
+      </forwardRef<Button>>
+    </ButtonBar>
   </SectionHeader>
   <SectionContents />
 </DataSection>

+ 1 - 1
tests/js/spec/views/sharedGroupDetails/__snapshots__/index.spec.jsx.snap

@@ -581,7 +581,7 @@ exports[`SharedGroupDetails renders 1`] = `
                                     id="message"
                                   >
                                     <div
-                                      className="css-mxpc8d-SectionHeader e1fbjd861"
+                                      className="css-zang02-SectionHeader e1fbjd861"
                                       id="message"
                                     >
                                       <Permalink