Skip to content

Commit

Permalink
BREAKING: refactor Button to new api
Browse files Browse the repository at this point in the history
  • Loading branch information
Artur Yorsh committed Feb 28, 2020
1 parent ee100a5 commit 8e8c8a9
Show file tree
Hide file tree
Showing 21 changed files with 1,197 additions and 279 deletions.
48 changes: 48 additions & 0 deletions src/components/devsupport/components/falsyFC/falsyFC.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react';

export type RenderProp<Props = {}> = (props?: Props) => React.ReactElement;

export type FalsyFCProps<Props = {}> = Props & {
component?: RenderProp<Props>;
fallback?: React.ReactElement;
};

/**
* Helper component for optional properties that should render a component.
*
* Accepts props of a component that is expected to be rendered,
* and `component` which may be a string, a function, null or undefined.
*
* If it is a function, will call it with props passed to this component.
* Otherwise, will return null.
*
* @property {RenderProp} component - function component to be rendered.
* @property {React.ReactElement} fallback - an element to render if children is null or undefined.
*
* @example Will render nothing.
* ```
* <FalsyFC />
* ```
*
* @example Will render red title.
* ```
* const Title = () => (
* <FalsyFC
* style={{ color: 'red' }}
* component={props => <Text {...props}>Title</Text>}
* />
* );
* ```
*/
export class FalsyFC<Props = {}> extends React.Component<FalsyFCProps<Props>> {

public render(): React.ReactElement {
const { component, fallback, ...props } = this.props;

if (!component) {
return fallback || null;
}

return component(props as Props);
}
}
35 changes: 35 additions & 0 deletions src/components/devsupport/components/falsyFC/falsyFC.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from 'react';
import { Text } from 'react-native';
import { render } from 'react-native-testing-library';
import { FalsyFC } from './falsyFC.component';

it('should render nothing', function () {
const component = render(<FalsyFC/>);
expect(component.toJSON()).toEqual(null);
});

it('should render provided function component', function () {
const component = render(
<FalsyFC style={{ color: 'red' }} component={props => <Text {...props}>I love Babel</Text>}/>,
);

const textComponent = component.getByText('I love Babel');

expect(textComponent).toBeTruthy();
expect(textComponent.props.style).toEqual({
color: 'red',
});
});

