diff --git a/src/framework/theme/component/application/application.spec.tsx b/src/framework/theme/application/application.spec.tsx similarity index 87% rename from src/framework/theme/component/application/application.spec.tsx rename to src/framework/theme/application/application.spec.tsx index 39cefbab0..0de0b4c73 100644 --- a/src/framework/theme/component/application/application.spec.tsx +++ b/src/framework/theme/application/application.spec.tsx @@ -7,10 +7,12 @@ import { import { ReactTestInstance } from 'react-test-renderer'; import { ApplicationProvider, - Props as ApplicationProviderProps, + ApplicationProviderProps, } from './applicationProvider.component'; -import { default as mapping } from '../../common/mapping.json'; -import { default as theme } from '../../common/theme.json'; +import { + mapping, + theme, +} from '../support/tests'; describe('@app: application wrapper check', () => { diff --git a/src/framework/theme/component/application/application.spec.tsx.snap b/src/framework/theme/application/application.spec.tsx.snap similarity index 100% rename from src/framework/theme/component/application/application.spec.tsx.snap rename to src/framework/theme/application/application.spec.tsx.snap diff --git a/src/framework/theme/component/application/applicationProvider.component.tsx b/src/framework/theme/application/applicationProvider.component.tsx similarity index 68% rename from src/framework/theme/component/application/applicationProvider.component.tsx rename to src/framework/theme/application/applicationProvider.component.tsx index 9526d8fa1..f61342124 100644 --- a/src/framework/theme/component/application/applicationProvider.component.tsx +++ b/src/framework/theme/application/applicationProvider.component.tsx @@ -5,41 +5,44 @@ */ import React from 'react'; +import merge from 'lodash.merge'; import { SchemaProcessor } from '@eva/processor-kitten'; import { ThemeStyleType, SchemaType, CustomSchemaType, } from '@eva/core'; -import merge from 'lodash.merge'; -import { ModalPanel } from '../modal'; -import { StyleProvider } from '../style'; -import { ThemeProviderProps } from '../theme'; -import { ThemeType } from '../../type'; +import { StyleProvider } from '../style/styleProvider.component'; +import { ThemeProviderProps } from '../theme/themeProvider.component'; +import { ModalPanel } from '../modal/modalPanel.component'; +import { ThemeType } from '../theme/type'; -interface ApplicationProviderProps { +interface ComponentProps { mapping: SchemaType; customMapping?: CustomSchemaType; } -export type Props = ApplicationProviderProps & ThemeProviderProps; +export type ApplicationProviderProps = ComponentProps & ThemeProviderProps; interface State { styles: ThemeStyleType; theme: ThemeType; } -export class ApplicationProvider extends React.Component { +export class ApplicationProvider extends React.Component { private schemaProcessor: SchemaProcessor = new SchemaProcessor(); - constructor(props: Props) { + constructor(props: ApplicationProviderProps) { super(props); const { mapping, customMapping, theme } = this.props; const styles: ThemeStyleType = this.createStyles(mapping, customMapping); - this.state = { styles, theme }; + this.state = { + styles, + theme, + }; } private createStyles = (mapping: SchemaType, custom: CustomSchemaType): ThemeStyleType => { diff --git a/src/framework/theme/component/application/index.ts b/src/framework/theme/component/application/index.ts deleted file mode 100644 index dc54d0da9..000000000 --- a/src/framework/theme/component/application/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { - ApplicationProvider, - Props as ApplicationProviderProps, -} from './applicationProvider.component'; diff --git a/src/framework/theme/component/index.ts b/src/framework/theme/component/index.ts deleted file mode 100644 index b818c0f76..000000000 --- a/src/framework/theme/component/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './mapping'; -export * from './theme'; -export * from './style'; -export * from './modal'; -export * from './application'; diff --git a/src/framework/theme/component/mapping/index.ts b/src/framework/theme/component/mapping/index.ts deleted file mode 100644 index ddf527878..000000000 --- a/src/framework/theme/component/mapping/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './mappingContext'; -export { - MappingProvider, - Props as MappingProviderProps, -} from './mappingProvider.component'; diff --git a/src/framework/theme/component/modal/index.ts b/src/framework/theme/component/modal/index.ts deleted file mode 100644 index 25fb51e85..000000000 --- a/src/framework/theme/component/modal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './modalPanel.component'; diff --git a/src/framework/theme/component/modal/modalPanel.spec.tsx b/src/framework/theme/component/modal/modalPanel.spec.tsx deleted file mode 100644 index ca841d177..000000000 --- a/src/framework/theme/component/modal/modalPanel.spec.tsx +++ /dev/null @@ -1,164 +0,0 @@ -import React from 'react'; -import { - fireEvent, - render, - RenderAPI, - shallow, -} from 'react-native-testing-library'; -import { - View, - Text, - Button, -} from 'react-native'; -import { ModalPanel } from './modalPanel.component'; -import { - ModalComponentCloseProps, - ModalService, -} from '../../../theme'; - -jest.useFakeTimers(); - -describe('@modal panel checks', () => { - - const showModalTestId: string = '@modal/show'; - const hideModalTestIdInner: string = '@modal/hide-inner'; - const hideModalTestIdOuter: string = '@modal/hide-outer'; - - interface HooksProps { - componentDidMount?: () => void; - componentWillUnmount?: () => void; - } - - class ModalPanelTest extends React.Component { - - private modalId: string = ''; - - public componentDidMount(): void { - if (this.props.componentDidMount) { - this.props.componentDidMount(); - } - } - - public componentWillUnmount() { - if (this.props.componentWillUnmount) { - this.props.componentWillUnmount(); - } - } - - public showModal() { - this.modalId = ModalService.show( - 1}/>, true, - ); - } - - public hideModal() { - ModalService.hide(this.modalId); - } - - public render(): React.ReactNode { - return ( - - - - Modal Panel Test - - - * ``` * * @example Button API example * * ``` - * import { Button } from '@kitten/ui'; + * import { + * Button, + * ButtonProps, + * } from '@kitten/ui'; * - * private onButtonPress = (event: GestureResponderEvent): void => { - * console.log('Button press); + * private onButtonPress = () => { + * console.log('Button press'); * }; * - * public render(): React.ReactNode { + * public render(): React.ReactElement { * return ( * * ); * } * ``` * */ -export class Button extends React.Component { +export class ButtonComponent extends React.Component { static styledComponentName: string = 'Button'; - static defaultProps: Partial = { - iconAlignment: 'left', - }; - private onPress = (event: GestureResponderEvent) => { if (this.props.onPress) { this.props.onPress(event); @@ -138,8 +130,8 @@ export class Button extends React.Component { } }; - private getComponentStyle = (style: StyleType): StyleType => { - const { style: containerStyle, textStyle } = this.props; + private getComponentStyle = (source: StyleType): StyleType => { + const { style, textStyle } = this.props; const { textColor, @@ -152,18 +144,13 @@ export class Button extends React.Component { iconTintColor, iconMarginHorizontal, ...containerParameters - } = style; - - const { iconAlignment } = this.props; - - const alignment: ButtonIconAlignment = ButtonIconAlignments.parse(iconAlignment, ALIGNMENT_DEFAULT); + } = source; return { container: { ...containerParameters, - ...StyleSheet.flatten(containerStyle), ...styles.container, - flexDirection: alignment.flex(), + ...StyleSheet.flatten(style), }, text: { color: textColor, @@ -171,8 +158,8 @@ export class Button extends React.Component { lineHeight: textLineHeight, fontWeight: textFontWeight, marginHorizontal: textMarginHorizontal, - ...StyleSheet.flatten(textStyle), ...styles.text, + ...StyleSheet.flatten(textStyle), }, icon: { width: iconWidth, @@ -184,39 +171,39 @@ export class Button extends React.Component { }; }; - private renderTextElement = (style: StyleType): React.ReactElement => { - const { children: text } = this.props; - + private renderTextElement = (style: StyleType): TextElement => { return ( - {text} + key={1} + style={style}> + {this.props.children} ); }; - private renderImageElement = (style: StyleType): React.ReactElement | null => { - const { icon } = this.props; - return icon ? React.cloneElement(icon(style), { key: 2 }) : null; + private renderIconElement = (style: StyleType): IconElement => { + const iconElement: IconElement = this.props.icon(style); + + return React.cloneElement(iconElement, { + key: 2, + style: [style, iconElement.props.style], + }); }; - private renderComponentChildren = (style: StyleType): React.ReactNode => { + private renderComponentChildren = (style: StyleType): React.ReactNodeArray => { const { icon, children } = this.props; - const hasIcon: boolean = icon !== undefined; - const hasText: boolean = children !== undefined; - return [ - hasIcon ? this.renderImageElement(style.icon) : undefined, - hasText ? this.renderTextElement(style.text) : undefined, + icon && this.renderIconElement(style.icon), + isValidString(children) && this.renderTextElement(style.text), ]; }; public render(): React.ReactElement { const { themedStyle, ...derivedProps } = this.props; const { container, ...componentStyles } = this.getComponentStyle(themedStyle); - const componentChildren: React.ReactNode = this.renderComponentChildren(componentStyles); + + const [iconElement, textElement] = this.renderComponentChildren(componentStyles); return ( { onPress={this.onPress} onPressIn={this.onPressIn} onPressOut={this.onPressOut}> - {componentChildren} + {iconElement} + {textElement} ); } @@ -234,9 +222,12 @@ export class Button extends React.Component { const styles = StyleSheet.create({ container: { + flexDirection: 'row', justifyContent: 'center', alignItems: 'center', }, text: {}, icon: {}, }); + +export const Button = styled(ButtonComponent); diff --git a/src/framework/ui/button/button.spec.tsx b/src/framework/ui/button/button.spec.tsx index e2aa60b3a..58e2578dd 100644 --- a/src/framework/ui/button/button.spec.tsx +++ b/src/framework/ui/button/button.spec.tsx @@ -12,19 +12,18 @@ import { RenderAPI, } from 'react-native-testing-library'; import { - styled, ApplicationProvider, ApplicationProviderProps, StyleType, } from '@kitten/theme'; import { - Button as ButtonComponent, - Props as ButtonProps, + Button, + ButtonProps, } from './button.component'; -import { default as mapping } from '../common/mapping.json'; -import { default as theme } from '../common/theme.json'; - -const Button = styled(ButtonComponent); +import { + mapping, + theme, +} from '../support/tests'; const Mock = (props?: ButtonProps): React.ReactElement => { return ( @@ -48,7 +47,7 @@ describe('@button: matches snapshot', () => { it('* stateless', () => { const component: RenderAPI = renderComponent(); - const { output } = shallow(component.getByType(ButtonComponent)); + const { output } = shallow(component.getByType(Button)); expect(output).toMatchSnapshot(); }); @@ -72,21 +71,21 @@ describe('@button: matches snapshot', () => { it('* empty', () => { const component: RenderAPI = renderComponent(); - const { output } = shallow(component.getByType(ButtonComponent)); + const { output } = shallow(component.getByType(Button)); expect(output).toMatchSnapshot(); }); it('* icon', () => { const component: RenderAPI = renderComponent({ icon }); - const { output } = shallow(component.getByType(ButtonComponent)); + const { output } = shallow(component.getByType(Button)); expect(output).toMatchSnapshot(); }); it('* text', () => { const component: RenderAPI = renderComponent({ children: text }); - const { output } = shallow(component.getByType(ButtonComponent)); + const { output } = shallow(component.getByType(Button)); expect(output).toMatchSnapshot(); }); @@ -96,7 +95,7 @@ describe('@button: matches snapshot', () => { icon, children: text, }); - const { output } = shallow(component.getByType(ButtonComponent)); + const { output } = shallow(component.getByType(Button)); expect(output).toMatchSnapshot(); }); @@ -106,9 +105,12 @@ describe('@button: matches snapshot', () => { icon, children: text, size: 'giant', - textStyle: { fontSize: 32, lineHeight: 34 }, + textStyle: { + fontSize: 32, + lineHeight: 34, + }, }); - const { output } = shallow(component.getByType(ButtonComponent)); + const { output } = shallow(component.getByType(Button)); expect(output).toMatchSnapshot(); }); diff --git a/src/framework/ui/button/button.spec.tsx.snap b/src/framework/ui/button/button.spec.tsx.snap index 0e9eb44f1..7da517a3d 100644 --- a/src/framework/ui/button/button.spec.tsx.snap +++ b/src/framework/ui/button/button.spec.tsx.snap @@ -1,884 +1,53 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`@button: matches snapshot * appearance * empty 1`] = ` - `; exports[`@button: matches snapshot * appearance * icon 1`] = ` - - - +/> `; exports[`@button: matches snapshot * appearance * icon and text (styled) 1`] = ` - - - - BUTTON - - + BUTTON + `; exports[`@button: matches snapshot * appearance * icon and text 1`] = ` - - - - BUTTON - - + BUTTON + `; exports[`@button: matches snapshot * appearance * text 1`] = ` - - - BUTTON - - + BUTTON + `; exports[`@button: matches snapshot * interaction * stateless 1`] = ` - `; diff --git a/src/framework/ui/button/type.ts b/src/framework/ui/button/type.ts deleted file mode 100644 index 4155ee452..000000000 --- a/src/framework/ui/button/type.ts +++ /dev/null @@ -1,50 +0,0 @@ -export interface ButtonIconAlignment { - rawValue: string; - - flex(): FlexAlignment; -} - -export type FlexAlignment = 'row' | 'row-reverse'; - -export class ButtonIconAlignments { - static LEFT: ButtonIconAlignment = new class implements ButtonIconAlignment { - rawValue: string = 'left'; - - flex(): FlexAlignment { - return 'row'; - } - }; - - static RIGHT: ButtonIconAlignment = new class implements ButtonIconAlignment { - rawValue: string = 'right'; - - flex(): FlexAlignment { - return 'row-reverse'; - } - }; - - static parse(value: string | ButtonIconAlignment, fallback?: ButtonIconAlignment): ButtonIconAlignment | undefined { - if (ButtonIconAlignments.typeOf(value)) { - return value; - } - - return ButtonIconAlignments.parseString(value, fallback); - } - - private static parseString(rawValue: string, fallback?: ButtonIconAlignment): ButtonIconAlignment | undefined { - switch (rawValue) { - case ButtonIconAlignments.LEFT.rawValue: - return ButtonIconAlignments.LEFT; - case ButtonIconAlignments.RIGHT.rawValue: - return ButtonIconAlignments.RIGHT; - default: - return fallback; - } - } - - private static typeOf(value: any): value is ButtonIconAlignment { - const { rawValue } = (value); - - return rawValue !== undefined; - } -} diff --git a/src/framework/ui/buttonGroup/buttonGroup.component.tsx b/src/framework/ui/buttonGroup/buttonGroup.component.tsx index b383bd3a9..88f6387b7 100644 --- a/src/framework/ui/buttonGroup/buttonGroup.component.tsx +++ b/src/framework/ui/buttonGroup/buttonGroup.component.tsx @@ -12,20 +12,25 @@ import { ViewStyle, } from 'react-native'; import { + styled, StyledComponentProps, StyleType, } from '@kitten/theme'; -import { Props as ButtonProps } from '../button/button.component'; +import { + Button, + ButtonProps, +} from '../button/button.component'; type ButtonElement = React.ReactElement; +type ChildrenProp = ButtonElement | ButtonElement[]; -interface ButtonGroupProps { - children: ButtonElement | ButtonElement[]; +interface ComponentProps { + children: ChildrenProp; size?: string; status?: string; } -export type Props = ButtonGroupProps & StyledComponentProps & ViewProps; +export type ButtonGroupProps = StyledComponentProps & ViewProps & ComponentProps; /** * The `ButtonGroup` component is a component for placing buttons in row. @@ -71,21 +76,26 @@ export type Props = ButtonGroupProps & StyledComponentProps & ViewProps; * ``` * */ -export class ButtonGroup extends React.Component { +class ButtonGroupComponent extends React.Component { static styledComponentName: string = 'ButtonGroup'; - private getComponentStyle = (style: StyleType): StyleType => { + static Button = Button; + + private getComponentStyle = (source: StyleType): StyleType => { + const { style } = this.props; + const { buttonBorderRightColor, buttonBorderRightWidth, ...containerParameters - } = style; + } = source; return { container: { ...containerParameters, ...styles.container, + ...StyleSheet.flatten(style), }, button: { borderRightColor: buttonBorderRightColor, @@ -101,37 +111,37 @@ export class ButtonGroup extends React.Component { return index === React.Children.count(children) - 1; }; - private renderComponentChild = (element: ButtonElement, index: number, style: StyleType): ButtonElement => { + private renderButtonElement = (element: ButtonElement, index: number, style: StyleType): ButtonElement => { const { appearance, size, status } = this.props; const additionalStyle: ViewStyle = this.isLastElement(index) ? styles.lastButton : style; return React.cloneElement(element, { + key: index, appearance: appearance, size: size, status: status, - key: index, style: [element.props.style, additionalStyle], }); }; - private renderComponentChildren = (source: ButtonElement | ButtonElement[], style: StyleType): ButtonElement[] => { + private renderButtonElements = (source: ChildrenProp, style: StyleType): ButtonElement[] => { return React.Children.map(source, (element: ButtonElement, index: number): ButtonElement => { - return this.renderComponentChild(element, index, style); + return this.renderButtonElement(element, index, style.button); }); }; public render(): React.ReactElement { const { style, themedStyle, children, ...derivedProps } = this.props; - const { container, button } = this.getComponentStyle(themedStyle); + const { container, ...componentStyles } = this.getComponentStyle(themedStyle); - const componentChildren: ButtonElement[] = this.renderComponentChildren(children, button); + const buttonElements: ButtonElement[] = this.renderButtonElements(children, componentStyles); return ( - {componentChildren} + style={container}> + {buttonElements} ); } @@ -153,3 +163,5 @@ const styles = StyleSheet.create({ borderRadius: 0, }, }); + +export const ButtonGroup = styled(ButtonGroupComponent); diff --git a/src/framework/ui/buttonGroup/buttonGroup.spec.tsx b/src/framework/ui/buttonGroup/buttonGroup.spec.tsx index d35f4fde0..fc74c4ed8 100644 --- a/src/framework/ui/buttonGroup/buttonGroup.spec.tsx +++ b/src/framework/ui/buttonGroup/buttonGroup.spec.tsx @@ -5,23 +5,18 @@ import { } from 'react-native-testing-library'; import { ReactTestInstance } from 'react-test-renderer'; import { - styled, ApplicationProvider, ApplicationProviderProps, } from '@kitten/theme'; import { - ButtonGroup as ButtonGroupComponent, - Props as ButtonGroupProps, + ButtonGroup, + ButtonGroupProps, } from './buttonGroup.component'; +import { Button } from '../button/button.component'; import { - Button as ButtonComponent, - Props as ButtonProps, -} from '../button/button.component'; -import { default as mapping } from '../common/mapping.json'; -import { default as theme } from '../common/theme.json'; - -const Button = styled(ButtonComponent); -const ButtonGroup = styled(ButtonGroupComponent); + mapping, + theme, +} from '../support/tests'; const Mock = (props?: ButtonGroupProps): React.ReactElement => { return ( diff --git a/src/framework/ui/checkbox/checkbox.component.tsx b/src/framework/ui/checkbox/checkbox.component.tsx index 81144f0bf..28eedebb4 100644 --- a/src/framework/ui/checkbox/checkbox.component.tsx +++ b/src/framework/ui/checkbox/checkbox.component.tsx @@ -22,12 +22,16 @@ import { StyleType, } from '@kitten/theme'; import { - Props as TextProps, - Text as TextComponent, + Text, + TextProps, } from '../text/text.component'; -import { CheckMark } from '../drawable'; +import { CheckMark } from '../support/components'; +import { isValidString } from '../support/services'; -interface CheckBoxProps { +type IconElement = React.ReactElement; +type TextElement = React.ReactElement; + +interface ComponentProps { textStyle?: StyleProp; text?: string; checked?: boolean; @@ -37,9 +41,7 @@ interface CheckBoxProps { onChange?: (checked: boolean, indeterminate: boolean) => void; } -const Text = styled(TextComponent); - -export type Props = CheckBoxProps & StyledComponentProps & TouchableOpacityProps; +export type CheckBoxProps = StyledComponentProps & TouchableOpacityProps & ComponentProps; /** * The `Checkbox` component is an analog of html checkbox button. @@ -103,7 +105,7 @@ export type Props = CheckBoxProps & StyledComponentProps & TouchableOpacityProps * ``` * */ -export class CheckBox extends React.Component { +class CheckBoxComponent extends React.Component { static styledComponentName: string = 'CheckBox'; @@ -131,8 +133,8 @@ export class CheckBox extends React.Component { } }; - private getComponentStyle = (style: StyleType): StyleType => { - const { style: containerStyle, textStyle } = this.props; + private getComponentStyle = (source: StyleType): StyleType => { + const { style, textStyle } = this.props; const { textMarginHorizontal, @@ -149,11 +151,11 @@ export class CheckBox extends React.Component { highlightBorderRadius, highlightBackgroundColor, ...containerParameters - } = style; + } = source; return { container: { - ...StyleSheet.flatten(containerStyle), + ...StyleSheet.flatten(style), ...styles.container, }, highlightContainer: styles.highlightContainer, @@ -186,7 +188,7 @@ export class CheckBox extends React.Component { }; }; - private renderTextElement = (style: StyleType): React.ReactElement => { + private renderTextElement = (style: StyleType): TextElement => { const { text } = this.props; return ( @@ -194,19 +196,19 @@ export class CheckBox extends React.Component { ); }; - private renderSelectIconElement = (style: StyleType): React.ReactElement => { + private renderSelectIconElement = (style: StyleType): IconElement => { return ( ); }; - private renderIndeterminateIconElement = (style: StyleType): React.ReactElement => { + private renderIndeterminateIconElement = (style: StyleType): IconElement => { return ( ); }; - private renderIconElement = (style: StyleType): React.ReactElement => { + private renderIconElement = (style: StyleType): IconElement => { if (this.props.indeterminate) { return this.renderIndeterminateIconElement(style); } else { @@ -214,12 +216,12 @@ export class CheckBox extends React.Component { } }; - private renderComponentChildren = (style: StyleType): React.ReactElement[] => { + private renderComponentChildren = (style: StyleType): React.ReactNodeArray => { const { text } = this.props; return [ this.renderIconElement(style.icon), - text ? this.renderTextElement(style.text) : null, + isValidString(text) && this.renderTextElement(style.text), ]; }; @@ -285,3 +287,5 @@ const styles = StyleSheet.create({ }, text: {}, }); + +export const CheckBox = styled(CheckBoxComponent); diff --git a/src/framework/ui/checkbox/checkbox.spec.tsx b/src/framework/ui/checkbox/checkbox.spec.tsx index 554ae9103..62f637a6c 100644 --- a/src/framework/ui/checkbox/checkbox.spec.tsx +++ b/src/framework/ui/checkbox/checkbox.spec.tsx @@ -9,19 +9,18 @@ import { } from 'react-native-testing-library'; import { ReactTestInstance } from 'react-test-renderer'; import { - styled, ApplicationProvider, ApplicationProviderProps, } from '@kitten/theme'; import { - CheckBox as CheckBoxComponent, - Props as CheckBoxProps, + CheckBox, + CheckBoxProps, } from './checkbox.component'; -import { Text as TextComponent } from '../text/text.component'; -import { default as mapping } from '../common/mapping.json'; -import { default as theme } from '../common/theme.json'; - -const CheckBox = styled(CheckBoxComponent); +import { Text } from '../text/text.component'; +import { + mapping, + theme, +} from '../support/tests'; const Mock = (props?: CheckBoxProps): React.ReactElement => { return ( @@ -43,7 +42,7 @@ describe('@checkbox matches snapshots', () => { it('* default', () => { const component: RenderAPI = renderComponent(); - const { output } = shallow(component.getByType(CheckBoxComponent)); + const { output } = shallow(component.getByType(CheckBox)); expect(output).toMatchSnapshot(); }); @@ -53,7 +52,7 @@ describe('@checkbox matches snapshots', () => { checked: true, disabled: true, }); - const { output } = shallow(component.getByType(CheckBoxComponent)); + const { output } = shallow(component.getByType(CheckBox)); expect(output).toMatchSnapshot(); }); @@ -64,14 +63,14 @@ describe('@checkbox matches snapshots', () => { fireEvent(component.getByType(TouchableOpacity), 'pressIn'); const active: ReactTestInstance = await waitForElement(() => { - return component.getByType(CheckBoxComponent); + return component.getByType(CheckBox); }); const { output: activeOutput } = shallow(active); fireEvent(component.getByType(TouchableOpacity), 'pressOut'); const inactive: ReactTestInstance = await waitForElement(() => { - return component.getByType(CheckBoxComponent); + return component.getByType(CheckBox); }); const { output: inactiveOutput } = shallow(inactive); @@ -84,10 +83,13 @@ describe('@checkbox matches snapshots', () => { const component: RenderAPI = renderComponent({ checked: true, text: text, - textStyle: { fontSize: 18, color: 'red' }, + textStyle: { + fontSize: 18, + color: 'red', + }, }); - const { output } = shallow(component.getByType(CheckBoxComponent)); - expect(component.getByType(TextComponent).props.children).toBe(text); + const { output } = shallow(component.getByType(CheckBox)); + expect(component.getByType(Text).props.children).toBe(text); expect(output).toMatchSnapshot(); }); diff --git a/src/framework/ui/checkbox/checkbox.spec.tsx.snap b/src/framework/ui/checkbox/checkbox.spec.tsx.snap index 94811b85b..86effd8c8 100644 --- a/src/framework/ui/checkbox/checkbox.spec.tsx.snap +++ b/src/framework/ui/checkbox/checkbox.spec.tsx.snap @@ -1,909 +1,41 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`@checkbox matches snapshots * active 1`] = ` - - - - - - - - + `; exports[`@checkbox matches snapshots * active: default 1`] = ` - - - - - - - - + `; exports[`@checkbox matches snapshots * checked.disabled 1`] = ` - - - - - - - - + forwardedRef={null} +/> `; exports[`@checkbox matches snapshots * default 1`] = ` - - - - - - - - + `; exports[`@checkbox matches snapshots * with text 1`] = ` - - - - - - - - - Text - - +/> `; diff --git a/src/framework/ui/drawable/index.ts b/src/framework/ui/drawable/index.ts deleted file mode 100644 index 3a689887c..000000000 --- a/src/framework/ui/drawable/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export { - Arrow, - Props as ArrowProps, -} from './arrow/arrow.component'; -export { - CheckMark, - Props as CheckMarkProps, -} from './checkmark/checkmark.component'; diff --git a/src/framework/ui/index.ts b/src/framework/ui/index.ts index 0d236645e..615c88f4d 100644 --- a/src/framework/ui/index.ts +++ b/src/framework/ui/index.ts @@ -1,205 +1,102 @@ -import { styled } from '@kitten/theme'; -import { - Avatar as AvatarComponent, - Props as AvatarProps, +export { + Avatar, + AvatarProps, } from './avatar/avatar.component'; -import { - BottomNavigationTab as BottomNavigationTabComponent, - Props as BottomNavigatorTabProps, -} from './bottomNavigation/bottomNavigationTab.component'; -import { - BottomNavigation as BottomNavigationComponent, - Props as BottomTabNavigatorProps, +export { + BottomNavigation, + BottomNavigationProps, } from './bottomNavigation/bottomNavigation.component'; -import { - Button as ButtonComponent, - Props as ButtonProps, +export { + BottomNavigationTab, + BottomNavigationTabProps, +} from './bottomNavigation/bottomNavigationTab.component'; +export { + Button, + ButtonProps, } from './button/button.component'; -import { - ButtonGroup as ButtonGroupComponent, - Props as ButtonGroupProps, +export { + ButtonGroup, + ButtonGroupProps, } from './buttonGroup/buttonGroup.component'; -import { - CheckBox as CheckBoxComponent, - Props as CheckBoxProps, +export { + CheckBox, + CheckBoxProps, } from './checkbox/checkbox.component'; -import { - Input as InputComponent, - Props as InputProps, +export { + Input, + InputProps, } from './input/input.component'; -import { - Layout as LayoutComponent, - Props as LayoutProps, +export { + Layout, + LayoutProps, } from './layout/layout.component'; -import { - List as ListComponent, - Props as ListProps, +export { + List, + ListProps, } from './list/list.component'; -import { - ListItem as ListItemComponent, - Props as ListItemProps, +export { + ListItem, + ListItemProps, } from './list/listItem.component'; -import { +export { Modal, - Props as ModalProps, + ModalProps, + ModalAnimationType, } from './modal/modal.component'; -import { - OverflowMenu as OverflowMenuComponent, - Props as OverflowMenuProps, +export { + OverflowMenu, + OverflowMenuProps, } from './overflowMenu/overflowMenu.component'; -import { - OverflowMenuItem as OverflowMenuItemComponent, - Props as OverflowMenuItemProps, +export { + OverflowMenuItem, + OverflowMenuItemProps, OverflowMenuItemType, } from './overflowMenu/overflowMenuItem.component'; -import { - Popover as PopoverComponent, - Props as PopoverProps, +export { + Popover, + PopoverProps, } from './popover/popover.component'; -import { - Radio as RadioComponent, - Props as RadioProps, +export { + Radio, + RadioProps, } from './radio/radio.component'; -import { - RadioGroup as RadioGroupComponent, - Props as RadioGroupProps, +export { + RadioGroup, + RadioGroupProps, } from './radioGroup/radioGroup.component'; -import { - Tab as TabComponent, - Props as TabProps, -} from './tab/tab.component'; -import { - TabBar as TabBarComponent, - Props as TabBarProps, -} from './tab/tabBar.component'; -import { +export { TabView, - Props as TabViewProps, - ChildProps as TabViewChildProps, + TabViewProps, } from './tab/tabView.component'; -import { - Text as TextComponent, - Props as TextProps, -} from './text/text.component'; -import { - Toggle as ToggleComponent, - Props as ToggleProps, -} from './toggle/toggle.component'; -import { - Tooltip as TooltipComponent, - Props as TooltipProps, -} from './tooltip/tooltip.component'; -import { - TopNavigation as TopNavigationComponent, - Props as TopNavigationBarProps, -} from './topNavigation/topNavigation.component'; -import { - TopNavigationAction as TopNavigationActionComponent, - Props as TopNavigationBarActionProps, -} from './topNavigation/topNavigationAction.component'; -import { - ViewPager, - Props as ViewPagerProps, -} from './viewPager/viewPager.component'; -import { - ButtonIconAlignment, - ButtonIconAlignments, -} from './button/type'; -import { - Placement as PopoverPlacement, - Placements as PopoverPlacements, -} from './popover/type'; -import { - TopNavigationAlignment, - TopNavigationAlignments, -} from './topNavigation/type'; - -const Avatar = styled(AvatarComponent); -const BottomNavigationTab = styled(BottomNavigationTabComponent); -const BottomNavigation = styled(BottomNavigationComponent); -const Button = styled(ButtonComponent); -const ButtonGroup = styled(ButtonGroupComponent); -const CheckBox = styled(CheckBoxComponent); -const Input = styled(InputComponent); -const Layout = styled(LayoutComponent); -const List = styled(ListComponent); -const ListItem = styled(ListItemComponent); -const OverflowMenu = styled(OverflowMenuComponent); -const OverflowMenuItem = styled(OverflowMenuItemComponent); -const Popover = styled(PopoverComponent); -const Radio = styled(RadioComponent); -const RadioGroup = styled(RadioGroupComponent); -const Tab = styled(TabComponent); -const TabBar = styled(TabBarComponent); -const Text = styled(TextComponent); -const Toggle = styled(ToggleComponent); -const Tooltip = styled(TooltipComponent); -const TopNavigation = styled(TopNavigationComponent); -const TopNavigationAction = styled(TopNavigationActionComponent); - export { - Avatar, - BottomNavigationTab, - BottomNavigation, - Button, - ButtonGroup, - CheckBox, - Input, - Layout, - List, - ListItem, - Modal, - Popover, - Radio, - RadioGroup, - Tab, TabBar, - TabView, + TabBarProps, +} from './tab/tabBar.component'; +export { + Tab, + TabProps, +} from './tab/tab.component'; +export { Text, + TextProps, +} from './text/text.component'; +export { Toggle, + ToggleProps, +} from './toggle/toggle.component'; +export { Tooltip, + TooltipProps, +} from './tooltip/tooltip.component'; +export { TopNavigation, + TopNavigationProps, +} from './topNavigation/topNavigation.component'; +export { TopNavigationAction, - ViewPager, - OverflowMenu, - OverflowMenuItem, -}; - + TopNavigationActionProps, +} from './topNavigation/topNavigationAction.component'; export { - AvatarProps, - BottomNavigatorTabProps, - BottomTabNavigatorProps, - ButtonProps, - ButtonGroupProps, - CheckBoxProps, - InputProps, - LayoutProps, - ListProps, - ListItemProps, - ModalProps, - PopoverProps, - RadioProps, - RadioGroupProps, - TabProps, - TabBarProps, - TabViewProps, - TabViewChildProps, - TextProps, - TooltipProps, - ToggleProps, - TopNavigationBarProps, - TopNavigationBarActionProps, + ViewPager, ViewPagerProps, - OverflowMenuProps, - OverflowMenuItemProps, -}; - -export { - ButtonIconAlignment, - ButtonIconAlignments, - PopoverPlacement, - PopoverPlacements, - TopNavigationAlignment, - TopNavigationAlignments, - OverflowMenuItemType, -}; +} from './viewPager/viewPager.component'; diff --git a/src/framework/ui/input/input.component.tsx b/src/framework/ui/input/input.component.tsx index 0280199ec..4a613f931 100644 --- a/src/framework/ui/input/input.component.tsx +++ b/src/framework/ui/input/input.component.tsx @@ -6,6 +6,7 @@ import React from 'react'; import { + Image, ImageProps, StyleProp, StyleSheet, @@ -15,27 +16,30 @@ import { View, } from 'react-native'; import { - allWithRest, Interaction, styled, StyledComponentProps, StyleType, } from '@kitten/theme'; +import { + Text, + TextProps, +} from '../text/text.component'; +import { + allWithRest, + isValidString, +} from '../support/services'; import { InputFocusEvent, InputEndEditEvent, -} from '../common/type'; -import { - Text as TextComponent, - Props as TextProps, -} from '../text/text.component'; -import { FlexStyleProps } from '../common/props'; + FlexStyleProps, +} from '../support/typings'; -type IconElement = React.ReactElement; type TextElement = React.ReactElement; -type IconProp = (style: StyleType) => React.ReactElement; +type IconElement = React.ReactElement; +type IconProp = (style: StyleType) => IconElement; -interface InputProps { +interface ComponentProps { status?: string; disabled?: boolean; label?: string; @@ -47,9 +51,7 @@ interface InputProps { captionTextStyle?: StyleProp; } -export type Props = InputProps & StyledComponentProps & TextInputProps; - -const Text = styled(TextComponent); +export type InputProps = StyledComponentProps & TextInputProps & ComponentProps; /** * The `Input` component is an analog of html input. @@ -127,10 +129,12 @@ const Text = styled(TextComponent); * ``` * */ -export class Input extends React.Component { +export class InputComponent extends React.Component { static styledComponentName: string = 'Input'; + static Icon: React.ComponentClass = Image; + private onFocus = (event: InputFocusEvent) => { this.props.dispatch([Interaction.FOCUSED]); @@ -147,18 +151,15 @@ export class Input extends React.Component { } }; - private getComponentStyle = (style: StyleType): StyleType => { + private getComponentStyle = (source: StyleType): StyleType => { const { - style: derivedContainerStyle, + style, textStyle, labelStyle, captionTextStyle, } = this.props; - const { - rest: inputContainerStyle, - ...containerStyle - } = allWithRest(StyleSheet.flatten(derivedContainerStyle), FlexStyleProps); + const { rest: inputContainerStyle, ...containerStyle } = allWithRest(StyleSheet.flatten(style), FlexStyleProps); const { textMarginHorizontal, @@ -184,7 +185,7 @@ export class Input extends React.Component { captionIconMarginRight, captionIconTintColor, ...containerParameters - } = style; + } = source; return { container: { @@ -205,8 +206,8 @@ export class Input extends React.Component { fontSize: textFontSize, lineHeight: textLineHeight, color: textColor, - ...StyleSheet.flatten(textStyle), ...styles.text, + ...StyleSheet.flatten(textStyle), }, icon: { width: iconWidth, @@ -221,8 +222,8 @@ export class Input extends React.Component { lineHeight: labelLineHeight, marginBottom: labelMarginBottom, fontWeight: labelFontWeight, - ...StyleSheet.flatten(labelStyle), ...styles.label, + ...StyleSheet.flatten(labelStyle), }, captionIcon: { width: captionIconWidth, @@ -236,47 +237,88 @@ export class Input extends React.Component { fontWeight: captionTextFontWeight, lineHeight: captionTextLineHeight, color: captionTextColor, - ...StyleSheet.flatten(captionTextStyle), ...styles.captionLabel, + ...StyleSheet.flatten(captionTextStyle), }, }; }; - private renderIconElement = (style: StyleType, icon: IconProp): IconElement => { - return icon(style); + private renderIconElement = (style: StyleType): IconElement => { + const iconElement: IconElement = this.props.icon(style); + + return React.cloneElement(iconElement, { + key: 0, + style: [style, iconElement.props.style], + }); + }; + + private renderLabelElement = (style: StyleType): TextElement => { + return ( + + {this.props.label} + + ); }; - private renderTextElement = (style: StyleType, text: string): TextElement => { + private renderCaptionElement = (style: StyleType): TextElement => { return ( - {text} + + {this.props.caption} + ); }; + private renderCaptionIconElement = (style: StyleType): IconElement => { + const iconElement: IconElement = this.props.captionIcon(style); + + return React.cloneElement(iconElement, { + key: 3, + style: [style, iconElement.props.style], + }); + }; + + private renderComponentChildren = (style: StyleType): React.ReactNodeArray => { + const { icon, label, captionIcon, caption } = this.props; + + return [ + icon && this.renderIconElement(style.icon), + isValidString(label) && this.renderLabelElement(style.label), + isValidString(caption) && this.renderCaptionElement(style.captionLabel), + captionIcon && this.renderCaptionIconElement(style.captionIcon), + ]; + }; + public render(): React.ReactElement { - const { themedStyle, disabled, label, icon, caption, captionIcon, ...restProps } = this.props; - const style: StyleType = this.getComponentStyle(themedStyle); + const { themedStyle, disabled, ...restProps } = this.props; + const componentStyle: StyleType = this.getComponentStyle(themedStyle); - const iconElement: IconElement = icon ? this.renderIconElement(style.icon, icon) : null; - const labelElement: TextElement = label ? this.renderTextElement(style.label, label) : null; - const captionIconElement: IconElement = captionIcon ? this.renderIconElement(style.captionIcon, captionIcon) : null; - const captionLabelElement: TextElement = caption ? this.renderTextElement(style.captionLabel, caption) : null; + const [ + iconElement, + labelElement, + captionElement, + captionIconElement, + ] = this.renderComponentChildren(componentStyle); return ( - + {labelElement} - + {iconElement} - + {captionIconElement} - {captionLabelElement} + {captionElement} ); @@ -304,3 +346,5 @@ const styles = StyleSheet.create({ captionIcon: {}, captionLabel: {}, }); + +export const Input = styled(InputComponent); diff --git a/src/framework/ui/input/input.spec.tsx b/src/framework/ui/input/input.spec.tsx index 90f0240b3..d3fb7cffe 100644 --- a/src/framework/ui/input/input.spec.tsx +++ b/src/framework/ui/input/input.spec.tsx @@ -13,19 +13,18 @@ import { } from 'react-native-testing-library'; import { ReactTestInstance } from 'react-test-renderer'; import { - styled, ApplicationProvider, ApplicationProviderProps, StyleType, } from '@kitten/theme'; import { - Input as InputComponent, - Props as InputProps, + Input, + InputProps, } from './input.component'; -import { default as mapping } from '../common/mapping.json'; -import { default as theme } from '../common/theme.json'; - -const Input = styled(InputComponent); +import { + mapping, + theme, +} from '../support/tests'; const Mock = (props?: InputProps): React.ReactElement => { return ( @@ -50,7 +49,7 @@ describe('@input: matches snapshot', () => { it('* stateless', () => { const component: RenderAPI = renderComponent(); - const { output } = shallow(component.getByType(InputComponent)); + const { output } = shallow(component.getByType(Input)); expect(output).toMatchSnapshot(); }); @@ -73,7 +72,7 @@ describe('@input: matches snapshot', () => { const component: RenderAPI = renderComponent({ icon }); - const { output } = shallow(component.getByType(InputComponent)); + const { output } = shallow(component.getByType(Input)); expect(output).toMatchSnapshot(); }); @@ -96,7 +95,7 @@ describe('@input: matches snapshot', () => { captionIcon, }); - const { output } = shallow(component.getByType(InputComponent)); + const { output } = shallow(component.getByType(Input)); expect(output).toMatchSnapshot(); }); @@ -117,12 +116,18 @@ describe('@input: matches snapshot', () => { label, caption, captionIcon, - textStyle: { fontSize: 24, lineHeight: 26 }, + textStyle: { + fontSize: 24, + lineHeight: 26, + }, labelStyle: { color: 'blue' }, - captionTextStyle: { letterSpacing: 8, fontFamily: 'opensans-bold' }, + captionTextStyle: { + letterSpacing: 8, + fontFamily: 'opensans-bold', + }, }); - const { output } = shallow(component.getByType(InputComponent)); + const { output } = shallow(component.getByType(Input)); expect(output).toMatchSnapshot(); }); diff --git a/src/framework/ui/input/input.spec.tsx.snap b/src/framework/ui/input/input.spec.tsx.snap index 427b379ca..4e0797a58 100644 --- a/src/framework/ui/input/input.spec.tsx.snap +++ b/src/framework/ui/input/input.spec.tsx.snap @@ -1,749 +1,49 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`@input: matches snapshot * appearance * icon 1`] = ` - - - - - - - + `; exports[`@input: matches snapshot * appearance * label + caption 1`] = ` - - - Label - - - - - - - - Caption Text - - - + `; exports[`@input: matches snapshot * appearance * text styled 1`] = ` - - - Label - - - - - - - - Caption Text - - - + `; exports[`@input: matches snapshot * interaction * stateless 1`] = ` - - - - - - + `; diff --git a/src/framework/ui/layout/layout.component.tsx b/src/framework/ui/layout/layout.component.tsx index 1e8fd8254..87a76cc33 100644 --- a/src/framework/ui/layout/layout.component.tsx +++ b/src/framework/ui/layout/layout.component.tsx @@ -6,19 +6,24 @@ import React from 'react'; import { + StyleSheet, View, ViewProps, } from 'react-native'; import { + styled, StyledComponentProps, StyleType, } from '@kitten/theme'; -interface LayoutProps { - children?: React.ReactElement; +type ChildElement = React.ReactElement; +type ChildrenProp = ChildElement | ChildElement[]; + +interface ComponentProps { + children?: ChildrenProp; } -export type Props = LayoutProps & StyledComponentProps & ViewProps; +export type LayoutProps = StyledComponentProps & ViewProps & ComponentProps; /** * The `Layout` component is component which behaves like React Native View. @@ -49,24 +54,28 @@ export type Props = LayoutProps & StyledComponentProps & ViewProps; * ``` * */ -export class Layout extends React.Component { +export class LayoutComponent extends React.Component { static styledComponentName: string = 'Layout'; - private getComponentStyle = (style: StyleType): StyleType => { + private getComponentStyle = (source: StyleType): StyleType => { return { - container: style, + ...source, + ...StyleSheet.flatten(this.props.style), }; }; public render(): React.ReactElement { - const { style, themedStyle, children, ...restProps } = this.props; + const { style, themedStyle, ...derivedProps } = this.props; const componentStyle: StyleType = this.getComponentStyle(themedStyle); return ( - - {children} - + ); } } + +export const Layout = styled(LayoutComponent); diff --git a/src/framework/ui/layout/layout.spec.tsx b/src/framework/ui/layout/layout.spec.tsx index cc949f2b6..2e3c474eb 100644 --- a/src/framework/ui/layout/layout.spec.tsx +++ b/src/framework/ui/layout/layout.spec.tsx @@ -5,18 +5,17 @@ import { RenderAPI, } from 'react-native-testing-library'; import { - styled, ApplicationProvider, ApplicationProviderProps, } from '@kitten/theme'; import { - Layout as LayoutComponent, - Props as LayoutProps, + Layout, + LayoutProps, } from './layout.component'; -import { default as mapping } from '../common/mapping.json'; -import { default as theme } from '../common/theme.json'; - -const Layout = styled(LayoutComponent); +import { + mapping, + theme, +} from '../support/tests'; const Mock = (props?: LayoutProps): React.ReactElement => { return ( @@ -39,7 +38,7 @@ describe('@layout: matches snapshot', () => { it('default', () => { const component: RenderAPI = renderComponent(); - const { output } = shallow(component.getByType(LayoutComponent)); + const { output } = shallow(component.getByType(Layout)); expect(output).toMatchSnapshot(); }); @@ -47,7 +46,7 @@ describe('@layout: matches snapshot', () => { it('with styles', () => { const component: RenderAPI = renderComponent({ style: { height: 300 } }); - const { output } = shallow(component.getByType(LayoutComponent)); + const { output } = shallow(component.getByType(Layout)); expect(output).toMatchSnapshot(); }); diff --git a/src/framework/ui/layout/layout.spec.tsx.snap b/src/framework/ui/layout/layout.spec.tsx.snap index 974dc7fbb..b932c645f 100644 --- a/src/framework/ui/layout/layout.spec.tsx.snap +++ b/src/framework/ui/layout/layout.spec.tsx.snap @@ -1,250 +1,17 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`@layout: matches snapshot default 1`] = ` - `; exports[`@layout: matches snapshot with styles 1`] = ` - diff --git a/src/framework/ui/list/list.component.tsx b/src/framework/ui/list/list.component.tsx index 88c3d17d9..b98610ec4 100644 --- a/src/framework/ui/list/list.component.tsx +++ b/src/framework/ui/list/list.component.tsx @@ -12,19 +12,22 @@ import { StyleSheet, } from 'react-native'; import { + styled, StyledComponentProps, StyleType, } from '@kitten/theme'; -import { Props as ListItemProps } from './listItem.component'; +import { ListItemProps } from './listItem.component'; // this is basically needed to avoid generics in required props type ItemType = any; +type ListItemElement = React.ReactElement; +type RenderItemProp = (info: ListRenderItemInfo, style: StyleType) => ListItemElement; -interface ListProps { - renderItem: (info: ListRenderItemInfo, style: StyleType) => React.ReactElement; +interface ComponentProps { + renderItem: RenderItemProp; } -export type Props = ListProps & StyledComponentProps & FlatListProps; +export type ListProps = StyledComponentProps & FlatListProps & ComponentProps; /** * The `List` component is a performant interface for rendering simple, flat lists. Extends FlatList. Renders list of @@ -113,7 +116,7 @@ export type Props = ListProps & StyledComponentProps & FlatListProps { +class ListComponent extends React.Component { static styledComponentName: string = 'List'; @@ -144,10 +147,12 @@ export class List extends React.Component { } private getComponentStyle = (source: StyleType): StyleType => { - const { item, ...container } = source; + const { style } = this.props; return { - container: container, + ...source, + ...styles.container, + ...StyleSheet.flatten(style), }; }; @@ -157,33 +162,30 @@ export class List extends React.Component { return item; }; - private extractItemKey = (item: ItemType, index: number): string => { + private keyExtractor = (item: ItemType, index: number): string => { return index.toString(); }; - private renderItem = (info: ListRenderItemInfo): React.ReactElement => { - const { renderItem, themedStyle } = this.props; - const { index } = info; - - const itemStyle: StyleType = this.getItemStyle(themedStyle, index); - const itemElement: React.ReactElement = renderItem(info, itemStyle); + private renderItem = (info: ListRenderItemInfo): ListItemElement => { + const itemStyle: StyleType = this.getItemStyle(this.props.themedStyle, info.index); + const itemElement: React.ReactElement = this.props.renderItem(info, itemStyle); return React.cloneElement(itemElement, { - style: [itemStyle, itemElement.props.style, styles.item], - index: index, + style: [itemStyle, styles.item, itemElement.props.style], + index: info.index, }); }; public render(): React.ReactElement> { const { style, themedStyle, ...derivedProps } = this.props; - const { container } = this.getComponentStyle(themedStyle); + const componentStyle: StyleType = this.getComponentStyle(themedStyle); return ( ); @@ -194,3 +196,5 @@ const styles = StyleSheet.create({ container: {}, item: {}, }); + +export const List = styled(ListComponent); diff --git a/src/framework/ui/list/list.spec.tsx b/src/framework/ui/list/list.spec.tsx index c5a26a5be..6dd0ed7ad 100644 --- a/src/framework/ui/list/list.spec.tsx +++ b/src/framework/ui/list/list.spec.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { - GestureResponderEvent, Image, ImageProps, ImageSourcePropType, @@ -14,24 +13,22 @@ import { } from 'react-native-testing-library'; import { ReactTestInstance } from 'react-test-renderer'; import { - styled, ApplicationProvider, ApplicationProviderProps, StyleType, } from '@kitten/theme'; import { - List as ListComponent, - Props as ListProps, + List, + ListProps, } from './list.component'; import { - ListItem as ListItemComponent, - Props as ListItemProps, + ListItem, + ListItemProps, } from './listItem.component'; -import { default as mapping } from '../common/mapping.json'; -import { default as theme } from '../common/theme.json'; - -const List = styled(ListComponent); -const ListItem = styled(ListItemComponent); +import { + mapping, + theme, +} from '../support/tests'; const data: any[] = Array(8); @@ -67,7 +64,7 @@ describe('@list: component checks', () => { />, ); - const items: ReactTestInstance[] = component.getAllByType(ListItemComponent); + const items: ReactTestInstance[] = component.getAllByType(ListItem); expect(items.length).toEqual(8); }); @@ -92,7 +89,7 @@ describe('@list-item: template matches snapshot', () => { />, ); - const items: ReactTestInstance[] = component.getAllByType(ListItemComponent); + const items: ReactTestInstance[] = component.getAllByType(ListItem); const { output } = shallow(items[0]); expect(output).toMatchSnapshot(); @@ -112,7 +109,7 @@ describe('@list-item: template matches snapshot', () => { />, ); - const items: ReactTestInstance[] = component.getAllByType(ListItemComponent); + const items: ReactTestInstance[] = component.getAllByType(ListItem); const { output } = shallow(items[0]); expect(output).toMatchSnapshot(); @@ -123,9 +120,15 @@ describe('@list-item: template matches snapshot', () => { return ( ); }; @@ -137,7 +140,7 @@ describe('@list-item: template matches snapshot', () => { />, ); - const items: ReactTestInstance[] = component.getAllByType(ListItemComponent); + const items: ReactTestInstance[] = component.getAllByType(ListItem); const { output } = shallow(items[0]); expect(output).toMatchSnapshot(); @@ -170,7 +173,7 @@ describe('@list-item: template matches snapshot', () => { />, ); - const items: ReactTestInstance[] = component.getAllByType(ListItemComponent); + const items: ReactTestInstance[] = component.getAllByType(ListItem); const { output } = shallow(items[0]); expect(output).toMatchSnapshot(); @@ -203,7 +206,7 @@ describe('@list-item: template matches snapshot', () => { />, ); - const items: ReactTestInstance[] = component.getAllByType(ListItemComponent); + const items: ReactTestInstance[] = component.getAllByType(ListItem); const { output } = shallow(items[0]); expect(output).toMatchSnapshot(); @@ -236,7 +239,7 @@ describe('@list-item: component checks', () => { />, ); - const items: ReactTestInstance[] = component.getAllByType(ListItemComponent); + const items: ReactTestInstance[] = component.getAllByType(ListItem); const touchable: ReactTestInstance = items[pressIndex].findByType(TouchableOpacity); fireEvent.press(touchable); diff --git a/src/framework/ui/list/list.spec.tsx.snap b/src/framework/ui/list/list.spec.tsx.snap index 0f4b6206c..b6635abdd 100644 --- a/src/framework/ui/list/list.spec.tsx.snap +++ b/src/framework/ui/list/list.spec.tsx.snap @@ -1,191 +1,22 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`@list-item: template matches snapshot * description 1`] = ` - - - - Description - - - +/> `; exports[`@list-item: template matches snapshot * text styles 1`] = ` - - - - Title - - - Description - - - +/> `; exports[`@list-item: template matches snapshot * title 1`] = ` - - - - Title - - - +/> `; exports[`@list-item: template matches snapshot * with accessory 1`] = ` - - - - Title - - - Description - - - - +/> `; exports[`@list-item: template matches snapshot * with icon 1`] = ` - - - - - Title - - - Description - - - +/> `; diff --git a/src/framework/ui/list/listItem.component.tsx b/src/framework/ui/list/listItem.component.tsx index c02f7e817..40c11070f 100644 --- a/src/framework/ui/list/listItem.component.tsx +++ b/src/framework/ui/list/listItem.component.tsx @@ -24,28 +24,34 @@ import { Interaction, } from '@kitten/theme'; import { - Text as TextComponent, - Props as TextProps, + Text, + TextProps, } from '../text/text.component'; -import { TouchableOpacityIndexedProps } from '../common/type'; +import { TouchableIndexedProps } from '../support/typings'; +import { isValidString } from '../support/services'; + +type TextElement = React.ReactElement; +type IconElement = React.ReactElement; +type AccessoryElement = React.ReactElement; +type IconProp = (style: StyleType, index: number) => IconElement; +type AccessoryProp = (style: StyleType, index: number) => AccessoryElement; interface ListDerivedProps { index?: number; } interface TemplateBaseProps { - index: number; - icon: (style: StyleType, index: number) => React.ReactElement; - accessory: (style: StyleType, index: number) => React.ReactElement; + icon?: IconProp; + accessory?: AccessoryProp; } -interface TemplateTitleProps extends Partial { +interface TemplateTitleProps extends TemplateBaseProps { title: string; - titleStyle?: StyleProp; description?: string; + titleStyle?: StyleProp; } -interface TemplateDescriptionProps extends Partial { +interface TemplateDescriptionProps extends TemplateBaseProps { title?: string; description: string; descriptionStyle?: StyleProp; @@ -55,11 +61,9 @@ interface CustomContentProps { children?: React.ReactNode; } -type ListItemProps = (TemplateTitleProps | TemplateDescriptionProps | CustomContentProps) & ListDerivedProps; +type ComponentProps = (TemplateTitleProps | TemplateDescriptionProps | CustomContentProps) & ListDerivedProps; -const Text = styled(TextComponent); - -export type Props = ListItemProps & StyledComponentProps & TouchableOpacityIndexedProps; +export type ListItemProps = StyledComponentProps & TouchableIndexedProps & ComponentProps; /** * The `ListItem` component is the "support" component for List. @@ -112,7 +116,7 @@ export type Props = ListItemProps & StyledComponentProps & TouchableOpacityIndex * ``` * */ -export class ListItem extends React.Component { +export class ListItemComponent extends React.Component { static styledComponentName: string = 'ListItem'; @@ -146,7 +150,8 @@ export class ListItem extends React.Component { private getComponentStyle = (source: StyleType): StyleType => { // @ts-ignore: will be not executed if `titleStyle` and `descriptionStyle` properties are provided - const { titleStyle, descriptionStyle } = this.props; + const { style, titleStyle, descriptionStyle } = this.props; + const { iconWidth, iconHeight, @@ -169,6 +174,7 @@ export class ListItem extends React.Component { container: { ...containerParameters, ...styles.container, + ...StyleSheet.flatten(style), }, icon: { width: iconWidth, @@ -183,16 +189,16 @@ export class ListItem extends React.Component { lineHeight: titleLineHeight, fontWeight: titleFontWeight, color: titleColor, - ...StyleSheet.flatten(titleStyle), ...styles.title, + ...StyleSheet.flatten(titleStyle), }, description: { color: descriptionColor, fontSize: descriptionFontSize, lineHeight: descriptionLineHeight, marginHorizontal: descriptionMarginHorizontal, - ...StyleSheet.flatten(descriptionStyle), ...styles.description, + ...StyleSheet.flatten(descriptionStyle), }, accessory: { marginHorizontal: accessoryMarginHorizontal, @@ -201,44 +207,45 @@ export class ListItem extends React.Component { }; }; - private renderIconElement = (style: StyleType): React.ReactElement => { + private renderIconElement = (style: StyleType): IconElement => { // @ts-ignore: will be not executed if `icon` prop is provided const { index, icon } = this.props; - const iconElement: React.ReactElement = icon(style, index); + const iconElement: IconElement = icon(style, index); return React.cloneElement(iconElement, { key: 0, - style: [style, iconElement.props.style, styles.icon], + style: [style, styles.icon, iconElement.props.style], }); }; private renderContentElement = (style: StyleType): React.ReactElement => { - const contentChildren: React.ReactNode = this.renderContentElementChildren(style); + const [titleElement, descriptionElement] = this.renderContentElementChildren(style); return ( - {contentChildren} + style={styles.contentContainer}> + {titleElement} + {descriptionElement} ); }; - private renderTitleElement = (style: StyleType): React.ReactElement => { + private renderTitleElement = (style: StyleType): TextElement => { // @ts-ignore: will be not executed if `title` property is provided const { title } = this.props; return ( + key={2} + style={style}> {title} ); }; - private renderDescriptionElement = (style: StyleType): React.ReactElement => { + private renderDescriptionElement = (style: StyleType): TextElement => { // @ts-ignore: will be not executed if `description` property is provided const { description } = this.props; @@ -251,11 +258,11 @@ export class ListItem extends React.Component { ); }; - private renderAccessoryElement = (style: StyleType): React.ReactElement => { + private renderAccessoryElement = (style: StyleType): AccessoryElement => { // @ts-ignore: will be not executed if `accessory` property is provided const { index, accessory } = this.props; - const accessoryElement: React.ReactElement = accessory(style, index); + const accessoryElement: AccessoryElement = accessory(style, index); return React.cloneElement(accessoryElement, { key: 4, @@ -263,24 +270,24 @@ export class ListItem extends React.Component { }); }; - private renderContentElementChildren = (style: StyleType): React.ReactNode => { + private renderContentElementChildren = (style: StyleType): React.ReactNodeArray => { // @ts-ignore: will be not executed if any of properties below is provided const { title, description } = this.props; return [ - title ? this.renderTitleElement(style.title) : undefined, - description ? this.renderDescriptionElement(style.description) : undefined, + isValidString(title) && this.renderTitleElement(style.title), + isValidString(description) && this.renderDescriptionElement(style.description), ]; }; - private renderTemplateChildren = (style: StyleType): React.ReactNode => { + private renderTemplateChildren = (style: StyleType): React.ReactNodeArray => { // @ts-ignore: following props could not be provided const { icon, title, description, accessory } = this.props; return [ - icon ? this.renderIconElement(style.icon) : undefined, - title || description ? this.renderContentElement(style) : undefined, - accessory ? this.renderAccessoryElement(style.accessory) : undefined, + icon && this.renderIconElement(style.icon), + (title || description) && this.renderContentElement(style), + accessory && this.renderAccessoryElement(style.accessory), ]; }; @@ -291,15 +298,16 @@ export class ListItem extends React.Component { }; public render(): React.ReactElement { - const { style, themedStyle, ...derivedProps } = this.props; + const { themedStyle, ...derivedProps } = this.props; const { container, ...componentStyles } = this.getComponentStyle(themedStyle); + const componentChildren: React.ReactNode = this.renderComponentChildren(componentStyles); return ( (ListItemComponent); diff --git a/src/framework/ui/modal/modal.component.tsx b/src/framework/ui/modal/modal.component.tsx index df67fd130..af0297949 100644 --- a/src/framework/ui/modal/modal.component.tsx +++ b/src/framework/ui/modal/modal.component.tsx @@ -18,9 +18,12 @@ import { StyleType } from '@kitten/theme'; export type ModalAnimationType = 'slideInUp' | 'fade' | 'none'; -interface ModalProps { +type ChildElement = React.ReactElement; +type ChildrenProp = ChildElement | ChildElement[]; + +interface ComponentProps { visible: boolean; - children: React.ReactElement | React.ReactElement[]; + children: ChildrenProp; isBackDropAllowed?: boolean; identifier?: string; animationType?: ModalAnimationType; @@ -30,7 +33,7 @@ interface ModalProps { const { width, height } = Dimensions.get('window'); -export type Props = ViewProps & ModalProps; +export type ModalProps = ViewProps & ComponentProps; /** * The `Modal` component is a wrapper than presents content above an enclosing view. @@ -104,9 +107,9 @@ export type Props = ViewProps & ModalProps; * ``` * */ -export class Modal extends React.Component { +export class Modal extends React.Component { - static defaultProps: Partial = { + static defaultProps: Partial = { visible: false, isBackDropAllowed: false, animationType: 'none', @@ -115,7 +118,7 @@ export class Modal extends React.Component { private animation: Animated.Value; - constructor(props: Props) { + constructor(props: ModalProps) { super(props); this.setAnimation(); @@ -125,7 +128,7 @@ export class Modal extends React.Component { this.startAnimation(); } - public componentWillReceiveProps(nextProps: Readonly): void { + public componentWillReceiveProps(nextProps: Readonly): void { const { visible, animationType } = this.props; const isVisibilityChanged: boolean = nextProps.visible !== visible; diff --git a/src/framework/ui/overflowMenu/overflowMenu.component.tsx b/src/framework/ui/overflowMenu/overflowMenu.component.tsx index 3b4cb6b6b..5e977f38d 100644 --- a/src/framework/ui/overflowMenu/overflowMenu.component.tsx +++ b/src/framework/ui/overflowMenu/overflowMenu.component.tsx @@ -15,30 +15,29 @@ import { StyledComponentProps, StyleType, styled, + ModalComponentCloseProps, } from '@kitten/theme'; import { - OverflowMenuItemType, - OverflowMenuItem as OverflowMenuItemComponent, - Props as OverflowMenuItemProps, + OverflowMenuItem, + OverflowMenuItemProps, } from './overflowMenuItem.component'; import { - Popover as PopoverComponent, - Props as PopoverProps, + Popover, + PopoverProps, } from '../popover/popover.component'; -import { Omit } from '../common/type'; +import { Omit } from '../support/typings'; type MenuItemElement = React.ReactElement; -interface OverflowMenuProps { +type PopoverContentProps = Omit; + +interface ComponentProps extends PopoverContentProps, ModalComponentCloseProps { children: React.ReactElement; - items: OverflowMenuItemType[]; + items: OverflowMenuItemProps[]; onSelect?: (index: number, event: GestureResponderEvent) => void; } -const Popover = styled(PopoverComponent); -const OverflowMenuItem = styled(OverflowMenuItemComponent); - -export type Props = & StyledComponentProps & OverflowMenuProps & Omit; +export type OverflowMenuProps = & StyledComponentProps & ComponentProps; /** * The `OverflowMenu` component is a component for showing menu content over the screen. @@ -95,15 +94,15 @@ export type Props = & StyledComponentProps & OverflowMenuProps & Omit { +export class OverflowMenuComponent extends React.Component { static styledComponentName: string = 'OverflowMenu'; - static defaultProps: Partial = { + static defaultProps: Partial = { indicatorOffset: 12, }; - private onSelect = (index: number, event: GestureResponderEvent): void => { + private onItemSelect = (index: number, event: GestureResponderEvent): void => { if (this.props.onSelect) { this.props.onSelect(index, event); } @@ -131,26 +130,27 @@ export class OverflowMenu extends React.Component { }; }; - private renderItemElement = (item: OverflowMenuItemType, index: number, style: StyleType): MenuItemElement => { + private isLastItem = (index: number): boolean => { + return index === this.props.items.length - 1; + }; + + private renderItemElement = (item: OverflowMenuItemProps, index: number, style: StyleType): MenuItemElement => { return ( ); }; private renderContentElementChildren = (style: StyleType): MenuItemElement[] => { - const { items } = this.props; - - return this.props.items.map((item: OverflowMenuItemType, index: number) => { + return this.props.items.map((item: OverflowMenuItemProps, index: number) => { const itemElement: MenuItemElement = this.renderItemElement(item, index, style); - const isLast: boolean = index === items.length - 1; - const borderBottomWidth: number = isLast ? 0 : style.borderBottomWidth; + const borderBottomWidth: number = this.isLastItem(index) ? 0 : style.borderBottomWidth; return React.cloneElement(itemElement, { style: [itemElement.props.style, { borderBottomWidth }], @@ -171,6 +171,7 @@ export class OverflowMenu extends React.Component { public render(): React.ReactNode { const { style, themedStyle, children, ...restProps } = this.props; const { popover, ...componentStyle } = this.getComponentStyle(themedStyle); + const contentElement: React.ReactElement = this.renderPopoverContentElement(componentStyle); return ( @@ -190,3 +191,5 @@ const styles = StyleSheet.create({ }, item: {}, }); + +export const OverflowMenu = styled(OverflowMenuComponent); diff --git a/src/framework/ui/overflowMenu/overflowMenu.spec.tsx b/src/framework/ui/overflowMenu/overflowMenu.spec.tsx index 451b60720..ced82a81f 100644 --- a/src/framework/ui/overflowMenu/overflowMenu.spec.tsx +++ b/src/framework/ui/overflowMenu/overflowMenu.spec.tsx @@ -15,27 +15,26 @@ import { } from 'react-native-testing-library'; import { ReactTestInstance } from 'react-test-renderer'; import { - styled, ApplicationProvider, ApplicationProviderProps, StyleType, } from '@kitten/theme'; import { OverflowMenuItemType, - OverflowMenuItem as OverflowMenuItemComponent, - Props as OverflowMenuItemComponentProps, + OverflowMenuItemProps, + OverflowMenuItem, } from './overflowMenuItem.component'; import { - OverflowMenu as OverflowMenuComponent, - Props as OverflowMenuComponentProps, + OverflowMenu, + OverflowMenuProps, } from './overflowMenu.component'; -import { default as mapping } from '../common/mapping.json'; -import { default as theme } from '../common/theme.json'; +import { + mapping, + theme, +} from '../support/tests'; -const OverflowMenuItem = styled(OverflowMenuItemComponent); -const OverflowMenu = styled(OverflowMenuComponent); -const MockMenu = (props?: OverflowMenuComponentProps): React.ReactElement => { +const MockMenu = (props?: OverflowMenuProps): React.ReactElement => { return ( => { +const MockMenuItem = (props?: OverflowMenuItemProps): React.ReactElement => { return ( { />, ); - const { output } = shallow(component.getByType(OverflowMenuItemComponent)); + const { output } = shallow(component.getByType(OverflowMenuItem)); expect(output).toMatchSnapshot(); }); @@ -109,7 +111,7 @@ describe('@overflow-menu-item: component checks', () => { />, ); - const { output } = shallow(component.getByType(OverflowMenuItemComponent)); + const { output } = shallow(component.getByType(OverflowMenuItem)); expect(output).toMatchSnapshot(); }); @@ -162,14 +164,14 @@ describe('@overflow-menu-item: component checks', () => { fireEvent(component.getByType(TouchableOpacity), 'pressIn'); const active: ReactTestInstance = await waitForElement(() => { - return component.getByType(OverflowMenuItemComponent); + return component.getByType(OverflowMenuItem); }); const { output: activeOutput } = shallow(active); fireEvent(component.getByType(TouchableOpacity), 'pressOut'); const inactive: ReactTestInstance = await waitForElement(() => { - return component.getByType(OverflowMenuItemComponent); + return component.getByType(OverflowMenuItem); }); const { output: inactiveOutput } = shallow(inactive); @@ -197,7 +199,7 @@ describe('@overflow-menu: component checks', () => { , ); - const { output } = shallow(component.getByType(OverflowMenuComponent)); + const { output } = shallow(component.getByType(OverflowMenu)); expect(output).toMatchSnapshot(); }); @@ -215,7 +217,7 @@ describe('@overflow-menu: component checks', () => { , ); - const { output } = shallow(component.getByType(OverflowMenuComponent)); + const { output } = shallow(component.getByType(OverflowMenu)); expect(output).toMatchSnapshot(); }); diff --git a/src/framework/ui/overflowMenu/overflowMenu.spec.tsx.snap b/src/framework/ui/overflowMenu/overflowMenu.spec.tsx.snap index 9cb4a90aa..ae3c61c4a 100644 --- a/src/framework/ui/overflowMenu/overflowMenu.spec.tsx.snap +++ b/src/framework/ui/overflowMenu/overflowMenu.spec.tsx.snap @@ -1,88 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`@overflow-menu: component checks * component renders properly 1`] = ` - - - - - - - } - dispatch={[Function]} - indicatorOffset={12} + - + `; exports[`@overflow-menu: component checks * single menu-item 1`] = ` - - - - } - dispatch={[Function]} - indicatorOffset={12} + - + `; exports[`@overflow-menu-item: component checks * menu item active checks 1`] = ` - - - Test Menu Item - - + } + text="Test Menu Item" +/> `; exports[`@overflow-menu-item: component checks * menu item active checks 2`] = ` - - - Test Menu Item - - + } + text="Test Menu Item" +/> `; exports[`@overflow-menu-item: component checks * menu item with "set-1" props 1`] = ` - - - - Test Menu Item - - +/> `; exports[`@overflow-menu-item: component checks * menu item with "set-2" props 1`] = ` - - - Test Menu Item - - +/> `; diff --git a/src/framework/ui/overflowMenu/overflowMenuItem.component.tsx b/src/framework/ui/overflowMenu/overflowMenuItem.component.tsx index 0058cffea..df182942d 100644 --- a/src/framework/ui/overflowMenu/overflowMenuItem.component.tsx +++ b/src/framework/ui/overflowMenu/overflowMenuItem.component.tsx @@ -20,22 +20,27 @@ import { styled, } from '@kitten/theme'; import { - Text as TextComponent, - Props as TextProps, + Text, + TextProps, } from '../text/text.component'; -import { TouchableOpacityIndexedProps } from '../common/type'; +import { TouchableIndexedProps } from '../support/typings'; -export interface OverflowMenuItemType { - icon?: (style: StyleType) => React.ReactElement; - text: React.ReactText; - textStyle?: StyleProp; - disabled?: boolean; +type TextElement = React.ReactElement; +type IconElement = React.ReactElement; +type IconProp = (style: StyleType) => IconElement; + +interface ListDerivedProps { index?: number; } -const Text = styled(TextComponent); +export interface OverflowMenuItemType extends ListDerivedProps { + icon?: IconProp; + text: string; + textStyle?: StyleProp; + disabled?: boolean; +} -export type Props = OverflowMenuItemType & StyledComponentProps & TouchableOpacityIndexedProps; +export type OverflowMenuItemProps = StyledComponentProps & TouchableIndexedProps & OverflowMenuItemType; /** * The `OverflowMenuItem` component is a part of the OverflowMenu component. @@ -65,7 +70,7 @@ export type Props = OverflowMenuItemType & StyledComponentProps & TouchableOpaci * * */ -export class OverflowMenuItem extends React.Component { +export class OverflowMenuItemComponent extends React.Component { static styledComponentName: string = 'OverflowMenuItem'; @@ -97,8 +102,9 @@ export class OverflowMenuItem extends React.Component { } }; - private getComponentStyle = (style: StyleType): StyleType => { - const { textStyle } = this.props; + private getComponentStyle = (source: StyleType): StyleType => { + const { style, textStyle } = this.props; + const { textMarginHorizontal, textFontSize, @@ -110,13 +116,13 @@ export class OverflowMenuItem extends React.Component { iconMarginHorizontal, iconTintColor, ...containerStyle - } = style; + } = source; return { container: { ...containerStyle, ...styles.container, - borderBottomColor: 'red', + ...StyleSheet.flatten(style), }, text: { marginHorizontal: textMarginHorizontal, @@ -135,29 +141,30 @@ export class OverflowMenuItem extends React.Component { }; }; - private renderTextElement = (style: StyleType): React.ReactElement => { - const { text } = this.props; - + private renderTextElement = (style: StyleType): TextElement => { return ( - {text} + {this.props.text} ); }; - private renderImageElement = (style: StyleType): React.ReactElement => { - const { icon } = this.props; + private renderIconElement = (style: StyleType): IconElement => { + const iconElement: IconElement = this.props.icon(style); - return React.cloneElement(icon(style), { key: 1 }); + return React.cloneElement(iconElement, { + key: 1, + style: [style, iconElement.props.style], + }); }; - private renderComponentChildren = (style: StyleType): React.ReactNode => { + private renderComponentChildren = (style: StyleType): React.ReactNodeArray => { const { icon } = this.props; return [ - icon ? this.renderImageElement(style.icon) : null, + icon && this.renderIconElement(style.icon), this.renderTextElement(style.text), ]; }; @@ -165,18 +172,20 @@ export class OverflowMenuItem extends React.Component { public render(): React.ReactNode { const { style, themedStyle, ...restProps } = this.props; const { container, ...componentStyles } = this.getComponentStyle(themedStyle); - const componentChildren: React.ReactNode = this.renderComponentChildren(componentStyles); + + const [iconElement, textElement] = this.renderComponentChildren(componentStyles); return ( - {componentChildren} + {iconElement} + {textElement} ); } @@ -188,3 +197,5 @@ const styles = StyleSheet.create({ alignItems: 'center', }, }); + +export const OverflowMenuItem = styled(OverflowMenuItemComponent); diff --git a/src/framework/ui/popover/measure.component.tsx b/src/framework/ui/popover/measure.component.tsx index 6a2dc34d5..5f4891913 100644 --- a/src/framework/ui/popover/measure.component.tsx +++ b/src/framework/ui/popover/measure.component.tsx @@ -10,7 +10,7 @@ import { import { Frame } from './type'; export type MeasuringElement = React.ReactElement; -export type MeasuringElementProps = { tag: string | number } & any; +export type MeasuringElementProps = { tag: React.ReactText } & any; export type MeasuringNode = React.ReactElement; export type MeasuringNodeProps = MeasureNodeProps & ViewProps; diff --git a/src/framework/ui/popover/popover.component.tsx b/src/framework/ui/popover/popover.component.tsx index febd3a523..62b245f69 100644 --- a/src/framework/ui/popover/popover.component.tsx +++ b/src/framework/ui/popover/popover.component.tsx @@ -17,12 +17,13 @@ import { import { ModalComponentCloseProps, ModalService, + styled, StyledComponentProps, StyleType, } from '@kitten/theme'; import { PopoverView, - Props as PopoverViewProps, + PopoverViewProps, } from './popoverView.component'; import { MeasuredElement, @@ -36,21 +37,24 @@ import { Frame, OffsetRect, Offsets, - Placement, - Placements, + PopoverPlacement, + PopoverPlacements, } from './type'; -interface PopoverProps { - content: React.ReactElement; - children: React.ReactElement; +type ContentElement = React.ReactElement; +type ChildElement = React.ReactElement; + +interface ComponentProps extends PopoverViewProps, ModalComponentCloseProps { + content: ContentElement; + children: ChildElement; visible?: boolean; } -export type Props = PopoverProps & ModalComponentCloseProps & StyledComponentProps & PopoverViewProps & ViewProps; +export type PopoverProps = StyledComponentProps & ViewProps & ComponentProps; const TAG_CHILD: number = 0; const TAG_CONTENT: number = 1; -const PLACEMENT_DEFAULT: Placement = Placements.BOTTOM; +const PLACEMENT_DEFAULT: PopoverPlacement = PopoverPlacements.BOTTOM; /** * The `Popover` component is a component that displays content when users focus on or tap an element. @@ -121,19 +125,19 @@ const PLACEMENT_DEFAULT: Placement = Placements.BOTTOM; * ``` * */ -export class Popover extends React.Component { +export class PopoverComponent extends React.Component { static styledComponentName: string = 'Popover'; - static defaultProps: Partial = { + static defaultProps: Partial = { placement: PLACEMENT_DEFAULT.rawValue, visible: false, }; - private popoverElement: MeasuredElement = undefined; + private popoverElement: MeasuredElement; private popoverModalId: string = ''; - public componentDidUpdate(prevProps: Props): void { + public componentDidUpdate(prevProps: PopoverProps) { const { visible } = this.props; if (prevProps.visible !== visible) { @@ -189,25 +193,25 @@ export class Popover extends React.Component { return ModalService.show(popover, true); }; - private getPopoverFrame = (layout: MeasureResult, rawPlacement: string | Placement): Frame => { + private getPopoverFrame = (layout: MeasureResult, rawPlacement: string | PopoverPlacement): Frame => { const { children } = this.props; const { [TAG_CONTENT]: popoverFrame, [TAG_CHILD]: childFrame } = layout; const offsetRect: OffsetRect = Offsets.find(children.props.style); - const placement: Placement = Placements.parse(rawPlacement, PLACEMENT_DEFAULT); + const placement: PopoverPlacement = PopoverPlacements.parse(rawPlacement, PLACEMENT_DEFAULT); return placement.frame(popoverFrame, childFrame, offsetRect); }; - private renderPopoverElement = (children: React.ReactElement, style: StyleProp): MeasuringElement => { + private renderPopoverElement = (children: ContentElement, style: StyleProp): MeasuringElement => { const { placement, ...derivedProps } = this.props; const measuringProps: MeasuringElementProps = { tag: TAG_CONTENT, }; - const popoverPlacement: Placement = Placements.parse(placement, PLACEMENT_DEFAULT); - const indicatorPlacement: Placement = popoverPlacement.reverse(); + const popoverPlacement: PopoverPlacement = PopoverPlacements.parse(placement, PLACEMENT_DEFAULT); + const indicatorPlacement: PopoverPlacement = popoverPlacement.reverse(); return ( { ); }; - private renderChildElement = (source: React.ReactElement, style: StyleProp): MeasuringElement => { + private renderChildElement = (source: ChildElement, style: StyleProp): MeasuringElement => { const measuringProps: MeasuringElementProps = { tag: TAG_CHILD }; return ( @@ -267,3 +271,5 @@ const styles = StyleSheet.create({ opacity: 0, }, }); + +export const Popover = styled(PopoverComponent); diff --git a/src/framework/ui/popover/popoverView.component.tsx b/src/framework/ui/popover/popoverView.component.tsx index 88d924d9a..29aad9628 100644 --- a/src/framework/ui/popover/popoverView.component.tsx +++ b/src/framework/ui/popover/popoverView.component.tsx @@ -6,29 +6,29 @@ import { StyleSheet, } from 'react-native'; import { StyleType } from '@kitten/theme'; -import { Arrow } from '../drawable/arrow/arrow.component'; import { - Placement, - Placements, + PopoverPlacement, + PopoverPlacements, } from './type'; +import { Arrow } from '../support/components'; -interface PopoverViewProps { - placement?: string | Placement; +interface ComponentProps { + placement?: string | PopoverPlacement; indicatorOffset?: number; } -const PLACEMENT_DEFAULT: Placement = Placements.TOP; +const PLACEMENT_DEFAULT: PopoverPlacement = PopoverPlacements.TOP; -export type Props = PopoverViewProps & ViewProps; +export type PopoverViewProps = ViewProps & ComponentProps; -export class PopoverView extends React.Component { +export class PopoverView extends React.Component { - static defaultProps: Partial = { + static defaultProps: Partial = { placement: PLACEMENT_DEFAULT.rawValue, indicatorOffset: 8, }; - private getComponentStyle = (source: StyleType, placement: Placement): StyleType => { + private getComponentStyle = (source: StyleType, placement: PopoverPlacement): StyleType => { const { direction, alignment } = placement.flex(); const { width: indicatorWidth } = styles.indicator; @@ -93,7 +93,7 @@ export class PopoverView extends React.Component { public render(): React.ReactElement { const { style, placement: rawPlacement, children, ...derivedProps } = this.props; - const placement: Placement = Placements.parse(rawPlacement, PLACEMENT_DEFAULT); + const placement: PopoverPlacement = PopoverPlacements.parse(rawPlacement, PLACEMENT_DEFAULT); const { container, indicator, content } = this.getComponentStyle(style, placement); @@ -113,7 +113,6 @@ export class PopoverView extends React.Component { const styles = StyleSheet.create({ container: { - backgroundColor: 'transparent', alignSelf: 'flex-start', }, content: { diff --git a/src/framework/ui/popover/type.spec.ts b/src/framework/ui/popover/type.spec.ts index ce74e4e33..cb2eb9d4e 100644 --- a/src/framework/ui/popover/type.spec.ts +++ b/src/framework/ui/popover/type.spec.ts @@ -1,8 +1,8 @@ import { Frame, OffsetRect, - Placement, - Placements, + PopoverPlacement, + PopoverPlacements, } from './type'; describe('@type: popover model checks', () => { @@ -62,84 +62,84 @@ describe('@type: popover model checks', () => { const rhsFrame: Frame = new Frame(6, 6, 2, 2); it('* left', () => { - const { origin: { x, y } } = Placements.LEFT.frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.LEFT.frame(rhsFrame, lhsFrame); expect(x).toEqual(0); expect(y).toEqual(3); }); it('* left start', () => { - const { origin: { x, y } } = Placements.LEFT_START.frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.LEFT_START.frame(rhsFrame, lhsFrame); expect(x).toEqual(0); expect(y).toEqual(2); }); it('* left end', () => { - const { origin: { x, y } } = Placements.LEFT_END.frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.LEFT_END.frame(rhsFrame, lhsFrame); expect(x).toEqual(0); expect(y).toEqual(4); }); it('* top', () => { - const { origin: { x, y } } = Placements.TOP.frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.TOP.frame(rhsFrame, lhsFrame); expect(x).toEqual(3); expect(y).toEqual(0); }); it('* top start', () => { - const { origin: { x, y } } = Placements.TOP_START.frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.TOP_START.frame(rhsFrame, lhsFrame); expect(x).toEqual(2); expect(y).toEqual(0); }); it('* top end', () => { - const { origin: { x, y } } = Placements.TOP_END.frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.TOP_END.frame(rhsFrame, lhsFrame); expect(x).toEqual(4); expect(y).toEqual(0); }); it('* right', () => { - const { origin: { x, y } } = Placements.RIGHT.frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.RIGHT.frame(rhsFrame, lhsFrame); expect(x).toEqual(6); expect(y).toEqual(3); }); it('* right start', () => { - const { origin: { x, y } } = Placements.RIGHT_START.frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.RIGHT_START.frame(rhsFrame, lhsFrame); expect(x).toEqual(6); expect(y).toEqual(2); }); it('* right end', () => { - const { origin: { x, y } } = Placements.RIGHT_END.frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.RIGHT_END.frame(rhsFrame, lhsFrame); expect(x).toEqual(6); expect(y).toEqual(4); }); it('* bottom', () => { - const { origin: { x, y } } = Placements.BOTTOM.frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.BOTTOM.frame(rhsFrame, lhsFrame); expect(x).toEqual(3); expect(y).toEqual(6); }); it('* bottom start', () => { - const { origin: { x, y } } = Placements.BOTTOM_START.frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.BOTTOM_START.frame(rhsFrame, lhsFrame); expect(x).toEqual(2); expect(y).toEqual(6); }); it('* bottom end', () => { - const { origin: { x, y } } = Placements.BOTTOM_END.frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.BOTTOM_END.frame(rhsFrame, lhsFrame); expect(x).toEqual(4); expect(y).toEqual(6); @@ -154,84 +154,84 @@ describe('@type: popover model checks', () => { const offset: OffsetRect = { left: 2, top: 2, right: 2, bottom: 2 }; it('* left', () => { - const { origin: { x, y } } = Placements.LEFT.frame(rhsFrame, lhsFrame, offset); + const { origin: { x, y } } = PopoverPlacements.LEFT.frame(rhsFrame, lhsFrame, offset); expect(x).toEqual(2); expect(y).toEqual(3); }); it('* left start', () => { - const { origin: { x, y } } = Placements.LEFT_START.frame(rhsFrame, lhsFrame, offset); + const { origin: { x, y } } = PopoverPlacements.LEFT_START.frame(rhsFrame, lhsFrame, offset); expect(x).toEqual(2); expect(y).toEqual(4); }); it('* left end', () => { - const { origin: { x, y } } = Placements.LEFT_END.frame(rhsFrame, lhsFrame, offset); + const { origin: { x, y } } = PopoverPlacements.LEFT_END.frame(rhsFrame, lhsFrame, offset); expect(x).toEqual(2); expect(y).toEqual(2); }); it('* top', () => { - const { origin: { x, y } } = Placements.TOP.frame(rhsFrame, lhsFrame, offset); + const { origin: { x, y } } = PopoverPlacements.TOP.frame(rhsFrame, lhsFrame, offset); expect(x).toEqual(3); expect(y).toEqual(2); }); it('* top start', () => { - const { origin: { x, y } } = Placements.TOP_START.frame(rhsFrame, lhsFrame, offset); + const { origin: { x, y } } = PopoverPlacements.TOP_START.frame(rhsFrame, lhsFrame, offset); expect(x).toEqual(4); expect(y).toEqual(2); }); it('* top end', () => { - const { origin: { x, y } } = Placements.TOP_END.frame(rhsFrame, lhsFrame, offset); + const { origin: { x, y } } = PopoverPlacements.TOP_END.frame(rhsFrame, lhsFrame, offset); expect(x).toEqual(2); expect(y).toEqual(2); }); it('* right', () => { - const { origin: { x, y } } = Placements.RIGHT.frame(rhsFrame, lhsFrame, offset); + const { origin: { x, y } } = PopoverPlacements.RIGHT.frame(rhsFrame, lhsFrame, offset); expect(x).toEqual(4); expect(y).toEqual(3); }); it('* right start', () => { - const { origin: { x, y } } = Placements.RIGHT_START.frame(rhsFrame, lhsFrame, offset); + const { origin: { x, y } } = PopoverPlacements.RIGHT_START.frame(rhsFrame, lhsFrame, offset); expect(x).toEqual(4); expect(y).toEqual(4); }); it('* right end', () => { - const { origin: { x, y } } = Placements.RIGHT_END.frame(rhsFrame, lhsFrame, offset); + const { origin: { x, y } } = PopoverPlacements.RIGHT_END.frame(rhsFrame, lhsFrame, offset); expect(x).toEqual(4); expect(y).toEqual(2); }); it('* bottom', () => { - const { origin: { x, y } } = Placements.BOTTOM.frame(rhsFrame, lhsFrame, offset); + const { origin: { x, y } } = PopoverPlacements.BOTTOM.frame(rhsFrame, lhsFrame, offset); expect(x).toEqual(3); expect(y).toEqual(4); }); it('* bottom start', () => { - const { origin: { x, y } } = Placements.BOTTOM_START.frame(rhsFrame, lhsFrame, offset); + const { origin: { x, y } } = PopoverPlacements.BOTTOM_START.frame(rhsFrame, lhsFrame, offset); expect(x).toEqual(4); expect(y).toEqual(4); }); it('* bottom end', () => { - const { origin: { x, y } } = Placements.BOTTOM_END.frame(rhsFrame, lhsFrame, offset); + const { origin: { x, y } } = PopoverPlacements.BOTTOM_END.frame(rhsFrame, lhsFrame, offset); expect(x).toEqual(2); expect(y).toEqual(4); @@ -245,84 +245,84 @@ describe('@type: popover model checks', () => { const rhsFrame: Frame = new Frame(6, 6, 2, 2); it('* left', () => { - const { origin: { x, y } } = Placements.LEFT.reverse().frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.LEFT.reverse().frame(rhsFrame, lhsFrame); expect(x).toEqual(6); expect(y).toEqual(3); }); it('* left start', () => { - const { origin: { x, y } } = Placements.LEFT_START.reverse().frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.LEFT_START.reverse().frame(rhsFrame, lhsFrame); expect(x).toEqual(6); expect(y).toEqual(2); }); it('* left end', () => { - const { origin: { x, y } } = Placements.LEFT_END.reverse().frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.LEFT_END.reverse().frame(rhsFrame, lhsFrame); expect(x).toEqual(6); expect(y).toEqual(4); }); it('* top', () => { - const { origin: { x, y } } = Placements.TOP.reverse().frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.TOP.reverse().frame(rhsFrame, lhsFrame); expect(x).toEqual(3); expect(y).toEqual(6); }); it('* top start', () => { - const { origin: { x, y } } = Placements.TOP_START.reverse().frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.TOP_START.reverse().frame(rhsFrame, lhsFrame); expect(x).toEqual(2); expect(y).toEqual(6); }); it('* top end', () => { - const { origin: { x, y } } = Placements.TOP_END.reverse().frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.TOP_END.reverse().frame(rhsFrame, lhsFrame); expect(x).toEqual(4); expect(y).toEqual(6); }); it('* right', () => { - const { origin: { x, y } } = Placements.RIGHT.reverse().frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.RIGHT.reverse().frame(rhsFrame, lhsFrame); expect(x).toEqual(0); expect(y).toEqual(3); }); it('* right start', () => { - const { origin: { x, y } } = Placements.RIGHT_START.reverse().frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.RIGHT_START.reverse().frame(rhsFrame, lhsFrame); expect(x).toEqual(0); expect(y).toEqual(2); }); it('* right end', () => { - const { origin: { x, y } } = Placements.RIGHT_END.reverse().frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.RIGHT_END.reverse().frame(rhsFrame, lhsFrame); expect(x).toEqual(0); expect(y).toEqual(4); }); it('* bottom', () => { - const { origin: { x, y } } = Placements.BOTTOM.reverse().frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.BOTTOM.reverse().frame(rhsFrame, lhsFrame); expect(x).toEqual(3); expect(y).toEqual(0); }); it('* bottom start', () => { - const { origin: { x, y } } = Placements.BOTTOM_START.reverse().frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.BOTTOM_START.reverse().frame(rhsFrame, lhsFrame); expect(x).toEqual(2); expect(y).toEqual(0); }); it('* bottom end', () => { - const { origin: { x, y } } = Placements.BOTTOM_END.reverse().frame(rhsFrame, lhsFrame); + const { origin: { x, y } } = PopoverPlacements.BOTTOM_END.reverse().frame(rhsFrame, lhsFrame); expect(x).toEqual(4); expect(y).toEqual(0); @@ -333,81 +333,81 @@ describe('@type: popover model checks', () => { describe('* raw constructor', () => { it('* left', () => { - const placement: Placement = Placements.parse('left'); + const placement: PopoverPlacement = PopoverPlacements.parse('left'); - expect(placement.rawValue).toEqual(Placements.LEFT.rawValue); + expect(placement.rawValue).toEqual(PopoverPlacements.LEFT.rawValue); }); it('* left start', () => { - const placement: Placement = Placements.parse('left start'); + const placement: PopoverPlacement = PopoverPlacements.parse('left start'); - expect(placement.rawValue).toEqual(Placements.LEFT_START.rawValue); + expect(placement.rawValue).toEqual(PopoverPlacements.LEFT_START.rawValue); }); it('* left end', () => { - const placement: Placement = Placements.parse('left end'); + const placement: PopoverPlacement = PopoverPlacements.parse('left end'); - expect(placement.rawValue).toEqual(Placements.LEFT_END.rawValue); + expect(placement.rawValue).toEqual(PopoverPlacements.LEFT_END.rawValue); }); it('* top', () => { - const placement: Placement = Placements.parse('top'); + const placement: PopoverPlacement = PopoverPlacements.parse('top'); - expect(placement.rawValue).toEqual(Placements.TOP.rawValue); + expect(placement.rawValue).toEqual(PopoverPlacements.TOP.rawValue); }); it('* top start', () => { - const placement: Placement = Placements.parse('top start'); + const placement: PopoverPlacement = PopoverPlacements.parse('top start'); - expect(placement.rawValue).toEqual(Placements.TOP_START.rawValue); + expect(placement.rawValue).toEqual(PopoverPlacements.TOP_START.rawValue); }); it('* top end', () => { - const placement: Placement = Placements.parse('top end'); + const placement: PopoverPlacement = PopoverPlacements.parse('top end'); - expect(placement.rawValue).toEqual(Placements.TOP_END.rawValue); + expect(placement.rawValue).toEqual(PopoverPlacements.TOP_END.rawValue); }); it('* right', () => { - const placement: Placement = Placements.parse('right'); + const placement: PopoverPlacement = PopoverPlacements.parse('right'); - expect(placement.rawValue).toEqual(Placements.RIGHT.rawValue); + expect(placement.rawValue).toEqual(PopoverPlacements.RIGHT.rawValue); }); it('* right start', () => { - const placement: Placement = Placements.parse('right start'); + const placement: PopoverPlacement = PopoverPlacements.parse('right start'); - expect(placement.rawValue).toEqual(Placements.RIGHT_START.rawValue); + expect(placement.rawValue).toEqual(PopoverPlacements.RIGHT_START.rawValue); }); it('* right end', () => { - const placement: Placement = Placements.parse('right end'); + const placement: PopoverPlacement = PopoverPlacements.parse('right end'); - expect(placement.rawValue).toEqual(Placements.RIGHT_END.rawValue); + expect(placement.rawValue).toEqual(PopoverPlacements.RIGHT_END.rawValue); }); it('* bottom', () => { - const placement: Placement = Placements.parse('bottom'); + const placement: PopoverPlacement = PopoverPlacements.parse('bottom'); - expect(placement.rawValue).toEqual(Placements.BOTTOM.rawValue); + expect(placement.rawValue).toEqual(PopoverPlacements.BOTTOM.rawValue); }); it('* bottom start', () => { - const placement: Placement = Placements.parse('bottom start'); + const placement: PopoverPlacement = PopoverPlacements.parse('bottom start'); - expect(placement.rawValue).toEqual(Placements.BOTTOM_START.rawValue); + expect(placement.rawValue).toEqual(PopoverPlacements.BOTTOM_START.rawValue); }); it('* bottom end', () => { - const placement: Placement = Placements.parse('bottom end'); + const placement: PopoverPlacement = PopoverPlacements.parse('bottom end'); - expect(placement.rawValue).toEqual(Placements.BOTTOM_END.rawValue); + expect(placement.rawValue).toEqual(PopoverPlacements.BOTTOM_END.rawValue); }); it('* fallback', () => { - const placement: Placement = Placements.parse('undefined', Placements.BOTTOM); + const placement: PopoverPlacement = PopoverPlacements.parse('undefined', PopoverPlacements.BOTTOM); - expect(placement.rawValue).toEqual(Placements.BOTTOM.rawValue); + expect(placement.rawValue).toEqual(PopoverPlacements.BOTTOM.rawValue); }); }); diff --git a/src/framework/ui/popover/type.ts b/src/framework/ui/popover/type.ts index d171f6ad3..c4cc82f4a 100644 --- a/src/framework/ui/popover/type.ts +++ b/src/framework/ui/popover/type.ts @@ -249,16 +249,16 @@ export class Offsets { } } -export interface Placement { +export interface PopoverPlacement { rawValue: string; frame(source: Frame, other: Frame, offset?: OffsetRect): Frame; flex(): FlexPlacement; - parent(): Placement; + parent(): PopoverPlacement; - reverse(): Placement; + reverse(): PopoverPlacement; } export interface FlexPlacement { @@ -266,9 +266,9 @@ export interface FlexPlacement { alignment: 'flex-start' | 'flex-end' | 'center'; } -export class Placements { +export class PopoverPlacements { - static LEFT: Placement = new class implements Placement { + static LEFT: PopoverPlacement = new class implements PopoverPlacement { rawValue: string = 'left'; frame(source: Frame, other: Frame, offset: OffsetRect = OffsetRect.zero()): Frame { @@ -289,16 +289,16 @@ export class Placements { }; } - parent(): Placement { + parent(): PopoverPlacement { return this; } - reverse(): Placement { - return Placements.RIGHT; + reverse(): PopoverPlacement { + return PopoverPlacements.RIGHT; } }; - static LEFT_START: Placement = new class implements Placement { + static LEFT_START: PopoverPlacement = new class implements PopoverPlacement { rawValue: string = 'left start'; frame(source: Frame, other: Frame, offset: OffsetRect = OffsetRect.zero()): Frame { @@ -319,16 +319,16 @@ export class Placements { }; } - parent(): Placement { - return Placements.LEFT; + parent(): PopoverPlacement { + return PopoverPlacements.LEFT; } - reverse(): Placement { - return Placements.RIGHT_START; + reverse(): PopoverPlacement { + return PopoverPlacements.RIGHT_START; } }; - static LEFT_END: Placement = new class implements Placement { + static LEFT_END: PopoverPlacement = new class implements PopoverPlacement { rawValue = 'left end'; frame(source: Frame, other: Frame, offset: OffsetRect = OffsetRect.zero()): Frame { @@ -349,16 +349,16 @@ export class Placements { }; } - parent(): Placement { - return Placements.LEFT; + parent(): PopoverPlacement { + return PopoverPlacements.LEFT; } - reverse(): Placement { - return Placements.RIGHT_END; + reverse(): PopoverPlacement { + return PopoverPlacements.RIGHT_END; } }; - static TOP: Placement = new class implements Placement { + static TOP: PopoverPlacement = new class implements PopoverPlacement { rawValue = 'top'; frame(source: Frame, other: Frame, offset: OffsetRect = OffsetRect.zero()): Frame { @@ -379,16 +379,16 @@ export class Placements { }; } - parent(): Placement { + parent(): PopoverPlacement { return this; } - reverse(): Placement { - return Placements.BOTTOM; + reverse(): PopoverPlacement { + return PopoverPlacements.BOTTOM; } }; - static TOP_START: Placement = new class implements Placement { + static TOP_START: PopoverPlacement = new class implements PopoverPlacement { rawValue = 'top start'; frame(source: Frame, other: Frame, offset: OffsetRect = OffsetRect.zero()): Frame { @@ -409,16 +409,16 @@ export class Placements { }; } - parent(): Placement { - return Placements.TOP; + parent(): PopoverPlacement { + return PopoverPlacements.TOP; } - reverse(): Placement { - return Placements.BOTTOM_START; + reverse(): PopoverPlacement { + return PopoverPlacements.BOTTOM_START; } }; - static TOP_END: Placement = new class implements Placement { + static TOP_END: PopoverPlacement = new class implements PopoverPlacement { rawValue = 'top end'; frame(source: Frame, other: Frame, offset: OffsetRect = OffsetRect.zero()): Frame { @@ -439,16 +439,16 @@ export class Placements { }; } - parent(): Placement { - return Placements.TOP; + parent(): PopoverPlacement { + return PopoverPlacements.TOP; } - reverse(): Placement { - return Placements.BOTTOM_END; + reverse(): PopoverPlacement { + return PopoverPlacements.BOTTOM_END; } }; - static RIGHT: Placement = new class implements Placement { + static RIGHT: PopoverPlacement = new class implements PopoverPlacement { rawValue = 'right'; frame(source: Frame, other: Frame, offset: OffsetRect = OffsetRect.zero()): Frame { @@ -469,16 +469,16 @@ export class Placements { }; } - parent(): Placement { + parent(): PopoverPlacement { return this; } - reverse(): Placement { - return Placements.LEFT; + reverse(): PopoverPlacement { + return PopoverPlacements.LEFT; } }; - static RIGHT_START: Placement = new class implements Placement { + static RIGHT_START: PopoverPlacement = new class implements PopoverPlacement { rawValue = 'right start'; frame(source: Frame, other: Frame, offset: OffsetRect = OffsetRect.zero()): Frame { @@ -499,16 +499,16 @@ export class Placements { }; } - parent(): Placement { - return Placements.RIGHT; + parent(): PopoverPlacement { + return PopoverPlacements.RIGHT; } - reverse(): Placement { - return Placements.LEFT_START; + reverse(): PopoverPlacement { + return PopoverPlacements.LEFT_START; } }; - static RIGHT_END: Placement = new class implements Placement { + static RIGHT_END: PopoverPlacement = new class implements PopoverPlacement { rawValue = 'right end'; frame(source: Frame, other: Frame, offset: OffsetRect = OffsetRect.zero()): Frame { @@ -529,16 +529,16 @@ export class Placements { }; } - parent(): Placement { - return Placements.RIGHT; + parent(): PopoverPlacement { + return PopoverPlacements.RIGHT; } - reverse(): Placement { - return Placements.LEFT_END; + reverse(): PopoverPlacement { + return PopoverPlacements.LEFT_END; } }; - static BOTTOM: Placement = new class implements Placement { + static BOTTOM: PopoverPlacement = new class implements PopoverPlacement { rawValue = 'bottom'; frame(source: Frame, other: Frame, offset: OffsetRect = OffsetRect.zero()): Frame { @@ -559,16 +559,16 @@ export class Placements { }; } - parent(): Placement { + parent(): PopoverPlacement { return this; } - reverse(): Placement { - return Placements.TOP; + reverse(): PopoverPlacement { + return PopoverPlacements.TOP; } }; - static BOTTOM_START: Placement = new class implements Placement { + static BOTTOM_START: PopoverPlacement = new class implements PopoverPlacement { rawValue = 'bottom start'; frame(source: Frame, other: Frame, offset: OffsetRect = OffsetRect.zero()): Frame { @@ -589,16 +589,16 @@ export class Placements { }; } - parent(): Placement { - return Placements.BOTTOM; + parent(): PopoverPlacement { + return PopoverPlacements.BOTTOM; } - reverse(): Placement { - return Placements.TOP_START; + reverse(): PopoverPlacement { + return PopoverPlacements.TOP_START; } }; - static BOTTOM_END: Placement = new class implements Placement { + static BOTTOM_END: PopoverPlacement = new class implements PopoverPlacement { rawValue = 'bottom end'; frame(source: Frame, other: Frame, offset: OffsetRect = OffsetRect.zero()): Frame { @@ -619,52 +619,52 @@ export class Placements { }; } - parent(): Placement { - return Placements.BOTTOM; + parent(): PopoverPlacement { + return PopoverPlacements.BOTTOM; } - reverse(): Placement { - return Placements.TOP_END; + reverse(): PopoverPlacement { + return PopoverPlacements.TOP_END; } }; - static parse(value: string | Placement, fallback?: Placement): Placement | undefined { - return Placements.typeOf(value) ? value : Placements.parseString(value, fallback); + static parse(value: string | PopoverPlacement, fallback?: PopoverPlacement): PopoverPlacement | undefined { + return PopoverPlacements.typeOf(value) ? value : PopoverPlacements.parseString(value, fallback); } - private static parseString(rawValue: string, fallback?: Placement): Placement | undefined { + private static parseString(rawValue: string, fallback?: PopoverPlacement): PopoverPlacement | undefined { switch (rawValue) { - case Placements.LEFT.rawValue: - return Placements.LEFT; - case Placements.TOP.rawValue: - return Placements.TOP; - case Placements.RIGHT.rawValue: - return Placements.RIGHT; - case Placements.BOTTOM.rawValue: - return Placements.BOTTOM; - case Placements.LEFT_START.rawValue: - return Placements.LEFT_START; - case Placements.LEFT_END.rawValue: - return Placements.LEFT_END; - case Placements.TOP_START.rawValue: - return Placements.TOP_START; - case Placements.TOP_END.rawValue: - return Placements.TOP_END; - case Placements.RIGHT_START.rawValue: - return Placements.RIGHT_START; - case Placements.RIGHT_END.rawValue: - return Placements.RIGHT_END; - case Placements.BOTTOM_START.rawValue: - return Placements.BOTTOM_START; - case Placements.BOTTOM_END.rawValue: - return Placements.BOTTOM_END; + case PopoverPlacements.LEFT.rawValue: + return PopoverPlacements.LEFT; + case PopoverPlacements.TOP.rawValue: + return PopoverPlacements.TOP; + case PopoverPlacements.RIGHT.rawValue: + return PopoverPlacements.RIGHT; + case PopoverPlacements.BOTTOM.rawValue: + return PopoverPlacements.BOTTOM; + case PopoverPlacements.LEFT_START.rawValue: + return PopoverPlacements.LEFT_START; + case PopoverPlacements.LEFT_END.rawValue: + return PopoverPlacements.LEFT_END; + case PopoverPlacements.TOP_START.rawValue: + return PopoverPlacements.TOP_START; + case PopoverPlacements.TOP_END.rawValue: + return PopoverPlacements.TOP_END; + case PopoverPlacements.RIGHT_START.rawValue: + return PopoverPlacements.RIGHT_START; + case PopoverPlacements.RIGHT_END.rawValue: + return PopoverPlacements.RIGHT_END; + case PopoverPlacements.BOTTOM_START.rawValue: + return PopoverPlacements.BOTTOM_START; + case PopoverPlacements.BOTTOM_END.rawValue: + return PopoverPlacements.BOTTOM_END; default: return fallback; } } - private static typeOf(value: any): value is Placement { - const { rawValue } = (value); + private static typeOf(value: any): value is PopoverPlacement { + const { rawValue } = (value); return rawValue !== undefined; } diff --git a/src/framework/ui/radio/radio.component.tsx b/src/framework/ui/radio/radio.component.tsx index a8c0432f6..b268762ff 100644 --- a/src/framework/ui/radio/radio.component.tsx +++ b/src/framework/ui/radio/radio.component.tsx @@ -21,11 +21,14 @@ import { Interaction, } from '@kitten/theme'; import { - Text as TextComponent, - Props as TextProps, + Text, + TextProps, } from '../text/text.component'; +import { isValidString } from '../support/services'; -interface RadioProps { +type TextElement = React.ReactElement; + +interface ComponentProps { textStyle?: StyleProp; text?: string; checked?: boolean; @@ -34,9 +37,7 @@ interface RadioProps { onChange?: (selected: boolean) => void; } -const Text = styled(TextComponent); - -export type Props = RadioProps & StyledComponentProps & TouchableOpacityProps; +export type RadioProps = StyledComponentProps & TouchableOpacityProps & ComponentProps; /** * The `Radio` component is an analog of html radio button. @@ -99,7 +100,7 @@ export type Props = RadioProps & StyledComponentProps & TouchableOpacityProps; * ``` * */ -export class Radio extends React.Component { +export class RadioComponent extends React.Component { static styledComponentName: string = 'Radio'; @@ -125,8 +126,8 @@ export class Radio extends React.Component { } }; - private getComponentStyle = (style: StyleType): StyleType => { - const { style: containerStyle, textStyle } = this.props; + private getComponentStyle = (source: StyleType): StyleType => { + const { style, textStyle } = this.props; const { textMarginHorizontal, @@ -143,12 +144,12 @@ export class Radio extends React.Component { highlightBorderRadius, highlightBackgroundColor, ...containerParameters - } = style; + } = source; return { container: { - ...StyleSheet.flatten(containerStyle), ...styles.container, + ...StyleSheet.flatten(style), }, highlightContainer: styles.highlightContainer, selectContainer: { @@ -180,24 +181,27 @@ export class Radio extends React.Component { }; }; - private renderTextElement = (style: StyleType): React.ReactElement => { - const { text } = this.props; - + private renderTextElement = (style: StyleType): TextElement => { return ( - {text} + + {this.props.text} + ); }; - private renderComponentChildren = (style: StyleType): React.ReactNode => { + private renderComponentChildren = (style: StyleType): React.ReactNodeArray => { const { text } = this.props; return [ - text ? this.renderTextElement(style.text) : undefined, + isValidString(text) && this.renderTextElement(style.text), ]; }; public render(): React.ReactElement { const { style, themedStyle, disabled, ...derivedProps } = this.props; + const { container, highlightContainer, @@ -207,7 +211,7 @@ export class Radio extends React.Component { ...componentStyles } = this.getComponentStyle(themedStyle); - const componentChildren: React.ReactNode = this.renderComponentChildren(componentStyles); + const [textElement] = this.renderComponentChildren(componentStyles); return ( { - {componentChildren} + {textElement} ); } @@ -255,3 +259,5 @@ const styles = StyleSheet.create({ }, text: {}, }); + +export const Radio = styled(RadioComponent); diff --git a/src/framework/ui/radio/radio.spec.tsx b/src/framework/ui/radio/radio.spec.tsx index 8c38de3e9..7850a5939 100644 --- a/src/framework/ui/radio/radio.spec.tsx +++ b/src/framework/ui/radio/radio.spec.tsx @@ -9,18 +9,18 @@ import { } from 'react-native-testing-library'; import { ReactTestInstance } from 'react-test-renderer'; import { - styled, ApplicationProvider, ApplicationProviderProps, } from '@kitten/theme'; import { - Radio as RadioComponent, - Props as RadioProps, + Radio, + RadioComponent, + RadioProps, } from './radio.component'; -import { default as mapping } from '../common/mapping.json'; -import { default as theme } from '../common/theme.json'; - -const Radio = styled(RadioComponent); +import { + mapping, + theme, +} from '../support/tests'; const Mock = (props?: RadioProps): React.ReactElement => { return ( @@ -49,14 +49,14 @@ describe('@radio: matches snapshot', () => { it('checked', () => { const component: RenderAPI = renderComponent({ checked: true }); - const { output } = shallow(component.getByType(RadioComponent)); + const { output } = shallow(component.getByType(Radio)); expect(output).toMatchSnapshot(); }); it('disabled', () => { const component: RenderAPI = renderComponent({ disabled: true }); - const { output } = shallow(component.getByType(RadioComponent)); + const { output } = shallow(component.getByType(Radio)); expect(output).toMatchSnapshot(); }); @@ -66,7 +66,7 @@ describe('@radio: matches snapshot', () => { checked: true, disabled: true, }); - const { output } = shallow(component.getByType(RadioComponent)); + const { output } = shallow(component.getByType(Radio)); expect(output).toMatchSnapshot(); }); @@ -77,14 +77,14 @@ describe('@radio: matches snapshot', () => { fireEvent(component.getByType(TouchableOpacity), 'pressIn'); const active: ReactTestInstance = await waitForElement(() => { - return component.getByType(RadioComponent); + return component.getByType(Radio); }); const { output: activeOutput } = shallow(active); fireEvent(component.getByType(TouchableOpacity), 'pressOut'); const inactive: ReactTestInstance = await waitForElement(() => { - return component.getByType(RadioComponent); + return component.getByType(Radio); }); const { output: inactiveOutput } = shallow(inactive); @@ -98,14 +98,14 @@ describe('@radio: matches snapshot', () => { fireEvent(component.getByType(TouchableOpacity), 'pressIn'); const active: ReactTestInstance = await waitForElement(() => { - return component.getByType(RadioComponent); + return component.getByType(Radio); }); const { output: activeOutput } = shallow(active); fireEvent(component.getByType(TouchableOpacity), 'pressOut'); const inactive: ReactTestInstance = await waitForElement(() => { - return component.getByType(RadioComponent); + return component.getByType(Radio); }); const { output: inactiveOutput } = shallow(inactive); @@ -116,9 +116,12 @@ describe('@radio: matches snapshot', () => { it('with text (styled)', () => { const component: RenderAPI = renderComponent({ text: 'Place your text', - textStyle: { fontSize: 22, lineHeight: 24 }, + textStyle: { + fontSize: 22, + lineHeight: 24, + }, }); - const { output } = shallow(component.getByType(RadioComponent)); + const { output } = shallow(component.getByType(Radio)); expect(output).toMatchSnapshot(); }); diff --git a/src/framework/ui/radio/radio.spec.tsx.snap b/src/framework/ui/radio/radio.spec.tsx.snap index 901651b2a..c49a596bf 100644 --- a/src/framework/ui/radio/radio.spec.tsx.snap +++ b/src/framework/ui/radio/radio.spec.tsx.snap @@ -1,1219 +1,49 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`@radio: matches snapshot active 1`] = ` - - - - - - - - -`; - -exports[`@radio: matches snapshot active checked 1`] = ` - - - - - - - - -`; - -exports[`@radio: matches snapshot active checked: checked 1`] = ` - - - - - - - - -`; - -exports[`@radio: matches snapshot active: default 1`] = ` - - - - - - - - -`; - -exports[`@radio: matches snapshot checked 1`] = ` - - - - - - - - -`; - -exports[`@radio: matches snapshot checked disabled 1`] = ` - - - - - - - - -`; - -exports[`@radio: matches snapshot default 1`] = ` - - - - - - - - + `; -exports[`@radio: matches snapshot disabled 1`] = ` +exports[`@radio: matches snapshot active checked 1`] = ` + +`; + +exports[`@radio: matches snapshot active checked: checked 1`] = ` + +`; + +exports[`@radio: matches snapshot active: default 1`] = ` + +`; + +exports[`@radio: matches snapshot checked 1`] = ` + +`; + +exports[`@radio: matches snapshot checked disabled 1`] = ` + +`; + +exports[`@radio: matches snapshot default 1`] = ` `; +exports[`@radio: matches snapshot disabled 1`] = ` + +`; + exports[`@radio: matches snapshot with text (styled) 1`] = ` - - - - - - - - - Place your text - - +/> `; diff --git a/src/framework/ui/radioGroup/radioGroup.component.tsx b/src/framework/ui/radioGroup/radioGroup.component.tsx index ed2cbfe2e..38e18d24d 100644 --- a/src/framework/ui/radioGroup/radioGroup.component.tsx +++ b/src/framework/ui/radioGroup/radioGroup.component.tsx @@ -6,72 +6,77 @@ import React from 'react'; import { + StyleSheet, View, ViewProps, } from 'react-native'; import { + styled, StyledComponentProps, StyleType, } from '@kitten/theme'; -import { Props as RadioProps } from '../radio/radio.component'; +import { RadioProps } from '../radio/radio.component'; type RadioElement = React.ReactElement; +type ChildrenProp = RadioElement | RadioElement[]; -interface RadioGroupProps { - children: RadioElement | RadioElement[]; +interface ComponentProps { + children: ChildrenProp; selectedIndex?: number; onChange?: (index: number) => void; } -export type Props = RadioGroupProps & StyledComponentProps & ViewProps; +export type RadioGroupProps = StyledComponentProps & ViewProps & ComponentProps; -export class RadioGroup extends React.Component { +class RadioGroupComponent extends React.Component { static styledComponentName: string = 'RadioGroup'; - static defaultProps: Partial = { + static defaultProps: Partial = { selectedIndex: -1, }; - private onChildSelected = (index: number) => { + private onRadioChange = (index: number) => { if (this.props.onChange) { this.props.onChange(index); } }; - private getComponentStyle = (style: StyleType): StyleType => { + private getComponentStyle = (source: StyleType): StyleType => { + const { style } = this.props; + return { - container: { - padding: style.padding, - }, + ...source, + ...StyleSheet.flatten(style), }; }; - private renderComponentChild = (element: RadioElement, index: number): RadioElement => { + private renderRadioElement = (element: RadioElement, index: number): RadioElement => { return React.cloneElement(element, { - ...element.props, key: index, checked: this.props.selectedIndex === index, - onChange: () => this.onChildSelected(index), + onChange: () => this.onRadioChange(index), }); }; - private renderComponentChildren = (source: RadioElement | RadioElement[]): RadioElement[] => { - return React.Children.map(source, this.renderComponentChild); + private renderRadioElements = (source: RadioElement | RadioElement[]): RadioElement[] => { + return React.Children.map(source, this.renderRadioElement); }; public render(): React.ReactElement { const { style, themedStyle, children, ...derivedProps } = this.props; - const { container }: StyleType = this.getComponentStyle(themedStyle); + const componentStyle: StyleType = this.getComponentStyle(themedStyle); - const componentChildren: RadioElement[] = this.renderComponentChildren(children); + const radioElements: RadioElement[] = this.renderRadioElements(children); return ( - {componentChildren} + style={componentStyle}> + {radioElements} ); } } + +export const RadioGroup = styled(RadioGroupComponent); diff --git a/src/framework/ui/radioGroup/radioGroup.spec.tsx b/src/framework/ui/radioGroup/radioGroup.spec.tsx index 369c243de..0dd0d52c0 100644 --- a/src/framework/ui/radioGroup/radioGroup.spec.tsx +++ b/src/framework/ui/radioGroup/radioGroup.spec.tsx @@ -7,23 +7,19 @@ import { } from 'react-native-testing-library'; import { ReactTestInstance } from 'react-test-renderer'; import { - styled, ApplicationProvider, ApplicationProviderProps, } from '@kitten/theme'; import { - RadioGroup as RadioGroupComponent, - Props as RadioGroupProps, + RadioGroup, + RadioGroupProps, } from './radioGroup.component'; +import { Radio } from '../radio/radio.component'; import { - Radio as RadioComponent, - Props as RadioProps, -} from '../radio/radio.component'; -import { default as mapping } from '../common/mapping.json'; -import { default as theme } from '../common/theme.json'; - -const RadioGroup = styled(RadioGroupComponent); -const Radio = styled(RadioComponent); + mapping, + theme, +} from '../support/tests'; + const Mock = (props?: RadioGroupProps): React.ReactElement => { return ( diff --git a/src/framework/ui/drawable/arrow/arrow.component.tsx b/src/framework/ui/support/components/arrow/arrow.component.tsx similarity index 89% rename from src/framework/ui/drawable/arrow/arrow.component.tsx rename to src/framework/ui/support/components/arrow/arrow.component.tsx index 05c1bd97c..582be6b5c 100644 --- a/src/framework/ui/drawable/arrow/arrow.component.tsx +++ b/src/framework/ui/support/components/arrow/arrow.component.tsx @@ -5,9 +5,9 @@ import { } from 'react-native'; import { StyleType } from '@kitten/theme'; -export type Props = ViewProps; +export type ArrowProps = ViewProps; -export class Arrow extends React.Component { +export class Arrow extends React.Component { private getComponentStyle = (source: StyleType): StyleType => { return { diff --git a/src/framework/ui/drawable/checkmark/checkmark.component.tsx b/src/framework/ui/support/components/checkmark/checkmark.component.tsx similarity index 92% rename from src/framework/ui/drawable/checkmark/checkmark.component.tsx rename to src/framework/ui/support/components/checkmark/checkmark.component.tsx index 7233dd979..9ccb00217 100644 --- a/src/framework/ui/drawable/checkmark/checkmark.component.tsx +++ b/src/framework/ui/support/components/checkmark/checkmark.component.tsx @@ -7,13 +7,13 @@ import { } from 'react-native'; import { StyleType } from '@kitten/theme'; -interface CheckMarkProps { +interface ComponentProps { isAnimated?: boolean; } -export type Props = CheckMarkProps & ViewProps; +export type CheckMarkProps = ViewProps & ComponentProps; -export class CheckMark extends React.Component { +export class CheckMark extends React.Component { static defaultProps = { isAnimated: false, diff --git a/src/framework/ui/support/components/index.ts b/src/framework/ui/support/components/index.ts new file mode 100644 index 000000000..987166763 --- /dev/null +++ b/src/framework/ui/support/components/index.ts @@ -0,0 +1,12 @@ +export { + Arrow, + ArrowProps, +} from './arrow/arrow.component'; +export { + CheckMark, + CheckMarkProps, +} from './checkmark/checkmark.component'; +export { + TabIndicator, + TabIndicatorProps, +} from './tabIndicator/tabIndicator.component'; diff --git a/src/framework/ui/tab/tabBarIndicator.component.tsx b/src/framework/ui/support/components/tabIndicator/tabIndicator.component.tsx similarity index 73% rename from src/framework/ui/tab/tabBarIndicator.component.tsx rename to src/framework/ui/support/components/tabIndicator/tabIndicator.component.tsx index 4ac0ab0a0..5b33fad2a 100644 --- a/src/framework/ui/tab/tabBarIndicator.component.tsx +++ b/src/framework/ui/support/components/tabIndicator/tabIndicator.component.tsx @@ -3,21 +3,22 @@ import { Animated, Easing, LayoutChangeEvent, + StyleSheet, ViewProps, } from 'react-native'; import { StyleType } from '@kitten/theme'; -interface TabBarIndicatorProps { +interface ComponentProps { positions: number; selectedPosition?: number; animationDuration?: number; } -export type Props = TabBarIndicatorProps & ViewProps; +export type TabIndicatorProps = ViewProps & ComponentProps; -export class TabBarIndicator extends React.Component { +export class TabIndicator extends React.Component { - static defaultProps: Partial = { + static defaultProps: Partial = { selectedPosition: 0, animationDuration: 200, }; @@ -25,19 +26,25 @@ export class TabBarIndicator extends React.Component { private contentOffset: Animated.Value = new Animated.Value(0); private indicatorWidth: number; - constructor(props: Props) { - super(props); + public componentDidMount() { this.contentOffset.addListener(this.onContentOffsetAnimationStateChanged); } - public shouldComponentUpdate(nextProps: Props): boolean { + public shouldComponentUpdate(nextProps: TabIndicatorProps): boolean { return this.props.selectedPosition !== nextProps.selectedPosition; } public componentDidUpdate() { const { selectedPosition: index } = this.props; - this.scrollToIndex({ index, animated: true }); + this.scrollToIndex({ + index, + animated: true, + }); + } + + public componentWillUnmount() { + this.contentOffset.removeAllListeners(); } /** @@ -94,26 +101,26 @@ export class TabBarIndicator extends React.Component { }; private getComponentStyle = (source: StyleType): StyleType => { - const widthPercent: number = 100 / this.props.positions; + const { style, positions } = this.props; + + const widthPercent: number = 100 / positions; return { - container: { - ...source, - width: `${widthPercent}%`, - transform: [{ translateX: this.contentOffset }], - }, + ...source, + ...StyleSheet.flatten(style), + width: `${widthPercent}%`, + transform: [{ translateX: this.contentOffset }], }; }; public render(): React.ReactElement { - const { style, ...derivedProps } = this.props; - const { container } = this.getComponentStyle(style); + const componentStyle: StyleType = this.getComponentStyle(this.props.style); return ( ); } diff --git a/src/framework/theme/service/props/index.ts b/src/framework/ui/support/services/index.ts similarity index 50% rename from src/framework/theme/service/props/index.ts rename to src/framework/ui/support/services/index.ts index 76ead08a0..44838ad43 100644 --- a/src/framework/theme/service/props/index.ts +++ b/src/framework/ui/support/services/index.ts @@ -2,3 +2,5 @@ export { all, allWithRest, } from './props.service'; + +export { isValidString } from './validation.service'; diff --git a/src/framework/theme/service/props/props.service.ts b/src/framework/ui/support/services/props.service.ts similarity index 100% rename from src/framework/theme/service/props/props.service.ts rename to src/framework/ui/support/services/props.service.ts diff --git a/src/framework/theme/service/props/props.spec.ts b/src/framework/ui/support/services/props.spec.ts similarity index 100% rename from src/framework/theme/service/props/props.spec.ts rename to src/framework/ui/support/services/props.spec.ts diff --git a/src/framework/ui/support/services/validation.service.ts b/src/framework/ui/support/services/validation.service.ts new file mode 100644 index 000000000..7617e4188 --- /dev/null +++ b/src/framework/ui/support/services/validation.service.ts @@ -0,0 +1,6 @@ +export const isValidString = (source: string | null): boolean => { + if (source && source.length > 0) { + return true; + } + return false; +}; diff --git a/src/framework/ui/support/services/validation.spec.ts b/src/framework/ui/support/services/validation.spec.ts new file mode 100644 index 000000000..815eebaa2 --- /dev/null +++ b/src/framework/ui/support/services/validation.spec.ts @@ -0,0 +1,23 @@ +import { isValidString } from '@kitten/ui/support/services/validation.service'; + +describe('@validation: service checks', () => { + + it('* isValidString: non-empty', () => { + const result: boolean = isValidString('test'); + + expect(result).toBe(true); + }); + + it('* isValidString: empty', () => { + const result: boolean = isValidString(''); + + expect(result).toBe(false); + }); + + it('* isValidString: falsy', () => { + const result: boolean = isValidString(undefined); + + expect(result).toBe(false); + }); + +}); diff --git a/src/framework/ui/support/tests/index.ts b/src/framework/ui/support/tests/index.ts new file mode 100644 index 000000000..aa55e82b2 --- /dev/null +++ b/src/framework/ui/support/tests/index.ts @@ -0,0 +1,2 @@ +export { default as mapping } from './mapping.json'; +export { default as theme } from './theme.json'; diff --git a/src/framework/ui/common/mapping.json b/src/framework/ui/support/tests/mapping.json similarity index 99% rename from src/framework/ui/common/mapping.json rename to src/framework/ui/support/tests/mapping.json index f2d07bc73..033b5a7c0 100644 --- a/src/framework/ui/common/mapping.json +++ b/src/framework/ui/support/tests/mapping.json @@ -1,4 +1,5 @@ { + "$schema": "./node_modules/@eva/core/schema/schema.json", "version": 1.0, "components": { "Avatar": { @@ -149,12 +150,12 @@ "appearances": { "default": { "mapping": { - "iconWidth": 32, - "iconHeight": 32, - "iconMarginVertical": 4, + "iconWidth": 24, + "iconHeight": 24, + "iconMarginVertical": 2, "iconTintColor": "color-basic-500", - "textMarginVertical": 4, - "textFontSize": 13, + "textMarginVertical": 2, + "textFontSize": 11, "textLineHeight": 16, "textFontWeight": "600", "textColor": "color-basic-600", @@ -207,8 +208,6 @@ "mapping": { "paddingVertical": 4, "backgroundColor": "color-white", - "borderTopColor": "color-basic-400", - "borderTopWidth": 1, "indicatorHeight": 4, "indicatorBackgroundColor": "color-primary-500" } @@ -2648,6 +2647,9 @@ "meta": { "scope": "mobile", "parameters": { + "minHeight": { + "type": "number" + }, "paddingVertical": { "type": "number" }, @@ -2717,6 +2719,7 @@ "appearances": { "default": { "mapping": { + "minHeight": 56, "paddingVertical": 8, "paddingHorizontal": 8, "backgroundColor": "color-white", diff --git a/src/framework/ui/common/theme.json b/src/framework/ui/support/tests/theme.json similarity index 100% rename from src/framework/ui/common/theme.json rename to src/framework/ui/support/tests/theme.json diff --git a/src/framework/ui/support/typings/index.ts b/src/framework/ui/support/typings/index.ts new file mode 100644 index 000000000..0ebe36366 --- /dev/null +++ b/src/framework/ui/support/typings/index.ts @@ -0,0 +1,11 @@ +export { + TextStyleProps, + FlexStyleProps, +} from './props'; +export { + ScrollEvent, + InputFocusEvent, + InputEndEditEvent, + Omit, + TouchableIndexedProps, +} from './type'; diff --git a/src/framework/ui/common/props.ts b/src/framework/ui/support/typings/props.ts similarity index 100% rename from src/framework/ui/common/props.ts rename to src/framework/ui/support/typings/props.ts diff --git a/src/framework/ui/common/type.ts b/src/framework/ui/support/typings/type.ts similarity index 91% rename from src/framework/ui/common/type.ts rename to src/framework/ui/support/typings/type.ts index ae40da375..699ee2ffd 100644 --- a/src/framework/ui/common/type.ts +++ b/src/framework/ui/support/typings/type.ts @@ -14,7 +14,7 @@ export type InputEndEditEvent = NativeSyntheticEvent = Pick>; // @ts-ignore: props override -export interface TouchableOpacityIndexedProps extends TouchableOpacityProps { +export interface TouchableIndexedProps extends TouchableOpacityProps { onPress?: (index: number, event: GestureResponderEvent) => void; onPressIn?: (index: number, event: GestureResponderEvent) => void; onPressOut?: (index: number, event: GestureResponderEvent) => void; diff --git a/src/framework/ui/tab/tab.component.tsx b/src/framework/ui/tab/tab.component.tsx index b256501df..ec0638dc4 100644 --- a/src/framework/ui/tab/tab.component.tsx +++ b/src/framework/ui/tab/tab.component.tsx @@ -19,22 +19,26 @@ import { StyleType, } from '@kitten/theme'; import { - Text as TextComponent, - Props as TextProps, + Text, + TextProps, } from '../text/text.component'; +import { isValidString } from '../support/services'; -interface TabProps { +type TitleElement = React.ReactElement; +type IconElement = React.ReactElement; +type IconProp = (style: StyleType) => React.ReactElement; +type ContentElement = React.ReactElement; + +interface ComponentProps { title?: string; titleStyle?: StyleProp; - icon?: (style: StyleType) => React.ReactElement; + icon?: IconProp; selected?: boolean; onSelect?: (selected: boolean) => void; - children?: React.ReactElement; + children?: ContentElement; } -const Text = styled(TextComponent); - -export type Props = TabProps & StyledComponentProps & TouchableOpacityProps; +export type TabProps = StyledComponentProps & TouchableOpacityProps & ComponentProps; /** * The `Tab` component is a part of TabBar or TabView component. @@ -71,11 +75,11 @@ export type Props = TabProps & StyledComponentProps & TouchableOpacityProps; * ``` * */ -export class Tab extends React.Component { +export class TabComponent extends React.Component { static styledComponentName: string = 'Tab'; - static defaultProps: Partial = { + static defaultProps: Partial = { selected: false, }; @@ -86,7 +90,7 @@ export class Tab extends React.Component { }; private getComponentStyle = (source: StyleType): StyleType => { - const { titleStyle } = this.props; + const { style, titleStyle } = this.props; const { textMarginVertical, @@ -105,6 +109,7 @@ export class Tab extends React.Component { container: { ...containerParameters, ...styles.container, + ...StyleSheet.flatten(style), }, icon: { width: iconWidth, @@ -119,54 +124,58 @@ export class Tab extends React.Component { lineHeight: textLineHeight, fontWeight: textFontWeight, color: textColor, - ...StyleSheet.flatten(titleStyle), ...styles.title, + ...StyleSheet.flatten(titleStyle), }, }; }; - private renderTextComponent = (style: StyleType): React.ReactElement => { + private renderTitleElement = (style: StyleType): TitleElement => { const { title: text } = this.props; return ( + key={1} + style={style}> {text} ); }; - private renderImageComponent = (style: StyleType): React.ReactElement => { + private renderIconElement = (style: StyleType): IconElement => { const { icon } = this.props; const iconElement: React.ReactElement = icon(style); - return React.cloneElement(iconElement, { key: 2 }); + return React.cloneElement(iconElement, { + key: 2, + style: [style, iconElement.props.style], + }); }; - private renderComponentChildren = (style: StyleType): React.ReactNode => { + private renderComponentChildren = (style: StyleType): React.ReactNodeArray => { const { title, icon } = this.props; return [ - icon ? this.renderImageComponent(style.icon) : undefined, - title ? this.renderTextComponent(style.title) : undefined, + icon && this.renderIconElement(style.icon), + isValidString(title) && this.renderTitleElement(style.title), ]; }; public render(): React.ReactElement { - const { style, themedStyle, ...derivedProps } = this.props; - const { container, ...componentStyles }: StyleType = this.getComponentStyle(themedStyle); + const { themedStyle, ...derivedProps } = this.props; + const { container, ...componentStyles } = this.getComponentStyle(themedStyle); - const componentChildren: React.ReactNode = this.renderComponentChildren(componentStyles); + const [iconElement, titleElement] = this.renderComponentChildren(componentStyles); return ( - {componentChildren} + {iconElement} + {titleElement} ); } @@ -174,10 +183,11 @@ export class Tab extends React.Component { const styles = StyleSheet.create({ container: { - flex: 1, justifyContent: 'center', alignItems: 'center', }, icon: {}, title: {}, }); + +export const Tab = styled(TabComponent); diff --git a/src/framework/ui/tab/tab.spec.tsx b/src/framework/ui/tab/tab.spec.tsx index bd9350bfc..9adfd62bb 100644 --- a/src/framework/ui/tab/tab.spec.tsx +++ b/src/framework/ui/tab/tab.spec.tsx @@ -2,9 +2,9 @@ import React from 'react'; import { View, ScrollView, - Image, ImageProps, ImageSourcePropType, + Image, } from 'react-native'; import { fireEvent, @@ -14,29 +14,26 @@ import { } from 'react-native-testing-library'; import { ReactTestInstance } from 'react-test-renderer'; import { - styled, ApplicationProvider, ApplicationProviderProps, StyleType, } from '@kitten/theme'; import { - Tab as TabComponent, - Props as TabProps, + Tab, + TabProps, } from './tab.component'; import { - TabBar as TabBarComponent, - Props as TabBarProps, + TabBar, + TabBarProps, } from './tabBar.component'; import { TabView, - Props as TabViewProps, - ChildProps as TabViewChildProps, + TabViewProps, } from './tabView.component'; -import { default as mapping } from '../common/mapping.json'; -import { default as theme } from '../common/theme.json'; - -const Tab = styled(TabComponent); -const TabBar = styled(TabBarComponent); +import { + mapping, + theme, +} from '../support/tests'; describe('@tab: component checks', () => { @@ -55,7 +52,7 @@ describe('@tab: component checks', () => { , ); - const { output } = shallow(component.getByType(TabComponent)); + const { output } = shallow(component.getByType(Tab)); expect(output).toMatchSnapshot(); }); @@ -65,7 +62,7 @@ describe('@tab: component checks', () => { , ); - const { output } = shallow(component.getByType(TabComponent)); + const { output } = shallow(component.getByType(Tab)); expect(output).toMatchSnapshot(); }); @@ -86,7 +83,7 @@ describe('@tab: component checks', () => { , ); - const { output } = shallow(component.getByType(TabComponent)); + const { output } = shallow(component.getByType(Tab)); expect(output).toMatchSnapshot(); }); @@ -102,7 +99,7 @@ describe('@tab: component checks', () => { />, ); - const { output } = shallow(component.getByType(TabComponent)); + const { output } = shallow(component.getByType(Tab)); expect(output).toMatchSnapshot(); }); @@ -161,7 +158,7 @@ describe('@tab-view: component checks', () => { ); }; - const ChildMock = (props?: TabViewChildProps): React.ReactElement => { + const ChildMock = (props?: TabProps): React.ReactElement => { return ( ); diff --git a/src/framework/ui/tab/tab.spec.tsx.snap b/src/framework/ui/tab/tab.spec.tsx.snap index cfaa621df..0b4c1e4c2 100644 --- a/src/framework/ui/tab/tab.spec.tsx.snap +++ b/src/framework/ui/tab/tab.spec.tsx.snap @@ -1,391 +1,21 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`@tab: component checks * empty 1`] = ` - `; exports[`@tab: component checks * icon 1`] = ` - - - +/> `; exports[`@tab: component checks * title (styled) 1`] = ` - - - title - - +/> `; exports[`@tab: component checks * title 1`] = ` - - - title - - +/> `; diff --git a/src/framework/ui/tab/tabBar.component.tsx b/src/framework/ui/tab/tabBar.component.tsx index 07b8f9bdb..bcb6f6289 100644 --- a/src/framework/ui/tab/tabBar.component.tsx +++ b/src/framework/ui/tab/tabBar.component.tsx @@ -8,28 +8,29 @@ import React from 'react'; import { StyleProp, StyleSheet, - TextStyle, View, ViewProps, ViewStyle, } from 'react-native'; import { + styled, StyledComponentProps, StyleType, } from '@kitten/theme'; -import { TabBarIndicator } from './tabBarIndicator.component'; -import { Props as TabProps } from './tab.component'; +import { TabProps } from './tab.component'; +import { TabIndicator } from '../support/components'; type TabElement = React.ReactElement; +type ChildrenProp = TabElement | TabElement[]; -interface TabBarProps { - children: TabElement | TabElement[]; +interface ComponentProps { + children: ChildrenProp; selectedIndex?: number; indicatorStyle?: StyleProp; onSelect?: (index: number) => void; } -export type Props = TabBarProps & StyledComponentProps & ViewProps; +export type TabBarProps = StyledComponentProps & ViewProps & ComponentProps; /** * The `TabBar` component that manages Tab components. @@ -69,24 +70,24 @@ export type Props = TabBarProps & StyledComponentProps & ViewProps; * - * - * - * + * + * + * * * ); * } * ``` * */ -export class TabBar extends React.Component { +export class TabBarComponent extends React.Component { static styledComponentName: string = 'TabBar'; - static defaultProps: Partial = { + static defaultProps: Partial = { selectedIndex: 0, }; - private tabIndicatorRef: React.RefObject = React.createRef(); + private tabIndicatorRef: React.RefObject = React.createRef(); public scrollToIndex(params: { index: number, animated?: boolean }) { const { current: tabIndicator } = this.tabIndicatorRef; @@ -100,14 +101,15 @@ export class TabBar extends React.Component { tabIndicator.scrollToOffset(params); } - private onChildPress = (index: number) => { + private onTabSelect = (index: number) => { if (this.props.onSelect) { this.props.onSelect(index); } }; private getComponentStyle = (source: StyleType): StyleType => { - const { indicatorStyle } = this.props; + const { style, indicatorStyle } = this.props; + const { indicatorHeight, indicatorBorderRadius, @@ -116,49 +118,58 @@ export class TabBar extends React.Component { } = source; return { - container: containerParameters, + container: { + ...containerParameters, + ...styles.container, + ...StyleSheet.flatten(style), + }, indicator: { height: indicatorHeight, borderRadius: indicatorBorderRadius, backgroundColor: indicatorBackgroundColor, + ...styles.indicator, ...StyleSheet.flatten(indicatorStyle), }, }; }; - private renderComponentChild = (element: TabElement, index: number): TabElement => { - const derivedStyle: TextStyle = StyleSheet.flatten(element.props.style); + private isTabSelected = (index: number): boolean => { + const { selectedIndex } = this.props; + + return index === selectedIndex; + }; + private renderTabElement = (element: TabElement, index: number): TabElement => { return React.cloneElement(element, { key: index, - style: { ...derivedStyle, flex: 1 }, - selected: index === this.props.selectedIndex, - onSelect: () => this.onChildPress(index), + style: [styles.item, element.props.style], + selected: this.isTabSelected(index), + onSelect: () => this.onTabSelect(index), }); }; - private renderComponentChildren = (source: TabElement | TabElement[]): TabElement[] => { - return React.Children.map(source, this.renderComponentChild); + private renderTabElements = (source: ChildrenProp): TabElement[] => { + return React.Children.map(source, this.renderTabElement); }; public render(): React.ReactElement { - const { style, themedStyle, selectedIndex, children, ...derivedProps } = this.props; - const { container, indicator } = this.getComponentStyle(themedStyle); + const { themedStyle, selectedIndex, children, ...derivedProps } = this.props; + const componentStyle: StyleType = this.getComponentStyle(themedStyle); - const componentChildren: TabElement[] = this.renderComponentChildren(children); + const tabElements: TabElement[] = this.renderTabElements(children); return ( - {componentChildren} + style={componentStyle.container}> + {tabElements} - ); @@ -170,4 +181,9 @@ const styles = StyleSheet.create({ flexDirection: 'row', }, indicator: {}, + item: { + flex: 1, + }, }); + +export const TabBar = styled(TabBarComponent); diff --git a/src/framework/ui/tab/tabView.component.tsx b/src/framework/ui/tab/tabView.component.tsx index a8133a8bd..d6e7143b7 100644 --- a/src/framework/ui/tab/tabView.component.tsx +++ b/src/framework/ui/tab/tabView.component.tsx @@ -11,55 +11,41 @@ import { ViewProps, ViewStyle, } from 'react-native'; -import { styled } from '@kitten/theme'; -import { - Tab as TabComponent, - Props as TabProps, -} from './tab.component'; -import { - TabBar as TabBarComponent, - Props as TabBarProps, -} from './tabBar.component'; +import { TabProps } from './tab.component'; +import { TabBar } from './tabBar.component'; import { ViewPager } from '../viewPager/viewPager.component'; +type TabContentElement = React.ReactElement; type TabElement = React.ReactElement; -type ChildElement = React.ReactElement; -type ChildContentElement = React.ReactElement; +type ChildrenProp = TabElement | TabElement[]; class TabViewChildElement { tab: TabElement; - content: ChildContentElement; + content: TabContentElement; } class TabViewChildren { tabs: TabElement[] = []; - content: ChildContentElement[] = []; + content: TabContentElement[] = []; } -interface TabViewProps { - children: ChildElement | ChildElement[]; +interface ComponentProps { + children: ChildrenProp; selectedIndex?: number; - contentWidth?: number; indicatorStyle?: StyleProp; shouldLoadComponent?: (index: number) => boolean; onOffsetChange?: (offset: number) => void; onSelect?: (index: number) => void; } -const Tab = styled(TabComponent); -const TabBar = styled(TabBarComponent); - -export type Props = TabViewProps & ViewProps; -export type ChildProps = TabProps & { children: ChildContentElement }; +export type TabViewProps = ViewProps & ComponentProps; /** * The `TabView` component that manages Tab components in whole view. * * @extends React.Component - * - * @type {TabProps & { children: React.ReactElement }} ChildProps - Determines child props. - * - * @type {React.ReactElement} ChildElement - Determines child of the component. + ** + * @type {React.ReactElement} ChildElement - Determines child of the component. * * @property {number} selectedIndex - Determines current tab index. * @@ -119,14 +105,14 @@ export type ChildProps = TabProps & { children: ChildContentElement }; * ``` * */ -export class TabView extends React.Component { +export class TabView extends React.Component { - static defaultProps: Partial = { + static defaultProps: Partial = { selectedIndex: 0, }; private viewPagerRef: React.RefObject = React.createRef(); - private tabBarRef: React.RefObject = React.createRef(); + private tabBarRef: React.RefObject = React.createRef(); private onBarSelect = (index: number) => { const { current: viewPager } = this.viewPagerRef; @@ -147,17 +133,15 @@ export class TabView extends React.Component { } }; - private renderComponentChild = (element: ChildElement, index: number): TabViewChildElement => { - const { children, ...elementProps } = element.props; - + private renderComponentChild = (element: TabElement, index: number): TabViewChildElement => { return { - tab: React.cloneElement(element, { key: index, ...elementProps }), - content: children, + tab: React.cloneElement(element, { key: index }), + content: element.props.children, }; }; - private renderComponentChildren = (source: ChildElement | ChildElement[]): TabViewChildren => { - return React.Children.toArray(source).reduce((acc: TabViewChildren, element: ChildElement, index: number) => { + private renderComponentChildren = (source: ChildrenProp): TabViewChildren => { + return React.Children.toArray(source).reduce((acc: TabViewChildren, element: TabElement, index: number) => { const { tab, content } = this.renderComponentChild(element, index); return { tabs: [...acc.tabs, tab], @@ -167,13 +151,8 @@ export class TabView extends React.Component { }; public render(): React.ReactElement { - const { - selectedIndex, - contentWidth, - children, - indicatorStyle, - ...derivedProps - } = this.props; + const { selectedIndex, children, indicatorStyle, ...derivedProps } = this.props; + const { tabs, content } = this.renderComponentChildren(children); return ( diff --git a/src/framework/ui/text/text.component.tsx b/src/framework/ui/text/text.component.tsx index ce2c706a6..0bd108d9a 100644 --- a/src/framework/ui/text/text.component.tsx +++ b/src/framework/ui/text/text.component.tsx @@ -6,21 +6,23 @@ import React from 'react'; import { - Text as TextComponent, - TextProps as TextComponentProps, + Text as RNText, + TextProps as RNTextProps, + StyleSheet, } from 'react-native'; import { + styled, StyledComponentProps, StyleType, } from '@kitten/theme'; -interface TextProps { +interface ComponentProps { category?: string; status?: string; - children?: React.ReactText; + children?: string; } -export type Props = TextProps & StyledComponentProps & TextComponentProps; +export type TextProps = StyledComponentProps & RNTextProps & ComponentProps; /** * The `Text` component is a component used to render text blocks. @@ -31,7 +33,7 @@ export type Props = TextProps & StyledComponentProps & TextComponentProps; * Can be 'primary' | 'success' | 'info' | 'warning' | 'danger' | 'white'. * By default status is 'primary'. * - * @property {React.ReactText} children - Determines text of the component. + * @property {string} children - Determines text of the component. * * @property {string} category - Determines the category of the component. * Can be 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 's1' | 's2' | 'p1' | 'p2' | 'c1' | 'c2' | 'overline' | 'label'. @@ -71,28 +73,33 @@ export type Props = TextProps & StyledComponentProps & TextComponentProps; * ``` * */ -export class Text extends React.Component { +export class TextComponent extends React.Component { static styledComponentName: string = 'Text'; private getComponentStyle = (source: StyleType): StyleType => { + const { style } = this.props; + return { fontSize: source.fontSize, lineHeight: source.lineHeight, fontWeight: source.fontWeight, color: source.color, + ...StyleSheet.flatten(style), }; }; - public render(): React.ReactElement { - const { style, themedStyle, ...derivedProps } = this.props; + public render(): React.ReactElement { + const { themedStyle, ...derivedProps } = this.props; const componentStyle: StyleType = this.getComponentStyle(themedStyle); return ( - ); } } + +export const Text = styled(TextComponent); diff --git a/src/framework/ui/toggle/toggle.component.tsx b/src/framework/ui/toggle/toggle.component.tsx index 515d7891b..621cf5747 100644 --- a/src/framework/ui/toggle/toggle.component.tsx +++ b/src/framework/ui/toggle/toggle.component.tsx @@ -22,10 +22,11 @@ import { StyledComponentProps, StyleType, Interaction, + styled, } from '@kitten/theme'; -import { CheckMark } from '../drawable/checkmark/checkmark.component'; +import { CheckMark } from '../support/components'; -interface ToggleComponentProps { +interface ComponentProps { checked?: boolean; disabled?: boolean; status?: string; @@ -33,7 +34,7 @@ interface ToggleComponentProps { onChange?: (checked: boolean) => void; } -export type Props = ToggleComponentProps & StyledComponentProps & ViewProps; +export type ToggleProps = StyledComponentProps & ViewProps & ComponentProps; /** * The `Toggle` component is an analog of html checkbox and radio buttons. @@ -90,7 +91,7 @@ export type Props = ToggleComponentProps & StyledComponentProps & ViewProps; * } * ``` * */ -export class Toggle extends React.Component implements PanResponderCallbacks { +export class ToggleComponent extends React.Component implements PanResponderCallbacks { static styledComponentName: string = 'Toggle'; @@ -100,7 +101,7 @@ export class Toggle extends React.Component implements PanResponderCallba private ellipseScaleAnimation: Animated.Value; private thumbTranslateAnimationActive: boolean; - constructor(props: Props) { + constructor(props: ToggleProps) { super(props); const { checked, themedStyle } = props; @@ -185,8 +186,8 @@ export class Toggle extends React.Component implements PanResponderCallba } }; - private getComponentStyle = (style: StyleType): StyleType => { - const { checked, disabled } = this.props; + private getComponentStyle = (source: StyleType): StyleType => { + const { style, checked, disabled } = this.props; const { highlightWidth, @@ -204,7 +205,7 @@ export class Toggle extends React.Component implements PanResponderCallba backgroundColor, borderColor, ...containerParameters - } = style; + } = source; const interpolatedBackgroundColor: Animated.AnimatedDiffClamp = this.getInterpolatedColor( backgroundColor, @@ -221,6 +222,7 @@ export class Toggle extends React.Component implements PanResponderCallba return { container: { ...styles.container, + ...StyleSheet.flatten(style), }, componentContainer: { borderColor: borderColor, @@ -238,7 +240,7 @@ export class Toggle extends React.Component implements PanResponderCallba ellipse: { width: containerParameters.width - (containerParameters.borderWidth * 2), height: containerParameters.height - (containerParameters.borderWidth * 2), - borderRadius: (style.height - (style.borderWidth * 2)) / 2, + borderRadius: (source.height - (source.borderWidth * 2)) / 2, backgroundColor: interpolatedBackgroundColor, transform: [{ scale: checked ? thumbScale : this.ellipseScaleAnimation }], ...styles.ellipse, @@ -254,8 +256,8 @@ export class Toggle extends React.Component implements PanResponderCallba ...styles.thumb, }, icon: { - width: style.iconWidth, - height: style.iconHeight, + width: source.iconWidth, + height: source.iconHeight, backgroundColor: interpolatedIconColor, }, }; @@ -330,11 +332,13 @@ export class Toggle extends React.Component implements PanResponderCallba }; public render(): React.ReactElement { - const { style, themedStyle, disabled, checked, ...restProps } = this.props; + const { themedStyle, disabled, checked, ...restProps } = this.props; const componentStyle: StyleType = this.getComponentStyle(themedStyle); return ( - + (ToggleComponent); diff --git a/src/framework/ui/toggle/toggle.spec.tsx b/src/framework/ui/toggle/toggle.spec.tsx index 669c97336..c2508c902 100644 --- a/src/framework/ui/toggle/toggle.spec.tsx +++ b/src/framework/ui/toggle/toggle.spec.tsx @@ -9,20 +9,19 @@ import { } from 'react-native-testing-library'; import { ReactTestInstance } from 'react-test-renderer'; import { - styled, ApplicationProvider, ApplicationProviderProps, } from '@kitten/theme'; import { - Toggle as ToggleComponent, - Props, + Toggle, + ToggleProps, } from './toggle.component'; -import { default as mapping } from '../common/mapping.json'; -import { default as theme } from '../common/theme.json'; - -const Toggle = styled(ToggleComponent); +import { + mapping, + theme, +} from '../support/tests'; -const Mock = (props?: Props): React.ReactElement => { +const Mock = (props?: ToggleProps): React.ReactElement => { return ( => { ); }; -const renderComponent = (props?: Props): RenderAPI => { +const renderComponent = (props?: ToggleProps): RenderAPI => { return render( , ); @@ -42,21 +41,21 @@ describe('@toggle: matches snapshot', () => { it('default', () => { const component: RenderAPI = renderComponent(); - const { output } = shallow(component.getByType(ToggleComponent)); + const { output } = shallow(component.getByType(Toggle)); expect(output).toMatchSnapshot(); }); it('checked', () => { const component: RenderAPI = renderComponent({ checked: true }); - const { output } = shallow(component.getByType(ToggleComponent)); + const { output } = shallow(component.getByType(Toggle)); expect(output).toMatchSnapshot(); }); it('disabled', () => { const component: RenderAPI = renderComponent({ disabled: true }); - const { output } = shallow(component.getByType(ToggleComponent)); + const { output } = shallow(component.getByType(Toggle)); expect(output).toMatchSnapshot(); }); @@ -66,7 +65,7 @@ describe('@toggle: matches snapshot', () => { checked: true, disabled: true, }); - const { output } = shallow(component.getByType(ToggleComponent)); + const { output } = shallow(component.getByType(Toggle)); expect(output).toMatchSnapshot(); }); @@ -77,14 +76,14 @@ describe('@toggle: matches snapshot', () => { fireEvent(component.getByType(TouchableOpacity), 'pressIn'); const active: ReactTestInstance = await waitForElement(() => { - return component.getByType(ToggleComponent); + return component.getByType(Toggle); }); const { output: activeOutput } = shallow(active); fireEvent(component.getByType(TouchableOpacity), 'pressOut'); const inactive: ReactTestInstance = await waitForElement(() => { - return component.getByType(ToggleComponent); + return component.getByType(Toggle); }); const { output: inactiveOutput } = shallow(inactive); @@ -97,14 +96,14 @@ describe('@toggle: matches snapshot', () => { fireEvent(component.getByType(TouchableOpacity), 'pressIn'); const active: ReactTestInstance = await waitForElement(() => { - return component.getByType(ToggleComponent); + return component.getByType(Toggle); }); const { output: activeOutput } = shallow(active); fireEvent(component.getByType(TouchableOpacity), 'pressOut'); const inactive: ReactTestInstance = await waitForElement(() => { - return component.getByType(ToggleComponent); + return component.getByType(Toggle); }); const { output: inactiveOutput } = shallow(inactive); diff --git a/src/framework/ui/toggle/toggle.spec.tsx.snap b/src/framework/ui/toggle/toggle.spec.tsx.snap index f168f3c3c..aff712176 100644 --- a/src/framework/ui/toggle/toggle.spec.tsx.snap +++ b/src/framework/ui/toggle/toggle.spec.tsx.snap @@ -1,1737 +1,55 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`@toggle: matches snapshot active 1`] = ` - - - - - - - - - - - + `; exports[`@toggle: matches snapshot active checked 1`] = ` - - - - - - - - - - - + `; exports[`@toggle: matches snapshot active checked: checked 1`] = ` - - - - - - - - - - - + `; exports[`@toggle: matches snapshot active: default 1`] = ` - - - - - - - - - - - + `; exports[`@toggle: matches snapshot checked 1`] = ` - - - - - - - - - - - + `; exports[`@toggle: matches snapshot checked disabled 1`] = ` - - - - - - - - - - - + `; exports[`@toggle: matches snapshot default 1`] = ` - - - - - - - - - - - + `; exports[`@toggle: matches snapshot disabled 1`] = ` - - - - - - - - - - - + `; diff --git a/src/framework/ui/tooltip/tooltip.component.tsx b/src/framework/ui/tooltip/tooltip.component.tsx index d49c54243..7c6776e9e 100644 --- a/src/framework/ui/tooltip/tooltip.component.tsx +++ b/src/framework/ui/tooltip/tooltip.component.tsx @@ -14,31 +14,36 @@ import { ViewProps, } from 'react-native'; import { + ModalComponentCloseProps, styled, StyledComponentProps, StyleType, } from '@kitten/theme'; import { - Text as TextComponent, - Props as TextProps, + Text, + TextProps, } from '../text/text.component'; import { - Popover as PopoverComponent, - Props as PopoverProps, + Popover, + PopoverProps, } from '../popover/popover.component'; -import { Omit } from '../common/type'; +import { Omit } from '../support/typings'; -interface TooltipProps { +type TextElement = React.ReactElement; +type IconElement = React.ReactElement; +type IconProp = (style: StyleType) => IconElement; +type WrappingElement = React.ReactElement; + +type PopoverContentProps = Omit; + +interface ComponentProps extends PopoverContentProps, ModalComponentCloseProps { text: string; textStyle?: StyleProp; - icon?: (style: StyleType) => React.ReactElement; - children: React.ReactElement; + icon?: IconProp; + children: WrappingElement; } -const Popover = styled(PopoverComponent); -const Text = styled(TextComponent); - -export type Props = TooltipProps & StyledComponentProps & Omit; +export type TooltipProps = StyledComponentProps & ComponentProps; /** * The `Tooltip` component is a component that displays informative text when users focus on or tap an element. @@ -106,16 +111,17 @@ export type Props = TooltipProps & StyledComponentProps & Omit { +export class TooltipComponent extends React.Component { static styledComponentName: string = 'Tooltip'; - static defaultProps: Partial = { + static defaultProps: Partial = { indicatorOffset: 8, }; private getComponentStyle = (source: StyleType): StyleType => { - const { textStyle } = this.props; + const { style, textStyle } = this.props; + const { popoverPaddingHorizontal, popoverPaddingVertical, @@ -138,6 +144,7 @@ export class Tooltip extends React.Component { borderRadius: popoverBorderRadius, backgroundColor: popoverBackgroundColor, ...styles.popover, + ...StyleSheet.flatten(style), }, content: styles.content, icon: { @@ -152,37 +159,36 @@ export class Tooltip extends React.Component { fontSize: textFontSize, lineHeight: textLineHeight, color: textColor, - ...StyleSheet.flatten(textStyle), ...styles.text, + ...StyleSheet.flatten(textStyle), }, }; }; - private renderTextElement = (style: StyleType): React.ReactElement => { - const { text } = this.props; - + private renderTextElement = (style: StyleType): TextElement => { return ( - {text} + {this.props.text} ); }; - private renderIconElement = (style: StyleType): React.ReactElement => { - const { icon } = this.props; - - const iconElement: React.ReactElement = icon(style); + private renderIconElement = (style: StyleType): IconElement => { + const iconElement: IconElement = this.props.icon(style); - return React.cloneElement(iconElement, { key: 0 }); + return React.cloneElement(iconElement, { + key: 0, + style: [style, iconElement.props.style], + }); }; - private renderContentElementChildren = (style: StyleType): React.ReactNode => { + private renderContentElementChildren = (style: StyleType): React.ReactNodeArray => { const { icon } = this.props; return [ - icon ? this.renderIconElement(style.icon) : null, + icon && this.renderIconElement(style.icon), this.renderTextElement(style.text), ]; }; @@ -202,12 +208,13 @@ export class Tooltip extends React.Component { public render(): React.ReactElement { const { style, children, themedStyle, ...derivedProps } = this.props; const { popover, ...componentStyle } = this.getComponentStyle(themedStyle); + const contentElement: React.ReactElement = this.renderPopoverContentElement(componentStyle); return ( {children} @@ -225,3 +232,5 @@ const styles = StyleSheet.create({ alignSelf: 'center', }, }); + +export const Tooltip = styled(TooltipComponent); diff --git a/src/framework/ui/topNavigation/topNavigation.component.tsx b/src/framework/ui/topNavigation/topNavigation.component.tsx index 05925a493..bcd1abf2a 100644 --- a/src/framework/ui/topNavigation/topNavigation.component.tsx +++ b/src/framework/ui/topNavigation/topNavigation.component.tsx @@ -6,37 +6,43 @@ import React from 'react'; import { - View, - StyleSheet, - ViewProps, - Text, - TextProps, StyleProp, + StyleSheet, TextStyle, + View, + ViewProps, } from 'react-native'; import { + styled, StyledComponentProps, StyleType, } from '@kitten/theme'; -import { Props as ActionProps } from './topNavigationAction.component'; +import { TopNavigationActionProps } from './topNavigationAction.component'; import { TopNavigationAlignment, TopNavigationAlignments, } from './type'; +import { + Text, + TextProps, +} from '../text/text.component'; +import { isValidString } from '../support/services'; -type ActionElement = React.ReactElement; +type TextElement = React.ReactElement; +type ActionElement = React.ReactElement; +type ActionElementProp = ActionElement | ActionElement[]; -interface TopNavigationProps { +interface ComponentProps { title?: string; titleStyle?: StyleProp; subtitle?: string; subtitleStyle?: StyleProp; alignment?: string | TopNavigationAlignment; leftControl?: ActionElement; - rightControls?: ActionElement[]; + rightControls?: ActionElementProp; } -export type Props = TopNavigationProps & StyledComponentProps & ViewProps; +export type TopNavigationProps = StyledComponentProps & ViewProps & ComponentProps; /** * The `TopNavigation` component is a component that work like AppBar component. @@ -87,12 +93,13 @@ export type Props = TopNavigationProps & StyledComponentProps & ViewProps; * ``` * */ -export class TopNavigation extends React.Component { +export class TopNavigationComponent extends React.Component { static styledComponentName: string = 'TopNavigation'; - private getComponentStyle = (style: StyleType): StyleType => { + private getComponentStyle = (source: StyleType): StyleType => { const { + style, alignment: alignmentValue, leftControl, rightControls, @@ -115,7 +122,7 @@ export class TopNavigation extends React.Component { actionHeight, actionMarginHorizontal, ...containerStyle - } = style; + } = source; const leftControlsCount: number = React.Children.count(leftControl); const rightControlsCount: number = React.Children.count(rightControls); @@ -127,6 +134,7 @@ export class TopNavigation extends React.Component { container: { ...containerStyle, ...styles.container, + ...StyleSheet.flatten(style), }, titleContainer: { ...styles.titleContainer, @@ -138,8 +146,8 @@ export class TopNavigation extends React.Component { lineHeight: titleLineHeight, fontWeight: titleFontWeight, color: titleColor, - ...StyleSheet.flatten(titleStyle), ...styles.title, + ...StyleSheet.flatten(titleStyle), }, subtitle: { textAlign: subtitleTextAlign, @@ -147,8 +155,8 @@ export class TopNavigation extends React.Component { color: subtitleColor, fontWeight: subtitleFontWeight, lineHeight: subtitleLineHeight, - ...StyleSheet.flatten(subtitleStyle), ...styles.subtitle, + ...StyleSheet.flatten(subtitleStyle), }, action: { width: actionWidth, @@ -160,19 +168,15 @@ export class TopNavigation extends React.Component { }; }; - private renderTextElement = (text: string, style: StyleType): React.ReactElement => { + private renderTextElement = (text: string, style: StyleType): TextElement => { return ( - {text} + + {text} + ); }; - private renderTitleElement = (text: string, style: StyleType): React.ReactElement | null => { - const isValid: boolean = text && text.length !== 0; - - return isValid ? this.renderTextElement(text, style) : null; - }; - - private renderActionElements(source: React.ReactNode, style: StyleType): ActionElement[] { + private renderActionElements(source: ActionElementProp, style: StyleType): ActionElement[] { return React.Children.map(source, (element: ActionElement): ActionElement => { return React.cloneElement(element, { style: [style, element.props.style], @@ -180,23 +184,47 @@ export class TopNavigation extends React.Component { }); } + private renderComponentChildren = (style: StyleType): React.ReactNodeArray => { + const { title, subtitle, leftControl, rightControls } = this.props; + + return [ + isValidString('Loh Pidr') && this.renderTextElement(title, style.title), + isValidString(subtitle) && this.renderTextElement(subtitle, style.subtitle), + leftControl && this.renderActionElements(leftControl, style.action), + rightControls && this.renderActionElements(rightControls, style.action), + ]; + }; + public render(): React.ReactNode { - const { style, themedStyle, title, subtitle, leftControl, rightControls, ...restProps } = this.props; - const componentStyle: StyleType = this.getComponentStyle(themedStyle); + const { themedStyle, ...restProps } = this.props; + + const { + container, + leftControlContainer, + titleContainer, + rightControlsContainer, + ...componentStyles + } = this.getComponentStyle(themedStyle); + + const [ + titleElement, + subtitleElement, + leftControlElement, + rightControlElements, + ] = this.renderComponentChildren(componentStyles); return ( - - {this.renderActionElements(leftControl, componentStyle.action)} + style={container}> + + {leftControlElement} - - {this.renderTitleElement(title, componentStyle.title)} - {this.renderTitleElement(subtitle, componentStyle.subtitle)} + + {titleElement} + {subtitleElement} - - {this.renderActionElements(rightControls, componentStyle.action)} + + {rightControlElements} ); @@ -222,3 +250,5 @@ const styles = StyleSheet.create({ zIndex: 1, }, }); + +export const TopNavigation = styled(TopNavigationComponent); diff --git a/src/framework/ui/topNavigation/topNavigation.spec.tsx b/src/framework/ui/topNavigation/topNavigation.spec.tsx index 2b566df23..95b3e7f48 100644 --- a/src/framework/ui/topNavigation/topNavigation.spec.tsx +++ b/src/framework/ui/topNavigation/topNavigation.spec.tsx @@ -12,24 +12,23 @@ import { RenderAPI, } from 'react-native-testing-library'; import { - styled, ApplicationProvider, ApplicationProviderProps, StyleType, } from '@kitten/theme'; import { - TopNavigation as TopNavigationComponent, - Props as TopNavigationProps, + TopNavigation, + TopNavigationProps, } from './topNavigation.component'; import { - TopNavigationAction as TopNavigationActionComponent, - Props as TopNavigationActionProps, + TopNavigationAction, + TopNavigationActionProps, } from './topNavigationAction.component'; -import { default as mapping } from '../common/mapping.json'; -import { default as theme } from '../common/theme.json'; +import { + mapping, + theme, +} from '../support/tests'; -const TopNavigation = styled(TopNavigationComponent); -const TopNavigationAction = styled(TopNavigationActionComponent); const Mock = (props?: TopNavigationProps): React.ReactElement => { return ( @@ -73,7 +72,7 @@ describe('@top-navigation-bar/action', () => { , ); - const { output } = shallow(component.getByType(TopNavigationComponent)); + const { output } = shallow(component.getByType(TopNavigation)); expect(output).toMatchSnapshot(); }); @@ -86,7 +85,7 @@ describe('@top-navigation-bar/action', () => { />, ); - const { output } = shallow(component.getByType(TopNavigationComponent)); + const { output } = shallow(component.getByType(TopNavigation)); expect(output).toMatchSnapshot(); }); @@ -101,7 +100,7 @@ describe('@top-navigation-bar/action', () => { />, ); - const { output } = shallow(component.getByType(TopNavigationComponent)); + const { output } = shallow(component.getByType(TopNavigation)); expect(output).toMatchSnapshot(); }); @@ -114,7 +113,7 @@ describe('@top-navigation-bar/action', () => { />, ); - const { output } = shallow(component.getByType(TopNavigationComponent)); + const { output } = shallow(component.getByType(TopNavigation)); expect(output).toMatchSnapshot(); }); @@ -156,7 +155,7 @@ describe('@top-navigation-bar/action', () => { ]} />, ); - const { output } = shallow(component.getByType(TopNavigationComponent)); + const { output } = shallow(component.getByType(TopNavigation)); expect(output).toMatchSnapshot(); @@ -178,7 +177,7 @@ describe('@top-navigation-bar/action', () => { />, ); - const { output } = shallow(component.getByType(TopNavigationActionComponent)); + const { output } = shallow(component.getByType(TopNavigationAction)); expect(output).toMatchSnapshot(); }); @@ -193,7 +192,7 @@ describe('@top-navigation-bar/action', () => { />, ); - const { output } = shallow(component.getByType(TopNavigationActionComponent)); + const { output } = shallow(component.getByType(TopNavigationAction)); fireEvent.press(component.getByType(TouchableOpacity)); diff --git a/src/framework/ui/topNavigation/topNavigation.spec.tsx.snap b/src/framework/ui/topNavigation/topNavigation.spec.tsx.snap index a5beaac70..8cd453866 100644 --- a/src/framework/ui/topNavigation/topNavigation.spec.tsx.snap +++ b/src/framework/ui/topNavigation/topNavigation.spec.tsx.snap @@ -1,1197 +1,105 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`@top-navigation-bar/action * action/on press check 1`] = ` - - - +/> `; exports[`@top-navigation-bar/action * action/with icon uri 1`] = ` - - - + `; exports[`@top-navigation-bar/action * bar/alignment: center 1`] = ` - - - - - Test - - - - + forwardedRef={null} + title="Test" +/> `; exports[`@top-navigation-bar/action * bar/subtitle 1`] = ` - - - - - Test - - - Subtitle - - - - + `; exports[`@top-navigation-bar/action * bar/title 1`] = ` - - - - - Test - - - - + `; exports[`@top-navigation-bar/action * bar/with actions 1`] = ` - - + - - - - Test - - - Subtitle - - - - - - - - -`; - -exports[`@top-navigation-bar/action * texts (styled) 1`] = ` -, + , + , ] } + subtitle="Subtitle" + title="Test" +/> +`; + +exports[`@top-navigation-bar/action * texts (styled) 1`] = ` + - - - - Test - - - Subtitle - - - - +/> `; diff --git a/src/framework/ui/topNavigation/topNavigationAction.component.tsx b/src/framework/ui/topNavigation/topNavigationAction.component.tsx index da60145c6..e95cb999b 100644 --- a/src/framework/ui/topNavigation/topNavigationAction.component.tsx +++ b/src/framework/ui/topNavigation/topNavigationAction.component.tsx @@ -14,17 +14,21 @@ import { } from 'react-native'; import { Interaction, + styled, StyledComponentProps, StyleType, } from '@kitten/theme'; -interface TopNavigationActionProps { - icon: (style: StyleType) => React.ReactElement; +type IconElement = React.ReactElement; +type IconProp = (style: StyleType) => IconElement; + +interface ComponentProps { + icon: IconProp; } -export type Props = StyledComponentProps & TouchableOpacityProps & TopNavigationActionProps; +export type TopNavigationActionProps = StyledComponentProps & TouchableOpacityProps & ComponentProps; -export class TopNavigationAction extends React.Component { +class TopNavigationActionComponent extends React.Component { static styledComponentName: string = 'TopNavigationAction'; @@ -50,13 +54,14 @@ export class TopNavigationAction extends React.Component { } }; - private getComponentStyle = (style: StyleType): StyleType => { - const { iconTintColor, ...containerStyle } = style; + private getComponentStyle = (source: StyleType): StyleType => { + const { iconTintColor, ...containerParameters } = source; return { container: { - ...containerStyle, + ...containerParameters, ...styles.container, + ...StyleSheet.flatten(this.props.style), }, icon: { tintColor: iconTintColor, @@ -65,12 +70,16 @@ export class TopNavigationAction extends React.Component { }; }; - private renderIcon(style: StyleType): React.ReactElement { - return this.props.icon(style); - } + private renderIconElement = (style: StyleType): React.ReactElement => { + const iconElement = this.props.icon(style); + + return React.cloneElement(iconElement, { + style: [style, iconElement.props.style], + }); + }; public render(): React.ReactNode { - const { style, themedStyle, icon, ...touchableProps } = this.props; + const { themedStyle, icon, ...touchableProps } = this.props; const componentStyle: StyleType = this.getComponentStyle(themedStyle); @@ -78,11 +87,11 @@ export class TopNavigationAction extends React.Component { - {this.renderIcon(componentStyle.icon)} + {this.renderIconElement(componentStyle.icon)} ); } @@ -94,3 +103,5 @@ const styles = StyleSheet.create({ flex: 1, }, }); + +export const TopNavigationAction = styled(TopNavigationActionComponent); diff --git a/src/framework/ui/viewPager/viewPager.component.tsx b/src/framework/ui/viewPager/viewPager.component.tsx index cd8f2a0f4..27ae24a75 100644 --- a/src/framework/ui/viewPager/viewPager.component.tsx +++ b/src/framework/ui/viewPager/viewPager.component.tsx @@ -12,19 +12,20 @@ import { LayoutChangeEvent, StyleSheet, } from 'react-native'; -import { ScrollEvent } from '../common/type'; +import { ScrollEvent } from '../support/typings'; type ChildElement = React.ReactElement; +type ChildrenProp = ChildElement | ChildElement[]; -interface ViewPagerProps { - children: ChildElement | ChildElement[]; +interface ComponentProps { + children: ChildrenProp; selectedIndex?: number; shouldLoadComponent?: (index: number) => boolean; onOffsetChange?: (offset: number) => void; onSelect?: (index: number) => void; } -export type Props = ScrollViewProps & ViewPagerProps; +export type ViewPagerProps = ScrollViewProps & ComponentProps; /** * The `ViewPager` is the component that allows flipping through the "pages". Extends ScrollView. @@ -77,9 +78,9 @@ export type Props = ScrollViewProps & ViewPagerProps; * ``` * */ -export class ViewPager extends React.Component { +export class ViewPager extends React.Component { - static defaultProps: Partial = { + static defaultProps: Partial = { selectedIndex: 0, shouldLoadComponent: (): boolean => true, }; @@ -93,14 +94,17 @@ export class ViewPager extends React.Component { this.scrollToIndex({ index }); } - public shouldComponentUpdate(nextProps: Props): boolean { + public shouldComponentUpdate(nextProps: ViewPagerProps): boolean { return this.props.selectedIndex !== nextProps.selectedIndex; } public componentDidUpdate() { const { selectedIndex: index } = this.props; - this.scrollToIndex({ index, animated: true }); + this.scrollToIndex({ + index, + animated: true, + }); } public scrollToIndex(params: { index: number; animated?: boolean }) { diff --git a/src/framework/ui/viewPager/viewPager.spec.tsx b/src/framework/ui/viewPager/viewPager.spec.tsx index ea1f4431a..0b83e7389 100644 --- a/src/framework/ui/viewPager/viewPager.spec.tsx +++ b/src/framework/ui/viewPager/viewPager.spec.tsx @@ -12,7 +12,7 @@ import { import { ReactTestInstance } from 'react-test-renderer'; import { ViewPager, - Props as ViewPagerProps, + ViewPagerProps, } from './viewPager.component'; describe('@view-pager: component checks', () => { diff --git a/src/playground/src/ui/screen/bottomNavigation.component.tsx b/src/playground/src/ui/screen/bottomNavigation.component.tsx index c8bf964c8..3c255b470 100644 --- a/src/playground/src/ui/screen/bottomNavigation.component.tsx +++ b/src/playground/src/ui/screen/bottomNavigation.component.tsx @@ -1,18 +1,18 @@ import React from 'react'; import { - ViewProps, + Image, ImageSourcePropType, - View, Text, - Image, + View, + ViewProps, } from 'react-native'; import { createBottomTabNavigator, NavigationContainer, NavigationContainerProps, + NavigationRoute, NavigationScreenProp, NavigationState, - NavigationRoute, } from 'react-navigation'; import { BottomNavigation, diff --git a/src/playground/src/ui/screen/button.component.tsx b/src/playground/src/ui/screen/button.component.tsx index ceb9b3d66..0504e4d48 100644 --- a/src/playground/src/ui/screen/button.component.tsx +++ b/src/playground/src/ui/screen/button.component.tsx @@ -3,6 +3,7 @@ import { ImageSourcePropType, Text, View, + ImageProps, Image, } from 'react-native'; import { NavigationScreenProps } from 'react-navigation'; @@ -10,7 +11,6 @@ import { withStyles, ThemeType, ThemedComponentProps, - StyleType, } from '@kitten/theme'; import { Button } from '@kitten/ui'; @@ -18,8 +18,7 @@ type Props = & ThemedComponentProps & NavigationScreenProps; const APPEARANCE: string = 'filled'; const STATUS: string = 'primary'; -const ALIGNMENT: string = 'left'; -const TEXT: React.ReactText = 'BUTTON'; +const TEXT: string = 'BUTTON'; const ICON: ImageSourcePropType = { uri: 'https://akveo.github.io/eva-icons/fill/png/128/star.png' }; interface State { @@ -35,6 +34,12 @@ class ButtonScreen extends React.Component { title: 'Button', }; + private renderIcon = (): React.ReactElement => { + return ( + + ); + }; + public render(): React.ReactNode { return ( @@ -46,40 +51,35 @@ class ButtonScreen extends React.Component { style={this.props.themedStyle.component} status={STATUS} size='giant' - iconAlignment={ALIGNMENT} - icon={(style: StyleType) => } + icon={this.renderIcon} /> @@ -136,8 +131,7 @@ class ButtonScreen extends React.Component { style={this.props.themedStyle.component} status={STATUS} size='giant' - iconAlignment={ALIGNMENT} - icon={(style: StyleType) => }> + icon={this.renderIcon}> {TEXT} diff --git a/src/playground/src/ui/screen/buttonGroup.component.tsx b/src/playground/src/ui/screen/buttonGroup.component.tsx index d135a7258..c7b8748af 100644 --- a/src/playground/src/ui/screen/buttonGroup.component.tsx +++ b/src/playground/src/ui/screen/buttonGroup.component.tsx @@ -3,15 +3,15 @@ import { ImageSourcePropType, Text, View, - Image, ScrollView, + ImageProps, + Image, } from 'react-native'; import { NavigationScreenProps } from 'react-navigation'; import { withStyles, ThemeType, ThemedComponentProps, - StyleType, } from '@kitten/theme'; import { Button, @@ -37,6 +37,12 @@ class ButtonGroupScreen extends React.Component { title: 'Button Group', }; + private renderIcon = (): React.ReactElement => { + return ( + + ); + }; + public render(): React.ReactNode { const { themedStyle } = this.props; @@ -51,41 +57,41 @@ class ButtonGroupScreen extends React.Component { style={themedStyle.component} appearance={APPEARANCE} size='giant'> - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + @@ -186,41 +192,41 @@ class ButtonGroupScreen extends React.Component { style={themedStyle.component} appearance={APPEARANCE} status='primary'> - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + diff --git a/src/playground/src/ui/screen/layout.component.tsx b/src/playground/src/ui/screen/layout.component.tsx index a0c8a2635..a7f28b4ee 100644 --- a/src/playground/src/ui/screen/layout.component.tsx +++ b/src/playground/src/ui/screen/layout.component.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { - Text, View, ViewProps, } from 'react-native'; @@ -10,7 +9,10 @@ import { ThemeType, ThemedComponentProps, } from '@kitten/theme'; -import { Layout } from '@kitten/ui'; +import { + Layout, + Text, +} from '@kitten/ui'; type Props = & ThemedComponentProps & NavigationScreenProps; diff --git a/src/playground/src/ui/screen/radioGroup.component.tsx b/src/playground/src/ui/screen/radioGroup.component.tsx index 587c1a7de..2160a740b 100644 --- a/src/playground/src/ui/screen/radioGroup.component.tsx +++ b/src/playground/src/ui/screen/radioGroup.component.tsx @@ -79,23 +79,23 @@ class RadioGroupScreen extends React.Component { - Error + Danger diff --git a/src/playground/src/ui/screen/tabBar.component.tsx b/src/playground/src/ui/screen/tabBar.component.tsx index 67075b731..c26850007 100644 --- a/src/playground/src/ui/screen/tabBar.component.tsx +++ b/src/playground/src/ui/screen/tabBar.component.tsx @@ -10,8 +10,8 @@ import { ThemeType, } from '@kitten/theme'; import { - TabBar as TabBar, - Tab as Tab, + TabBar, + Tab, } from '@kitten/ui'; type Props = & ThemedComponentProps & NavigationScreenProps; diff --git a/src/playground/src/ui/screen/tabView.component.tsx b/src/playground/src/ui/screen/tabView.component.tsx index 4d818daf4..e1a6a9306 100644 --- a/src/playground/src/ui/screen/tabView.component.tsx +++ b/src/playground/src/ui/screen/tabView.component.tsx @@ -3,6 +3,7 @@ import { Text, Image, ImageSourcePropType, + ImageProps, } from 'react-native'; import { NavigationScreenProps } from 'react-navigation'; import { @@ -11,15 +12,13 @@ import { StyleType, } from '@kitten/theme'; import { - TabView, Tab, + TabView, } from '@kitten/ui'; type Props = & ThemedComponentProps & NavigationScreenProps; -const ICON1: ImageSourcePropType = { uri: 'https://akveo.github.io/eva-icons/fill/png/128/star.png' }; -const ICON2: ImageSourcePropType = { uri: 'https://akveo.github.io/eva-icons/fill/png/128/email.png' }; -const ICON3: ImageSourcePropType = { uri: 'https://akveo.github.io/eva-icons/fill/png/128/info.png' }; +const ICON: ImageSourcePropType = { uri: 'https://akveo.github.io/eva-icons/fill/png/128/star.png' }; interface State { selectedIndex: number; @@ -39,6 +38,12 @@ class TabViewScreen extends React.Component { this.setState({ selectedIndex }); }; + private renderIcon = (style: StyleType): React.ReactElement => { + return ( + + ); + }; + public render(): React.ReactNode { return ( { onSelect={this.onSelect}> }> + icon={this.renderIcon}> Tab 1 }> + icon={this.renderIcon}> Tab 2 }> + icon={this.renderIcon}> Tab 3 diff --git a/src/playground/src/ui/screen/text.component.tsx b/src/playground/src/ui/screen/text.component.tsx index 2c560f71a..4f375a83d 100644 --- a/src/playground/src/ui/screen/text.component.tsx +++ b/src/playground/src/ui/screen/text.component.tsx @@ -27,7 +27,14 @@ class TextScreen extends React.Component { H4 H5 H6 - Body + S1 + S2 + C1 + C2 + P1 + P2 + OVERLINE + LABEL ); } diff --git a/src/playground/src/ui/screen/tooltip.component.tsx b/src/playground/src/ui/screen/tooltip.component.tsx index 0d48bde58..3a3f44b78 100644 --- a/src/playground/src/ui/screen/tooltip.component.tsx +++ b/src/playground/src/ui/screen/tooltip.component.tsx @@ -4,8 +4,8 @@ import { Text, TouchableOpacity, ImageProps, - Image, ImageSourcePropType, + Image, } from 'react-native'; import { NavigationScreenProps } from 'react-navigation'; import {