Skip to content

DT-5926: Datetimepicker for generated components #4900

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

Open
wants to merge 10 commits into
base: v3
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
98 changes: 97 additions & 1 deletion 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,11 @@ const EmbeddedSearch = (props, context) => {
});
});

const [state, setState] = useState({
isAlwaysOpen: true,
time: undefined,
arriveBy: false,
});
const defaultOriginExists = query.lat1 && query.lon1;
const defaultOrigin = {
lat: Number(query.lat1),
Expand All @@ -103,6 +109,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 +248,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,11 +312,66 @@ const EmbeddedSearch = (props, context) => {
return <Loading />;
}

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

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

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

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

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

return (
<div
className={`embedded-seach-container ${
bikeOnly ? 'bike' : walkOnly ? 'walk' : ''
}`}
} ${isTimepickerSelected ? 'with-timepicker' : ''}`}
id={appElement}
>
<div className="background-container">{drawBackgroundIcon()}</div>
Expand All @@ -321,6 +390,33 @@ const EmbeddedSearch = (props, context) => {
targets={locationSearchTargets}
{...locationSearchProps}
/>

{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={null}
onClose={null}
openPicker
isAlwaysOpen={state.isAlwaysOpen}
/>
</div>
)}

<div className="embedded-search-button-container">
{logo ? (
<img
Expand Down
69 changes: 59 additions & 10 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 All @@ -181,7 +201,7 @@ const EmbeddedSearchGenerator = (props, context) => {
/>
</h2>

<fieldset id="lang">
<fieldset id="lang" className="fieldset">
<legend>
<h3>
<FormattedMessage
Expand Down Expand Up @@ -228,7 +248,7 @@ const EmbeddedSearchGenerator = (props, context) => {
</label>
</fieldset>

<fieldset id="width">
<fieldset id="width" className="fieldset">
<legend>
<h3>
<FormattedMessage
Expand All @@ -254,11 +274,11 @@ const EmbeddedSearchGenerator = (props, context) => {
hanldeWidthOnBlur(event.target.value);
}}
/>
<span> px x 250px</span>
<span> px x {isTimepickerSelected ? '400px' : '250px'}</span>
</label>
</fieldset>

<fieldset id="mode-restrictions">
<fieldset id="mode-restrictions" className="fieldset">
<legend>
<h3>
<FormattedMessage
Expand Down Expand Up @@ -306,7 +326,7 @@ const EmbeddedSearchGenerator = (props, context) => {
</label>
</fieldset>

<fieldset id="origin-and-destination">
<fieldset id="origin-and-destination" className="fieldset">
<legend>
<h3>
<FormattedMessage
Expand Down Expand Up @@ -411,6 +431,34 @@ const EmbeddedSearchGenerator = (props, context) => {
)}
</fieldset>

<fieldset id="time-picker" className="fieldset">
<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
6 changes: 6 additions & 0 deletions app/component/embeddedSearch.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
width: 100vw;
max-width: 100%;
overflow: hidden;

.background-container, .control-panel-container{
grid-column: 1;
grid-row: 1;
Expand Down Expand Up @@ -56,3 +57,8 @@
}
}
}

.with-timepicker {
height: 380px;
width: 450px;
}
13 changes: 8 additions & 5 deletions app/component/embeddedSearchGenerator.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
font-family: $font-family;
margin: auto;

fieldset {
.fieldset {
margin: 1.125rem 0;
padding: 1.125rem 0 1.125rem 0;
max-width: 350px;
border: none;

legend {
background: none;
}
Expand Down Expand Up @@ -121,10 +124,10 @@
}
}

.embed-preview, fieldset {
margin: 1.125rem 0;
padding: 1.125rem 0 1.125rem 0;
}
//.embed-preview, fieldset {
// margin: 1.125rem 0;
// padding: 1.125rem 0 1.125rem 0;
//}
.embed-preview {
pointer-events: none;
user-select: none;
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