Skip to content

Commit

Permalink
Add percentage support to react native
Browse files Browse the repository at this point in the history
Summary:
Adds support for percentage value in react native.

syntax: property: 100 | property | '100%'

supported properties:
padding
margin
width
height
minWidth
minHeight
maxWidth
maxHeight
flexBasis

```
class Playground extends React.Component {
  render() {
    return (
      <View style={{backgroundColor: 'white', padding: 10, paddingTop: 30, height: '100%'}}>
        <Text>
          If you want to quickly test out something,
          open the Playground.js file and start coding.
        </Text>
        <View style={{backgroundColor: 'red', height: 50, width: 50}}/>
        <View style={{backgroundColor: 'blue', height: '50%', width: '50%'}}/>
      </View>
    );
  }
}
```

Reviewed By: astreet

Differential Revision: D4376549

fbshipit-source-id: c41d68a7555396f95d063a7527ee081773ac56dc
  • Loading branch information
Emil Sjolander authored and facebook-github-bot committed Jan 11, 2017
1 parent 00d5674 commit 3f49e74
Show file tree
Hide file tree
Showing 16 changed files with 559 additions and 208 deletions.
46 changes: 46 additions & 0 deletions Examples/UIExplorer/UIExplorerUnitTests/RCTConvert_YGValueTests.m
@@ -0,0 +1,46 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#import <XCTest/XCTest.h>

#import <React/RCTConvert.h>
#import <React/RCTUtils.h>

@interface RCTConvert_YGValueTests : XCTestCase

@end

@implementation RCTConvert_YGValueTests

- (void)testUndefined
{
YGValue value = [RCTConvert YGValue:nil];
XCTAssertEqual(value.unit, YGUnitUndefined);
}

- (void)testNumberPoints
{
YGValue value = [RCTConvert YGValue:@100];
XCTAssertEqual(value.unit, YGUnitPixel);
XCTAssertEqual(value.value, 100);
}

- (void)testStringPercent
{
YGValue value = [RCTConvert YGValue:@"100%"];
XCTAssertEqual(value.unit, YGUnitPercent);
XCTAssertEqual(value.value, 100);
}

