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';