Skip to content

Commit

Permalink
BREAKING: refactor Radio 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 e9fc5a7 commit 577241d
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 665 deletions.
107 changes: 37 additions & 70 deletions src/components/ui/radio/radio.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,35 @@ import {
Platform,
StyleProp,
StyleSheet,
TextStyle,
TouchableOpacity,
TouchableOpacityProps,
View,
ViewStyle,
} from 'react-native';
import { Overwrite } from 'utility-types';
import {
FalsyText,
RenderProp,
TouchableWithoutFeedback,
WebEventResponder,
WebEventResponderInstance,
} from '../../devsupport';
import {
Interaction,
styled,
StyledComponentProps,
StyleType,
} from '@kitten/theme';
import {
Text,
TextElement,
} from '../text/text.component';
import {
isValidString,
WebEventResponder,
WebEventResponderInstance,
} from '../support/services';
} from '../../theme';
import { TextProps } from '../text/text.component';

type RadioStyledProps = Overwrite<StyledComponentProps, {
appearance?: 'default' | string;
}>;

export interface RadioProps extends StyledComponentProps, TouchableOpacityProps {
textStyle?: StyleProp<TextStyle>;
text?: string;
export interface RadioProps extends TouchableOpacityProps, RadioStyledProps {
checked?: boolean;
status?: string;
onChange?: (selected: boolean) => void;
text?: RenderProp<TextProps> | React.ReactText;
status?: 'basic' | 'primary' | 'success' | 'info' | 'warning' | 'danger' | 'control' | string;
}

export type RadioElement = React.ReactElement<RadioProps>;
Expand All @@ -51,18 +52,16 @@ export type RadioElement = React.ReactElement<RadioProps>;
* @property {boolean} checked - Determines whether component is checked.
* Default is `false`.
*
* @property {boolean} disabled - Determines whether component is disabled.
* Default is `false`.
*
* @property {string} status - Determines the status of the component.
* Can be `basic`, `primary`, `success`, `info`, `warning`, `danger` or `control`.
* Default is `basic`.
*
* @property {string} text - Determines text of the component.
*
* @property {StyleProp<TextStyle>} textStyle - Customizes text style.
* @property {string | (props: TextProps) => ReactElement} text - A string or a function component
* to render near the radio.
* If it is a function, it will be called with props provided by Eva.
* Otherwise, renders a Text styled by Eva.
*
* @property {(selected: boolean) => void} onChange - Triggered on onChange value.
* @property {(checked: boolean) => void} onChange - Called on radio value change.
*
* @property {TouchableOpacityProps} ...TouchableOpacityProps - Any props applied to TouchableOpacity component.
*
Expand Down Expand Up @@ -118,7 +117,7 @@ export class RadioComponent extends React.Component<RadioProps> {
}
};

