@kiwicom/smart-faq
Version:
290 lines (260 loc) • 7.84 kB
JavaScript
// @flow
import * as React from 'react';
import { graphql, createFragmentContainer } from 'react-relay';
import { withTheme } from 'styled-components';
import css from 'styled-jsx/css';
import Stack from '@kiwicom/orbit-components/lib/Stack';
import { ChevronDown, ChevronUp } from '@kiwicom/orbit-components/lib/icons';
import Button from '@kiwicom/nitro/lib/components/Button';
import ValueBind from '@kiwicom/nitro/lib/components/ValueBind';
import type { ThemeProps } from '@kiwicom/nitro/lib/records/Theme';
// TODO fix comment below
// $FlowExpectedError: Flow types are broken in TextNode
import TextNode from '@kiwicom/nitro/lib/components/TextNode';
import { updateFAQSection } from '../common/booking/utils';
import bookingTypes from '../common/booking/bookingTypes';
import { BookingState } from '../context/BookingState';
import OneWayBookingHeader from '../SingleBookingPage/bookingItem/BookingHeaders/OneWay';
import ReturnBookingHeader from '../SingleBookingPage/bookingItem/BookingHeaders/Return';
import MultiCityBookingHeader from '../SingleBookingPage/bookingItem/BookingHeaders/Multicity';
import formatBookingId from '../helpers/formatBookingId';
import { replaceWithCurrentDomain } from '../helpers/UrlHelpers';
import { Portrait, Landscape } from '../common/Responsive';
import SelectAnotherBookingLink from '../common/booking/SelectAnotherBookingLink';
import type { MobileBookingDetail_booking as MobileBookingDetailType } from './__generated__/MobileBookingDetail_booking.graphql';
type ContextProps = {
onSetFAQSection: (isUrgent: boolean, isPastBooking: boolean) => void,
};
type ComponentProps = {
+booking: MobileBookingDetailType,
onSetFAQSection: (isUrgent: boolean, isPastBooking: boolean) => void,
...ThemeProps,
};
type MobileBookingControlsProps = {|
+manageBookingURL: ?string,
|};
type Props = ComponentProps;
type TripIdProps = {|
isPastBooking: ?boolean,
databaseId: string,
|};
const TripId = ({ isPastBooking, databaseId }: TripIdProps) => (
<div style={{ marginTop: '8px' }}>
<TextNode
type="secondary"
dataTest="sfaq-booking-type"
spaceAfter="smallest"
t={
isPastBooking
? __('smartfaq.single_booking_page.header.booking_id.past')
: __('smartfaq.single_booking_page.header.booking_id.upcoming')
}
values={{
booking_id: (
<span dir="ltr" key={databaseId || 0}>
{formatBookingId(databaseId || 0)}
</span>
),
}}
/>
</div>
);
type ManageMyBookingButtonProps = {|
+manageBookingURL: ?string,
|};
const onClickButton = manageBookingURL => {
if (typeof window !== 'undefined') {
window.open(manageBookingURL, '_blank', 'noopener');
}
};
const ManageMyBookingButton = (props: ManageMyBookingButtonProps) => (
<ValueBind value={props.manageBookingURL || ''} onChange={onClickButton}>
{({ onClick }) => (
<Button
t="smartfaq.single_booking_page.booking_detail.manage_my_booking"
block
onClick={onClick}
type="secondary"
size="small"
dataTest="mobileMMBButton"
/>
)}
</ValueBind>
);
const MobileBookingControls = (props: MobileBookingControlsProps) => (
<>
<ManageMyBookingButton manageBookingURL={props.manageBookingURL} />
<div style={{ margin: '12px 0 8px', textAlign: 'center' }}>
<SelectAnotherBookingLink />
</div>
</>
);
const MobileBookingSummaryStyle = css.global`
.topRow {
display: flex;
}
.topRow:focus {
outline: 0;
}
.Chevron {
display: flex;
flex-direction: column;
justify-content: space-around;
cursor: pointer;
}
`;
type State = {
expanded: boolean,
};
const renderByType = booking => {
if (booking.type === bookingTypes.ONE_WAY) {
return <OneWayBookingHeader booking={booking} isMobile />;
}
if (booking.type === bookingTypes.RETURN) {
return <ReturnBookingHeader booking={booking} isMobile />;
}
if (booking.type === bookingTypes.MULTICITY) {
return <MultiCityBookingHeader booking={booking} isMobile />;
}
return null;
};
class MobileBookingDetail extends React.Component<Props, State> {
state = {
expanded: true,
};
componentDidMount() {
updateFAQSection(this.props);
}
componentDidUpdate() {
updateFAQSection(this.props);
}
toggle = () => {
this.setState(prevState => ({ expanded: !prevState.expanded }));
};
renderPortrait() {
const { booking, theme } = this.props;
const { isPastBooking, databaseId } = booking;
return (
<>
<div
className="topRow"
onClick={this.toggle}
onKeyDown={this.toggle}
role="button"
tabIndex="0"
>
<div style={{ flexGrow: 1 }} data-test="booking-sfaq-itinerary">
<TripId databaseId={databaseId} isPastBooking={isPastBooking} />
{renderByType(booking)}
</div>
<div className="Chevron">
{this.state.expanded ? (
<ChevronUp customColor={theme.orbit.colorIconTertiary} />
) : (
<ChevronDown customColor={theme.orbit.colorIconTertiary} />
)}
</div>
</div>
{this.state.expanded ? (
<MobileBookingControls
manageBookingURL={
booking.directAccessURL &&
replaceWithCurrentDomain(booking.directAccessURL)
}
/>
) : null}
<style jsx global>
{MobileBookingSummaryStyle}
</style>
</>
);
}
renderLandscape() {
const { booking, theme } = this.props;
const { isPastBooking, databaseId } = booking;
return (
<>
<Stack justify="between" flex>
<div>
<TripId databaseId={databaseId} isPastBooking={isPastBooking} />
{renderByType(booking)}
</div>
<div>
<SelectAnotherBookingLink />
</div>
</Stack>
{this.state.expanded && (
<ManageMyBookingButton
manageBookingURL={
booking.directAccessURL &&
replaceWithCurrentDomain(booking.directAccessURL)
}
/>
)}
<Stack justify="center" flex>
<div
onClick={this.toggle}
onKeyDown={this.toggle}
role="button"
tabIndex="0"
style={{ cursor: 'pointer', outline: 'none' }}
>
{this.state.expanded ? (
<ChevronUp customColor={theme.orbit.colorIconTertiary} />
) : (
<ChevronDown customColor={theme.orbit.colorIconTertiary} />
)}
</div>
</Stack>
</>
);
}
render() {
return (
<>
<Portrait>{this.renderPortrait()}</Portrait>
<Landscape>{this.renderLandscape()}</Landscape>
</>
);
}
}
const MobileBookingDetailWithFAQHandler = (props: ComponentProps) => (
<BookingState.Consumer>
{({ onSetFAQSection }: ContextProps) => (
<MobileBookingDetail {...props} onSetFAQSection={onSetFAQSection} />
)}
</BookingState.Consumer>
);
export default createFragmentContainer(
withTheme(MobileBookingDetailWithFAQHandler),
graphql`
fragment MobileBookingDetail_booking on BookingInterface {
type: __typename
databaseId: id(opaque: false)
isPastBooking
directAccessURL
... on BookingOneWay {
...OneWay_booking
trip {
departure {
time
}
}
}
... on BookingReturn {
...Return_booking
outbound {
departure {
time
}
}
}
... on BookingMulticity {
...Multicity_booking
start {
time
}
}
}
`,
);