Repository: mapbox/mr-ui
Pull request #38 (2018-08-12 20:53:37)

Remove IconButton, with lots of fallout
Things got a little out of hand here. I tried to improve IconButton and failed, ended up deleting it, and had to change quite a few things as a result. I decided to delete IconButton because it doesn't provide sufficient value. Before Tooltip could accept a non-function child, it made some sense to avoid having to work with the awkwardness of Tooltip; but now it's not trouble to use Tooltip directly. Almost none of the IconButtons that we have out in the wild share much styling, so I think it makes sense to remove IconButton and expect people to use Tooltip and Icon on their own, with whatever styling suits there needs.

Especially because I've been doing this work pretty much solo, I'm not going to burden anybody with reviewing such a nasty PR. Instead, some time early next week when I've merged this and the other outstanding PR, I'd like to talk with @samanpwbb about a sweeping review of all the changes that are currently in the `HEAD` of the changelog.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4f5cff8..c3db830 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
 
 ## HEAD
 
+- 🚨 [breaking change] Remove **IconButton** component.
 - [feature] Add **Heading** component.
 - [feature] Add **Button** component.
 - [feature] Add **IconText** component.
@@ -9,21 +10,29 @@
 - **Popover**
   - 🚨 [breaking change] Remove `themePopover`, `backgroundColor`, and `accessibleTitle` props.
   - 🚨 [breaking change] Rename `contentElementAttributes` prop to `passthroughProps`, to match incoming convention.
-  - [feature] Add `coloring` and `padded`.
+  - [feature] Add `coloring` and `padding`.
   - [chore] Update `focus-trap` and `tabbable` dependencies.
 - **PopoverTrigger**
-  - 🚨 [breaking change] Rename `triggerProps` to `passthroughTriggerProps`.
+  - 🚨 [breaking change] Rename `triggerProps` prop to `passthroughTriggerProps`.
+  - 🚨 [breaking change] Replace `display` prop with `block`.
   - [fix] If the trigger responds to focus but not click, and you focus the trigger *first* and *then* click, that click *closes* the popover instead of leaving it open even after you move the mouse away.
+  - [fix] If you use `disable` to disable a trigger with an open popover, the popover will hide.
 - **CopyButton**
+  - 🚨 [breaking change] Arbitrary props will not be passed through to the button.
+  - 🚨 [breaking change] Remove `iconButtonProps` and `feedbackTime` props.
+  - [feature] Add `block`, `className`, and `passthroughProps` props.
+  - [feature] Add `CopyButton.copyIsSupported()` static function.
+  - [feature] Improve appearance of copy instructions popover.
   - [fix] Works in modals.
 - **Tooltip**
   - 🚨 [breaking change] Remove `backgroundColor` and `themeTooltip` props.
-  - [feature] Add `coloring`, `padded`, and `maxWidth` props.
+  - 🚨 [breaking change] Replace `display` prop with `block`.
+  - [feature] Add `coloring`, `padding`, and `maxWidth` props.
   - [feature] Children of Tooltip no longer need to be functions! If you provide a standard DOM element (e.g. `<button>`, `<div>`, `<span>`) or a Button component, things will just work. You can still use a function if your trigger is a custom component.
 - **Modal**
   - 🚨 [breaking change] Remove `underlayStyle`, `underlayProps`, `themeUnderlay`, `themeModal`, and `themeButtonClose` props.
   - 🚨 [breaking change] Rename `title` to `accessibleTitle`.
-  - [feature] Add `padded`, `alert`, `primaryAction`, `secondaryAction`, and `tertiaryAction` props.
+  - [feature] Add `padding`, `alert`, `primaryAction`, `secondaryAction`, and `tertiaryAction` props.
   - [fix] Modals taller than the viewport will now show padding above them: the browser will not longer automatically scroll down to the top of the modal.
   - [fix] Update `react-aria-modal` to get some bug fixes and focus-management improvements.
 
diff --git a/package.json b/package.json
index 8dae9ec..6ce1f2f 100644
--- a/package.json
+++ b/package.json
@@ -79,6 +79,7 @@
     "enzyme": "^3.0.0",
     "enzyme-adapter-react-16": "^1.0.0",
     "enzyme-to-json": "^3.3.4",
+    "es6-promise": "^4.2.4",
     "eslint": "^5.1.0",
     "eslint-config-prettier": "^2.9.0",
     "eslint-plugin-import": "^2.13.0",