@end
153 changes: 114 additions & 39 deletions Libraries/StyleSheet/LayoutPropTypes.js
Expand Up @@ -30,184 +30,256 @@ var LayoutPropTypes = {
/** `width` sets the width of this component.
*
* It works similarly to `width` in CSS, but in React Native you
* must use logical pixel units, rather than percents, ems, or any of that.
* must use points or percentages. Ems and other units are not supported.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/width for more details.
*/
width: ReactPropTypes.number,
width: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** `height` sets the height of this component.
*
* It works similarly to `height` in CSS, but in React Native you
* must use logical pixel units, rather than percents, ems, or any of that.
* must use points or percentages. Ems and other units are not supported.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/height for more details.
*/
height: ReactPropTypes.number,
height: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** `top` is the number of logical pixels to offset the top edge of
* this component.
*
* It works similarly to `top` in CSS, but in React Native you must
* use logical pixel units, rather than percents, ems, or any of that.
* It works similarly to `top` in CSS, but in React Native you
* must use points or percentages. Ems and other units are not supported.
*
* See https://developer.mozilla.org/en-US/docs/Web/CSS/top
* for more details of how `top` affects layout.
*/
top: ReactPropTypes.number,
top: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** `left` is the number of logical pixels to offset the left edge of
* this component.
*
* It works similarly to `left` in CSS, but in React Native you must
* use logical pixel units, rather than percents, ems, or any of that.
* It works similarly to `left` in CSS, but in React Native you
* must use points or percentages. Ems and other units are not supported.
*
* See https://developer.mozilla.org/en-US/docs/Web/CSS/left
* for more details of how `left` affects layout.
*/
left: ReactPropTypes.number,
left: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** `right` is the number of logical pixels to offset the right edge of
* this component.
*
* It works similarly to `right` in CSS, but in React Native you must
* use logical pixel units, rather than percents, ems, or any of that.
* It works similarly to `right` in CSS, but in React Native you
* must use points or percentages. Ems and other units are not supported.
*
* See https://developer.mozilla.org/en-US/docs/Web/CSS/right
* for more details of how `right` affects layout.
*/
right: ReactPropTypes.number,
right: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** `bottom` is the number of logical pixels to offset the bottom edge of
* this component.
*
* It works similarly to `bottom` in CSS, but in React Native you must
* use logical pixel units, rather than percents, ems, or any of that.
* It works similarly to `bottom` in CSS, but in React Native you
* must use points or percentages. Ems and other units are not supported.
*
* See https://developer.mozilla.org/en-US/docs/Web/CSS/bottom
* for more details of how `bottom` affects layout.
*/
bottom: ReactPropTypes.number,
bottom: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** `minWidth` is the minimum width for this component, in logical pixels.
*
* It works similarly to `min-width` in CSS, but in React Native you
* must use logical pixel units, rather than percents, ems, or any of that.
* must use points or percentages. Ems and other units are not supported.
*
* See https://developer.mozilla.org/en-US/docs/Web/CSS/min-width
* for more details.
*/
minWidth: ReactPropTypes.number,
minWidth: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** `maxWidth` is the maximum width for this component, in logical pixels.
*
* It works similarly to `max-width` in CSS, but in React Native you
* must use logical pixel units, rather than percents, ems, or any of that.
* must use points or percentages. Ems and other units are not supported.
*
* See https://developer.mozilla.org/en-US/docs/Web/CSS/max-width
* for more details.
*/
maxWidth: ReactPropTypes.number,
maxWidth: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** `minHeight` is the minimum height for this component, in logical pixels.
*
* It works similarly to `min-height` in CSS, but in React Native you
* must use logical pixel units, rather than percents, ems, or any of that.
* must use points or percentages. Ems and other units are not supported.
*
* See https://developer.mozilla.org/en-US/docs/Web/CSS/min-height
* for more details.
*/
minHeight: ReactPropTypes.number,
minHeight: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** `maxHeight` is the maximum height for this component, in logical pixels.
*
* It works similarly to `max-height` in CSS, but in React Native you
* must use logical pixel units, rather than percents, ems, or any of that.
* must use points or percentages. Ems and other units are not supported.
*
* See https://developer.mozilla.org/en-US/docs/Web/CSS/max-height
* for more details.
*/
maxHeight: ReactPropTypes.number,
maxHeight: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** Setting `margin` has the same effect as setting each of
* `marginTop`, `marginLeft`, `marginBottom`, and `marginRight`.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/margin
* for more details.
*/
margin: ReactPropTypes.number,
margin: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** Setting `marginVertical` has the same effect as setting both
* `marginTop` and `marginBottom`.
*/
marginVertical: ReactPropTypes.number,
marginVertical: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** Setting `marginHorizontal` has the same effect as setting
* both `marginLeft` and `marginRight`.
*/
marginHorizontal: ReactPropTypes.number,
marginHorizontal: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** `marginTop` works like `margin-top` in CSS.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/margin-top
* for more details.
*/
marginTop: ReactPropTypes.number,
marginTop: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** `marginBottom` works like `margin-bottom` in CSS.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/margin-bottom
* for more details.
*/
marginBottom: ReactPropTypes.number,
marginBottom: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** `marginLeft` works like `margin-left` in CSS.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/margin-left
* for more details.
*/
marginLeft: ReactPropTypes.number,
marginLeft: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** `marginRight` works like `margin-right` in CSS.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/margin-right
* for more details.
*/
marginRight: ReactPropTypes.number,
marginRight: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** Setting `padding` has the same effect as setting each of
* `paddingTop`, `paddingBottom`, `paddingLeft`, and `paddingRight`.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/padding
* for more details.
*/
padding: ReactPropTypes.number,
padding: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** Setting `paddingVertical` is like setting both of
* `paddingTop` and `paddingBottom`.
*/
paddingVertical: ReactPropTypes.number,
paddingVertical: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** Setting `paddingHorizontal` is like setting both of
* `paddingLeft` and `paddingRight`.
*/
paddingHorizontal: ReactPropTypes.number,
paddingHorizontal: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** `paddingTop` works like `padding-top` in CSS.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-top
* for more details.
*/
paddingTop: ReactPropTypes.number,
paddingTop: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** `paddingBottom` works like `padding-bottom` in CSS.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-bottom
* for more details.
*/
paddingBottom: ReactPropTypes.number,
paddingBottom: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** `paddingLeft` works like `padding-left` in CSS.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-left
* for more details.
*/
paddingLeft: ReactPropTypes.number,
paddingLeft: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** `paddingRight` works like `padding-right` in CSS.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-right
* for more details.
*/
paddingRight: ReactPropTypes.number,
paddingRight: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/** `borderWidth` works like `border-width` in CSS.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/border-width
Expand Down Expand Up @@ -365,7 +437,10 @@ var LayoutPropTypes = {
flex: ReactPropTypes.number,
flexGrow: ReactPropTypes.number,
flexShrink: ReactPropTypes.number,
flexBasis: ReactPropTypes.number,
flexBasis: ReactPropTypes.oneOfType([
ReactPropTypes.number,
ReactPropTypes.string,
]),

/**
* Aspect ratio control the size of the undefined dimension of a node. Aspect ratio is a
Expand Down
4 changes: 3 additions & 1 deletion React/Base/RCTConvert.h
Expand Up @@ -10,13 +10,13 @@
#import <QuartzCore/QuartzCore.h>
#import <UIKit/UIKit.h>

#import <yoga/Yoga.h>
#import <React/RCTAnimationType.h>
#import <React/RCTBorderStyle.h>
#import <React/RCTDefines.h>
#import <React/RCTLog.h>
#import <React/RCTPointerEvents.h>
#import <React/RCTTextDecorationLineType.h>
#import <yoga/Yoga.h>

/**
* This class provides a collection of conversion functions for mapping
Expand Down Expand Up @@ -90,6 +90,8 @@ typedef NSURL RCTFileURL;
+ (UIColor *)UIColor:(id)json;
+ (CGColorRef)CGColor:(id)json CF_RETURNS_NOT_RETAINED;

+ (YGValue)YGValue:(id)json;

+ (NSArray<NSArray *> *)NSArrayArray:(id)json;
+ (NSArray<NSString *> *)NSStringArray:(id)json;
+ (NSArray<NSArray<NSString *> *> *)NSStringArrayArray:(id)json;
Expand Down

13 comments on commit 3f49e74

@ahmetabdi
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work

@henrikra
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@emilsjolander Can these values be animated with Animated? :O

@fragno
Copy link

@fragno fragno commented on 3f49e74 Feb 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

amazing work

@emilsjolander
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@henrikra i dont see why not. May need some changes in RN though

@henrikra
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@emilsjolander Maybe you should try and add example to the docs? Since this changes so many things

@emilsjolander
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@henrikra i know this is on the todo list for RN. Feel free to help us out with doc PRs :)

@henrikra
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@emilsjolander I tried this today and it worked nicely with width and height. When I tried like left: 50% it did not work like expected. I am sure you know this already though :D

@emilsjolander
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not. Please file an issue over at facebook/yoga

@henrikra
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@emilsjolander Look like it has to do something with flexDirection: 'row'. Here is the issue I did: #12660 :) Tell me if you need more info.

@kschrute
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't believe it's happening 😱 Great job @emilsjolander !!!

@clemerjr
Copy link

@clemerjr clemerjr commented on 3f49e74 Dec 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@emilsjolander I am trying to use this on Android, with react-native v0.51.0 and got this error:
Error while updating property 'width' in shadow node of type: RCTViewnullInvalid float: "90%"

On iOS its working normally. I will open an issue ticket.

Thanks for this feature

@t-benze
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome! One question, any reason this is not documented yet? is it ready to use in production?

@emilioicai
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feature is really nice but the fact it remains undocumented for so long prevents me from using it on production

Please sign in to comment.