-
Notifications
You must be signed in to change notification settings - Fork 139
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
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 }); | ||||||
| }; | ||||||
|
||||||
|
|
||||||
| return ( | ||||||
| <div | ||||||
| className={`embedded-seach-container ${ | ||||||
| bikeOnly ? 'bike' : walkOnly ? 'walk' : '' | ||||||
| }`} | ||||||
| id={appElement} | ||||||
| style={{ height: isTimepickerSelected ? '380px' : '250px' }} | ||||||
|
||||||
| > | ||||||
| <div className="background-container">{drawBackgroundIcon()}</div> | ||||||
| <div className="control-panel-container"> | ||||||
| <div className="control-panel-container" style={{ position: 'relative' }}> | ||||||
|
||||||
| <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} | ||||||
|
Member
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} | ||||||
|
||||||
| isHideCloseButton={state.isHideCloseButton} | ||||||
|
||||||
| /> | ||||||
| </div> | ||||||
| )} | ||||||
|
|
||||||
| <div | ||||||
| className="embedded-search-button-container" | ||||||
| style={{ margin: '10px 0 0 0' }} | ||||||
|
||||||
| > | ||||||
| {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"> | ||
|
||
| <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 comment
The reason will be displayed to describe this comment to others. Learn more.
I think this
keepPickerOpencan be removed as well from everywhere here.