-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(react): add aspect ratio component (#7123)
* feat(react): add aspect ratio component * feat(react): add aspect ratio to entrypoint * docs(react): add usage link for aspect ratio docs Co-authored-by: Alessandra Davila <[email protected]> Co-authored-by: TJ Egan <[email protected]> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
- Loading branch information
1 parent
8600958
commit e992909
Showing
9 changed files
with
369 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
packages/react/src/components/AspectRatio/AspectRatio-story.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) => <div className="aspect-ratio-story">{story()}</div>, | ||
], | ||
parameters: { | ||
docs: { | ||
page: mdx, | ||
}, | ||
}, | ||
}; | ||
|
||
export const aspectRatio = () => { | ||
return ( | ||
<Grid> | ||
<Row> | ||
<Column> | ||
<AspectRatio ratio="1x1">Content</AspectRatio> | ||
</Column> | ||
<Column> | ||
<AspectRatio ratio="1x1">Content</AspectRatio> | ||
</Column> | ||
<Column> | ||
<AspectRatio ratio="1x1">Content</AspectRatio> | ||
</Column> | ||
<Column> | ||
<AspectRatio ratio="1x1">Content</AspectRatio> | ||
</Column> | ||
</Row> | ||
</Grid> | ||
); | ||
}; | ||
|
||
export const playground = () => { | ||
const ratio = select( | ||
'ratio', | ||
['16x9', '9x16', '2x1', '1x2', '4x3', '3x4', '1x1'], | ||
'1x1' | ||
); | ||
return ( | ||
<Grid> | ||
<Row> | ||
<Column> | ||
<AspectRatio ratio={ratio}>Content</AspectRatio> | ||
</Column> | ||
<Column> | ||
<AspectRatio ratio={ratio}>Content</AspectRatio> | ||
</Column> | ||
<Column> | ||
<AspectRatio ratio={ratio}>Content</AspectRatio> | ||
</Column> | ||
<Column> | ||
<AspectRatio ratio={ratio}>Content</AspectRatio> | ||
</Column> | ||
</Row> | ||
</Grid> | ||
); | ||
}; |
11 changes: 11 additions & 0 deletions
11
packages/react/src/components/AspectRatio/AspectRatio-story.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<BaseComponent className={className} {...rest}> | ||
{children} | ||
</BaseComponent> | ||
); | ||
} | ||
|
||
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 | ||
* <AspectRatio as="article">My content</AspectRatio> | ||
* ``` | ||
*/ | ||
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
|
||
<!-- START doctoc generated TOC please keep comment here to allow auto update --> | ||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> | ||
|
||
## Table of Contents | ||
|
||
- [Overview](#overview) | ||
- [Component API](#component-api) | ||
- [Feedback](#feedback) | ||
|
||
<!-- END doctoc generated TOC please keep comment here to allow auto update --> | ||
|
||
## 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. | ||
|
||
<Preview> | ||
<Story id="aspectratio--aspect-ratio" /> | ||
</Preview> | ||
|
||
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 | ||
|
||
<Props /> | ||
|
||
### 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 | ||
`<div>` that is used. | ||
|
||
For example, to render an `article` you could use `as="article"`: | ||
|
||
```jsx | ||
<AspectRatio as="article" ratio="4x3"> | ||
Your content | ||
</AspectRatio> | ||
``` | ||
|
||
You can also provide custom components, for example: | ||
|
||
```jsx | ||
function Article({ children, ...rest }) { | ||
return <article {...rest}>{children}</article>; | ||
} | ||
|
||
<AspectRatio as={Article} ratio="4x3"> | ||
Your content | ||
</AspectRatio>; | ||
``` | ||
|
||
### 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). |
101 changes: 101 additions & 0 deletions
101
packages/react/src/components/AspectRatio/__tests__/AspectRatio-test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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( | ||
<AspectRatio ratio={ratio}>{ratio}</AspectRatio>, | ||
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( | ||
<AspectRatio className="test" data-testid="test"> | ||
test | ||
</AspectRatio>, | ||
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( | ||
<AspectRatio data-testid="test" onClick={onClick}> | ||
test | ||
</AspectRatio>, | ||
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 ( | ||
<> | ||
<AspectRatio as="section">test</AspectRatio> | ||
<AspectRatio | ||
className="test" | ||
as={(props) => <article {...props} />}> | ||
test as component | ||
</AspectRatio> | ||
</> | ||
); | ||
} | ||
|
||
ReactDOM.render(<Test />, 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); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters