Skip to content

Commit

Permalink
feat: Add string support for aspectRatio (#34629)
Browse files Browse the repository at this point in the history
Summary:
This updates `aspectRatio` to support string values and ratio formats, i.e., `'16 / 9'`, thus aligning it with the [CSS Box Sizing Module Level 4](https://drafts.csswg.org/css-sizing-4/#aspect-ratio) specification as requested on #34425. This also adds unit tests to the `processAspectRatio` function ensuring the style processing works as expected.

## Changelog

[General] [Added] - Add string support for aspectRatio

Pull Request resolved: #34629

Test Plan:
This can be tested either through `processAspectRatio-tests` or by using the following code:

```js
 <View
   style={{
     backgroundColor: '#527FE4',
     aspectRatio: '16 / 9',
  }} />
```

https://user-images.githubusercontent.com/11707729/189029904-da1dc0a6-85de-46aa-8ec2-3567802c8719.mov

Reviewed By: jacdebug

Differential Revision: D39423304

Pulled By: cipolleschi

fbshipit-source-id: d323de93d6524e411e7ab9943335a8ca323b6e61
  • Loading branch information
gabrieldonadel authored and facebook-github-bot committed Sep 27, 2022
1 parent 71fda5e commit 14c91cd
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 5 deletions.
3 changes: 2 additions & 1 deletion Libraries/Components/View/ReactNativeStyleAttributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/

import type {AnyAttributeType} from '../../Renderer/shims/ReactNativeTypes';
import processAspectRatio from '../../StyleSheet/processAspectRatio';
import processColor from '../../StyleSheet/processColor';
import processFontVariant from '../../StyleSheet/processFontVariant';
import processTransform from '../../StyleSheet/processTransform';
Expand All @@ -23,7 +24,7 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = {
alignContent: true,
alignItems: true,
alignSelf: true,
aspectRatio: true,
aspectRatio: {process: processAspectRatio},
borderBottomWidth: true,
borderEndWidth: true,
borderLeftWidth: true,
Expand Down
2 changes: 1 addition & 1 deletion Libraries/StyleSheet/StyleSheetTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export interface FlexStyle {
| undefined;
alignItems?: FlexAlignType | undefined;
alignSelf?: 'auto' | FlexAlignType | undefined;
aspectRatio?: number | undefined;
aspectRatio?: number | string | undefined;
borderBottomWidth?: number | undefined;
borderEndWidth?: number | string | undefined;
borderLeftWidth?: number | undefined;
Expand Down
10 changes: 7 additions & 3 deletions Libraries/StyleSheet/StyleSheetTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -446,8 +446,7 @@ type ____LayoutStyle_Internal = $ReadOnly<{
flexBasis?: number | string,

/**
* Aspect ratio control the size of the undefined dimension of a node. Aspect ratio is a
* non-standard property only available in react native and not CSS.
* Aspect ratio control the size of the undefined dimension of a node.
*
* - On a node with a set width/height aspect ratio control the size of the unset dimension
* - On a node with a set flex basis aspect ratio controls the size of the node in the cross axis
Expand All @@ -457,8 +456,13 @@ type ____LayoutStyle_Internal = $ReadOnly<{
* - On a node with flex grow/shrink aspect ratio controls the size of the node in the cross axis
* if unset
* - Aspect ratio takes min/max dimensions into account
*
* Supports a number or a ratio, e.g.:
* - aspectRatio: '1 / 1'
* - aspectRatio: '1'
* - aspectRatio: '1'
*/
aspectRatio?: number,
aspectRatio?: number | string,

/** `zIndex` controls which components display on top of others.
* Normally, you don't use `zIndex`. Components render according to
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`processAspectRatio should not accept invalid formats 1`] = `"aspectRatio must either be a number, a ratio or \`auto\`. You passed: 0a"`;

exports[`processAspectRatio should not accept invalid formats 2`] = `"aspectRatio must either be a number, a ratio or \`auto\`. You passed: 1 / 1 1"`;

exports[`processAspectRatio should not accept invalid formats 3`] = `"aspectRatio must either be a number, a ratio or \`auto\`. You passed: auto 1/1"`;
50 changes: 50 additions & 0 deletions Libraries/StyleSheet/__tests__/processAspectRatio-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @emails oncall+react_native
*/

'use strict';

const processAspectRatio = require('../processAspectRatio');

describe('processAspectRatio', () => {
it('should accept numbers', () => {
expect(processAspectRatio(1)).toBe(1);
expect(processAspectRatio(0)).toBe(0);
expect(processAspectRatio(1.5)).toBe(1.5);
});

it('should accept string numbers', () => {
expect(processAspectRatio('1')).toBe(1);
expect(processAspectRatio('0')).toBe(0);
expect(processAspectRatio('1.5')).toBe(1.5);
expect(processAspectRatio('+1.5')).toBe(1.5);
expect(processAspectRatio(' 1')).toBe(1);
expect(processAspectRatio(' 0 ')).toBe(0);
});

it('should accept `auto`', () => {
expect(processAspectRatio('auto')).toBe(undefined);
expect(processAspectRatio(' auto')).toBe(undefined);
expect(processAspectRatio(' auto ')).toBe(undefined);
});

it('should accept ratios', () => {
expect(processAspectRatio('+1/1')).toBe(1);
expect(processAspectRatio('0 / 10')).toBe(0);
expect(processAspectRatio('117/ 13')).toBe(9);
expect(processAspectRatio('1.5 /1.2')).toBe(1.25);
expect(processAspectRatio('1/0')).toBe(Infinity);
});

it('should not accept invalid formats', () => {
expect(() => processAspectRatio('0a')).toThrowErrorMatchingSnapshot();
expect(() => processAspectRatio('1 / 1 1')).toThrowErrorMatchingSnapshot();
expect(() => processAspectRatio('auto 1/1')).toThrowErrorMatchingSnapshot();
});
});
53 changes: 53 additions & 0 deletions Libraries/StyleSheet/processAspectRatio.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/

'use strict';

const invariant = require('invariant');

function processAspectRatio(aspectRatio: number | string): ?number {
if (typeof aspectRatio === 'number') {
return aspectRatio;
}

const matches = aspectRatio.split('/').map(s => s.trim());

if (matches.includes('auto')) {
if (__DEV__) {
invariant(
matches.length,
'aspectRatio does not support `auto <ratio>`. You passed: %s',
aspectRatio,
);
}
return;
}

const hasNonNumericValues = matches.some(n => Number.isNaN(Number(n)));
if (__DEV__) {
invariant(
!hasNonNumericValues && (matches.length === 1 || matches.length === 2),
'aspectRatio must either be a number, a ratio or `auto`. You passed: %s',
aspectRatio,
);
}

if (hasNonNumericValues) {
return;
}

if (matches.length === 2) {
return Number(matches[0]) / Number(matches[1]);
}

return Number(matches[0]);
}

module.exports = processAspectRatio;

0 comments on commit 14c91cd

Please sign in to comment.