Skip to content

Commit

Permalink
fix(components): modal - backdrop behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
artyorsh authored Jan 14, 2020
1 parent 00b3d1d commit 5c78095
Show file tree
Hide file tree
Showing 14 changed files with 120 additions and 268 deletions.
3 changes: 1 addition & 2 deletions src/components/theme/modal/modal.service.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import {
*
* const showModal = () => {
* const contentElement = this.renderModalContentElement();
* this.modalID = ModalService.show(contentElement, { allowBackdrop: true, onBackdropPress: this.hideModal });
* this.modalID = ModalService.show(contentElement, { onBackdropPress: this.hideModal });
* };
*
* const hideModal = () => {
Expand Down Expand Up @@ -90,7 +90,6 @@ class ModalServiceType {
}

export interface ModalPresentingConfig {
allowBackdrop?: boolean;
backdropStyle?: StyleProp<ViewStyle>;
onBackdropPress?: () => void;
}
Expand Down
10 changes: 3 additions & 7 deletions src/components/theme/modal/modalPanel.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,7 @@ export class ModalPanel extends React.Component<ModalPanelProps, ModalPanelState

const childElement: React.ReactElement = panelChild.element;

panelChild.element = React.cloneElement(childElement, {
children: children,
});
panelChild.element = React.cloneElement(childElement, childElement.props, children);

const components: Map<string, ModalPanelChild> = this.state.components;
components.delete(identifier);
Expand All @@ -93,11 +91,9 @@ export class ModalPanel extends React.Component<ModalPanelProps, ModalPanelState
private renderModal = (config: ModalPanelChild, index: number): React.ReactElement<ModalResolverProps> => {
return (
<ModalResolver
{...config.element.props}
style={config.backdropStyle}
visible={true}
key={index}
allowBackdrop={config.allowBackdrop}
visible={true}
backdropStyle={config.backdropStyle}
onBackdropPress={config.onBackdropPress}>
{config.element}
</ModalResolver>
Expand Down
5 changes: 0 additions & 5 deletions src/components/theme/modal/modalPanel.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ describe('@modal-service: service checks', () => {
textTestId={textId(1)}
/>,
{
allowBackdrop: false,
onBackdropPress: () => null,
},
);
Expand All @@ -52,7 +51,6 @@ describe('@modal-service: service checks', () => {
textTestId={textId(1)}
/>,
{
allowBackdrop: false,
onBackdropPress: () => null,
},
);
Expand All @@ -64,7 +62,6 @@ describe('@modal-service: service checks', () => {
textTestId={textId(2)}
/>,
{
allowBackdrop: false,
onBackdropPress: () => null,
},
);
Expand All @@ -78,7 +75,6 @@ describe('@modal-service: service checks', () => {
textTestId={textId(0)}
/>,
{
allowBackdrop: true,
onBackdropPress: () => null,
},
);
Expand Down Expand Up @@ -215,7 +211,6 @@ describe('@modal panel checks', () => {
this.modalId = ModalService.show(
<TestModal onBackdropPress={this.hideModal}/>,
{
allowBackdrop: true,
onBackdropPress: () => null,
},
);
Expand Down
102 changes: 21 additions & 81 deletions src/components/theme/modal/modalResolver.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,117 +6,57 @@

import React from 'react';
import {
View,
ViewProps,
StyleProp,
StyleSheet,
TouchableOpacity,
TouchableOpacityProps,
View,
ViewProps,
ViewStyle,
} from 'react-native';
import { ModalPresentingConfig } from './modal.service';

type ChildElement = React.ReactElement;
type ChildrenProp = ChildElement | ChildElement[];

interface ComponentProps {
export interface ModalResolverProps extends ViewProps, ModalPresentingConfig {
visible: boolean;
children: ChildrenProp;
allowBackdrop: boolean;
backdropStyle: StyleProp<ViewStyle>;
onBackdropPress: () => void;
}

export type ModalResolverProps = ViewProps & ComponentProps;

export class ModalResolver extends React.Component<ModalResolverProps> {

static defaultProps: Partial<ModalResolverProps> = {
visible: false,
};

private onBackdropPress = (): void => {
const { allowBackdrop, onBackdropPress } = this.props;

if (allowBackdrop) {
onBackdropPress();
}
};

private onStartShouldSetResponder = (): boolean => {
return true;
};

private onResponderRelease = (): void => {
return;
};

private onStartShouldSetResponderCapture = (): boolean => {
return false;
};

private renderComponentChild = (source: React.ReactElement): React.ReactElement => {
private renderChildElement = (source: ChildElement): ChildElement => {
return React.cloneElement(source, {
style: [source.props.style, this.props.style],
});
};

private renderComponentChildren = (source: React.ReactNode): React.ReactElement[] => {
return React.Children.map(source, this.renderComponentChild);
private renderComponentChildren = (source: ChildrenProp): ChildElement[] => {
return React.Children.map(source, this.renderChildElement);
};

private renderWithBackDrop = (component: React.ReactElement<ViewProps>):
React.ReactElement<TouchableOpacityProps> => {

return (
<TouchableOpacity
style={[styles.container, this.props.style]}
onPress={this.onBackdropPress}
activeOpacity={1}>
{component}
</TouchableOpacity>
);
};
private renderComponent = (): React.ReactElement<ViewProps> => {
const componentChildren = this.renderComponentChildren(this.props.children);

private renderWithoutBackDrop = (component: React.ReactElement<ViewProps>): React.ReactElement<ViewProps> => {
return (
<View style={styles.notVisibleWrapper}>
<View
style={styles.container}
pointerEvents='none'/>
{component}
<View style={StyleSheet.absoluteFill}>
<TouchableOpacity
style={[StyleSheet.absoluteFill, this.props.backdropStyle]}
activeOpacity={1.0}
onPress={this.props.onBackdropPress}
/>
{componentChildren}
</View>
);
};

private renderComponent = (): React.ReactElement<TouchableOpacityProps | ViewProps> => {
const { children, allowBackdrop, ...derivedProps } = this.props;
const componentChildren: React.ReactElement[] = this.renderComponentChildren(children);

const dialog: React.ReactElement<ViewProps> =
<View
{...derivedProps}
style={styles.contentWrapper}
onStartShouldSetResponder={this.onStartShouldSetResponder}
onResponderRelease={this.onResponderRelease}
onStartShouldSetResponderCapture={this.onStartShouldSetResponderCapture}
pointerEvents='box-none'>
{componentChildren}
</View>;

return allowBackdrop ?
this.renderWithBackDrop(dialog) : this.renderWithoutBackDrop(dialog);
};

public render(): React.ReactElement<ViewProps | TouchableOpacityProps> | null {
return this.props.visible ? this.renderComponent() : null;
public render(): React.ReactElement<ViewProps> | undefined {
return this.props.visible && this.renderComponent();
}
}

const styles = StyleSheet.create({
container: StyleSheet.absoluteFillObject,
notVisibleWrapper: {
position: 'absolute',
width: 0,
height: 0,
},
contentWrapper: {
alignSelf: 'flex-start',
},
});
14 changes: 3 additions & 11 deletions src/components/theme/modal/modalResolver.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ describe('@modal resolver component checks', () => {

it('* modal resolver component renders properly', () => {
const modal1: RenderAPI = render(
<ModalResolver
visible={true}
allowBackdrop={false}>
<ModalResolver visible={true}>
<View>
<Text>
Test1
Expand All @@ -30,9 +28,7 @@ describe('@modal resolver component checks', () => {
);

const modal2: RenderAPI = render(
<ModalResolver
visible={false}
allowBackdrop={false}>
<ModalResolver visible={false}>
<View>
<Text>
Test2
Expand All @@ -51,21 +47,17 @@ describe('@modal resolver component checks', () => {
it('* modal resolver component props checks', () => {
const modalPassingProps = {
visible: true,
allowBackdrop: false,
};
const modal = <ModalResolver {...modalPassingProps}/>;

expect(modal.props.visible).toBe(modalPassingProps.visible);
expect(modal.props.allowBackdrop).toBe(modalPassingProps.allowBackdrop);
});

it('* modal resolver backdrop press calling checks', () => {
const onBackdropPress = jest.fn();

const component: RenderAPI = render(
<ModalResolver
visible={true}
allowBackdrop={true}>
<ModalResolver visible={true}>
<View>
<Text>Test1</Text>
<Button
Expand Down
56 changes: 24 additions & 32 deletions src/components/theme/modal/modalResolver.spec.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4,50 +4,42 @@ exports[`@modal resolver component checks * modal resolver component renders pro
<View
style={
Object {
"height": 0,
"bottom": 0,
"left": 0,
"position": "absolute",
"width": 0,
"right": 0,
"top": 0,
}
}
>
<View
pointerEvents="none"
<TouchableOpacity
activeOpacity={1}
style={
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
Array [
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
},
undefined,
]
}
/>
<View
onResponderRelease={[Function]}
onStartShouldSetResponder={[Function]}
onStartShouldSetResponderCapture={[Function]}
pointerEvents="box-none"
style={
Object {
"alignSelf": "flex-start",
}
Array [
undefined,
undefined,
]
}
visible={true}
>
<View
style={
Array [
undefined,
undefined,
]
}
>
<Text>
Test1
</Text>
</View>
<Text>
Test1
</Text>
</View>
</View>
`;

exports[`@modal resolver component checks * modal resolver component renders properly 2`] = `null`;
exports[`@modal resolver component checks * modal resolver component renders properly 2`] = `false`;
7 changes: 7 additions & 0 deletions src/components/ui/measure/measure.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,11 @@ describe('* frame', () => {
expect(y).toEqual(2);
});

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

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

});
9 changes: 9 additions & 0 deletions src/components/ui/measure/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,15 @@ export class Frame {
);
}

public centerOf(other: Frame): Frame {
return new Frame(
other.origin.x + (other.size.width - this.size.width) / 2,
other.origin.y + (other.size.height - this.size.height) / 2,
this.size.width,
this.size.height,
);
}

static zero(): Frame {
return new Frame(0, 0, 0, 0);
}
Expand Down
Loading

0 comments on commit 5c78095

Please sign in to comment.