diff --git a/src/components/button/button.js b/src/components/button/button.js
index 4106830..2fff445 100644
--- a/src/components/button/button.js
+++ b/src/components/button/button.js
@@ -184,6 +184,8 @@ Button.propTypes = {
   size: PropTypes.oneOf(['small', 'medium', 'large']),
   /**
    * The width of the button: `"small"`, `"medium"`, `"large"`, or `"full"`.
+   * For every option but `"full"` this prop determines horizontal padding.
+   * `"full"` sets the button to fill the width of its container.
    *
    * This is distinguished from `size` because buttons of the same general size
    * can vary by width.
@@ -207,11 +209,14 @@ Button.propTypes = {
   /**
    * If `true`, the element will be `block` displayed instead of `inline-block`.
    *
-   * This is sometimes necessary to get your pixel-perfect layout.
+   * This is sometimes necessary to get your pixel-perfect layout, if you don't
+   * want the extra line-height that wraps inline elements. Typically, you
+   * should only set `block` to `true` if the parent element is controlling
+   * width (in a layout that uses flexbox, absolute positioning, or floats).
    */
   block: PropTypes.bool,
   /**
-   * Is it disbaled?
+   * Is it disabled?
    */
   disabled: PropTypes.bool,
   /**
@@ -222,7 +227,7 @@ Button.propTypes = {
   component: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
   /**
    * An object of props that you want to pass through to the element that
-   * Button renders.  This can be useful if you want to disable
+   * Button renders. This can be useful if you want to disable
    * the button, assign an ID for testing, add an ARIA attribute, toss in some
    * custom style properties, etc.
    */
diff --git a/src/components/control-file/control-file.js b/src/components/control-file/control-file.js
index 57ec5e0..68dd65a 100644
--- a/src/components/control-file/control-file.js
+++ b/src/components/control-file/control-file.js
@@ -1,7 +1,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import omit from '../utils/omit';
-import IconButton from '../icon-button';
+import Tooltip from '../tooltip';
 import Icon from '../icon';
 import ControlLabel from '../control-label';
 import ControlWrapper from '../control-wrapper';
@@ -176,14 +176,16 @@ export default class ControlFile extends React.Component {
           </div>
           {this.state.displayValue && (
             <div className="flex-child">
-              <IconButton
-                icon="trash"
-                tooltipProps={{
-                  content: 'Clear'
-                }}
-                themeButton={`link link--gray relative bg-transparent py0 ${themeControlFileClear}`}
-                onClick={this.onClear}
-              />
+              <Tooltip content="Clear" block={true}>
+                <button
+                  type="button"
+                  className={`block link link--gray relative bg-transparent py0 ${themeControlFileClear}`}
+                  onClick={this.onClear}
+                  data-test="control-file-clear"
+                >
+                  <Icon name="trash" />
+                </button>
+              </Tooltip>
             </div>
           )}
         </div>
diff --git a/src/components/copiable/copiable.js b/src/components/copiable/copiable.js
index 52d5d90..01605b3 100644
--- a/src/components/copiable/copiable.js
+++ b/src/components/copiable/copiable.js
@@ -4,39 +4,29 @@ import CopyButton from '../copy-button';
 import Popover from '../popover';
 import OSKey from 'os-key';
 import select from 'select';
-import Clipboard from 'clipboard/dist/clipboard.min.js';
-import debounce from 'debounce';
 import getWindow from '../utils/get-window';
 
 const DISABLE_CLICK_TO_SELECT_THRESHOLD = 640;
 const FEEDBACK_TIME = 2000;
 
+/**
+ * Make some text easily copiable.
+ *
+ * Comes with a [CopyButton](#copybutton) that you can click to automatically
+ * copy the text.
+ *
+ * Also, on wider viewports a click on the text will automatically select it
+ * all, so you can easily copy with your favorite keyboard shortcut.
+ */
 export default class Copiable extends React.Component {
-  static propTypes = {
-    /** The text that will be displayed and copied. */
-    value: PropTypes.string.isRequired,
-    /** Styling for the component wrapper. */
-    themeWrapper: PropTypes.string,
-    /** Styling for the copy button's container. */
-    themeCopyButtonContainer: PropTypes.string,
-    /** Styling for the copy button. */
-    themeCopyButton: PropTypes.string,
-    /** Styling for the text readout. */
-    themeText: PropTypes.string,
-    /** Styling for the copy hint tooltip. */
-    themeTooltip: PropTypes.string
-  };
-  static defaultProps = {
-    themeWrapper: 'relative round z0 scroll-styled',
-    themeCopyButtonContainer: 'absolute z2 top right mr3 mt3 color-white',
-    themeCopyButton: 'btn btn--xs py3 px3 round',
-    themeText: 'color-gray bg-darken5 round-l txt-mono py6 px6 pr36 txt-s'
-  };
+  constructor(props) {
+    super(props);
 
-  state = {
-    copyTooltipActive: false,
-    disableClickToSelect: false
-  };
+    this.copyHintText = '';
+    this.state = {
+      copyTooltipActive: false
+    };
+  }
 
   showCopyTooltip = () => {
     this.setState({ copyTooltipActive: true }, () => {
@@ -47,36 +37,19 @@ export default class Copiable extends React.Component {
   };
 
   componentDidMount() {
-    const { handleWindowResize } = this;
-    handleWindowResize();
-    window.addEventListener('resize', handleWindowResize);
-    this.copyHintText = `${this.getCopyKeys(
-      getWindow().navigator.userAgent
-    )} to copy!`;
+    this.copyHintText = (
+      <span>
+        <span className="txt-kbd">
+          {getCopyKeys(getWindow().navigator.userAgent)}
+        </span>{' '}
+        to copy
+      </span>
+    );
   }
 
-  componentWillUnmount = () => {
-    const { handleWindowResize, revertTimer } = this;
-    if (revertTimer) {
-      clearTimeout(revertTimer);
-    }
-    window.removeEventListener('resize', handleWindowResize);
-  };
-
-  handleWindowResize = debounce(() => {
-    const w = this.getWindowWidth();
-    if (
-      w <= DISABLE_CLICK_TO_SELECT_THRESHOLD &&
-      !this.state.disableClickToSelect
-    ) {
-      this.setState({ disableClickToSelect: true });
-    } else if (
-      w > DISABLE_CLICK_TO_SELECT_THRESHOLD &&
-      this.state.disableClickToSelect
-    ) {
-      this.setState({ disableClickToSelect: false });
-    }
-  }, 300);
+  componentWillUnmount() {
+    clearTimeout(this.revertTimer);
+  }
 
   setTextEl = el => {
     this.textEl = el;
@@ -90,72 +63,75 @@ export default class Copiable extends React.Component {
     return getWindow().innerWidth;
   }
 
-  getCopyKeys = ua => {
-    const keys = OSKey(ua);
-    if (!keys) return;
-    if (keys.primaryMeta) {
-      // ⌘+C
-      return `${keys.meta.symbol}+C`;
-    } else {
-      // Ctrl+C
-      return `${keys.ctrl.abbreviation}+C`;
-    }
-  };
-
-  handleSelect = () => {
-    if (this.state.disableClickToSelect) return;
+  handleTextFocus = () => {
+    if (getWindow().innerWidth < DISABLE_CLICK_TO_SELECT_THRESHOLD) return;
     select(this.getTextEl());
     this.showCopyTooltip();
   };
 
+  handleTextBlur = event => {
+    if (!this.textEl.contains(event.relatedTarget)) {
+      this.setState({ copyTooltipActive: false });
+    }
+  };
+
   render() {
-    const {
-      value,
-      themeWrapper,
-      themeCopyButton,
-      themeCopyButtonContainer,
-      themeText,
-      themeTooltip
-    } = this.props;
-
-    const quickSelect = (
-      <span ref={this.setTextEl} onClick={this.handleSelect}>
-        {this.props.value}
-      </span>
-    );
+    const { props } = this;
 
     const copyBtn = typeof document !== 'undefined' &&
-      Clipboard.isSupported() && (
-        <div className={themeCopyButtonContainer}>
-          <CopyButton
-            text={value}
-            iconButtonProps={{
-              themeButton: themeCopyButton
-            }}
-            textEl={this.textEl}
-          />
+      CopyButton.isCopySupported() && (
+        <div className="fr py6 px6">
+          <CopyButton text={props.value} textEl={this.textEl} block={true} />
         </div>
       );
 
     const copyHintPopover = this.state.copyTooltipActive && (
       <Popover
-        themePopover={themeTooltip}
         getAnchorElement={this.getTextEl}
         placement="top"
         alignment="center"
         hideWhenAnchorIsOffscreen={true}
-        accessibleTitle={this.copyHintText}
+        accessibleTitle="Copy the selected text"
+        padding="small"
       >
         <div className="txt-s">{this.copyHintText}</div>
       </Popover>
     );
 
     return (
-      <div className={themeWrapper}>
-        <div className={themeText}>{quickSelect}</div>
+      <div className="clearfix bg-darken5 round">
         {copyBtn}
+        <div
+          tabIndex="-1"
+          ref={this.setTextEl}
+          onFocus={this.handleTextFocus}
+          onBlur={this.handleTextBlur}
+          className="py6 px12 txt-mono txt-s"
+          data-test="copiable-text-el"
+        >
+          <div className="my3">{props.value}</div>
+        </div>
         {copyHintPopover}
       </div>
     );
   }
 }
+
+Copiable.propTypes = {
+  /**
+   * The text that will be displayed and copied.
+   */
+  value: PropTypes.string.isRequired
+};
+
+function getCopyKeys(ua) {
+  const keys = OSKey(ua);
+  if (!keys) return;
+  if (keys.primaryMeta) {
+    // ⌘+C
+    return `${keys.meta.symbol}+C`;
+  } else {
+    // Ctrl+C
+    return `${keys.ctrl.abbreviation}+C`;
+  }
+}
diff --git a/src/components/copiable/examples/copiable-a.js b/src/components/copiable/examples/copiable-a.js
new file mode 100644
index 0000000..7fe5352
--- /dev/null
+++ b/src/components/copiable/examples/copiable-a.js
@@ -0,0 +1,20 @@
+/*
+All Copiables work the same way.
+*/
+import React from 'react';
+import Copiable from '../copiable';
+
+export default class Example extends React.Component {
+  render() {
+    return (
+      <div>
+        <div className="mb12">
+          <Copiable value="https://www.mapbox.com/something/special/for/you" />
+        </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" />
+        </div>
+      </div>
+    );
+  }
+}
diff --git a/src/components/copiable/examples/copiable-example-basic.js b/src/components/copiable/examples/copiable-example-basic.js
deleted file mode 100644
index a152842..0000000
--- a/src/components/copiable/examples/copiable-example-basic.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
-Basic.
-*/
-import React from 'react';
-import Copiable from '../copiable';
-
-export default class Example extends React.Component {
-  state = { text: '' };
-
-  onCopy = text => {
-    this.setState({ text });
-  };
-
-  render() {
-    return (
-      <Copiable 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/copy-button/copy-button.js b/src/components/copy-button/copy-button.js
index 15cbbf7..48d24f0 100644
--- a/src/components/copy-button/copy-button.js
+++ b/src/components/copy-button/copy-button.js
@@ -1,47 +1,22 @@
 import React from 'react';
+import classnames from 'classnames';
 import PropTypes from 'prop-types';
-import xtend from 'xtend';
 import Clipboard from 'clipboard/dist/clipboard.min.js';
-import IconButton from '../icon-button';
 import Icon from '../icon';
 import Popover from '../popover';
-import omit from '../utils/omit';
-
+import Tooltip from '../tooltip';
+
+const FEEDBACK_TIME = 800;
+
+/**
+ * A button that, when clicked, copies the designated text to the clipboard.
+ *
+ * The static function `CopyButton.isCopySupported` returns a boolean
+ * indicating whether the current browser will support automatic copying.
+ * If it does not, you might want to hide your copy button and make sure
+ * the user is able to manually select and copy the text.
+ */
 export default class CopyButton extends React.PureComponent {
-  // If you add propsTypes, add the corresponding propNames.
-  static propTypes = {
-    /** The text that will be copied when the button is clicked. */
-    text: PropTypes.string.isRequired,
-    /**
-     * Pass in an element containing text to copy instead of the raw text to provide a better fallback,
-     * where the text is selected for you if execcopy is not supported
-     */
-    textEl: PropTypes.object,
-    /** The interval for which `Copied!` is displayed to the user. */
-    feedbackTime: PropTypes.number,
-    /**
-     * Invoked when the button is clicked.
-     * Passed one argument: the `text` prop.
-     */
-    onCopy: PropTypes.func,
-    /** Extra props to pass to the IconButton component. */
-    iconButtonProps: PropTypes.object
-  };
-  static propNames = [
-    'text',
-    'feedbackTime',
-    'onCopy',
-    'iconButtonProps',
-    'textEl'
-  ];
-
-  static defaultProps = {
-    feedbackTime: 800,
-    iconButtonProps: {
-      themeButton: 'btn--xs py3 px3 round'
-    }
-  };
-
   state = {
     showingFeedback: false
   };
@@ -53,7 +28,7 @@ export default class CopyButton extends React.PureComponent {
   }
 
   componentWillUnmount() {
-    if (this.revertTimer) clearTimeout(this.revertTimer);
+    clearTimeout(this.revertTimer);
     if (this.clipboard) {
       this.clipboard.destroy();
     }
@@ -72,7 +47,7 @@ export default class CopyButton extends React.PureComponent {
     this.setState({ showingFeedback: true });
     this.revertTimer = setTimeout(() => {
       this.setState({ showingFeedback: false });
-    }, this.props.feedbackTime);
+    }, FEEDBACK_TIME);
   };
 
   componentWillReceiveProps(nextProps) {
@@ -106,6 +81,27 @@ export default class CopyButton extends React.PureComponent {
     });
   };
 
+  renderFeedbackPopover() {
+    const { state } = this;
+    if (!state.showingFeedback) {
+      return null;
+    }
+
+    return (
+      <Popover
+        // We don't need this one to be as accessible as a regular tooltip
+        getAnchorElement={this.getContainer}
+        placement="top"
+        alignment="center"
+        hideWhenAnchorIsOffscreen={true}
+        accessibleTitle={null}
+        padding="small"
+      >
+        <div className="txt-s">Copied!</div>
+      </Popover>
+    );
+  }
+
   render() {
     const { props, state } = this;
     // don't render the button if you can't use it to copy,
@@ -114,67 +110,76 @@ export default class CopyButton extends React.PureComponent {
       return null;
     }
 
-    let tooltipText = state.showingFeedback ? 'Copied!' : 'Copy';
-    let icon = state.showingFeedback ? 'check' : 'clipboard';
-
-    const extraProps = omit(this.props, CopyButton.propNames);
-    let iconButtonProps = props.iconButtonProps;
-    let tooltipProps = {
-      content: tooltipText
-    };
-
-    if (props.iconButtonProps.tooltipProps) {
-      tooltipProps = xtend(tooltipProps, props.iconButtonProps.tooltipProps);
-    }
-    iconButtonProps.tooltipProps = tooltipProps;
-
-    let button = null;
-    if (state.showingFeedback) {
-      // When the action is complete, we switch from the tooltip-powered IconButton
-      // to a regular old button a regular old Popover. We do this so we can
-      // control the display of the Popover, for it to be there for the full
-      // this.props.feedbackTime.
-      button = (
-        <button
-          type="button"
-          className={`btn ${props.iconButtonProps.themeButton}`}
-          {...extraProps}
-        >
-          <Icon name={icon} />
-          <Popover
-            // We don't need this one to be as accessible as a regular tooltip
-            getAnchorElement={this.getContainer}
-            placement="top"
-            alignment="center"
-            hideWhenAnchorIsOffscreen={true}
-            accessibleTitle={null}
-          >
-            <span className="block txt-s">{tooltipText}</span>
-          </Popover>
-        </button>
-      );
-    } else {
-      button = (
-        <IconButton
-          onClick={this.handleCopyButtonClick}
-          icon={icon}
-          {...iconButtonProps}
-          {...extraProps}
-        />
-      );
-    }
+    const iconName = state.showingFeedback ? 'check' : 'clipboard';
+    const buttonClasses = classnames(props.className, {
+      block: props.block
+    });
 
     // data-clipboard-text and the container ref are used by clipboard.js
     // to copy text if you do not pass in a textEl as a prop.
     // Note that this wont have as nice a failure mode.
     return (
-      <div
-        ref={this.setContainer}
-        className="inline-block"
-        data-clipboard-text={this.props.text}
+      <Tooltip
+        disabled={state.showingFeedback}
+        content="Copy"
+        block={props.block}
       >
-        {button}
-      </div>
+        <button
+          type="button"
+          className={buttonClasses}
+          {...props.passthroughProps}
+          ref={this.setContainer}
+          data-clipboard-text={props.text}
+          onClick={this.handleCopyButtonClick}
+        >
+          <Icon name={iconName} />
+          {this.renderFeedbackPopover()}
+        </button>
+      </Tooltip>
     );
   }
 }
+
+CopyButton.propTypes = {
+  /**
+   * The text that will be copied when the button is clicked.
+   */
+  text: PropTypes.string.isRequired,
+  /**
+   * Pass in an element containing text to copy instead of the raw text
+   * to provide a better fallback, where the text is selected for you if
+   * `execcopy` is not supported.
+   */
+  textEl: PropTypes.object,
+  /**
+   * Invoked when the button is clicked.
+   * Passed one argument: the `text` prop.
+   */
+  onCopy: PropTypes.func,
+  /**
+   * If `true`, the element will be `block` displayed instead of `inline-block`.
+   *
+   * This is sometimes necessary to get your pixel-perfect layout, if you don't
+   * want the extra line-height that wraps inline elements. Typically, you
+   * should only set `block` to `true` if the parent element is controlling
+   * width (in a layout that uses flexbox, absolute positioning, or floats).
+   */
+  block: PropTypes.bool,
+  /**
+   * The `className` prop of the `<button>`.
+   */
+  className: PropTypes.string,
+  /**
+   * An object of props that you want to pass through to the `<button>`.
+   */
+  passthroughProps: PropTypes.object
+};
+
+CopyButton.defaultProps = {
+  className: 'btn btn--xs py3 px3 round',
+  block: false
+};
+
+CopyButton.isCopySupported = () => {
+  return Clipboard.isSupported();
+};
diff --git a/src/components/copy-button/examples/copy-button-a.js b/src/components/copy-button/examples/copy-button-a.js
new file mode 100644
index 0000000..1804b03
--- /dev/null
+++ b/src/components/copy-button/examples/copy-button-a.js
@@ -0,0 +1,23 @@
+/*
+Defaults.
+*/
+import React from 'react';
+import CopyButton from '../copy-button';
+
+export default class Example extends React.Component {
+  render() {
+    return (
+      <div className="flex-parent flex-parent--center-cross">
+        <div className="flex-child mr24">
+          <CopyButton text="Hello explorer" block={true} />
+        </div>
+        <div className="flex-child flex-child--grow">
+          <input
+            className="input input--s wmax360"
+            placeholder="Paste text here"
+          />
+        </div>
+      </div>
+    );
+  }
+}
diff --git a/src/components/copy-button/examples/copy-button-b.js b/src/components/copy-button/examples/copy-button-b.js
new file mode 100644
index 0000000..10d0a85
--- /dev/null
+++ b/src/components/copy-button/examples/copy-button-b.js
@@ -0,0 +1,26 @@
+/*
+Custom styling.
+*/
+import React from 'react';
+import CopyButton from '../copy-button';
+
+export default class Example extends React.Component {
+  render() {
+    return (
+      <div className="flex-parent flex-parent--center-cross">
+        <div className="flex-child mr24">
+          <CopyButton
+            text="Hello fancy explorer"
+            className="px6 py6 btn btn--purple btn--stroke"
+          />
+        </div>
+        <div className="flex-child flex-child--grow">
+          <input
+            className="input input--s wmax360"
+            placeholder="Paste text here"
+          />
+        </div>
+      </div>
+    );
+  }
+}
diff --git a/src/components/copy-button/examples/copy-button-example-basic.js b/src/components/copy-button/examples/copy-button-example-basic.js
deleted file mode 100644
index 659f9c3..0000000
--- a/src/components/copy-button/examples/copy-button-example-basic.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
-Basic.
-*/
-import React from 'react';
-import CopyButton from '../copy-button';
-
-export default class Example extends React.Component {
-  state = { text: '' };
-
-  onCopy = text => {
-    this.setState({ text });
-  };
-
-  render() {
-    return (
-      <CopyButton
-        text="Hello explorer"
-        feedbackTime={500}
-        onCopy={this.onCopy}
-      />
-    );
-  }
-}
diff --git a/src/components/copy-button/examples/copy-button-example-options.js b/src/components/copy-button/examples/copy-button-example-options.js
deleted file mode 100644
index e535ea1..0000000
--- a/src/components/copy-button/examples/copy-button-example-options.js
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
-Options.
-*/
-import React from 'react';
-import CopyButton from '../copy-button';
-
-export default class Example extends React.Component {
-  state = { text: '' };
-
-  onCopy = text => {
-    this.setState({ text });
-  };
-
-  render() {
-    return (
-      <CopyButton
-        text="Hello explorer"
-        feedbackTime={500}
-        onCopy={this.onCopy}
-        iconButtonProps={{
-          themeButton: 'btn--xs py3 px3 round bg-pink',
-          tooltipProps: {
-            content: 'Copy?'
-          }
-        }}
-      />
-    );
-  }
-}
diff --git a/src/components/icon-button/examples/icon-button-example-basic.js b/src/components/icon-button/examples/icon-button-example-basic.js
deleted file mode 100644
index 16506bc..0000000
--- a/src/components/icon-button/examples/icon-button-example-basic.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
-Basic.
-*/
-import React from 'react';
-import IconButton from '../icon-button';
-
-export default class Example extends React.Component {
-  render() {
-    return (
-      <IconButton
-        onClick={() => {
-          /* console.log('triggered on click') */
-        }}
-        icon="close"
-        tooltipProps={{
-          content: 'Close'
-        }}
-      />
-    );
-  }
-}
diff --git a/src/components/icon-button/examples/icon-button-example-options.js b/src/components/icon-button/examples/icon-button-example-options.js
deleted file mode 100644
index 65e73f9..0000000
--- a/src/components/icon-button/examples/icon-button-example-options.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
-Options.
-*/
-import React from 'react';
-import IconButton from '../icon-button';
-
-export default class Example extends React.Component {
-  render() {
-    return (
-      <IconButton
-        onClick={() => {
-          /* console.log('triggered on click') */
-        }}
-        icon="bike"
-        themeButton="py12 px12 bg-gray"
-        themeIcon="color-gray-dark"
-        tooltipProps={{
-          content: <strong className="txt-bold">tada</strong>,
-          placement: 'right'
-        }}
-      />
-    );
-  }
-}
diff --git a/src/components/icon-button/icon-button.js b/src/components/icon-button/icon-button.js
deleted file mode 100644
index ab3de8d..0000000
--- a/src/components/icon-button/icon-button.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import xtend from 'xtend';
-import Tooltip from '../tooltip';
-import Icon from '../icon';
-import omit from '../utils/omit';
-
-export default class IconButton extends React.PureComponent {
-  // If you change propTypes, also change propNames.
-  static propTypes = {
-    /** Callback when button is clicked. */
-    onClick: PropTypes.func.isRequired,
-    /** Name of the icon to use. */
-    icon: PropTypes.string.isRequired,
-    /** Extra props to pass to the Tooltip component. tooltipProps.content is required. */
-    tooltipProps: PropTypes.object,
-    /** Assembly classes to apply to the button element */
-    themeButton: PropTypes.string,
-    /** Assembly classes to apply to the icon element */
-    themeIcon: PropTypes.string
-  };
-
-  static propNames = [
-    'onClick',
-    'icon',
-    'tooltipProps',
-    'themeButton',
-    'themeIcon'
-  ];
-
-  static defaultProps = {
-    themeButton: 'btn--xs px0'
-  };
-
-  getBody = (triggerProps: Object) => {
-    const themeButton = `btn ${this.props.themeButton}`;
-    const extraProps = omit(this.props, IconButton.propNames);
-    const buttonProps = xtend(extraProps, triggerProps, {
-      type: 'button',
-      className: themeButton,
-      onClick: this.props.onClick
-    });
-    const iconProps = {
-      name: this.props.icon
-    };
-    if (this.props.themeIcon !== undefined) {
-      iconProps.themeIcon = this.props.themeIcon;
-    }
-
-    return (
-      <button {...buttonProps}>
-        <Icon {...iconProps} />
-      </button>
-    );
-  };
-
-  render() {
-    return <Tooltip {...this.props.tooltipProps}>{this.getBody}</Tooltip>;
-  }
-}
diff --git a/src/components/icon-button/index.js b/src/components/icon-button/index.js
deleted file mode 100644
index 5c8cb19..0000000
--- a/src/components/icon-button/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import main from './icon-button';
-
-export default main;
diff --git a/src/components/modal/examples/modal-b.js b/src/components/modal/examples/modal-b.js
index e9fa776..902f6af 100644
--- a/src/components/modal/examples/modal-b.js
+++ b/src/components/modal/examples/modal-b.js
@@ -30,7 +30,7 @@ export default class Example extends React.Component {
       <Modal
         accessibleTitle="Animals"
         onExit={this.closeModal}
-        padded={false}
+        padding="none"
         size="small"
         initialFocus="#modal-input"
       >
diff --git a/src/components/modal/modal.js b/src/components/modal/modal.js
index 328dd32..1c4ecd8 100644
--- a/src/components/modal/modal.js
+++ b/src/components/modal/modal.js
@@ -2,7 +2,8 @@ import React from 'react';
 import classnames from 'classnames';
 import PropTypes from 'prop-types';
 import AriaModal from 'react-aria-modal';
-import IconButton from '../icon-button';
+import Tooltip from '../tooltip';
+import Icon from '../icon';
 import getWindow from '../utils/get-window';
 import ModalActions from './modal-actions';
 
@@ -37,7 +38,7 @@ export default class Modal extends React.Component {
       if (offsetParent.tagName === 'BODY' || offsetParent.tagName === 'HTML') {
         return;
       }
-      offsetParent.scrollTo(0, 0);
+      offsetParent.scrollTop = 0;
     }, 0);
   }
 
@@ -74,14 +75,16 @@ export default class Modal extends React.Component {
     if (props.onExit) {
       closeButton = (
         <div className="absolute top right">
-          <IconButton
-            icon="close"
-            onClick={props.onExit}
-            tooltipProps={{
-              content: 'Close'
-            }}
-            themeButton="btn btn--transparent unround-t unround-br color-gray py12 px12"
-          />
+          <Tooltip block={true} content="Close">
+            <button
+              type="button"
+              className="btn btn--transparent unround-t unround-br color-gray py12 px12"
+              onClick={props.onExit}
+              data-test="modal-close"
+            >
+              <Icon name="close" />
+            </button>
+          </Tooltip>
         </div>
       );
     }
@@ -95,9 +98,7 @@ export default class Modal extends React.Component {
 
     const containerClasses = classnames(
       `relative wmax-full ${widthClass} bg-white round`,
-      {
-        'px36 py36': props.padded
-      }
+      { 'px36 py36': props.padding === 'large' }
     );
 
     const dialogBody = (
@@ -173,9 +174,9 @@ Modal.propTypes = {
    */
   children: PropTypes.node.isRequired,
   /**
-   * If `false`, the modal will not have its default padding.
+   * `'large'` or `'none'`.
    */
-  padded: PropTypes.bool,
+  padding: PropTypes.oneOf(['large', 'none']),
   /**
    * If `true`, the modal will have the accessibility props of an alert modal.
    * (Only affects screen readers.)
@@ -237,5 +238,5 @@ Modal.propTypes = {
 Modal.defaultProps = {
   size: 'large',
   getApplicationNode: () => document.getElementById('app'),
-  padded: true
+  padding: 'large'
 };
diff --git a/src/components/popover-trigger/popover-trigger.js b/src/components/popover-trigger/popover-trigger.js
index ce78fb0..44bc70f 100644
--- a/src/components/popover-trigger/popover-trigger.js
+++ b/src/components/popover-trigger/popover-trigger.js
@@ -1,5 +1,6 @@
 import React from 'react';
 import PropTypes from 'prop-types';
+import classnames from 'classnames';
 import hoverintent from 'hoverintent';
 import Popover from '../popover';
 
@@ -53,6 +54,10 @@ export default class PopoverTrigger extends React.Component {
     ) {
       popoverElement.addEventListener('mouseleave', this.onPopoverMouseLeave);
     }
+
+    if (!prevProps.disabled && this.props.disabled) {
+      this.hide();
+    }
   }
 
   componentWillUnmount() {
@@ -366,10 +371,15 @@ export default class PopoverTrigger extends React.Component {
       );
     }
 
+    const classes = classnames({
+      block: props.block,
+      'inline-block': !props.block
+    });
+
     return (
       <div
         ref={this.setTriggerElement}
-        style={{ display: props.display }}
+        className={classes}
         {...props.passthroughTriggerProps}
         onClick={this.onAnyClick}
         onFocus={this.onTriggerFocus}
@@ -390,10 +400,11 @@ PopoverTrigger.propTypes = {
    */
   children: PropTypes.node.isRequired,
   /**
-   * Accepts a CSS `display` value for the trigger: `'inline-block'` or
-   * `'block'`.
+   * If `true`, the element will be `block` displayed instead of `inline-block`.
+   *
+   * This is sometimes necessary to get your pixel-perfect layout.
    */
-  display: PropTypes.oneOf(['inline-block', 'block']),
+  block: PropTypes.bool,
   /**
    * The popover content. This can either be a string, valid JSX, or a function
    * returning either.
@@ -463,7 +474,7 @@ PopoverTrigger.propTypes = {
 };
 
 PopoverTrigger.defaultProps = {
-  display: 'inline-block',
+  block: false,
   disabled: false,
   renderHiddenContent: false,
   receiveFocus: true,
diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js
index 1cad1f7..40bed92 100644
--- a/src/components/popover/popover.js
+++ b/src/components/popover/popover.js
@@ -1,4 +1,5 @@
 import React from 'react';
+import classnames from 'classnames';
 import PropTypes from 'prop-types';
 import createFocusTrap from 'focus-trap';
 import tabbable from 'tabbable';
@@ -186,10 +187,13 @@ export default class Popover extends React.Component {
   render() {
     const { props } = this;
     const colors = getColors(props.coloring);
-    let bodyClasses = `${colors.textClass} shadow-darken25 round`;
-    if (props.padded) {
-      bodyClasses += ' px12 py12';
-    }
+    const bodyClasses = classnames(
+      `${colors.textClass} shadow-darken25 round`,
+      {
+        'px12 py12': props.padding === 'medium',
+        'px12 py6': props.padding === 'small'
+      }
+    );
 
     return (
       <PopoverPositioner
@@ -251,10 +255,9 @@ Popover.propTypes = {
    */
   coloring: PropTypes.oneOf(['light', 'dark', 'warning']),
   /**
-   * Set to `false` to remove the default padding on the popover body
-   * (and, hopefully, apply your own).
+   * `'medium'`, `'small'`, or `'none'`.
    */
-  padded: PropTypes.bool,
+  padding: PropTypes.oneOf(['medium', 'small', 'none']),
   /**
    * Whether or not the popover has a triangle pointer.
    */
@@ -337,7 +340,7 @@ Popover.defaultProps = {
   coloring: 'light',
   placement: 'right',
   alignment: 'top',
-  padded: true,
+  padding: 'medium',
   hasPointer: true,
   hideWhenAnchorIsOffscreen: false,
   allowPlacementAxisChange: true,
diff --git a/src/components/tooltip/tooltip.js b/src/components/tooltip/tooltip.js
index b3b83db..f17d318 100644
--- a/src/components/tooltip/tooltip.js
+++ b/src/components/tooltip/tooltip.js
@@ -1,6 +1,7 @@
 import React from 'react';
 import classnames from 'classnames';
 import PropTypes from 'prop-types';
+import Button from '../button';
 import PopoverTrigger from '../popover-trigger';
 
 let tooltipCounter = 0;
@@ -55,7 +56,7 @@ export default class Tooltip extends React.Component {
     if (children.type && typeof children.type === 'string') {
       return React.cloneElement(children, triggerProps);
     }
-    if (children.type && children.type.name === 'Button') {
+    if (children.type && children.type === Button) {
       return React.cloneElement(children, { passthroughProps: triggerProps });
     }
     throw new Error(
@@ -77,7 +78,6 @@ export default class Tooltip extends React.Component {
     }
 
     const bodyClasses = classnames({
-      'px12 py6': props.padded,
       'txt-s': props.textSize === 's',
       'txt-xs': props.textSize === 'xs',
       wmax120: props.maxWidth === 'small',
@@ -91,7 +91,7 @@ export default class Tooltip extends React.Component {
         content={content}
         renderHiddenContent={true}
         disabled={props.disabled}
-        display={props.display}
+        block={props.block}
         respondsToClick={props.respondsToClick}
         respondsToHover={true}
         respondsToFocus={true}
@@ -101,7 +101,7 @@ export default class Tooltip extends React.Component {
           placement: props.placement,
           alignment: props.alignment,
           coloring: props.coloring,
-          padded: false,
+          padding: props.padding,
           hideWhenAnchorIsOffscreen: true,
           passthroughProps: popoverPassthroughProps
         }}
@@ -157,18 +157,22 @@ Tooltip.propTypes = {
    */
   maxWidth: PropTypes.oneOf(['small', 'medium', 'none']),
   /**
-   * Set to `false` to remove the default padding on the popover body
-   * (and, hopefully, apply your own).
+   * `'small'` or `'none'`.
    */
-  padded: PropTypes.bool,
+  padding: PropTypes.oneOf(['small', 'none']),
   /**
    * If `true`, tooltip can be opened with a mouse click.
    */
   respondsToClick: PropTypes.bool,
   /**
-   * The CSS `display` value of the trigger element: '`block'` or '`inline-block'`.
+   * If `true`, the element will be `block` displayed instead of `inline-block`.
+   *
+   * This is sometimes necessary to get your pixel-perfect layout, if you don't
+   * want the extra line-height that wraps inline elements. Typically, you
+   * should only set `block` to `true` if the parent element is controlling
+   * width (in a layout that uses flexbox, absolute positioning, or floats).
    */
-  display: PropTypes.oneOf(['block', 'inline-block']),
+  block: PropTypes.bool,
   /**
    * Added as `data-test` to the tooltip element.
    */
@@ -181,8 +185,8 @@ Tooltip.defaultProps = {
   coloring: 'light',
   disabled: false,
   respondsToClick: false,
-  padded: true,
-  display: 'inline-block',
+  padding: 'small',
+  block: false,
   textSize: 's',
   maxWidth: 'medium'
 };


Test patch:
diff --git a/src/components/code-snippet/__tests__/__snapshots__/code-snippet.test.js.snap b/src/components/code-snippet/__tests__/__snapshots__/code-snippet.test.js.snap
index b4536ea..b716094 100644
--- a/src/components/code-snippet/__tests__/__snapshots__/code-snippet.test.js.snap
+++ b/src/components/code-snippet/__tests__/__snapshots__/code-snippet.test.js.snap
@@ -753,12 +753,8 @@ exports[`CodeSnippet copy ranges, with onCopy callback renders as expected 1`] =
     }
   >
     <CopyButton
-      feedbackTime={800}
-      iconButtonProps={
-        Object {
-          "themeButton": "btn--xs py3 px3 round",
-        }
-      }
+      block={false}
+      className="btn btn--xs py3 px3 round"
       onCopy={[Function]}
       text="import Mapbox
 "
@@ -776,12 +772,8 @@ exports[`CodeSnippet copy ranges, with onCopy callback renders as expected 1`] =
     }
   >
     <CopyButton
-      feedbackTime={800}
-      iconButtonProps={
-        Object {
-          "themeButton": "btn--xs py3 px3 round",
-        }
-      }
+      block={false}
+      className="btn btn--xs py3 px3 round"
       onCopy={[Function]}
       text="    let url = URL(string: \\"mapbox://styles/mapbox/streets-v9\\")
     let mapView = MGLMapView(frame: view.bounds, styleURL: url)
@@ -1065,12 +1057,8 @@ exports[`CodeSnippet highlighted, no ranges, with onCopy callback renders as exp
     className="absolute z2 top right mr6 mt6 color-white"
   >
     <CopyButton
-      feedbackTime={800}
-      iconButtonProps={
-        Object {
-          "themeButton": "btn--xs py3 px3 round",
-        }
-      }
+      block={false}
+      className="btn btn--xs py3 px3 round"
       onCopy={[MockFunction]}
       text="dependencies {
   compile('com.mapbox.mapboxsdk:mapbox-android-sdk:5.0.2@aar') {
diff --git a/src/components/control-file/__tests__/__snapshots__/control-file.test.js.snap b/src/components/control-file/__tests__/__snapshots__/control-file.test.js.snap
index ef99948..d350170 100644
--- a/src/components/control-file/__tests__/__snapshots__/control-file.test.js.snap
+++ b/src/components/control-file/__tests__/__snapshots__/control-file.test.js.snap
@@ -125,16 +125,30 @@ exports[`ControlFile basic receives an array of one file as value, displaying it
     <div
       className="flex-child"
     >
-      <IconButton
-        icon="trash"
-        onClick={[Function]}
-        themeButton="link link--gray relative bg-transparent py0 "
-        tooltipProps={
-          Object {
-            "content": "Clear",
-          }
-        }
-      />
+      <Tooltip
+        alignment="center"
+        block={true}
+        coloring="light"
+        content="Clear"
+        disabled={false}
+        maxWidth="medium"
+        padding="small"
+        placement="top"
+        respondsToClick={false}
+        textSize="s"
+      >
+        <button
+          className="block link link--gray relative bg-transparent py0 "
+          data-test="control-file-clear"
+          onClick={[Function]}
+          type="button"
+        >
+          <Icon
+            inline={false}
+            name="trash"
+          />
+        </button>
+      </Tooltip>
     </div>
   </div>
 </ControlWrapper>
@@ -314,16 +328,30 @@ exports[`ControlFile multiple receives an array of files as value, displaying th
     <div
       className="flex-child"
     >
-      <IconButton
-        icon="trash"
-        onClick={[Function]}
-        themeButton="link link--gray relative bg-transparent py0 "
-        tooltipProps={
-          Object {
-            "content": "Clear",
-          }
-        }
-      />
+      <Tooltip
+        alignment="center"
+        block={true}
+        coloring="light"
+        content="Clear"
+        disabled={false}
+        maxWidth="medium"
+        padding="small"
+        placement="top"
+        respondsToClick={false}
+        textSize="s"
+      >
+        <button
+          className="block link link--gray relative bg-transparent py0 "
+          data-test="control-file-clear"
+          onClick={[Function]}
+          type="button"
+        >
+          <Icon
+            inline={false}
+            name="trash"
+          />
+        </button>
+      </Tooltip>
     </div>
   </div>
 </ControlWrapper>
diff --git a/src/components/control-file/__tests__/control-file-test-cases.js b/src/components/control-file/__tests__/control-file-test-cases.js
index 1eabcf5..53e6b20 100644
--- a/src/components/control-file/__tests__/control-file-test-cases.js
+++ b/src/components/control-file/__tests__/control-file-test-cases.js
@@ -4,6 +4,34 @@ import safeSpy from '../../../test-utils/safe-spy';
 
 const testCases = {};
 
+class Interactive extends React.Component {
+  constructor(props) {
+    super(props);
+    this.state = { files: null };
+    this.setFiles = this.setFiles.bind(this);
+  }
+
+  setFiles(files) {
+    this.setState({ files });
+  }
+
+  render() {
+    return (
+      <ControlFile
+        id="interactive-file"
+        label="Select a file"
+        onChange={this.setFiles}
+        value={this.state.files}
+      />
+    );
+  }
+}
+
+testCases.interactive = {
+  description: 'Interactive',
+  element: <Interactive />
+};
+
 testCases.basic = {
   description: 'basic',
   component: ControlFile,
diff --git a/src/components/control-file/__tests__/control-file.test.js b/src/components/control-file/__tests__/control-file.test.js
index dfc9b18..ab771cb 100644
--- a/src/components/control-file/__tests__/control-file.test.js
+++ b/src/components/control-file/__tests__/control-file.test.js
@@ -52,10 +52,7 @@ describe('ControlFile', () => {
       const files = [bonJoviFile];
       wrapper.setProps({ value: files });
       wrapper.update();
-      wrapper
-        .find('IconButton')
-        .props()
-        .onClick();
+      wrapper.find({ 'data-test': 'control-file-clear' }).prop('onClick')();
       wrapper.update();
       expect(testCase.props.onChange).toHaveBeenCalledTimes(1);
       expect(testCase.props.onChange).toHaveBeenCalledWith('', 'testinput-1');
diff --git a/src/components/copiable/__tests__/__snapshots__/copiable.test.js.snap b/src/components/copiable/__tests__/__snapshots__/copiable.test.js.snap
index d33de59..2621836 100644
--- a/src/components/copiable/__tests__/__snapshots__/copiable.test.js.snap
+++ b/src/components/copiable/__tests__/__snapshots__/copiable.test.js.snap
@@ -1,171 +1,62 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`Copiable all props renders 1`] = `
+exports[`basic renders as expected 1`] = `
 <div
-  className="relative"
+  className="clearfix bg-darken5 round"
 >
   <div
-    className="color-gray bg-darken5 round-l txt-fancy py6 px6 txt-s"
-  >
-    <span
-      onClick={[Function]}
-    >
-      the text you copy the text you copy the text you copy the text you copy the text you copy 
-    </span>
-  </div>
-  <div
-    className="absolute z2 bottom right mr3 mt3 color-white"
+    className="fr py6 px6"
   >
     <CopyButton
-      feedbackTime={800}
-      iconButtonProps={
-        Object {
-          "themeButton": "btn round-r bg-gray-dark px0 py0",
-        }
-      }
+      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 "
     />
   </div>
-</div>
-`;
-
-exports[`Copiable basic Copy hint disappears at the right time 1`] = `
-<div
-  className="relative round z0 scroll-styled"
->
-  <div
-    className="color-gray bg-darken5 round-l txt-mono py6 px6 pr36 txt-s"
-  >
-    <span
-      onClick={[Function]}
-    >
-      the text you copy the text you copy the text you copy the text you copy the text you copy 
-    </span>
-  </div>
   <div
-    className="absolute z2 top right mr3 mt3 color-white"
-  >
-    <CopyButton
-      feedbackTime={800}
-      iconButtonProps={
-        Object {
-          "themeButton": "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 "
-    />
-  </div>
-  <Popover
-    accessibleTitle="⌘+C to copy!"
-    alignment="center"
-    allowPlacementAxisChange={true}
-    clickOutsideCloses={true}
-    coloring="light"
-    containWithinViewport={true}
-    escapeCloses={true}
-    getAnchorElement={[Function]}
-    hasPointer={true}
-    hideWhenAnchorIsOffscreen={true}
-    padded={true}
-    placement="top"
-    receiveFocus={true}
-    trapFocus={false}
+    className="py6 px12 txt-mono txt-s"
+    data-test="copiable-text-el"
+    onBlur={[Function]}
+    onFocus={[Function]}
+    tabIndex="-1"
   >
     <div
-      className="txt-s"
-    >
-      ⌘+C to copy!
-    </div>
-  </Popover>
-</div>
-`;
-
-exports[`Copiable basic Copy hint disappears at the right time 2`] = `
-<div
-  className="relative round z0 scroll-styled"
->
-  <div
-    className="color-gray bg-darken5 round-l txt-mono py6 px6 pr36 txt-s"
-  >
-    <span
-      onClick={[Function]}
+      className="my3"
     >
       the text you copy the text you copy the text you copy the text you copy the text you copy 
-    </span>
-  </div>
-  <div
-    className="absolute z2 top right mr3 mt3 color-white"
-  >
-    <CopyButton
-      feedbackTime={800}
-      iconButtonProps={
-        Object {
-          "themeButton": "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 "
-    />
+    </div>
   </div>
 </div>
 `;
 
-exports[`Copiable basic renders 1`] = `
+exports[`basic shows copy hint when focused 1`] = `
 <div
-  className="relative round z0 scroll-styled"
+  className="clearfix bg-darken5 round"
 >
   <div
-    className="color-gray bg-darken5 round-l txt-mono py6 px6 pr36 txt-s"
-  >
-    <span
-      onClick={[Function]}
-    >
-      the text you copy the text you copy the text you copy the text you copy the text you copy 
-    </span>
-  </div>
-  <div
-    className="absolute z2 top right mr3 mt3 color-white"
+    className="fr py6 px6"
   >
     <CopyButton
-      feedbackTime={800}
-      iconButtonProps={
-        Object {
-          "themeButton": "btn btn--xs py3 px3 round",
-        }
-      }
+      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 "
     />
   </div>
-</div>
-`;
-
-exports[`Copiable basic shows copy hint when clicked 1`] = `
-<div
-  className="relative round z0 scroll-styled"
->
   <div
-    className="color-gray bg-darken5 round-l txt-mono py6 px6 pr36 txt-s"
+    className="py6 px12 txt-mono txt-s"
+    data-test="copiable-text-el"
+    onBlur={[Function]}
+    onFocus={[Function]}
+    tabIndex="-1"
   >
-    <span
-      onClick={[Function]}
+    <div
+      className="my3"
     >
       the text you copy the text you copy the text you copy the text you copy the text you copy 
-    </span>
-  </div>
-  <div
-    className="absolute z2 top right mr3 mt3 color-white"
-  >
-    <CopyButton
-      feedbackTime={800}
-      iconButtonProps={
-        Object {
-          "themeButton": "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 "
-    />
+    </div>
   </div>
   <Popover
-    accessibleTitle="⌘+C to copy!"
+    accessibleTitle="Copy the selected text"
     alignment="center"
     allowPlacementAxisChange={true}
     clickOutsideCloses={true}
@@ -175,7 +66,7 @@ exports[`Copiable basic shows copy hint when clicked 1`] = `
     getAnchorElement={[Function]}
     hasPointer={true}
     hideWhenAnchorIsOffscreen={true}
-    padded={true}
+    padding="small"
     placement="top"
     receiveFocus={true}
     trapFocus={false}
@@ -183,7 +74,15 @@ exports[`Copiable basic shows copy hint when clicked 1`] = `
     <div
       className="txt-s"
     >
-      ⌘+C to copy!
+      <span>
+        <span
+          className="txt-kbd"
+        >
+          eh+C
+        </span>
+         
+        to copy
+      </span>
     </div>
   </Popover>
 </div>
diff --git a/src/components/copiable/__tests__/copiable-test-cases.js b/src/components/copiable/__tests__/copiable-test-cases.js
index aaf3cc8..d96489a 100644
--- a/src/components/copiable/__tests__/copiable-test-cases.js
+++ b/src/components/copiable/__tests__/copiable-test-cases.js
@@ -11,18 +11,4 @@ testCases.basic = {
   }
 };
 
-testCases.allProps = {
-  description: 'all props',
-  component: Copiable,
-  props: {
-    value:
-      'the text you copy the text you copy the text you copy the text you copy the text you copy ',
-    themeWrapper: 'relative',
-    themeCopyButtonContainer: 'absolute z2 bottom right mr3 mt3 color-white',
-    themeCopyButton: 'btn round-r bg-gray-dark px0 py0',
-    themeText: 'color-gray bg-darken5 round-l txt-fancy py6 px6 txt-s',
-    themeTooltip: 'round py6 px12 shadow-darken25 bg-blue'
-  }
-};
-
 export { testCases };
diff --git a/src/components/copiable/__tests__/copiable.test.js b/src/components/copiable/__tests__/copiable.test.js
index 39274e3..c80e24d 100644
--- a/src/components/copiable/__tests__/copiable.test.js
+++ b/src/components/copiable/__tests__/copiable.test.js
@@ -1,106 +1,75 @@
 import React from 'react';
 import { shallow } from 'enzyme';
 import delay from 'delay';
+import CopyButton from '../../copy-button';
 import { testCases } from './copiable-test-cases';
-import getWindow from '../../utils/get-window';
 
 const select = require('select');
 
 const FEEDBACK_TIME = 2000;
-const mockUserAgent =
-  'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36';
 const mockEvent = {
   preventDefault: jest.fn()
 };
 
-jest.mock('select');
-jest.mock('clipboard/dist/clipboard.min.js', () => {
-  class Clipboard {}
-  Clipboard.isSupported = () => true;
-  return Clipboard;
-});
-jest.mock('../../utils/get-window', () => {
-  return jest.fn();
-});
-select.mockImplementation(() => jest.fn());
+jest.mock('select', () => jest.fn());
+jest.mock('os-key', () =>
+  jest.fn(() => ({ primaryMeta: true, meta: { symbol: 'eh' } }))
+);
+jest.mock('../../utils/get-window', () =>
+  jest.fn(() => ({
+    navigator: {
+      userAgent: 'mock-user-agent'
+    }
+  }))
+);
+jest.spyOn(CopyButton, 'isCopySupported').mockImplementation(() => true);
 
-describe('Copiable', () => {
-  let testCase;
-  let wrapper;
+let testCase;
+let wrapper;
 
+describe(testCases.basic.description, () => {
   beforeEach(() => {
-    getWindow.mockReturnValue({
-      navigator: {
-        userAgent: mockUserAgent
-      }
-    });
+    testCase = testCases.basic;
+    wrapper = shallow(React.createElement(testCase.component, testCase.props));
   });
 
-  describe(testCases.basic.description, () => {
-    beforeEach(() => {
-      testCase = testCases.basic;
-      wrapper = shallow(
-        React.createElement(testCase.component, testCase.props)
-      );
-    });
+  test('renders as expected', () => {
+    expect(wrapper).toMatchSnapshot();
+  });
 
-    test('renders', () => {
-      expect(wrapper).toMatchSnapshot();
-    });
+  test('calls select library with element holding text to copy', () => {
+    const mockSelectableEl = <span>text to copy</span>;
+    wrapper.instance().getTextEl = jest.fn(() => mockSelectableEl);
+    expect(wrapper.find('Popover').length).toBe(0);
+    wrapper.find({ 'data-test': 'copiable-text-el' }).prop('onFocus')(
+      mockEvent
+    );
+    wrapper.update();
+    expect(select).toHaveBeenCalledTimes(1);
+    expect(wrapper.instance().getTextEl).toHaveBeenCalledTimes(1);
+    expect(select).toHaveBeenCalledWith(mockSelectableEl);
+  });
 
-    test('calls select library with element holding text to copy', () => {
-      const mockSelectableEl = <span>text to copy</span>;
-      wrapper.instance().getTextEl = jest.fn(() => mockSelectableEl);
-      expect(wrapper.find('Popover').length).toBe(0);
-      wrapper
-        .find('span')
-        .props()
-        .onClick(mockEvent);
-      wrapper.update();
-      expect(select).toHaveBeenCalledTimes(1);
-      expect(wrapper.instance().getTextEl).toHaveBeenCalledTimes(1);
-      expect(select).toHaveBeenCalledWith(mockSelectableEl);
-    });
+  test('shows copy hint when focused', () => {
+    expect(wrapper.find('Popover').length).toBe(0);
+    wrapper.find({ 'data-test': 'copiable-text-el' }).prop('onFocus')(
+      mockEvent
+    );
+    wrapper.update();
+    expect(wrapper.find('Popover').length).toBe(1);
+    expect(wrapper).toMatchSnapshot();
+  });
 
-    test('shows copy hint when clicked', () => {
-      expect(wrapper.find('Popover').length).toBe(0);
-      wrapper
-        .find('span')
-        .props()
-        .onClick(mockEvent);
+  test('copy hint disappears at the right time', () => {
+    expect(wrapper.find('Popover').length).toBe(0);
+    wrapper.find({ 'data-test': 'copiable-text-el' }).prop('onFocus')(
+      mockEvent
+    );
+    wrapper.update();
+    expect(wrapper.find('Popover').length).toBe(1);
+    return delay(FEEDBACK_TIME + 100).then(() => {
       wrapper.update();
-      expect(wrapper.find('Popover').length).toBe(1);
-      expect(wrapper).toMatchSnapshot();
-    });
-
-    test('Copy hint disappears at the right time', () => {
       expect(wrapper.find('Popover').length).toBe(0);
-      wrapper
-        .find('span')
-        .props()
-        .onClick(mockEvent);
-      wrapper.update();
-      expect(wrapper).toMatchSnapshot();
-      expect(wrapper.find('Popover').length).toBe(1);
-      return delay(FEEDBACK_TIME + 100).then(() => {
-        wrapper.update();
-        expect(wrapper.find('Popover').length).toBe(0);
-        expect(wrapper).toMatchSnapshot();
-      });
-    });
-  });
-
-  describe(testCases.allProps.description, () => {
-    beforeEach(() => {
-      testCase = testCases.allProps;
-      wrapper = shallow(
-        React.createElement(testCase.component, testCase.props)
-      );
-      wrapper.instance().selectText = jest.fn();
-    });
-
-    test('renders', () => {
-      expect(wrapper).toMatchSnapshot();
     });
   });
 });
diff --git a/src/components/copy-button/__tests__/__snapshots__/copy-button.test.js.snap b/src/components/copy-button/__tests__/__snapshots__/copy-button.test.js.snap
index 78f9220..ee01717 100644
--- a/src/components/copy-button/__tests__/__snapshots__/copy-button.test.js.snap
+++ b/src/components/copy-button/__tests__/__snapshots__/copy-button.test.js.snap
@@ -1,12 +1,22 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`CopyButton all props changes state on click 1`] = `
-<div
-  className="inline-block"
-  data-clipboard-text="more copiable text"
+exports[`CopyButton Defaults changes state on click 1`] = `
+<Tooltip
+  alignment="center"
+  block={false}
+  coloring="light"
+  content="Copy"
+  disabled={true}
+  maxWidth="medium"
+  padding="small"
+  placement="top"
+  respondsToClick={false}
+  textSize="s"
 >
   <button
-    className="btn px12 py12 btn--red"
+    className="btn btn--xs py3 px3 round"
+    data-clipboard-text="Copied by the default test case"
+    onClick={[Function]}
     type="button"
   >
     <Icon
@@ -24,89 +34,93 @@ exports[`CopyButton all props changes state on click 1`] = `
       getAnchorElement={[Function]}
       hasPointer={true}
       hideWhenAnchorIsOffscreen={true}
-      padded={true}
+      padding="small"
       placement="top"
       receiveFocus={true}
       trapFocus={false}
     >
-      <span
-        className="block txt-s"
+      <div
+        className="txt-s"
       >
         Copied!
-      </span>
+      </div>
     </Popover>
   </button>
-</div>
+</Tooltip>
 `;
 
-exports[`CopyButton all props changes state on click 2`] = `
-<div
-  className="inline-block"
-  data-clipboard-text="more copiable text"
+exports[`CopyButton Defaults changes state on click 2`] = `
+<Tooltip
+  alignment="center"
+  block={false}
+  coloring="light"
+  content="Copy"
+  disabled={false}
+  maxWidth="medium"
+  padding="small"
+  placement="top"
+  respondsToClick={false}
+  textSize="s"
 >
   <button
-    className="btn px12 py12 btn--red"
+    className="btn btn--xs py3 px3 round"
+    data-clipboard-text="Copied by the default test case"
+    onClick={[Function]}
     type="button"
   >
     <Icon
       inline={false}
-      name="check"
+      name="clipboard"
     />
-    <Popover
-      accessibleTitle={null}
-      alignment="center"
-      allowPlacementAxisChange={true}
-      clickOutsideCloses={true}
-      coloring="light"
-      containWithinViewport={true}
-      escapeCloses={true}
-      getAnchorElement={[Function]}
-      hasPointer={true}
-      hideWhenAnchorIsOffscreen={true}
-      padded={true}
-      placement="top"
-      receiveFocus={true}
-      trapFocus={false}
-    >
-      <span
-        className="block txt-s"
-      >
-        Copied!
-      </span>
-    </Popover>
   </button>
-</div>
+</Tooltip>
 `;
 
-exports[`CopyButton all props renders 1`] = `
-<div
-  className="inline-block"
-  data-clipboard-text="more copiable text"
+exports[`CopyButton Defaults renders as expected 1`] = `
+<Tooltip
+  alignment="center"
+  block={false}
+  coloring="light"
+  content="Copy"
+  disabled={false}
+  maxWidth="medium"
+  padding="small"
+  placement="top"
+  respondsToClick={false}
+  textSize="s"
 >
-  <IconButton
-    data-test="copy-button"
-    icon="clipboard"
+  <button
+    className="btn btn--xs py3 px3 round"
+    data-clipboard-text="Copied by the default test case"
     onClick={[Function]}
-    themeButton="px12 py12 btn--red"
-    themeIcon="color-yellow"
-    tooltipProps={
-      Object {
-        "content": "Copy",
-        "placement": "bottom",
-        "themeTooltip": "color-gray shadow-darken50",
-      }
-    }
-  />
-</div>
+    type="button"
+  >
+    <Icon
+      inline={false}
+      name="clipboard"
+    />
+  </button>
+</Tooltip>
 `;
 
-exports[`CopyButton basic changes state on click 1`] = `
-<div
-  className="inline-block"
-  data-clipboard-text="the text you copy"
+exports[`CopyButton all props changes state on click 1`] = `
+<Tooltip
+  alignment="center"
+  block={false}
+  coloring="light"
+  content="Copy"
+  disabled={true}
+  maxWidth="medium"
+  padding="small"
+  placement="top"
+  respondsToClick={false}
+  textSize="s"
 >
   <button
-    className="btn btn--xs py3 px3 round"
+    className="px6 py6 btn btn--purple btn--stroke"
+    data-clipboard-text="more copiable text"
+    data-test="copy-button"
+    onClick={[Function]}
     type="button"
   >
     <Icon
@@ -124,53 +138,73 @@ exports[`CopyButton basic changes state on click 1`] = `
       getAnchorElement={[Function]}
       hasPointer={true}
       hideWhenAnchorIsOffscreen={true}
-      padded={true}
+      padding="small"
       placement="top"
       receiveFocus={true}
       trapFocus={false}
     >
-      <span
-        className="block txt-s"
+      <div
+        className="txt-s"
       >
         Copied!
-      </span>
+      </div>
     </Popover>
   </button>
-</div>
+</Tooltip>
 `;
 
-exports[`CopyButton basic changes state on click 2`] = `
-<div
-  className="inline-block"
-  data-clipboard-text="the text you copy"
+exports[`CopyButton all props changes state on click 2`] = `
+<Tooltip
+  alignment="center"
+  block={false}
+  coloring="light"
+  content="Copy"
+  disabled={false}
+  maxWidth="medium"
+  padding="small"
+  placement="top"
+  respondsToClick={false}
+  textSize="s"
 >
-  <IconButton
-    icon="clipboard"
+  <button
+    className="px6 py6 btn btn--purple btn--stroke"
+    data-clipboard-text="more copiable text"
+    data-test="copy-button"
     onClick={[Function]}
-    themeButton="btn--xs py3 px3 round"
-    tooltipProps={
-      Object {
-        "content": "Copy",
-      }
-    }
-  />
-</div>
+    type="button"
+  >
+    <Icon
+      inline={false}
+      name="clipboard"
+    />
+  </button>
+</Tooltip>
 `;
 
-exports[`CopyButton basic renders 1`] = `
-<div
-  className="inline-block"
-  data-clipboard-text="the text you copy"
+exports[`CopyButton all props renders as expected 1`] = `
+<Tooltip
+  alignment="center"
+  block={false}
+  coloring="light"
+  content="Copy"
+  disabled={false}
+  maxWidth="medium"
+  padding="small"
+  placement="top"
+  respondsToClick={false}
+  textSize="s"
 >
-  <IconButton
-    icon="clipboard"
+  <button
+    className="px6 py6 btn btn--purple btn--stroke"
+    data-clipboard-text="more copiable text"
+    data-test="copy-button"
     onClick={[Function]}
-    themeButton="btn--xs py3 px3 round"
-    tooltipProps={
-      Object {
-        "content": "Copy",
-      }
-    }
-  />
-</div>
+    type="button"
+  >
+    <Icon
+      inline={false}
+      name="clipboard"
+    />
+  </button>
+</Tooltip>
 `;
diff --git a/src/components/copy-button/__tests__/copy-button-test-cases.js b/src/components/copy-button/__tests__/copy-button-test-cases.js
index abeba50..c84f000 100644
--- a/src/components/copy-button/__tests__/copy-button-test-cases.js
+++ b/src/components/copy-button/__tests__/copy-button-test-cases.js
@@ -5,11 +5,11 @@ import safeSpy from '../../../test-utils/safe-spy';
 
 const testCases = {};
 
-testCases.basic = {
-  description: 'basic',
+testCases.defaults = {
+  description: 'Defaults',
   component: CopyButton,
   props: {
-    text: 'the text you copy'
+    text: 'Copied by the default test case'
   }
 };
 
@@ -19,15 +19,9 @@ testCases.allProps = {
   props: {
     text: 'more copiable text',
     onCopy: safeSpy(),
-    feedbackTime: 1000,
-    iconButtonProps: {
-      tooltipProps: {
-        themeTooltip: 'color-gray shadow-darken50',
-        placement: 'bottom'
-      },
-      themeButton: 'px12 py12 btn--red',
-      'data-test': 'copy-button',
-      themeIcon: 'color-yellow'
+    className: 'px6 py6 btn btn--purple btn--stroke',
+    passthroughProps: {
+      'data-test': 'copy-button'
     }
   }
 };
@@ -46,8 +40,8 @@ class InModal extends React.Component {
       return null;
     }
     return (
-      <Modal title="Copy test" onExit={this.toggleModal}>
-        <CopyButton text="This is the text you're copying" />
+      <Modal accessibleTitle="Copy test" onExit={this.toggleModal}>
+        <CopyButton text="You copied this text from a modal" />
       </Modal>
     );
   }
diff --git a/src/components/copy-button/__tests__/copy-button.test.js b/src/components/copy-button/__tests__/copy-button.test.js
index 3da9a66..154c8a6 100644
--- a/src/components/copy-button/__tests__/copy-button.test.js
+++ b/src/components/copy-button/__tests__/copy-button.test.js
@@ -28,23 +28,20 @@ describe('CopyButton', () => {
     });
   });
 
-  describe(testCases.basic.description, () => {
+  describe(testCases.defaults.description, () => {
     beforeEach(() => {
-      testCase = testCases.basic;
+      testCase = testCases.defaults;
       wrapper = shallow(
         React.createElement(testCase.component, testCase.props)
       );
     });
 
-    test('renders', () => {
+    test('renders as expected', () => {
       expect(wrapper).toMatchSnapshot();
     });
 
     test('changes state on click', () => {
-      wrapper
-        .find('IconButton')
-        .props()
-        .onClick();
+      wrapper.find('button').prop('onClick')();
       wrapper.update();
       expect(wrapper).toMatchSnapshot();
       return delay(820).then(() => {
@@ -62,16 +59,13 @@ describe('CopyButton', () => {
       );
     });
 
-    test('renders', () => {
+    test('renders as expected', () => {
       expect(wrapper).toMatchSnapshot();
     });
 
     // Extra classes need to carry over to both states
     test('changes state on click', () => {
-      wrapper
-        .find('IconButton')
-        .props()
-        .onClick();
+      wrapper.find('button').prop('onClick')();
       wrapper.update();
       expect(wrapper).toMatchSnapshot();
       return delay(820).then(() => {
@@ -81,10 +75,7 @@ describe('CopyButton', () => {
     });
 
     test('calls onCopy callback', () => {
-      wrapper
-        .find('IconButton')
-        .props()
-        .onClick();
+      wrapper.find('button').prop('onClick')();
       expect(testCase.props.onCopy).toHaveBeenCalledTimes(1);
       expect(testCase.props.onCopy).toHaveBeenCalledWith(testCase.props.text);
     });
diff --git a/src/components/icon-button/__tests__/__snapshots__/icon-button.test.js.snap b/src/components/icon-button/__tests__/__snapshots__/icon-button.test.js.snap
deleted file mode 100644
index 213ff7f..0000000
--- a/src/components/icon-button/__tests__/__snapshots__/icon-button.test.js.snap
+++ /dev/null
@@ -1,68 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`IconButton all props used, JSX content renders 1`] = `
-<Tooltip
-  alignment="right"
-  backgroundColor="yellow"
-  coloring="light"
-  content={
-    <span
-      className="txt-bold"
-    >
-      tada
-    </span>
-  }
-  disabled={false}
-  display="inline-block"
-  maxWidth="medium"
-  padded={true}
-  placement="right"
-  respondsToClick={true}
-  testId="tooltip-test-id"
-  textSize="s"
-  themeTooltip="color-gray round px12 py12 shadow-darken25"
-/>
-`;
-
-exports[`IconButton all props used, JSX content renders 2`] = `
-<button
-  className="btn px12 py12 bg-gray"
-  data-test="button-test-id"
-  onClick={[MockFunction]}
-  type="button"
->
-  <Icon
-    inline={false}
-    name="bike"
-    themeIcon="color-gray-dark"
-  />
-</button>
-`;
-
-exports[`IconButton basic renders 1`] = `
-<Tooltip
-  alignment="center"
-  coloring="light"
-  content="close"
-  disabled={false}
-  display="inline-block"
-  maxWidth="medium"
-  padded={true}
-  placement="top"
-  respondsToClick={false}
-  textSize="s"
-/>
-`;
-
-exports[`IconButton basic renders 2`] = `
-<button
-  className="btn btn--xs px0"
-  onClick={[MockFunction]}
-  type="button"
->
-  <Icon
-    inline={false}
-    name="close"
-  />
-</button>
-`;
diff --git a/src/components/icon-button/__tests__/icon-button-test-cases.js b/src/components/icon-button/__tests__/icon-button-test-cases.js
deleted file mode 100644
index 8477e8b..0000000
--- a/src/components/icon-button/__tests__/icon-button-test-cases.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import React from 'react';
-import safeSpy from '../../../test-utils/safe-spy';
-import IconButton from '../icon-button';
-
-const testCases = {};
-
-testCases.basic = {
-  description: 'basic',
-  component: IconButton,
-  props: {
-    onClick: safeSpy(),
-    icon: 'close',
-    tooltipProps: {
-      content: 'close'
-    }
-  }
-};
-
-testCases.allPropsJsxContent = {
-  description: 'all props used, JSX content',
-  component: IconButton,
-  props: {
-    onClick: safeSpy(),
-    icon: 'bike',
-    tooltipProps: {
-      content: <span className="txt-bold">tada</span>,
-      placement: 'right',
-      alignment: 'right',
-      disabled: false,
-      backgroundColor: 'yellow',
-      respondsToClick: true,
-      themeTooltip: 'color-gray round px12 py12 shadow-darken25',
-      display: 'inline-block',
-      testId: 'tooltip-test-id'
-    },
-    themeButton: 'px12 py12 bg-gray',
-    themeIcon: 'color-gray-dark',
-    'data-test': 'button-test-id'
-  }
-};
-
-export { testCases };
diff --git a/src/components/icon-button/__tests__/icon-button.test.js b/src/components/icon-button/__tests__/icon-button.test.js
deleted file mode 100644
index dfb5542..0000000
--- a/src/components/icon-button/__tests__/icon-button.test.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import React from 'react';
-import { shallow } from 'enzyme';
-import { testCases } from './icon-button-test-cases';
-
-describe('IconButton', () => {
-  let testCase;
-  let wrapper;
-  let body;
-
-  describe(testCases.basic.description, () => {
-    beforeEach(() => {
-      testCase = testCases.basic;
-      wrapper = shallow(
-        React.createElement(testCase.component, testCase.props)
-      );
-      body = shallow(
-        wrapper
-          .find('Tooltip')
-          .props()
-          .children()
-      );
-    });
-
-    test('renders', () => {
-      expect(wrapper).toMatchSnapshot();
-      expect(body).toMatchSnapshot();
-    });
-
-    test('button responds to click', () => {
-      body.props().onClick();
-      expect(testCase.props.onClick).toHaveBeenCalledTimes(1);
-    });
-  });
-
-  describe(testCases.allPropsJsxContent.description, () => {
-    beforeEach(() => {
-      testCase = testCases.allPropsJsxContent;
-      wrapper = shallow(
-        React.createElement(testCase.component, testCase.props)
-      );
-      body = shallow(
-        wrapper
-          .find('Tooltip')
-          .props()
-          .children()
-      );
-    });
-
-    test('renders', () => {
-      expect(wrapper).toMatchSnapshot();
-      expect(body).toMatchSnapshot();
-    });
-  });
-});
diff --git a/src/components/modal/__tests__/__snapshots__/modal.test.js.snap b/src/components/modal/__tests__/__snapshots__/modal.test.js.snap
index a752129..b4b4649 100644
--- a/src/components/modal/__tests__/__snapshots__/modal.test.js.snap
+++ b/src/components/modal/__tests__/__snapshots__/modal.test.js.snap
@@ -28,16 +28,30 @@ exports[`Modal No fixed width, display only renders 1`] = `
     <div
       className="absolute top right"
     >
-      <IconButton
-        icon="close"
-        onClick={[MockFunction]}
-        themeButton="btn btn--transparent unround-t unround-br color-gray py12 px12"
-        tooltipProps={
-          Object {
-            "content": "Close",
-          }
-        }
-      />
+      <Tooltip
+        alignment="center"
+        block={true}
+        coloring="light"
+        content="Close"
+        disabled={false}
+        maxWidth="medium"
+        padding="small"
+        placement="top"
+        respondsToClick={false}
+        textSize="s"
+      >
+        <button
+          className="btn btn--transparent unround-t unround-br color-gray py12 px12"
+          data-test="modal-close"
+          onClick={[MockFunction]}
+          type="button"
+        >
+          <Icon
+            inline={false}
+            name="close"
+          />
+        </button>
+      </Tooltip>
     </div>
   </div>
 </Displaced>
@@ -63,7 +77,7 @@ exports[`Modal all options renders 1`] = `
   }
 >
   <div
-    className="relative wmax-full w360 bg-white round"
+    className="relative wmax-full w360 bg-white round px36 py36"
   >
     <div>
       <div
@@ -81,16 +95,30 @@ exports[`Modal all options renders 1`] = `
     <div
       className="absolute top right"
     >
-      <IconButton
-        icon="close"
-        onClick={[MockFunction]}
-        themeButton="btn btn--transparent unround-t unround-br color-gray py12 px12"
-        tooltipProps={
-          Object {
-            "content": "Close",
-          }
-        }
-      />
+      <Tooltip
+        alignment="center"
+        block={true}
+        coloring="light"
+        content="Close"
+        disabled={false}
+        maxWidth="medium"
+        padding="small"
+        placement="top"
+        respondsToClick={false}
+        textSize="s"
+      >
+        <button
+          className="btn btn--transparent unround-t unround-br color-gray py12 px12"
+          data-test="modal-close"
+          onClick={[MockFunction]}
+          type="button"
+        >
+          <Icon
+            inline={false}
+            name="close"
+          />
+        </button>
+      </Tooltip>
     </div>
   </div>
 </Displaced>
@@ -149,16 +177,30 @@ exports[`Modal basic default renders 1`] = `
     <div
       className="absolute top right"
     >
-      <IconButton
-        icon="close"
-        onClick={[MockFunction]}
-        themeButton="btn btn--transparent unround-t unround-br color-gray py12 px12"
-        tooltipProps={
-          Object {
-            "content": "Close",
-          }
-        }
-      />
+      <Tooltip
+        alignment="center"
+        block={true}
+        coloring="light"
+        content="Close"
+        disabled={false}
+        maxWidth="medium"
+        padding="small"
+        placement="top"
+        respondsToClick={false}
+        textSize="s"
+      >
+        <button
+          className="btn btn--transparent unround-t unround-br color-gray py12 px12"
+          data-test="modal-close"
+          onClick={[MockFunction]}
+          type="button"
+        >
+          <Icon
+            inline={false}
+            name="close"
+          />
+        </button>
+      </Tooltip>
     </div>
   </div>
 </Displaced>
@@ -204,16 +246,30 @@ exports[`Modal basic small renders 1`] = `
     <div
       className="absolute top right"
     >
-      <IconButton
-        icon="close"
-        onClick={[MockFunction]}
-        themeButton="btn btn--transparent unround-t unround-br color-gray py12 px12"
-        tooltipProps={
-          Object {
-            "content": "Close",
-          }
-        }
-      />
+      <Tooltip
+        alignment="center"
+        block={true}
+        coloring="light"
+        content="Close"
+        disabled={false}
+        maxWidth="medium"
+        padding="small"
+        placement="top"
+        respondsToClick={false}
+        textSize="s"
+      >
+        <button
+          className="btn btn--transparent unround-t unround-br color-gray py12 px12"
+          data-test="modal-close"
+          onClick={[MockFunction]}
+          type="button"
+        >
+          <Icon
+            inline={false}
+            name="close"
+          />
+        </button>
+      </Tooltip>
     </div>
   </div>
 </Displaced>
diff --git a/src/components/modal/__tests__/modal-test-cases.js b/src/components/modal/__tests__/modal-test-cases.js
index a4c76c5..2051600 100644
--- a/src/components/modal/__tests__/modal-test-cases.js
+++ b/src/components/modal/__tests__/modal-test-cases.js
@@ -107,7 +107,7 @@ testCases.unpadded = {
             <div className="flex-child px24 py24">The other side</div>
           </div>
         ),
-        padded: false
+        padding: 'none'
       }}
     />
   )
diff --git a/src/components/modal/__tests__/modal.test.js b/src/components/modal/__tests__/modal.test.js
index f482510..39b5312 100644
--- a/src/components/modal/__tests__/modal.test.js
+++ b/src/components/modal/__tests__/modal.test.js
@@ -20,7 +20,7 @@ describe('Modal', () => {
 
     test('fires onExit', () => {
       // find the button that triggers onExit method
-      wrapper.find('IconButton').simulate('click');
+      wrapper.find({ 'data-test': 'modal-close' }).prop('onClick')();
 
       expect(testCase.props.onExit).toHaveBeenCalledTimes(1);
     });
@@ -40,7 +40,7 @@ describe('Modal', () => {
 
     test('fires onExit', () => {
       // find the button that triggers onExit method
-      wrapper.find('IconButton').simulate('click');
+      wrapper.find({ 'data-test': 'modal-close' }).prop('onClick')();
 
       expect(testCase.props.onExit).toHaveBeenCalledTimes(1);
     });
@@ -60,7 +60,7 @@ describe('Modal', () => {
 
     test('fires onExit', () => {
       // find the button that triggers onExit method
-      wrapper.find('IconButton').simulate('click');
+      wrapper.find({ 'data-test': 'modal-close' }).prop('onClick')();
 
       expect(testCase.props.onExit).toHaveBeenCalledTimes(1);
     });
@@ -91,8 +91,8 @@ describe('Modal', () => {
       expect(wrapper).toMatchSnapshot();
     });
 
-    test('IconButton is not present', () => {
-      expect(wrapper.find('IconButton').exists()).toBe(false);
+    test('close button is not present', () => {
+      expect(wrapper.find({ 'data-test': 'modal-close' }).exists()).toBe(false);
     });
   });
 });
diff --git a/src/components/popover-trigger/__tests__/__snapshots__/popover-trigger.test.js.snap b/src/components/popover-trigger/__tests__/__snapshots__/popover-trigger.test.js.snap
index dce77b7..62c366a 100644
--- a/src/components/popover-trigger/__tests__/__snapshots__/popover-trigger.test.js.snap
+++ b/src/components/popover-trigger/__tests__/__snapshots__/popover-trigger.test.js.snap
@@ -2,15 +2,11 @@
 
 exports[`PopoverTrigger default props renders 1`] = `
 <div
+  className="inline-block"
   onBlur={[Function]}
   onClick={[Function]}
   onFocus={[Function]}
   onKeyDown={[Function]}
-  style={
-    Object {
-      "display": "inline-block",
-    }
-  }
 >
   <button
     className="btn"
@@ -22,15 +18,11 @@ exports[`PopoverTrigger default props renders 1`] = `
 
 exports[`PopoverTrigger responds to all interactions renders 1`] = `
 <div
+  className="inline-block"
   onBlur={[Function]}
   onClick={[Function]}
   onFocus={[Function]}
   onKeyDown={[Function]}
-  style={
-    Object {
-      "display": "inline-block",
-    }
-  }
 >
   <button
     className="btn"
diff --git a/src/components/popover/__tests__/__snapshots__/popover.test.js.snap b/src/components/popover/__tests__/__snapshots__/popover.test.js.snap
index b72edac..7f0470c 100644
--- a/src/components/popover/__tests__/__snapshots__/popover.test.js.snap
+++ b/src/components/popover/__tests__/__snapshots__/popover.test.js.snap
@@ -15,7 +15,7 @@ exports[`Popover all props renders 1`] = `
   pointerColor="#fff"
 >
   <div
-    className="color-text shadow-darken25 round px12 py12"
+    className="color-text shadow-darken25 round"
     data-popover-id={4}
     data-test={true}
     key="body"
diff --git a/src/components/tab-list/__tests__/__snapshots__/tab-list.test.js.snap b/src/components/tab-list/__tests__/__snapshots__/tab-list.test.js.snap
index 3f7aa24..cbc0cc8 100644
--- a/src/components/tab-list/__tests__/__snapshots__/tab-list.test.js.snap
+++ b/src/components/tab-list/__tests__/__snapshots__/tab-list.test.js.snap
@@ -64,6 +64,7 @@ exports[`TabList basic renders 1`] = `
     className="fl"
   >
     <PopoverTrigger
+      block={false}
       content={
         Array [
           <div
@@ -93,7 +94,6 @@ exports[`TabList basic renders 1`] = `
         ]
       }
       disabled={false}
-      display="inline-block"
       onPopoverClose={[Function]}
       onPopoverOpen={[Function]}
       popoverProps={
@@ -185,6 +185,7 @@ exports[`TabList links renders 1`] = `
     className="fl"
   >
     <PopoverTrigger
+      block={false}
       content={
         Array [
           <div
@@ -212,7 +213,6 @@ exports[`TabList links renders 1`] = `
         ]
       }
       disabled={false}
-      display="inline-block"
       onPopoverClose={[Function]}
       onPopoverOpen={[Function]}
       popoverProps={
@@ -304,6 +304,7 @@ exports[`TabList truncate all renders 1`] = `
     className="fl"
   >
     <PopoverTrigger
+      block={false}
       content={
         Array [
           <div
@@ -353,7 +354,6 @@ exports[`TabList truncate all renders 1`] = `
         ]
       }
       disabled={false}
-      display="inline-block"
       onPopoverClose={[Function]}
       onPopoverOpen={[Function]}
       popoverProps={
diff --git a/src/components/tooltip/__tests__/__snapshots__/tooltip.test.js.snap b/src/components/tooltip/__tests__/__snapshots__/tooltip.test.js.snap
index 6c80009..dc9ccfd 100644
--- a/src/components/tooltip/__tests__/__snapshots__/tooltip.test.js.snap
+++ b/src/components/tooltip/__tests__/__snapshots__/tooltip.test.js.snap
@@ -17,22 +17,22 @@ exports[`Tooltip Button child trigger renders as expected 1`] = `
 
 exports[`Tooltip DOM element child renders as expected 1`] = `
 <PopoverTrigger
+  block={false}
   content={
     <div
-      className="px12 py6 txt-s wmax240"
+      className="txt-s wmax240"
     >
       DOM element child
     </div>
   }
   disabled={false}
-  display="inline-block"
   popoverProps={
     Object {
       "alignment": "center",
       "coloring": "light",
       "hasPointer": true,
       "hideWhenAnchorIsOffscreen": true,
-      "padded": false,
+      "padding": "small",
       "passthroughProps": Object {
         "id": "tooltip-1",
         "role": "tooltip",
diff --git a/src/test-cases-app/test-cases-app.js b/src/test-cases-app/test-cases-app.js
index 94d64ad..eb1d66d 100644
--- a/src/test-cases-app/test-cases-app.js
+++ b/src/test-cases-app/test-cases-app.js
@@ -1,3 +1,4 @@
+import 'es6-promise/auto';
 import React from 'react';
 import ReactDOM from 'react-dom';
 import ReactTestKitchen from '@mapbox/react-test-kitchen';