Skip to content

Commit 91470c2

Browse files
authored
feat(v5): update ResponsiveEmbed to Ratio (react-bootstrap#5714)
* feat(v5): update ResponsiveEmbed to Ratio * Add custom ratios * Allow custom ratio strings
1 parent 2274625 commit 91470c2

File tree

15 files changed

+269
-132
lines changed

15 files changed

+269
-132
lines changed

src/Ratio.tsx

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import classNames from 'classnames';
2+
import * as React from 'react';
3+
import PropTypes from 'prop-types';
4+
5+
import { useBootstrapPrefix } from './ThemeProvider';
6+
import { BsPrefixProps } from './helpers';
7+
8+
export type AspectRatio = '1x1' | '4x3' | '16x9' | '21x9' | string;
9+
10+
export interface RatioProps
11+
extends BsPrefixProps,
12+
React.HTMLAttributes<HTMLDivElement> {
13+
children: React.ReactChild;
14+
aspectRatio?: AspectRatio | number;
15+
}
16+
17+
const propTypes = {
18+
/**
19+
* @default 'ratio'
20+
*/
21+
bsPrefix: PropTypes.string,
22+
23+
/**
24+
* This component requires a single child element
25+
*/
26+
children: PropTypes.element.isRequired,
27+
28+
/**
29+
* Set the aspect ratio of the embed. A fraction or a percentage can also
30+
* be used to create custom aspect ratios.
31+
*/
32+
aspectRatio: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
33+
};
34+
35+
const defaultProps = {
36+
aspectRatio: '1x1' as const,
37+
};
38+
39+
function toPercent(num: number): string {
40+
if (num <= 0 || num > 100) return '100%';
41+
if (num < 1) return `${num * 100}%`;
42+
return `${num}%`;
43+
}
44+
45+
const Ratio = React.forwardRef<HTMLDivElement, RatioProps>(
46+
({ bsPrefix, className, children, aspectRatio, style, ...props }, ref) => {
47+
bsPrefix = useBootstrapPrefix(bsPrefix, 'ratio');
48+
const isCustomRatio = typeof aspectRatio === 'number';
49+
50+
return (
51+
<div
52+
ref={ref}
53+
{...props}
54+
style={{
55+
...style,
56+
...(isCustomRatio && {
57+
'--bs-aspect-ratio': toPercent(aspectRatio as number),
58+
}),
59+
}}
60+
className={classNames(
61+
bsPrefix,
62+
className,
63+
!isCustomRatio && `${bsPrefix}-${aspectRatio}`,
64+
)}
65+
>
66+
{React.Children.only(children)}
67+
</div>
68+
);
69+
},
70+
);
71+
72+
Ratio.propTypes = propTypes;
73+
Ratio.defaultProps = defaultProps;
74+
75+
export default Ratio;

src/ResponsiveEmbed.tsx

Lines changed: 0 additions & 66 deletions
This file was deleted.

src/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,8 @@ export { default as PopoverBody } from './PopoverBody';
160160
export { default as ProgressBar } from './ProgressBar';
161161
export type { ProgressBarProps } from './ProgressBar';
162162

163-
export { default as ResponsiveEmbed } from './ResponsiveEmbed';
164-
export type { ResponsiveEmbedProps } from './ResponsiveEmbed';
163+
export { default as Ratio } from './Ratio';
164+
export type { RatioProps } from './Ratio';
165165

166166
export { default as Row } from './Row';
167167
export type { RowProps } from './Row';

test/RatioSpec.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { mount } from 'enzyme';
2+
3+
import Ratio from '../src/Ratio';
4+
5+
describe('Ratio', () => {
6+
it('should contain `ratio` class', () => {
7+
mount(
8+
<Ratio aspectRatio="1x1" className="custom-class">
9+
<div />
10+
</Ratio>,
11+
).assertSingle('div.custom-class.ratio.ratio-1x1 div');
12+
});
13+
14+
it('should support custom ratios using percent for aspectRatio', () => {
15+
const wrapper = mount(
16+
<Ratio aspectRatio={50}>
17+
<div />
18+
</Ratio>,
19+
);
20+
21+
expect(wrapper.find('.ratio').props().style['--bs-aspect-ratio']).to.equal(
22+
'50%',
23+
);
24+
});
25+
26+
it('should support custom ratios using fraction for aspectRatio', () => {
27+
const wrapper = mount(
28+
<Ratio aspectRatio={1 / 2}>
29+
<div />
30+
</Ratio>,
31+
);
32+
33+
expect(wrapper.find('.ratio').props().style['--bs-aspect-ratio']).to.equal(
34+
'50%',
35+
);
36+
});
37+
38+
it('should support use 100% as custom ratio if aspectRatio is less than 0', () => {
39+
const wrapper = mount(
40+
<Ratio aspectRatio={-1}>
41+
<div />
42+
</Ratio>,
43+
);
44+
45+
expect(wrapper.find('.ratio').props().style['--bs-aspect-ratio']).to.equal(
46+
'100%',
47+
);
48+
});
49+
50+
it('should support use 100% as custom ratio if aspectRatio is greater than 100', () => {
51+
const wrapper = mount(
52+
<Ratio aspectRatio={200}>
53+
<div />
54+
</Ratio>,
55+
);
56+
57+
expect(wrapper.find('.ratio').props().style['--bs-aspect-ratio']).to.equal(
58+
'100%',
59+
);
60+
});
61+
});

test/ResponsiveEmbedSpec.js

Lines changed: 0 additions & 15 deletions
This file was deleted.

www/src/components/SideNav.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ const components = [
139139
'toasts',
140140
];
141141

142-
const utilities = ['transitions', 'responsive-embed', 'react-overlays'];
142+
const utilities = ['transitions', 'ratio', 'react-overlays'];
143143

144144
// We need to configure this
145145
function attachSearch(ref) {

www/src/css/examples.module.scss

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,23 @@
44
max-width: 400px;
55
}
66

7+
.ratio-examples {
8+
:global(.ratio) {
9+
display: inline-block;
10+
width: 10rem;
11+
color: #6c757d;
12+
background-color: #f8f9fa;
13+
border: 1px solid #dee2e6;
14+
margin-right: 5px;
15+
16+
& > div {
17+
display: flex;
18+
align-items: center;
19+
justify-content: center;
20+
}
21+
}
22+
}
23+
724
.static-dropdown-menu {
825
composes: clearfix from global;
926

www/src/examples/.eslintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
"Popover": false,
5656
"ProgressBar": false,
5757
"Radio": false,
58-
"ResponsiveEmbed": false,
58+
"Ratio": false,
5959
"Row": false,
6060
"SplitButton": false,
6161
"Tab": false,
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<div style={{ width: 660, height: 'auto' }}>
2-
<ResponsiveEmbed aspectRatio="16by9">
2+
<Ratio aspectRatio="16x9">
33
<embed type="image/svg+xml" src="/TheresaKnott_castle.svg" />
4-
</ResponsiveEmbed>
4+
</Ratio>
55
</div>;

www/src/examples/Ratio/Custom.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<>
2+
<Ratio aspectRatio={1 / 2}>
3+
<div>2x1</div>
4+
</Ratio>
5+
<Ratio aspectRatio={50}>
6+
<div>2x1</div>
7+
</Ratio>
8+
</>;

0 commit comments

Comments
 (0)