private getComponentStyle = (source: StyleType): StyleType => {
private getComponentStyle = (source: StyleType) => {
const {
textMarginHorizontal,
textFontFamily,
Expand All @@ -138,8 +137,6 @@ export class RadioComponent extends React.Component<RadioProps> {
} = source;

return {
container: {},
highlightContainer: {},
selectContainer: containerParameters,
text: {
marginHorizontal: textMarginHorizontal,
Expand Down Expand Up @@ -178,62 +175,34 @@ export class RadioComponent extends React.Component<RadioProps> {
};
};

private renderTextElement = (style: StyleType): TextElement => {
const { text, textStyle } = this.props;

return (
<Text
key={0}
style={[style, styles.text, textStyle]}>
{text}
</Text>
);
};

private renderComponentChildren = (style: StyleType): React.ReactNodeArray => {
const { text } = this.props;

return [
isValidString(text) && this.renderTextElement(style.text),
];
};

public render(): React.ReactElement<TouchableOpacityProps> {
const { style, eva, disabled, ...derivedProps } = this.props;
const { eva, style, text, disabled, ...touchableProps } = this.props;
const evaStyle = this.getComponentStyle(eva.style);

const {
container,
highlightContainer,
selectContainer,
icon,
highlight,
...componentStyles
} = this.getComponentStyle(eva.style);

const selectContainerStyle: StyleProp<ViewStyle> = [selectContainer, styles.selectContainer];
const selectContainerStyle: StyleProp<ViewStyle> = [evaStyle.selectContainer, styles.selectContainer];
const hitSlopInsets: Insets = this.createHitSlopInsets(selectContainerStyle);

const [textElement] = this.renderComponentChildren(componentStyles);

return (
<TouchableOpacity
activeOpacity={1.0}
{...derivedProps}
<TouchableWithoutFeedback
{...touchableProps}
{...this.webEventResponder.eventHandlers}
style={[container, styles.container, webStyles.container, style]}
style={[styles.container, webStyles.container, style]}
disabled={disabled}
hitSlop={hitSlopInsets}
onPress={this.onPress}
onPressIn={this.onPressIn}
onPressOut={this.onPressOut}>
<View style={[highlightContainer, styles.highlightContainer]}>
<View style={[highlight, styles.highlight]}/>
<View style={styles.highlightContainer}>
<View style={[evaStyle.highlight, styles.highlight]}/>
<View style={selectContainerStyle}>
<View style={[icon, styles.icon]}/>
<View style={evaStyle.icon}/>
</View>
</View>
{textElement}
</TouchableOpacity>
<FalsyText
style={evaStyle.text}
component={text}
/>
</TouchableWithoutFeedback>
);
}
}
Expand All @@ -251,11 +220,9 @@ const styles = StyleSheet.create({
justifyContent: 'center',
alignItems: 'center',
},
icon: {},
highlight: {
position: 'absolute',
},
text: {},
});

const webStyles = Platform.OS === 'web' && StyleSheet.create({
Expand Down
168 changes: 66 additions & 102 deletions src/components/ui/radio/radio.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,146 +1,110 @@
/**
* @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 { TouchableOpacity } from 'react-native';
import {
render,
Text,
TouchableOpacity,
} from 'react-native';
import {
fireEvent,
waitForElement,
shallow,
RenderAPI,
render,
} from 'react-native-testing-library';
import { ReactTestInstance } from 'react-test-renderer';
import {
ApplicationProvider,
ApplicationProviderProps,
} from '@kitten/theme';
light,
mapping,
} from '@eva-design/eva';
import { ApplicationProvider } from '../../theme';
import {
Radio,
RadioComponent,
RadioProps,
} from './radio.component';
import {
mapping,
theme,
} from '../support/tests';

const Mock = (props?: RadioProps): React.ReactElement<ApplicationProviderProps> => {
return (
describe('@radio: component checks', () => {

const TestRadio = (props?: RadioProps) => (
<ApplicationProvider
mapping={mapping}
theme={theme}>
theme={light}>
<Radio {...props} />
</ApplicationProvider>
);
};

const renderComponent = (props?: RadioProps): RenderAPI => {
return render(
<Mock {...props}/>,
);
};

describe('@radio: matches snapshot', () => {

it('default', () => {
const component: RenderAPI = renderComponent();
const { output } = shallow(component.getByType(RadioComponent));
it('should request checking', () => {
const onCheckedChange = jest.fn();

expect(output).toMatchSnapshot();
});
const component = render(
<TestRadio
checked={false}
onChange={onCheckedChange}
/>,
);

it('checked', () => {
const component: RenderAPI = renderComponent({ checked: true });
const { output } = shallow(component.getByType(Radio));
fireEvent.press(component.getByType(TouchableOpacity));

expect(output).toMatchSnapshot();
expect(onCheckedChange).toBeCalledWith(true);
});

it('disabled', () => {
const component: RenderAPI = renderComponent({ disabled: true });
const { output } = shallow(component.getByType(Radio));
it('should request unchecking', () => {
const onCheckedChange = jest.fn();

expect(output).toMatchSnapshot();
});
const component = render(
<TestRadio
checked={true}
onChange={onCheckedChange}
/>,
);

it('checked disabled', () => {
const component: RenderAPI = renderComponent({
checked: true,
disabled: true,
});
const { output } = shallow(component.getByType(Radio));
fireEvent.press(component.getByType(TouchableOpacity));

expect(output).toMatchSnapshot();
expect(onCheckedChange).toBeCalledWith(false);
});

it('active', async () => {
const component: RenderAPI = renderComponent();
it('should render text', () => {
const component = render(
<TestRadio text='I love Babel'/>,
);

fireEvent(component.getByType(TouchableOpacity), 'pressIn');

const active: ReactTestInstance = await waitForElement(() => {
return component.getByType(Radio);
});
const { output: activeOutput } = shallow(active);

fireEvent(component.getByType(TouchableOpacity), 'pressOut');
const text = component.getByText('I love Babel');

const inactive: ReactTestInstance = await waitForElement(() => {
return component.getByType(Radio);
});
const { output: inactiveOutput } = shallow(inactive);

expect(activeOutput).toMatchSnapshot();
expect(inactiveOutput).toMatchSnapshot('default');
expect(text).toBeTruthy();
});

it('active checked', async () => {
const component: RenderAPI = renderComponent({ checked: true });
it('should render text as component', () => {
const component = render(
<TestRadio text={props => <Text {...props}>I love Babel</Text>}/>,
);

fireEvent(component.getByType(TouchableOpacity), 'pressIn');
const text = component.getByText('I love Babel');

const active: ReactTestInstance = await waitForElement(() => {
return component.getByType(Radio);
});
const { output: activeOutput } = shallow(active);
expect(text).toBeTruthy();
});

fireEvent(component.getByType(TouchableOpacity), 'pressOut');
it('should call onPressIn', () => {
const onPressIn = jest.fn();

const inactive: ReactTestInstance = await waitForElement(() => {
return component.getByType(Radio);
});
const { output: inactiveOutput } = shallow(inactive);
const component = render(
<TestRadio onPressIn={onPressIn}/>,
);

expect(activeOutput).toMatchSnapshot();
expect(inactiveOutput).toMatchSnapshot('checked');
});
fireEvent(component.getByType(TouchableOpacity), 'pressIn');

it('with text (styled)', () => {
const component: RenderAPI = renderComponent({
text: 'Place your text',
textStyle: {
fontSize: 22,
lineHeight: 24,
},
});
const { output } = shallow(component.getByType(Radio));

expect(output).toMatchSnapshot();
expect(onPressIn).toBeCalled();
});

});

describe('@radio: component checks', () => {

it('* emits onChange with correct args', () => {
const onChange = jest.fn();
it('should call onPressOut', () => {
const onPressOut = jest.fn();

const component: RenderAPI = renderComponent({
checked: false,
onChange: onChange,
});
const component = render(
<TestRadio onPressOut={onPressOut}/>,
);

fireEvent.press(component.getByType(TouchableOpacity));
fireEvent(component.getByType(TouchableOpacity), 'pressOut');

expect(onChange).toBeCalledWith(true);
expect(onPressOut).toBeCalled();
});

});
Loading

0 comments on commit 577241d

Please sign in to comment.