Skip to content
This repository has been archived by the owner on Mar 4, 2019. It is now read-only.

Commit

Permalink
Feat: Added Modal component
Browse files Browse the repository at this point in the history
Summary: Extracted `Modal` component from `Margarita`, because it is used even by some `universal-components` like `DatePicker` or `Picker`.

Implementation was updated to make `Modal` more flexible. 

Native version was mostly replaced by implementation from `mobile` repo: https://github.com/kiwicom/mobile/blob/master/packages/shared/src/Modal.js .
  • Loading branch information
JosefDuda authored and RobinCsl committed Feb 1, 2019
1 parent df6c373 commit c012e3c
Show file tree
Hide file tree
Showing 19 changed files with 457 additions and 104 deletions.
Binary file modified .loki/reference/chrome_laptop_DatePicker_Default.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .loki/reference/chrome_laptop_Modal_Default.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified .loki/reference/ios_iphoneX_DatePicker_Default.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .loki/reference/ios_iphoneX_Modal_Default.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"license": "MIT",
"dependencies": {
"@kiwicom/orbit-design-tokens": "^0.2.5",
"react-native-modal": "^7.0.2",
"react-native-multi-slider": "https://github.com/kiwicom/react-native-multi-slider.git#beb71bf5eb210fed393cb29b18032105b725611e",
"react-native-status-bar-height": "^2.2.0",
"styled-components": "^4.1.2"
Expand Down
70 changes: 27 additions & 43 deletions src/DatePicker/DatePicker.ios.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
/* @flow */

import * as React from 'react';
import {
View,
DatePickerIOS,
Modal,
TouchableWithoutFeedback,
} from 'react-native';
import { View, DatePickerIOS } from 'react-native';
import { defaultTokens } from '@kiwicom/orbit-design-tokens';

import { Text } from '../Text';
import { Modal } from '../Modal';
import { Touchable } from '../Touchable';
import { StyleSheet } from '../PlatformStyleSheet';

Expand Down Expand Up @@ -65,61 +61,49 @@ export default class iOSDatePicker extends React.Component<Props, State> {
};

render() {
const { isVisible, mode, maxDate, minDate } = this.props;
const { isVisible, labels, mode, maxDate, minDate } = this.props;

const { date } = this.state;

return (
<Modal
transparent
visible={isVisible}
isVisible={isVisible}
onRequestClose={this.handleDismiss}
animationType="slide"
onBackdropPress={this.handleDismiss}
>
<TouchableWithoutFeedback onPress={this.handleDismiss}>
<View style={styles.backdrop}>
<View style={styles.content}>
<DatePickerIOS
date={date}
onDateChange={this.handleChangeDate}
mode={mode}
maximumDate={maxDate}
minimumDate={minDate}
/>
<View style={styles.buttonsContainer}>
<Touchable
onPress={this.handleDismiss}
style={styles.confirmButton}
>
<Text style={styles.buttonText}>Cancel</Text>
</Touchable>
<Touchable
style={styles.confirmButton}
onPress={this.handleConfirm}
>
<Text style={styles.buttonText}>OK</Text>
</Touchable>
</View>
</View>
<View style={styles.content}>
<DatePickerIOS
date={date}
onDateChange={this.handleChangeDate}
mode={mode}
maximumDate={maxDate}
minimumDate={minDate}
/>
<View style={styles.buttonsContainer}>
<Touchable
onPress={this.handleDismiss}
style={styles.confirmButton}
>
<Text style={styles.buttonText}>{labels.cancel}</Text>
</Touchable>
<Touchable
style={styles.confirmButton}
onPress={this.handleConfirm}
>
<Text style={styles.buttonText}>{labels.confirm}</Text>
</Touchable>
</View>
</TouchableWithoutFeedback>
</View>
</Modal>
);
}
}

