Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DT-5926: Datetimepicker for generated components #4900

Open
wants to merge 10 commits into
base: v3
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 115 additions & 2 deletions app/component/EmbeddedSearch/EmbeddedSearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { matchShape } from 'found';
import DTAutosuggestPanel from '@digitransit-component/digitransit-component-autosuggest-panel';
import CtrlPanel from '@digitransit-component/digitransit-component-control-panel';
import i18next from 'i18next';
import Datetimepicker from '@digitransit-component/digitransit-component-datetimepicker';
import { getRefPoint } from '../../util/apiUtils';
import withSearchContext from '../WithSearchContext';
import {
Expand Down Expand Up @@ -87,6 +88,13 @@ const EmbeddedSearch = (props, context) => {
});
});

const [state, setState] = useState({
open: true,
isHideCloseButton: true,
time: undefined,
arriveBy: false,
keepPickerOpen: false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this keepPickerOpen can be removed as well from everywhere here.

});
const defaultOriginExists = query.lat1 && query.lon1;
const defaultOrigin = {
lat: Number(query.lat1),
Expand All @@ -103,6 +111,7 @@ const EmbeddedSearch = (props, context) => {
name: query.address2,
};
const useDestinationLocation = query?.destinationLoc;
const isTimepickerSelected = query.timepicker;
const [logo, setLogo] = useState();
const [origin, setOrigin] = useState(
useOriginLocation
Expand Down Expand Up @@ -241,6 +250,13 @@ const EmbeddedSearch = (props, context) => {
]);

targetUrl.search += buildQueryString(utmCampaignParams);
if (state.time !== undefined) {
targetUrl.search += `&time=${state.time}`;
}

if (state.arriveBy) {
targetUrl.search += `&arriveBy=${state.arriveBy}`;
}

addAnalyticsEvent({
category: 'EmbeddedSearch',
Expand Down Expand Up @@ -298,15 +314,82 @@ const EmbeddedSearch = (props, context) => {
return <Loading />;
}

const onDepartureClick = time => {
setState({ ...state, time, arriveBy: false, keepPickerOpen: true });
addAnalyticsEvent({
event: 'sendMatomoEvent',
category: 'EmbeddedSearch',
action: 'LeavingArrivingSelection',
name: 'SelectLeaving',
});
};

const onTimeChange = (time, arriveBy, onSubmit = false) => {
const keepPickerOpen = onSubmit === false;
setState({
...state,
time,
arriveBy: !!arriveBy,
keepPickerOpen,
});
addAnalyticsEvent({
action: 'EditJourneyTime',
category: 'EmbeddedSearch',
name: null,
});
};

const onDateChange = (time, arriveBy) => {
setState({
...state,
time,
arriveBy: !!arriveBy,
keepPickerOpen: true,
});
addAnalyticsEvent({
action: 'EditJourneyDate',
category: 'EmbeddedSearch',
name: null,
});
};

const onNowClick = () => {
setState({
...state,
time: undefined,
arriveBy: false,
keepPickerOpen: false,
});
};

const onArrivalClick = time => {
setState({ ...state, time, arriveBy: true, keepPickerOpen: true });
addAnalyticsEvent({
event: 'sendMatomoEvent',
category: 'EmbeddedSearch',
action: 'LeavingArrivingSelection',
name: 'SelectArriving',
});
};

const onClose = () => {
setState({ ...state, open: false });
};

const onOpen = () => {
setState({ ...state, open: true });
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these can be removed if we force the timepicker to be always open in this view and open can be removed from state.


return (
<div
className={`embedded-seach-container ${
bikeOnly ? 'bike' : walkOnly ? 'walk' : ''
}`}
id={appElement}
style={{ height: isTimepickerSelected ? '380px' : '250px' }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be moved sass file by providing a class name here if the timepicker is selected.

>
<div className="background-container">{drawBackgroundIcon()}</div>
<div className="control-panel-container">
<div className="control-panel-container" style={{ position: 'relative' }}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This style could be potentially moved to sass as well.

<CtrlPanel
instance="HSL"
language={lang}
Expand All @@ -321,7 +404,37 @@ const EmbeddedSearch = (props, context) => {
targets={locationSearchTargets}
{...locationSearchProps}
/>
<div className="embedded-search-button-container">

{isTimepickerSelected && (
<div className="datetimepicker-container">
<Datetimepicker
realtime={false}
initialTimestamp={state.time}
initialArriveBy={state.arriveBy}
onTimeChange={onTimeChange}
onDateChange={onDateChange}
onNowClick={onNowClick}
onDepartureClick={onDepartureClick}
onArrivalClick={onArrivalClick}
embedWhenClosed={null}
embedWhenOpen={null}
lang={lang}
color={colors.primary}
timeZone={config.timezoneData.split('|')[0]}
serviceTimeRange={context.config.itinerary.serviceTimeRange}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
serviceTimeRange={context.config.itinerary.serviceTimeRange}
serviceTimeRange={config.itinerary.serviceTimeRange}

fontWeights={config.fontWeights}
onOpen={onOpen}
onClose={onClose}
openPicker={state.open}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be hard-coded as true.

isHideCloseButton={state.isHideCloseButton}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think better name for this parameter could be isAlwaysOpen, this is used in multiple files so rename it everywhere. The design images showed that the grey background should be removed when the timepicker is rendered from the embedded search. You should use this same prop to remove the grey background if this is true.

/>
</div>
)}

<div
className="embedded-search-button-container"
style={{ margin: '10px 0 0 0' }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This style can be moved to sass.

>
{logo ? (
<img
src={logo}
Expand Down
61 changes: 55 additions & 6 deletions app/component/EmbeddedSearchGenerator.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import PropTypes from 'prop-types';
import React, { useState, useRef } from 'react';
import React, { useState, useRef, useEffect } from 'react';
import { FormattedMessage, intlShape } from 'react-intl';
import connectToStores from 'fluxible-addons-react/connectToStores';
import DTAutosuggest from '@digitransit-component/digitransit-component-autosuggest';
Expand All @@ -22,6 +22,7 @@ const EmbeddedSearchGenerator = (props, context) => {
const { breakpoint, lang } = props;
const { config, intl } = context;
const { colors, fontWeights } = config;
const [isTimepickerSelected, setIsTimepickerSelected] = useState(false);
const MIN_WIDTH = 360;
const MAX_WIDTH = 640;

Expand Down Expand Up @@ -112,14 +113,25 @@ const EmbeddedSearchGenerator = (props, context) => {
mode[searchModeRestriction.substring(0, searchModeRestriction.length - 2)] =
'true';
const searchMatch = {
location: { query: { ...mode, ...locData, lang: searchLang } },
location: {
query: {
...mode,
...locData,
lang: searchLang,
timepicker: isTimepickerSelected,
},
},
};
return <EmbeddedSearch match={searchMatch} />;
};

const generateComponentString = () => {
const currentURL = window.location.origin;
let iframeHTML = `<iframe width="${searchWidth}" height="250" style="border-radius: 10px;" src="${currentURL}${EMBEDDED_SEARCH_PATH}?${searchModeRestriction}&lang=${searchLang}`;
let iframeHTML = `<iframe width="${searchWidth}" height=${
isTimepickerSelected ? '380' : '250'
} style="border-radius: 10px;" src="${currentURL}${EMBEDDED_SEARCH_PATH}?${searchModeRestriction}&lang=${searchLang}${
isTimepickerSelected ? '&timepicker=true' : ''
}`;
if (!chooseFreely) {
if (searchOriginDefined && searchOrigin) {
if (originIsCurrentLocation()) {
Expand Down Expand Up @@ -164,6 +176,14 @@ const EmbeddedSearchGenerator = (props, context) => {
}
};

useEffect(() => {
if (isTimepickerSelected) {
setSearchWidth(MIN_WIDTH + 90);
} else {
setSearchWidth(MIN_WIDTH);
}
}, [isTimepickerSelected]);

return (
<section id="mainContent" className="content">
<div
Expand Down Expand Up @@ -254,7 +274,7 @@ const EmbeddedSearchGenerator = (props, context) => {
hanldeWidthOnBlur(event.target.value);
}}
/>
<span> px x 250px</span>
<span> px x {isTimepickerSelected ? '400px' : '250px'}</span>
</label>
</fieldset>

Expand Down Expand Up @@ -411,6 +431,34 @@ const EmbeddedSearchGenerator = (props, context) => {
)}
</fieldset>

<fieldset id="timePicker">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should use time-picker style naming instead of timePicker for ids and class names.

<legend>
<h3>
<FormattedMessage
id="timepicker-component"
defaultMessage="Time selector"
/>
</h3>
</legend>

<label htmlFor="choose-timepicker">
<input
type="checkbox"
value="0"
name="origin-and-destination"
id="choose-timepicker"
onChange={() =>
setIsTimepickerSelected(prevState => !prevState)
}
checked={isTimepickerSelected}
/>
<FormattedMessage
id="choose-timepicker"
defaultMessage="Add a timepicker"
/>
</label>
</fieldset>

<div
className="embed-preview"
onFocus={e => {
Expand All @@ -422,10 +470,11 @@ const EmbeddedSearchGenerator = (props, context) => {
<FormattedMessage id="preview" defaultMessage="Preview" />
</h3>
<div
className="embedded-search-container"
className={`embedded-search-container ${
isTimepickerSelected ? 'with-timepicker' : ''
}`}
id="embedded-search-container-id"
style={{
height: 250,
width: searchWidth,
minWidth: MIN_WIDTH,
maxWidth: MAX_WIDTH,
Expand Down
18 changes: 18 additions & 0 deletions app/component/embeddedSearch.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,22 @@
width: 100vw;
max-width: 100%;
overflow: hidden;

.background-container, .control-panel-container{
grid-column: 1;
grid-row: 1;

.preview-component {
position: absolute;
bottom: 100px;
left: -15px;
}

.ready-component {
position: relative;
margin-top: 10px;
max-width: 399px;
}
}
h1 {
margin-top: 25px;
Expand Down Expand Up @@ -56,3 +69,8 @@
}
}
}

.with-timepicker {
height: 380px;
width: 450px;
}
10 changes: 10 additions & 0 deletions app/component/embeddedSearchGenerator.scss
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@
z-index: 1;
}
}

.datetimepicker-container {
margin-top: 1rem;

& fieldset {
margin: 0 !important;
padding: 0 !important;
max-width: 399px !important;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should try to avoid using these !important style definitions. I think the issue here is probably that the styles from here leak to the timepicker. We should either make those generic style definitions here that affect all html elements be more specific and only affect certain classes or make some wrapper divs for the other elements in the generator and make the generic styles under those divs so they don't affect the preview.

}
}
}
}

Expand Down
6 changes: 6 additions & 0 deletions app/translations.js
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,7 @@ const translations = {
'choose-freely': 'Optional',
'choose-stop': 'Select stop',
'choose-stop-or-vehicle': 'Select vehicle or stop',
'choose-timepicker': 'Add a timepicker',
'choose-vehicle': 'Select vehicle',
citybike: 'City bike',
'citybike-distance-duration': 'Bike {duration} ({distance})',
Expand Down Expand Up @@ -1601,6 +1602,7 @@ const translations = {
time: 'Time',
'time-selector-hours-label': 'Hour',
'time-selector-minutes-label': 'Minute',
'timepicker-component': 'Time picker',
timetable: 'Timetable',
'to-frontpage': 'To the front page',
'to-rail': 'train',
Expand Down Expand Up @@ -2155,6 +2157,7 @@ const translations = {
'choose-freely': 'Vapaasti valittavat',
'choose-stop': 'Valitse pysäkki',
'choose-stop-or-vehicle': 'Valitse linja tai pysäkki',
'choose-timepicker': 'Lisää aikavalitsin',
'choose-vehicle': 'Valitse linja',
citybike: 'Kaupunkipyörä',
'citybike-distance-duration': 'Pyöräile {duration} ({distance})',
Expand Down Expand Up @@ -2775,6 +2778,7 @@ const translations = {
time: 'Aika',
'time-selector-hours-label': 'Tunti',
'time-selector-minutes-label': 'Minuutti',
'timepicker-component': 'Aikavalitsin',
timetable: 'Aikataulu',
'to-frontpage': 'Etusivulle',
'to-rail': 'junaan',
Expand Down Expand Up @@ -4102,6 +4106,7 @@ const translations = {
'choose-freely': 'Valfria',
'choose-stop': 'Välj hållplats',
'choose-stop-or-vehicle': 'Select vehicle or stop',
'choose-timepicker': 'Lägg till en tidsväljare',
'choose-vehicle': 'Select vehicle',
citybike: 'Stadscykel',
'citybike-distance-duration': 'Cykla {duration} ({distance})',
Expand Down Expand Up @@ -4729,6 +4734,7 @@ const translations = {
time: 'Tid',
'time-selector-hours-label': 'Timme',
'time-selector-minutes-label': 'Minut',
'timepicker-component': 'Tidsväljare',
timetable: 'Tidtabell',
'to-ferry': 'färjan',
'to-frontpage': 'Till startsidan',
Expand Down
Loading
Loading