diff --git a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
index bced7acc90f2..965ab3974f3f 100644
--- a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
+++ b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
@@ -65,6 +65,33 @@ Map {
},
},
},
+ "AspectRatio" => Object {
+ "propTypes": Object {
+ "as": Object {
+ "type": "elementType",
+ },
+ "children": Object {
+ "type": "node",
+ },
+ "className": Object {
+ "type": "string",
+ },
+ "ratio": Object {
+ "args": Array [
+ Array [
+ "16x9",
+ "9x16",
+ "2x1",
+ "1x2",
+ "4x3",
+ "3x4",
+ "1x1",
+ ],
+ ],
+ "type": "oneOf",
+ },
+ },
+ },
"Breadcrumb" => Object {
"propTypes": Object {
"aria-label": Object {
diff --git a/packages/react/src/__tests__/index-test.js b/packages/react/src/__tests__/index-test.js
index 9bcfa0f298f1..d8741d0a44f9 100644
--- a/packages/react/src/__tests__/index-test.js
+++ b/packages/react/src/__tests__/index-test.js
@@ -18,6 +18,7 @@ describe('Carbon Components React', () => {
"Accordion",
"AccordionItem",
"AccordionSkeleton",
+ "AspectRatio",
"Breadcrumb",
"BreadcrumbItem",
"BreadcrumbSkeleton",
diff --git a/packages/react/src/components/AspectRatio/AspectRatio-story.js b/packages/react/src/components/AspectRatio/AspectRatio-story.js
new file mode 100644
index 000000000000..b003b84cf9ee
--- /dev/null
+++ b/packages/react/src/components/AspectRatio/AspectRatio-story.js
@@ -0,0 +1,75 @@
+/**
+ * Copyright IBM Corp. 2016, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import './AspectRatio-story.scss';
+
+import { withKnobs, select } from '@storybook/addon-knobs';
+import React from 'react';
+import { Grid, Row, Column } from '../Grid';
+import { AspectRatio } from './';
+import mdx from './AspectRatio.mdx';
+
+export default {
+ title: 'AspectRatio',
+ component: AspectRatio,
+ decorators: [
+ withKnobs,
+ (story) =>
{story()}
,
+ ],
+ parameters: {
+ docs: {
+ page: mdx,
+ },
+ },
+};
+
+export const aspectRatio = () => {
+ return (
+
+
+
+ Content
+
+
+ Content
+
+
+ Content
+
+
+ Content
+
+
+
+ );
+};
+
+export const playground = () => {
+ const ratio = select(
+ 'ratio',
+ ['16x9', '9x16', '2x1', '1x2', '4x3', '3x4', '1x1'],
+ '1x1'
+ );
+ return (
+
+
+
+ Content
+
+
+ Content
+
+
+ Content
+
+
+ Content
+
+
+
+ );
+};
diff --git a/packages/react/src/components/AspectRatio/AspectRatio-story.scss b/packages/react/src/components/AspectRatio/AspectRatio-story.scss
new file mode 100644
index 000000000000..e761280682ea
--- /dev/null
+++ b/packages/react/src/components/AspectRatio/AspectRatio-story.scss
@@ -0,0 +1,11 @@
+//
+// Copyright IBM Corp. 2016, 2018
+//
+// This source code is licensed under the Apache-2.0 license found in the
+// LICENSE file in the root directory of this source tree.
+//
+
+.aspect-ratio-story .bx--aspect-ratio {
+ background: #f7f1ff;
+ padding: 1rem;
+}
diff --git a/packages/react/src/components/AspectRatio/AspectRatio.js b/packages/react/src/components/AspectRatio/AspectRatio.js
new file mode 100644
index 000000000000..7c25ddf09869
--- /dev/null
+++ b/packages/react/src/components/AspectRatio/AspectRatio.js
@@ -0,0 +1,69 @@
+/**
+ * Copyright IBM Corp. 2016, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { settings } from 'carbon-components';
+import cx from 'classnames';
+import PropTypes from 'prop-types';
+import React from 'react';
+
+const { prefix } = settings;
+
+/**
+ * The AspectRatio component provides a `ratio` prop that will be used to
+ * specify the aspect ratio that the children you provide will be displayed in.
+ * This is often useful alongside our grid components, or for media assets like
+ * images or videos.
+ */
+function AspectRatio({
+ as: BaseComponent = 'div',
+ className: containerClassName,
+ children,
+ ratio = '1x1',
+ ...rest
+}) {
+ const className = cx(
+ containerClassName,
+ `${prefix}--aspect-ratio`,
+ `${prefix}--aspect-ratio--${ratio}`
+ );
+ return (
+
+ {children}
+
+ );
+}
+
+AspectRatio.propTypes = {
+ /**
+ * Provide a custom component or string to be rendered as the outermost node
+ * of the component. This is useful if you want to deviate from the default
+ * `div` tag, where you could specify `section` or `article` instead.
+ *
+ * ```jsx
+ * My content
+ * ```
+ */
+ as: PropTypes.elementType,
+
+ /**
+ * Specify the content that will be placed in the aspect ratio
+ */
+ children: PropTypes.node,
+
+ /**
+ * Specify a class name for the outermost node of the component
+ */
+ className: PropTypes.string,
+
+ /**
+ * Specify the ratio to be used by the aspect ratio container. This will
+ * determine what aspect ratio your content will be displayed in.
+ */
+ ratio: PropTypes.oneOf(['16x9', '9x16', '2x1', '1x2', '4x3', '3x4', '1x1']),
+};
+
+export default AspectRatio;
diff --git a/packages/react/src/components/AspectRatio/AspectRatio.mdx b/packages/react/src/components/AspectRatio/AspectRatio.mdx
new file mode 100644
index 000000000000..5a3565abc95b
--- /dev/null
+++ b/packages/react/src/components/AspectRatio/AspectRatio.mdx
@@ -0,0 +1,76 @@
+import { Story, Props, Source, Preview } from '@storybook/addon-docs/blocks';
+import { Grid, Row, Column } from '../Grid';
+import { AspectRatio } from '../AspectRatio';
+
+# AspectRatio
+
+[Source code](https://github.com/carbon-design-system/carbon/tree/master/packages/react/src/components/AspectRatio)
+ |
+[Usage guidelines](https://www.carbondesignsystem.com/guidelines/2x-grid/overview#aspect-ratio)
+
+
+
+
+## Table of Contents
+
+- [Overview](#overview)
+- [Component API](#component-api)
+- [Feedback](#feedback)
+
+
+
+## Overview
+
+The `AspectRatio` component supports rendering your content in a specific aspect
+ratio through the `ratio` prop. This prop will specify the proportion between
+the width and the height of your content. The width will be determined by
+spanning 100% of the space available in your layout, and the height will be
+determined by the ratio that you specified.
+
+
+
+
+
+To see the full list of ratios supported by the `ratio` prop, check out the prop
+table in the [Component API](#component-api) section below.
+
+## Component API
+
+
+
+### AspectRatio as
+
+You can use the `as` prop to support rendering the outermost node in the
+component with a specific tag, or custom component, as opposed to the default
+`` that is used.
+
+For example, to render an `article` you could use `as="article"`:
+
+```jsx
+
+ Your content
+
+```
+
+You can also provide custom components, for example:
+
+```jsx
+function Article({ children, ...rest }) {
+ return
{children};
+}
+
+
+ Your content
+;
+```
+
+### AspectRatio className
+
+The `className` prop passed into `AspectRatio` will be forwarded to the
+outermost node in the component.
+
+## Feedback
+
+Help us improve this component by providing feedback, asking questions on Slack,
+or updating this file on
+[GitHub](https://github.com/carbon-design-system/carbon/edit/master/packages/react/src/components/AspectRatio/AspectRatio.mdx).
diff --git a/packages/react/src/components/AspectRatio/__tests__/AspectRatio-test.js b/packages/react/src/components/AspectRatio/__tests__/AspectRatio-test.js
new file mode 100644
index 000000000000..53b2911329a5
--- /dev/null
+++ b/packages/react/src/components/AspectRatio/__tests__/AspectRatio-test.js
@@ -0,0 +1,101 @@
+/**
+ * Copyright IBM Corp. 2016, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { Simulate } from 'react-dom/test-utils';
+import { AspectRatio } from '../';
+
+describe('AspectRatio', () => {
+ it('should support rendering content in different aspect ratios', () => {
+ const ratios = ['16x9', '9x16', '2x1', '1x2', '4x3', '3x4', '1x1'];
+
+ for (const ratio of ratios) {
+ const mountNode = document.createElement('div');
+ ReactDOM.render(
+
{ratio},
+ mountNode
+ );
+
+ // Verify the aspect ratio class exists on the container
+ const container = mountNode.firstChild;
+ const classes = Array.from(container.classList);
+ const ratioClass = classes.find((className) => {
+ return className.includes(ratio);
+ });
+ expect(ratioClass).not.toBe(null);
+ }
+ });
+
+ describe('Component API', () => {
+ let mountNode;
+
+ beforeEach(() => {
+ mountNode = document.createElement('div');
+ document.body.appendChild(mountNode);
+ });
+
+ afterEach(() => {
+ document.body.removeChild(mountNode);
+ });
+
+ it('should pass in a given className to the outermost node', () => {
+ ReactDOM.render(
+
+ test
+ ,
+ mountNode
+ );
+
+ const container = document.querySelector('[data-testid="test"]');
+ expect(container).not.toBe(null);
+ expect(container.classList.contains('test')).toBe(true);
+ });
+
+ it('should forward extra props to the outermost node', () => {
+ const onClick = jest.fn();
+ ReactDOM.render(
+
+ test
+ ,
+ mountNode
+ );
+ const container = mountNode.firstChild;
+ expect(container).not.toBe(null);
+ expect(container.getAttribute('data-testid')).toBe('test');
+
+ Simulate.click(container);
+ expect(onClick).toHaveBeenCalledTimes(1);
+ });
+
+ it('should support rendering custom elements with the `as` prop', () => {
+ function Test() {
+ return (
+ <>
+
test
+
}>
+ test as component
+
+ >
+ );
+ }
+
+ ReactDOM.render(
, mountNode);
+
+ const section = document.querySelector('section');
+ expect(section).not.toBe(null);
+
+ const article = document.querySelector('article');
+ expect(article).not.toBe(null);
+
+ // Make sure props are forwarded to a custom base component
+ expect(article.classList.contains('test')).toBe(true);
+ });
+ });
+});
diff --git a/packages/react/src/components/AspectRatio/index.js b/packages/react/src/components/AspectRatio/index.js
new file mode 100644
index 000000000000..6f2395beec81
--- /dev/null
+++ b/packages/react/src/components/AspectRatio/index.js
@@ -0,0 +1,8 @@
+/**
+ * Copyright IBM Corp. 2016, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+export { default as AspectRatio } from './AspectRatio';
diff --git a/packages/react/src/index.js b/packages/react/src/index.js
index cbf5ccee4e6a..4ba2c266aa23 100644
--- a/packages/react/src/index.js
+++ b/packages/react/src/index.js
@@ -7,6 +7,7 @@
export Accordion from './components/Accordion';
export AccordionItem from './components/AccordionItem';
+export { AspectRatio } from './components/AspectRatio';
export { Breadcrumb, BreadcrumbItem } from './components/Breadcrumb';
export Button from './components/Button';
export ButtonSet from './components/ButtonSet';