Repository: paularmstrong/build-tracker
Pull request #146 (2020-02-24 00:48:00)

[app] add copy as summary to appbar dropdown
<!--
Thank you so much for contributing to open source and the Build Tracker project!
-->

# Problem

Summaries are nice. Can't get them from the UI

# Solution

<!--
When trying to solve more solutions with Build Tracker, please keep in mind some of the following goals of the project:
* Be lightweight: small package sizes (single-digit KiBs, gzipped)
* Be easy: too many options in an API can become confusing
* Be clear: the intended purpose of every method should be as obvious as possible
* Is it easy to do this in "userland"? Would it be better off done there?
-->

Add `Copy summary` from the AppBar dropdown
<img width="367" alt="Screen Shot showing 'Copy summary' in a dropdown menu" src="https://user-images.githubusercontent.com/33297/75123426-2f5faa00-565c-11ea-9666-a9985e45e1a9.png">


# TODO

- [x] 🤓 Add & update tests (always try to _increase_ test coverage)
- [x] 🔬 Ensure CI is passing (`yarn lint:ci`, `yarn test`, `yarn tsc:ci`)
- [ ] 📖 Update relevant documentation



Patch:
diff --git a/src/app/src/icons/ListBulleted.tsx b/src/app/src/icons/ListBulleted.tsx
new file mode 100644
index 0000000..57695b2
--- /dev/null
+++ b/src/app/src/icons/ListBulleted.tsx
@@ -0,0 +1,32 @@
+/**
+ * Material design redistributed from https://github.com/google/material-design-icons
+ *
+ * SVG contents redistributed under Apache License 2.0:
+ * https://github.com/google/material-design-icons/blob/master/LICENSE
+ * Copyright 2015 Google, Inc. All Rights Reserved.
+ */
+import React from 'react';
+import styles from './styles';
+import { StyleProp, TextStyle, unstable_createElement, ViewProps } from 'react-native';
+
+interface Props extends ViewProps {
+  style?: StyleProp<TextStyle>;
+}
+
+const ListBulleted = (props: Props): React.ReactElement<Props> =>
+  unstable_createElement(
+    'svg',
+    {
+      ...props,
+      style: [styles.root, props.style],
+      viewBox: '0 0 24 24'
+    },
+    <g>
+      <path d="M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.68-1.5 1.5s.68 1.5 1.5 1.5 1.5-.68 1.5-1.5-.67-1.5-1.5-1.5zM7 19h14v-2H7v2zm0-6h14v-2H7v2zm0-8v2h14V5H7z" />
+      <path d="M0 0h24v24H0V0z" fill="none" />
+    </g>
+  );
+
+ListBulleted.metadata = { height: 24, width: 24 };
+
+export default ListBulleted;
diff --git a/src/app/src/views/AppBar.tsx b/src/app/src/views/AppBar.tsx
index 22ba49a..e316d52 100644
--- a/src/app/src/views/AppBar.tsx
+++ b/src/app/src/views/AppBar.tsx
@@ -8,6 +8,7 @@ import Divider from '../components/Divider';
 import DocumentIcon from '../icons/Document';
 import { Handles as DrawerHandles } from '../components/Drawer';
 import LinkIcon from '../icons/Link';
+import ListBulletedIcon from '../icons/ListBulleted';
 import MenuIcon from '../icons/Menu';
 import MenuItem from '../components/MenuItem';
 import React from 'react';
@@ -93,6 +94,12 @@ const AppBarView = (props: { drawerRef: React.RefObject<DrawerHandles> }): React
     appBarRef.current.dismissOverflow();
   }, [sizeKey, disabledArtifactsVisible, graphType, comparedRevisions, activeArtifacts, dispatch]);
 
+  const handleCopySummary = React.useCallback((): void => {
+    Clipboard.setString(`${activeComparator.toSummary().join(' \n')}`);
+    dispatch(addSnack('Copied summary'));
+    appBarRef.current.dismissOverflow();
+  }, [activeComparator, dispatch]);
+
   return (
     <AppBar
       navigationIcon={MenuIcon}
@@ -102,6 +109,7 @@ const AppBarView = (props: { drawerRef: React.RefObject<DrawerHandles> }): React
           <>
             <MenuItem key="clear" icon={ClearIcon} label="Clear selected revisions" onPress={handleClearRevisions} />
             <Divider />
+            <MenuItem key="summary" icon={ListBulletedIcon} label="Copy summary" onPress={handleCopySummary} />
             <MenuItem key="md" icon={TableIcon} label="Copy as markdown" onPress={handleCopyAsMarkdown} />
             <MenuItem key="csv" icon={DocumentIcon} label="Copy as CSV" onPress={handleCopyAsCsv} />
             <MenuItem key="link" icon={LinkIcon} label="Copy link" onPress={handleCopyLink} />


Test patch:
diff --git a/src/app/src/views/__tests__/AppBar.test.tsx b/src/app/src/views/__tests__/AppBar.test.tsx
index 0e8b5de..38b446a 100644
--- a/src/app/src/views/__tests__/AppBar.test.tsx
+++ b/src/app/src/views/__tests__/AppBar.test.tsx
@@ -101,6 +101,19 @@ describe('AppBarView', () => {
       expect(clearComparedSpy).toHaveBeenCalled();
     });
 
+    test('can copy a summary', () => {
+      const { getByProps } = render(
+        <Provider store={mockStore(seededState)}>
+          <AppBarView drawerRef={drawerRef} />
+        </Provider>
+      );
+
+      fireEvent.press(getByProps({ title: 'More actions' }));
+      fireEvent.press(getByProps({ label: 'Copy summary' }));
+
+      expect(clipboardSpy).toHaveBeenCalledWith(`${seededState.comparator.toSummary().join(' \n')}`);
+    });
+
     test('can copy as markdown', () => {
       const { getByProps } = render(
         <Provider store={mockStore(seededState)}>