const styles = StyleSheet.create({
backdrop: {
backgroundColor: 'rgba(0, 0, 0, 0.6)',
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
content: {
flexDirection: 'column',
backgroundColor: 'white',
borderRadius: parseFloat(defaultTokens.borderRadiusBadge),
width: '90%',
},
confirmButton: {
borderColor: defaultTokens.paletteWhite,
Expand Down
65 changes: 28 additions & 37 deletions src/DatePicker/DatePicker.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,34 @@ import * as React from 'react';
import { View, Platform } from 'react-native';
import { storiesOf } from '@storybook/react-native';
import { withKnobs, select, date as dateAddon } from '@storybook/addon-knobs';
import { StyleSheet } from '../PlatformStyleSheet';

import { Button } from '../Button';
import { Text } from '../Text';
import DatePicker from './DatePicker';

type Props = {|
+isOpen?: boolean,
|};

type State = {
isOpen: boolean,
date: Date,
};

// NOTE: currentDate is used for default min & max limit values and is defined outside
// of component because the default value for dateAddon must not change
const currentDate = new Date();
const dateMinMaxOffset = 1000 * 60 * 60 * 24 * 30;
const labels = {
cancel: 'CANCEL',
confirm: 'OK',
};

class DateTimePicker extends React.Component<{}, State> {
state = {
isOpen: false,
date: new Date(),
};
class DateTimePicker extends React.Component<Props, State> {
constructor(props: Props) {
super(props);

this.state = {
isOpen: props.isOpen ?? false,
date: new Date('2019-01-16'),
};
}

showDatePicker = () => this.setState({ isOpen: true });

Expand All @@ -43,11 +50,11 @@ class DateTimePicker extends React.Component<{}, State> {
const mode = select('Mode', ['date', 'time', 'datetime'], 'date');
const minDateStringTimestamp = dateAddon(
'Min date',
new Date(currentDate.getTime() - dateMinMaxOffset)
new Date('2019-01-14')
);
const maxDateStringTimestamp = dateAddon(
'Max date',
new Date(currentDate.getTime() + dateMinMaxOffset)
new Date('2019-02-18')
);
let datePickerMode;

Expand All @@ -60,10 +67,15 @@ class DateTimePicker extends React.Component<{}, State> {
}

return (
<View style={styles.container}>
<Button onPress={this.showDatePicker} label="Open date picker" />
<>
<View>
<Button onPress={this.showDatePicker} label="Open date picker" />
<Text>Selected date: {date.toString()}</Text>
<Button onPress={this.handleDateReset} label="Reset Date" />
</View>
<DatePicker
isVisible={isOpen}
labels={labels}
mode={mode}
datePickerMode={datePickerMode}
date={date}
Expand All @@ -72,33 +84,12 @@ class DateTimePicker extends React.Component<{}, State> {
onConfirm={this.handleDateChange}
onDismiss={this.hideDatePicker}
/>
<Text>Selected date: {date.toString()}</Text>
<Button onPress={this.handleDateReset} label="Reset Date" />
</View>
</>
);
}
}

const noop = () => {};

storiesOf('DatePicker', module)
.addDecorator(withKnobs)
.lokiSkip('Playground', () => <DateTimePicker />)
.add('Default', () => (
<DatePicker
isVisible
mode="date"
datePickerMode="default"
date={new Date('01/25/2019')}
minDate={new Date('01/24/2019')}
maxDate={new Date('01/29/2019')}
onConfirm={noop}
onDismiss={noop}
/>
));

const styles = StyleSheet.create({
container: {
minHeight: 360,
},
});
.add('Default', () => <DateTimePicker isOpen />);
33 changes: 10 additions & 23 deletions src/DatePicker/DatePicker.web.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// @flow

import * as React from 'react';
import { View, TouchableWithoutFeedback } from 'react-native';
import { View } from 'react-native';
import { defaultTokens } from '@kiwicom/orbit-design-tokens';
import { StyleSheet } from '../PlatformStyleSheet';
import DatePickerDayTile from './DatePickerDayTile';
import { Button } from '../Button';
import { Text } from '../Text';
import { Modal } from '../Modal';
import {
getPreviousMonthData,
getNextMonthData,
Expand Down Expand Up @@ -61,7 +62,7 @@ export default class WebDatePicker extends React.Component<Props, State> {
};

render() {
const { isVisible, minDate, maxDate, onDismiss } = this.props;
const { isVisible, labels, minDate, maxDate, onDismiss } = this.props;
const { date, month, year } = this.state;

if (!isVisible) {
Expand Down Expand Up @@ -108,10 +109,11 @@ export default class WebDatePicker extends React.Component<Props, State> {
}

return (
<View style={styles.container}>
<TouchableWithoutFeedback onPress={onDismiss}>
<View style={styles.dismiss} />
</TouchableWithoutFeedback>
<Modal
isVisible={isVisible}
onRequestClose={onDismiss}
onBackdropPress={onDismiss}
>
<View style={styles.picker}>
<View style={styles.head}>
<Button
Expand All @@ -134,29 +136,14 @@ export default class WebDatePicker extends React.Component<Props, State> {
/>
</View>
<View style={styles.month}>{weekRows}</View>
<Button onPress={onDismiss} label="Close" />
<Button onPress={onDismiss} label={labels.cancel} />
</View>
</View>
</Modal>
);
}
}

const styles = StyleSheet.create({
container: {
position: 'absolute',
width: '100%',
height: '100%',
alignItems: 'center',
justifyContent: 'center',
zIndex: parseInt(defaultTokens.zIndexModal, 10),
},
dismiss: {
position: 'absolute',
width: '100%',
height: '100%',
backgroundColor: defaultTokens.paletteInkDark,
opacity: 0.2,
},
picker: {
padding: 10,
backgroundColor: defaultTokens.paletteWhite,
Expand Down
6 changes: 6 additions & 0 deletions src/DatePicker/DatePickerTypes.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// @flow

import * as React from 'react';

export type Props = {|
+isVisible: boolean,
+mode?: 'date' | 'time' | 'datetime',
Expand All @@ -9,4 +11,8 @@ export type Props = {|
+maxDate?: Date,
+onConfirm: Date => void,
+onDismiss: () => void,
+labels: {|
+cancel: React.Node, // this prop is supported only on web & iOS
+confirm: React.Node, // this prop is supported only on iOS
|},
|};
29 changes: 29 additions & 0 deletions src/Modal/Modal.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// @flow

import * as React from 'react';
import ReactModal from 'react-native-modal';

import type { Props } from './ModalTypes';

/**
* NOTE: This is just a `react-native-modal` wrapper with default settings
* so it's going to be easy to replace in the future (if necessary).
*/
function Modal(props: Props) {
return (
<ReactModal
supportedOrientations={['portrait', 'landscape']} // iOS only
animationInTiming={150}
animationOutTiming={150}
useNativeDriver
hideModalContentWhileAnimating // this is workaround for `useNativeDriver` property (see: https://github.com/react-native-community/react-native-modal#the-modal-flashes-in-a-weird-way-when-animating)
{...props}
/>
);
}

Modal.defaultProps = {
backdropOpacity: 0.5,
};

export default Modal;
Loading

0 comments on commit c012e3c

Please sign in to comment.