-
Notifications
You must be signed in to change notification settings - Fork 133
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
base: v3
Are you sure you want to change the base?
Changes from 8 commits
f250344
9593783
60a7fbe
40a3279
391775a
c9369c8
c73dfbc
6084a15
2248df9
3c274a5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -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 { | ||||||
|
@@ -87,6 +88,13 @@ const EmbeddedSearch = (props, context) => { | |||||
}); | ||||||
}); | ||||||
|
||||||
const [state, setState] = useState({ | ||||||
open: true, | ||||||
isHideCloseButton: true, | ||||||
time: undefined, | ||||||
arriveBy: false, | ||||||
keepPickerOpen: false, | ||||||
}); | ||||||
const defaultOriginExists = query.lat1 && query.lon1; | ||||||
const defaultOrigin = { | ||||||
lat: Number(query.lat1), | ||||||
|
@@ -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 | ||||||
|
@@ -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', | ||||||
|
@@ -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 }); | ||||||
}; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||||||
|
||||||
return ( | ||||||
<div | ||||||
className={`embedded-seach-container ${ | ||||||
bikeOnly ? 'bike' : walkOnly ? 'walk' : '' | ||||||
}`} | ||||||
id={appElement} | ||||||
style={{ height: isTimepickerSelected ? '380px' : '250px' }} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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' }}> | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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} | ||||||
|
@@ -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} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
fontWeights={config.fontWeights} | ||||||
onOpen={onOpen} | ||||||
onClose={onClose} | ||||||
openPicker={state.open} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be hard-coded as true. |
||||||
isHideCloseButton={state.isHideCloseButton} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think better name for this parameter could be |
||||||
/> | ||||||
</div> | ||||||
)} | ||||||
|
||||||
<div | ||||||
className="embedded-search-button-container" | ||||||
style={{ margin: '10px 0 0 0' }} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This style can be moved to sass. |
||||||
> | ||||||
{logo ? ( | ||||||
<img | ||||||
src={logo} | ||||||
|
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'; | ||
|
@@ -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; | ||
|
||
|
@@ -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()) { | ||
|
@@ -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 | ||
|
@@ -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> | ||
|
||
|
@@ -411,6 +431,34 @@ const EmbeddedSearchGenerator = (props, context) => { | |
)} | ||
</fieldset> | ||
|
||
<fieldset id="timePicker"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should use |
||
<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 => { | ||
|
@@ -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, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -82,6 +82,16 @@ | |
z-index: 1; | ||
} | ||
} | ||
|
||
.datetimepicker-container { | ||
margin-top: 1rem; | ||
|
||
& fieldset { | ||
margin: 0 !important; | ||
padding: 0 !important; | ||
max-width: 399px !important; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
} | ||
} | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
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.