Repository: mapbox/mr-ui
Pull request #39 (2018-08-14 00:19:26)

A few fixes
A few fixes following up on recent changes:

- Fix text overflow in Copiable, introducing a `truncated` prop to allow for a truncated line of text.
- Consistently use the Button component in new examples that use buttons.
- Clean up some little typos.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c3db830..003b74a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,9 @@
   - [feature] Add `CopyButton.copyIsSupported()` static function.
   - [feature] Improve appearance of copy instructions popover.
   - [fix] Works in modals.
+- **Copiable**
+  - 🚨 [breaking change] Remove `themeWrapper`, `themeCopyButtonContainer`, `themeCopyButton`, `themeText`, and `themeTooltip` props.
+  - [feature] Add `truncate` prop.
 - **Tooltip**
   - 🚨 [breaking change] Remove `backgroundColor` and `themeTooltip` props.
   - 🚨 [breaking change] Replace `display` prop with `block`.
diff --git a/scripts/build-docs-data.js b/scripts/build-docs-data.js
index f258a3d..758398f 100755
--- a/scripts/build-docs-data.js
+++ b/scripts/build-docs-data.js
@@ -49,7 +49,7 @@ function processExampleFile(filename) {
 function getExamples(componentDirectory) {
   const examplesDirectory = path.join(componentDirectory, 'examples');
   return globby(path.join(examplesDirectory, '*.js')).then(filenames => {
-    return Promise.all(filenames.map(processExampleFile));
+    return Promise.all(filenames.sort().map(processExampleFile));
   });
 }
 
diff --git a/src/components/button/button.js b/src/components/button/button.js
index 2fff445..906b273 100644
--- a/src/components/button/button.js
+++ b/src/components/button/button.js
@@ -231,9 +231,7 @@ Button.propTypes = {
    * the button, assign an ID for testing, add an ARIA attribute, toss in some
    * custom style properties, etc.
    */
-  passthroughProps: PropTypes.objectOf(
-    PropTypes.oneOfType([PropTypes.string, PropTypes.bool, PropTypes.number])
-  )
+  passthroughProps: PropTypes.object
 };
 
 Button.defaultProps = {
diff --git a/src/components/copiable/copiable.js b/src/components/copiable/copiable.js
index 01605b3..11ea7ad 100644
--- a/src/components/copiable/copiable.js
+++ b/src/components/copiable/copiable.js
@@ -1,5 +1,6 @@
 import React from 'react';
 import PropTypes from 'prop-types';
+import classnames from 'classnames';
 import CopyButton from '../copy-button';
 import Popover from '../popover';
 import OSKey from 'os-key';
@@ -75,16 +76,23 @@ export default class Copiable extends React.Component {
     }
   };
 
-  render() {
+  renderCopyButton() {
     const { props } = this;
 
-    const copyBtn = typeof document !== 'undefined' &&
-      CopyButton.isCopySupported() && (
-        <div className="fr py6 px6">
-          <CopyButton text={props.value} textEl={this.textEl} block={true} />
-        </div>
-      );
+    if (typeof window === 'undefined' || !CopyButton.isCopySupported()) {
+      return null;
+    }
 
+    return (
+      <div className="absolute top right px6 py6">
+        <CopyButton text={props.value} textEl={this.textEl} block={true} />
+      </div>
+    );
+  }
+
+  render() {
+    const { props } = this;
+    const copyButton = this.renderCopyButton();
     const copyHintPopover = this.state.copyTooltipActive && (
       <Popover
         getAnchorElement={this.getTextEl}
@@ -98,18 +106,29 @@ export default class Copiable extends React.Component {
       </Popover>
     );
 
+    const textClasses = classnames('my3 txt-mono txt-s mr24', {
+      'txt-truncate': props.truncated
+    });
+    const textStyle = {};
+    if (!props.truncated) {
+      textStyle.wordWrap = 'break-word';
+      textStyle.overflowWrap = 'break-word';
+    }
+
     return (
-      <div className="clearfix bg-darken5 round">
-        {copyBtn}
+      <div className="relative clearfix bg-darken5 round">
+        {copyButton}
         <div
           tabIndex="-1"
           ref={this.setTextEl}
           onFocus={this.handleTextFocus}
           onBlur={this.handleTextBlur}
-          className="py6 px12 txt-mono txt-s"
+          className="py6 px12"
           data-test="copiable-text-el"
         >
-          <div className="my3">{props.value}</div>
+          <div className={textClasses} style={textStyle}>
+            {props.value}
+          </div>
         </div>
         {copyHintPopover}
       </div>
@@ -121,7 +140,27 @@ Copiable.propTypes = {
   /**
    * The text that will be displayed and copied.
    */
-  value: PropTypes.string.isRequired
+  value: PropTypes.string.isRequired,
+  /**
+   * If `false` (default), the text will be overflow to multiple lines,
+   * and words longer than a single line (e.g. long access tokens or URLs)
+   * will be broken to enforce wrapping.
+   *
+   * If `true`, the Copiable's text will be truncated to a single line
+   * of text. **Only set this to `true` if you know that your target
+   * browsers support the copy button!** Some browsers will not effectively
+   * copy text that is truncated by CSS, so the risk is that some of your
+   * users might have *no way* to view and copy all the text if the copy
+   * button does not work for them.
+   *
+   * Horizontal scrolling is not an option because of things end up getting
+   * pretty gross across browsers.
+   */
+  truncated: PropTypes.bool
+};
+
+Copiable.defaultProps = {
+  truncated: false
 };
 
 function getCopyKeys(ua) {
diff --git a/src/components/copiable/examples/copiable-a.js b/src/components/copiable/examples/copiable-a.js
index 7fe5352..e9121c0 100644
--- a/src/components/copiable/examples/copiable-a.js
+++ b/src/components/copiable/examples/copiable-a.js
@@ -9,10 +9,13 @@ export default class Example extends React.Component {
     return (
       <div>
         <div className="mb12">
-          <Copiable value="https://www.mapbox.com/something/special/for/you" />
+          <Copiable value="The length of this copiable text is to illustrate to you how this component's styling works when the text wraps to multiple lines" />
         </div>
         <div>
-          <Copiable value="The length of this copiable text is to illustrate to you how this component's styling works when the text wraps to multiple lines" />
+          <Copiable
+            value="https://www.mapbox.com/something/special/for/you"
+            truncated={true}
+          />
         </div>
       </div>
     );
diff --git a/src/components/icon/examples/icon-example-options.js b/src/components/icon/examples/icon-example-options.js
index 290a13a..54e7fcc 100644
--- a/src/components/icon/examples/icon-example-options.js
+++ b/src/components/icon/examples/icon-example-options.js
@@ -9,7 +9,6 @@ export default class Example extends React.Component {
     return (
       <Icon
         name="antialias"
-        inlineHeading={true}
         theme={{
           icon: 'icon--l'
         }}
diff --git a/src/components/popover-trigger/examples/popover-trigger-a.js b/src/components/popover-trigger/examples/popover-trigger-a.js
index d3fa331..c46bb80 100644
--- a/src/components/popover-trigger/examples/popover-trigger-a.js
+++ b/src/components/popover-trigger/examples/popover-trigger-a.js
@@ -3,6 +3,7 @@ A standard PopoverTrigger.
 */
 import React from 'react';
 import PopoverTrigger from '../popover-trigger';
+import Button from '../../button';
 
 export default class Example extends React.Component {
   getPopoverContent = () => {
@@ -13,9 +14,7 @@ export default class Example extends React.Component {
     return (
       <div>
         <PopoverTrigger content={this.getPopoverContent}>
-          <button type="button" className="btn btn--s btn--purple">
-            Trigger
-          </button>
+          <Button size="medium">Trigger</Button>
         </PopoverTrigger>
       </div>
     );
diff --git a/src/components/popover-trigger/examples/popover-trigger-b.js b/src/components/popover-trigger/examples/popover-trigger-b.js
index a95b821..fa38039 100644
--- a/src/components/popover-trigger/examples/popover-trigger-b.js
+++ b/src/components/popover-trigger/examples/popover-trigger-b.js
@@ -3,6 +3,7 @@ A PopoverTrigger that opens a dark popover and traps focus inside its content.
 */
 import React from 'react';
 import PopoverTrigger from '../popover-trigger';
+import Button from '../../button';
 
 export default class Example extends React.Component {
   constructor(props) {
@@ -48,9 +49,7 @@ export default class Example extends React.Component {
             'data-test': 'trigger-container'
           }}
         >
-          <button type="button" className="btn btn--s">
-            Trigger
-          </button>
+          <Button size="medium">Trigger</Button>
         </PopoverTrigger>
       </div>
     );
diff --git a/src/components/popover/examples/popover-a.js b/src/components/popover/examples/popover-a.js
index 95ebcb0..387c78e 100644
--- a/src/components/popover/examples/popover-a.js
+++ b/src/components/popover/examples/popover-a.js
@@ -3,6 +3,7 @@ Standard popover.
 */
 import React from 'react';
 import Popover from '../popover';
+import Button from '../../button';
 
 export default class Example extends React.Component {
   constructor(props) {
@@ -49,13 +50,15 @@ export default class Example extends React.Component {
   render() {
     return (
       <div>
-        <button
-          ref={this.setAnchor}
+        <Button
           onClick={this.togglePopover}
-          className="btn btn--s"
+          size="medium"
+          passthroughProps={{
+            ref: this.setAnchor
+          }}
         >
           Toggle popover
-        </button>
+        </Button>
         {this.renderPopover()}
       </div>
     );
diff --git a/src/components/popover/examples/popover-b.js b/src/components/popover/examples/popover-b.js
index 26e3379..524e0fa 100644
--- a/src/components/popover/examples/popover-b.js
+++ b/src/components/popover/examples/popover-b.js
@@ -3,6 +3,7 @@ Dark popover.
 */
 import React from 'react';
 import Popover from '../popover';
+import Button from '../../button';
 
 export default class Example extends React.Component {
   constructor(props) {
@@ -50,13 +51,15 @@ export default class Example extends React.Component {
   render() {
     return (
       <div>
-        <button
-          ref={this.setAnchor}
+        <Button
           onClick={this.togglePopover}
-          className="btn btn--s"
+          size="medium"
+          passthroughProps={{
+            ref: this.setAnchor
+          }}
         >
           Toggle popover
-        </button>
+        </Button>
         {this.renderPopover()}
       </div>
     );
diff --git a/src/components/popover/examples/popover-c.js b/src/components/popover/examples/popover-c.js
index 695e7e0..f688fe2 100644
--- a/src/components/popover/examples/popover-c.js
+++ b/src/components/popover/examples/popover-c.js
@@ -3,6 +3,7 @@ Warning popover.
 */
 import React from 'react';
 import Popover from '../popover';
+import Button from '../../button';
 
 export default class Example extends React.Component {
   constructor(props) {
@@ -50,13 +51,15 @@ export default class Example extends React.Component {
   render() {
     return (
       <div>
-        <button
-          ref={this.setAnchor}
+        <Button
           onClick={this.togglePopover}
-          className="btn btn--s"
+          size="medium"
+          passthroughProps={{
+            ref: this.setAnchor
+          }}
         >
           Toggle popover
-        </button>
+        </Button>
         {this.renderPopover()}
       </div>
     );
diff --git a/src/components/tooltip/examples/tooltip-a.js b/src/components/tooltip/examples/tooltip-a.js
index 15f1e75..866e65a 100644
--- a/src/components/tooltip/examples/tooltip-a.js
+++ b/src/components/tooltip/examples/tooltip-a.js
@@ -3,12 +3,13 @@ A standard tooltip.
 */
 import React from 'react';
 import Tooltip from '../tooltip';
+import Button from '../../button';
 
 export default class Example extends React.Component {
   render() {
     return (
       <Tooltip content="Here's your tooltip">
-        <button className="btn">Basic</button>
+        <Button size="medium">Basic</Button>
       </Tooltip>
     );
   }


Test patch:
diff --git a/src/components/copiable/__tests__/__snapshots__/copiable.test.js.snap b/src/components/copiable/__tests__/__snapshots__/copiable.test.js.snap
index 2621836..9779836 100644
--- a/src/components/copiable/__tests__/__snapshots__/copiable.test.js.snap
+++ b/src/components/copiable/__tests__/__snapshots__/copiable.test.js.snap
@@ -2,28 +2,34 @@
 
 exports[`basic renders as expected 1`] = `
 <div
-  className="clearfix bg-darken5 round"
+  className="relative clearfix bg-darken5 round"
 >
   <div
-    className="fr py6 px6"
+    className="absolute top right px6 py6"
   >
     <CopyButton
       block={true}
       className="btn btn--xs py3 px3 round"
-      text="the text you copy the text you copy the text you copy the text you copy the text you copy "
+      text="thetextyoucopythetextyoucopythetextyoucopythetext you copy the text you copy "
     />
   </div>
   <div
-    className="py6 px12 txt-mono txt-s"
+    className="py6 px12"
     data-test="copiable-text-el"
     onBlur={[Function]}
     onFocus={[Function]}
     tabIndex="-1"
   >
     <div
-      className="my3"
+      className="my3 txt-mono txt-s mr24"
+      style={
+        Object {
+          "overflowWrap": "break-word",
+          "wordWrap": "break-word",
+        }
+      }
     >
-      the text you copy the text you copy the text you copy the text you copy the text you copy 
+      thetextyoucopythetextyoucopythetextyoucopythetext you copy the text you copy 
     </div>
   </div>
 </div>
@@ -31,28 +37,34 @@ exports[`basic renders as expected 1`] = `
 
 exports[`basic shows copy hint when focused 1`] = `
 <div
-  className="clearfix bg-darken5 round"
+  className="relative clearfix bg-darken5 round"
 >
   <div
-    className="fr py6 px6"
+    className="absolute top right px6 py6"
   >
     <CopyButton
       block={true}
       className="btn btn--xs py3 px3 round"
-      text="the text you copy the text you copy the text you copy the text you copy the text you copy "
+      text="thetextyoucopythetextyoucopythetextyoucopythetext you copy the text you copy "
     />
   </div>
   <div
-    className="py6 px12 txt-mono txt-s"
+    className="py6 px12"
     data-test="copiable-text-el"
     onBlur={[Function]}
     onFocus={[Function]}
     tabIndex="-1"
   >
     <div
-      className="my3"
+      className="my3 txt-mono txt-s mr24"
+      style={
+        Object {
+          "overflowWrap": "break-word",
+          "wordWrap": "break-word",
+        }
+      }
     >
-      the text you copy the text you copy the text you copy the text you copy the text you copy 
+      thetextyoucopythetextyoucopythetextyoucopythetext you copy the text you copy 
     </div>
   </div>
   <Popover
diff --git a/src/components/copiable/__tests__/copiable-test-cases.js b/src/components/copiable/__tests__/copiable-test-cases.js
index d96489a..40ba794 100644
--- a/src/components/copiable/__tests__/copiable-test-cases.js
+++ b/src/components/copiable/__tests__/copiable-test-cases.js
@@ -6,6 +6,16 @@ testCases.basic = {
   description: 'basic',
   component: Copiable,
   props: {
+    value:
+      'thetextyoucopythetextyoucopythetextyoucopythetext you copy the text you copy '
+  }
+};
+
+testCases.truncated = {
+  description: 'truncated',
+  component: Copiable,
+  props: {
+    truncated: true,
     value:
       'the text you copy the text you copy the text you copy the text you copy the text you copy '
   }
diff --git a/src/components/icon/__tests__/icon-test-cases.js b/src/components/icon/__tests__/icon-test-cases.js
index 65d2590..8ec3b7d 100644
--- a/src/components/icon/__tests__/icon-test-cases.js
+++ b/src/components/icon/__tests__/icon-test-cases.js
@@ -72,20 +72,4 @@ testCases.inlineLargeDisplay = {
   )
 };
 
-testCases.inlineHeadingDisplay = {
-  description: 'inline txt-h2',
-  element: (
-    <div className="txt-h2">
-      Party because
-      <Icon
-        name="check"
-        themeIcon="mr3 color-green"
-        inline={true}
-        style={{ width: '1em' }}
-      />
-      you did it
-    </div>
-  )
-};
-
 export { testCases };
diff --git a/src/components/popover/__tests__/popover-test-cases.js b/src/components/popover/__tests__/popover-test-cases.js
index 9840a70..b9be811 100644
--- a/src/components/popover/__tests__/popover-test-cases.js
+++ b/src/components/popover/__tests__/popover-test-cases.js
@@ -292,7 +292,7 @@ noDisplayCases.allProps = {
     getAnchorElement: () => {},
     children: getPopoverContent(),
     placement: 'left',
-    padding: false,
+    padding: 'none',
     hasPointer: false,
     hideWhenAnchorIsOffscreen: true,
     allowPlacementAxisChange: false,