Browse Source

ref(ui) Improve the ButtonBar component (#17321)

Improve the button bar component so that it uses a grid layout and can
layout buttons with uniform spacing or create merged buttons.
Mark Story 5 years ago
parent
commit
8d2bbceb11

+ 35 - 1
docs-ui/components/button.stories.js

@@ -3,9 +3,10 @@ import styled from '@emotion/styled';
 import {storiesOf} from '@storybook/react';
 import {withInfo} from '@storybook/addon-info';
 import {action} from '@storybook/addon-actions';
-import {boolean} from '@storybook/addon-knobs';
+import {boolean, number} from '@storybook/addon-knobs';
 
 import Button from 'app/components/button';
+import ButtonBar from 'app/components/buttonBar';
 import DropdownButton from 'app/components/dropdownButton';
 import InlineSvg from 'app/components/inlineSvg';
 import space from 'app/styles/space';
@@ -204,4 +205,37 @@ storiesOf('UI|Buttons', module)
         </Item>
       </React.Fragment>
     ))
+  )
+  .add(
+    'ButtonBar',
+    withInfo('Buttons in a Bar container')(() => (
+      <div>
+        <Section>
+          <h3>With a Gap</h3>
+          <ButtonBar gap={number('button gap', 1)}>
+            <Button>First Button</Button>
+            <Button>Second Button</Button>
+            <Button>Third Button</Button>
+          </ButtonBar>
+        </Section>
+
+        <Section>
+          <h3>Merged Buttons</h3>
+          <ButtonBar merged>
+            <Button>Left Button</Button>
+            <Button priority="primary">Right Button</Button>
+          </ButtonBar>
+        </Section>
+
+        <Section>
+          <h3>Multiple Merged Buttons</h3>
+          <ButtonBar merged>
+            <Button>First Button</Button>
+            <Button>Second Button</Button>
+            <Button>Third Button</Button>
+            <Button>Fourth Button</Button>
+          </ButtonBar>
+        </Section>
+      </div>
+    ))
   );

+ 56 - 3
src/sentry/static/sentry/app/components/buttonBar.tsx

@@ -1,9 +1,62 @@
+import React from 'react';
 import styled from '@emotion/styled';
+import space, {ValidSize} from 'app/styles/space';
 
-const ButtonBar = styled('div')`
-  display: flex;
-  justify-content: flex-end;
+type ButtonBarProps = {
+  className?: string;
+  gap?: ValidSize;
+  merged?: boolean;
+  children: React.ReactNode;
+};
+
+function ButtonBar({children, className, merged = false, gap = 0}: ButtonBarProps) {
+  return (
+    <ButtonGrid merged={merged} gap={gap} className={className}>
+      {children}
+    </ButtonGrid>
+  );
+}
+
+const ButtonGrid = styled('div')<{gap: ValidSize; merged: boolean}>`
+  display: grid;
+  grid-auto-flow: column;
+  grid-column-gap: ${p => space(p.gap)};
   align-items: center;
+
+  ${p =>
+    p.merged &&
+    `
+    /* First button is square on the right side */
+    & > button:first-child:not(:last-child),
+    & > a:first-child:not(:last-child) {
+      border-top-right-radius: 0;
+      border-bottom-right-radius: 0;
+    }
+    /* Middle buttons are square */
+    & > button:not(:last-child):not(:first-child),
+    & > a:not(:last-child):not(:first-child) {
+      border-radius: 0;
+    }
+
+    /* 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;
+    }
+
+    /* 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;
+    }
+
+    /* Last button is square on the left side */
+    & > button:last-child:not(:first-child),
+    & > a:last-child:not(:first-child) {
+      border-top-left-radius: 0;
+      border-bottom-left-radius: 0;
+    }
+  `}
 `;
 
 export default ButtonBar;

+ 1 - 1
src/sentry/static/sentry/app/styles/space.tsx

@@ -10,7 +10,7 @@ const SPACES = {
   4: '30px',
 } as const;
 
-type ValidSize = keyof typeof SPACES;
+export type ValidSize = keyof typeof SPACES;
 
 const space = (size: ValidSize): typeof SPACES[ValidSize] => SPACES[size];
 

+ 2 - 13
src/sentry/static/sentry/app/views/organizationGroupDetails/groupEventAttachments/groupEventAttachmentsFilter.tsx

@@ -22,7 +22,7 @@ const GroupEventAttachmentsFilter = (props: WithRouterProps) => {
 
   return (
     <FilterWrapper>
-      <MergedButtonBar>
+      <ButtonBar merged>
         <Button
           size="small"
           to={{pathname, query: allAttachmentsQuery}}
@@ -37,7 +37,7 @@ const GroupEventAttachmentsFilter = (props: WithRouterProps) => {
         >
           {t('Only Crash Reports')}
         </Button>
-      </MergedButtonBar>
+      </ButtonBar>
     </FilterWrapper>
   );
 };
@@ -48,15 +48,4 @@ const FilterWrapper = styled('div')`
   margin-bottom: ${space(3)};
 `;
 
-const MergedButtonBar = styled(ButtonBar)`
-  & > a:first-child:not(:last-child) {
-    border-top-right-radius: 0;
-    border-bottom-right-radius: 0;
-  }
-  & > a:last-child:not(:first-child) {
-    border-top-left-radius: 0;
-    border-bottom-left-radius: 0;
-  }
-`;
-
 export default withRouter(GroupEventAttachmentsFilter);