it('should render fallback component', function () {
const component = render(
<FalsyFC
component={null}
fallback={<Text>I love Babel</Text>}
/>,
);

const textComponent = component.getByText('I love Babel');

expect(textComponent).toBeTruthy();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from 'react';
import { RenderProp } from '../falsyFC/falsyFC.component';
import {
Text,
TextProps,
} from '../../../ui/text/text.component';

export interface FalsyTextProps extends Omit<TextProps, 'children'> {
component?: RenderProp<TextProps> | React.ReactText;
}

/**
* Helper component for optional text properties.
*
* Accepts same props as Text component,
* and `component` which may be a string, a function, null or undefined.
*
* If it is null or undefined, will render nothing.
* If it is a function, will call it with props passed to this component.
* Otherwise, will render a Text with props passed to this component.
*
* @example Will render nothing.
* ```
* <FalsyText />
* ```
*
* @example Will render red title.
* ```
* const Title = () => (
* <FalsyText style={{ color: 'red' }} component='Title' />
* );
* ```
*
* @example Will render image and red title.
* ```
* const renderTitle = (props) => (
* <React.Fragment>
* <Image source={require('../asset.png')}/>
* <Text {...props}>Title</Text>
* </React.Fragment>
* );
*
* const Title = () => (
* <FalsyText
* style={{ color: 'red' }}
* component={renderTitle}
* />
* );
* ```
*/
export class FalsyText extends React.Component<FalsyTextProps> {

public render(): React.ReactElement {
const { component, ...textProps } = this.props;

if (!component) {
return null;
}

if (typeof component === 'function') {

This comment has been minimized.

Copy link
@heroic

heroic Apr 9, 2020

Can't we use something like this: https://github.com/fernandopasik/react-children-utilities/blob/master/src/lib/onlyText.ts to better detect a component vs string? this way we could open more possibilities than forcing a function component.

return component(textProps as TextProps);
}

return (
<Text {...textProps}>
{component}
</Text>
);
}
}
39 changes: 39 additions & 0 deletions src/components/devsupport/components/falsyText/falsyText.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import { Text } from 'react-native';
import { render } from 'react-native-testing-library';
import { mapping, light } from '@eva-design/eva';
import { FalsyText } from './falsyText.component';
import { ApplicationProvider } from '../../../theme';

it('should render nothing', function () {
const component = render(<FalsyText/>);
expect(component.toJSON()).toEqual(null);
});

it('should render provided function component', function () {
const component = render(
<FalsyText
style={{ color: 'red' }}
component={props => <Text {...props}>I love Babel</Text>}
/>,
);

const textComponent = component.getByText('I love Babel');

expect(textComponent).toBeTruthy();
expect(textComponent.props.style).toEqual({
color: 'red',
});
});

it('should render ui kitten text', function () {
const component = render(
<ApplicationProvider mapping={mapping} theme={light}>
<FalsyText component='I love Babel'/>
</ApplicationProvider>,
);

const textComponent = component.getByText('I love Babel');

expect(textComponent).toBeTruthy();
});
78 changes: 78 additions & 0 deletions src/components/devsupport/components/measure/measure.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/

import React from 'react';
import {
findNodeHandle,
UIManager,
} from 'react-native';
import { Frame } from './type';

export interface MeasureElementProps<P = any> {
force?: boolean;
onMeasure: (frame: Frame) => void;
children: React.ReactElement<P>;
}

export type MeasuringElement<P = any> = React.ReactElement;

/**
* Measures child element size and it's screen position asynchronously.
* Returns measure result in `onMeasure` callback.
*
* Usage:
*
* ```tsx
* const onMeasure = (frame: Frame): void => {
* const { x, y } = frame.origin;
* const { width, height } = frame.size;
* ...
* };
*
* <MeasureElement onMeasure={onMeasure}>
* <ElementToMeasure />
* </MeasureElement>
* ```
*
* By default, it measures each time onLayout is called,
* but `force` property may be used to measure any time it's needed.
* DON'T USE THIS FLAG IF THE COMPONENT RENDERS FIRST TIME OR YOU KNOW `onLayout` WILL BE CALLED.
*/
export const MeasureElement = (props: MeasureElementProps): MeasuringElement => {

const ref: React.RefObject<any> = React.useRef();

const bindToWindow = (frame: Frame, window: Frame): Frame => {
if (frame.origin.x < window.size.width) {
return frame;
}

const boundFrame: Frame = new Frame(
frame.origin.x - window.size.width,
frame.origin.y,
frame.size.width,
frame.size.height,
);

return bindToWindow(boundFrame, window);
};

const onUIManagerMeasure = (x: number, y: number, w: number, h: number): void => {
const frame: Frame = bindToWindow(new Frame(x, y, w, h), Frame.window());
props.onMeasure(frame);
};

const measureSelf = (): void => {
const node: number = findNodeHandle(ref.current);
UIManager.measureInWindow(node, onUIManagerMeasure);
};

if (props.force) {
measureSelf();
}

return React.cloneElement(props.children, { ref, onLayout: measureSelf });
};
58 changes: 58 additions & 0 deletions src/components/devsupport/components/measure/measure.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from 'react';
import { Frame } from './type';

describe('@measure: frame class instance checks', () => {

const lhsFrame: Frame = new Frame(2, 2, 2, 2);
const rhsFrame: Frame = new Frame(4, 4, 2, 2);

it('* left of', () => {
const { origin: { x, y } } = rhsFrame.leftOf(lhsFrame);

expect(x).toEqual(0);
expect(y).toEqual(4);
});

it('* top of', () => {
const { origin: { x, y } } = rhsFrame.topOf(lhsFrame);

expect(x).toEqual(4);
expect(y).toEqual(0);
});

it('* right of', () => {
const { origin: { x, y } } = rhsFrame.rightOf(lhsFrame);

expect(x).toEqual(4);
expect(y).toEqual(4);
});

it('* bottom of', () => {
const { origin: { x, y } } = rhsFrame.bottomOf(lhsFrame);

expect(x).toEqual(4);
expect(y).toEqual(4);
});

it('* center horizontal of', () => {
const { origin: { x, y } } = rhsFrame.centerHorizontalOf(lhsFrame);

expect(x).toEqual(2);
expect(y).toEqual(4);
});

it('* center vertical of', () => {
const { origin: { x, y } } = rhsFrame.centerVerticalOf(lhsFrame);

expect(x).toEqual(4);
expect(y).toEqual(2);
});

it('* center of', () => {
const { origin: { x, y } } = rhsFrame.centerOf(lhsFrame);

expect(x).toEqual(2);
expect(y).toEqual(2);
});

});
Loading

0 comments on commit 8e8c8a9

Please sign in to comment.