Skip to content

Commit

Permalink
Add draft implementation of react tour to exploration
Browse files Browse the repository at this point in the history
  • Loading branch information
danielfdsilva committed Nov 7, 2023
1 parent 36038d8 commit 7737b0e
Show file tree
Hide file tree
Showing 8 changed files with 244 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ export function DatasetListItem(props: DatasetListItemProps) {
onDragEnd={() => {
onDragEnd?.();
}}
data-tour='dataset-list-item'
>
<DatasetItem>
<DatasetHeader>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export function TimelineControls(props: TimelineControlsProps) {
setSelectedDay(d.start!);
}}
renderTriggerElement={(props, label) => (
<DatePickerButton {...props} size='small' disabled={!xScaled}>
<DatePickerButton {...props} size='small' disabled={!xScaled} data-tour='date-picker-a'>
<span className='head-reference'>A</span>
<span>{label}</span>
<CollecticonChevronDownSmall />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ interface TimelineHeadBaseProps {
width: number;
onDayChange: (date: Date) => void;
children: React.ReactNode;
'data-tour'?: string;
}

type TimelineHeadProps = Omit<TimelineHeadBaseProps, 'children'> & {
Expand Down Expand Up @@ -84,7 +85,10 @@ export function TimelineHeadBase(props: TimelineHeadBaseProps) {

return (
<TimelineHeadSVG width={width + SVG_PADDING * 2}>
<g transform={`translate(${SVG_PADDING}, 0)`}>
<g
transform={`translate(${SVG_PADDING}, 0)`}
data-tour={props['data-tour']}
>
<line x1={xPos} x2={xPos} y1={0} y2='100%' stroke={theme.color?.base} />
<g transform={`translate(${xPos}, 0)`} ref={rectRef}>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ export default function Timeline(props: TimelineProps) {
<InteractionRect
ref={interactionRef}
style={!shouldRenderTimeline ? { pointerEvents: 'none' } : undefined}
data-tour='timeline-interaction-rect'
/>
<TimelineHeader>
<TimelineDetails>
Expand All @@ -446,6 +447,7 @@ export default function Timeline(props: TimelineProps) {
<>
{selectedDay && (
<TimelineHeadPoint
data-tour='timeline-head-a'
label={selectedCompareDay ? 'A' : undefined}
domain={dataDomain}
xScaled={xScaled}
Expand Down
19 changes: 17 additions & 2 deletions app/scripts/components/exploration/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import React, { useCallback, useState } from 'react';
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
import styled from 'styled-components';
import { useAtomValue } from 'jotai';
import { TourProvider } from '@reactour/tour';
import { themeVal } from '@devseed-ui/theme-provider';

import { MockControls } from './datasets-mock';
import Timeline from './components/timeline/timeline';
import { ExplorationMap } from './components/map';
import { DatasetSelectorModal } from './components/dataset-selector-modal';
import { timelineDatasetsAtom } from './atoms/atoms';
import { PopoverTourComponent, TourManager } from './tour-manager';

import { LayoutProps } from '$components/common/layout-root';
import PageHero from '$components/common/page-hero';
Expand Down Expand Up @@ -56,6 +58,14 @@ const Container = styled.div`
}
`;

const tourProviderStyles = {
popover: (base) => ({
...base,
padding: '0',
background: 'none',
})
};

function Exploration() {
const datasets = useAtomValue(timelineDatasetsAtom);
const [datasetModalRevealed, setDatasetModalRevealed] = useState(
Expand All @@ -66,12 +76,17 @@ function Exploration() {
const closeModal = useCallback(() => setDatasetModalRevealed(false), []);

return (
<>
<TourProvider
steps={[]}
styles={tourProviderStyles}
ContentComponent={PopoverTourComponent}
>
<LayoutProps
title='Exploration'
description='Explore and analyze datasets'
hideFooter
/>
<TourManager />
<PageMainContent>
<PageHero title='Exploration' isHidden />

Expand All @@ -97,7 +112,7 @@ function Exploration() {
close={closeModal}
/>
</PageMainContent>
</>
</TourProvider>
);
}
export default Exploration;
176 changes: 176 additions & 0 deletions app/scripts/components/exploration/tour-manager.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import React, { useEffect } from 'react';
import { useTour, PopoverContentProps, StepType } from '@reactour/tour';
import { useAtomValue } from 'jotai';
import styled from 'styled-components';
import { glsp, themeVal } from '@devseed-ui/theme-provider';
import { Button } from '@devseed-ui/button';
import { Heading } from '@devseed-ui/typography';
import {
CollecticonChevronLeftSmall,
CollecticonChevronRightSmall,
CollecticonXmark
} from '@devseed-ui/collecticons';

import { timelineDatasetsAtom } from './atoms/atoms';

import { usePreviousValue } from '$utils/use-effect-previous';

const Popover = styled.div`
position: relative;
background: ${themeVal('color.surface')};
padding: ${glsp(1, 2, 1, 2)};
border-radius: ${themeVal('shape.rounded')};
display: flex;
flex-direction: column;
gap: ${glsp()};
`;

const CloseButton = styled(Button)`
position: absolute;
right: ${glsp(0.5)};
top: ${glsp(0.5)};
`;

const PopoverBody = styled.div``;

const PopoverFooter = styled.div`
display: flex;
justify-content: center;
align-items: center;
gap: ${glsp()};
font-weight: ${themeVal('type.base.bold')};
`;

const tourSteps = [
{
title: 'Welcome to Exploration',
selector: "[data-tour='dataset-list-item']",
content:
"Each timeline entry represents a dataset, and each of the gray segments represents a data unit in the dataset. This data unit can be a day, month or year, depending on the dataset's time density."
},
{
title: 'Playhead',
selector: "[data-tour='timeline-head-a']",
content:
'This is the main timeline playhead which can be used to select the date to view on the map. You can drag it around to change the date.'
},
{
title: 'Date picker',
selector: "[data-tour='date-picker-a']",
content: 'Alternatively you can also select a date through the date picker.'
},
{
title: 'Timeline',
selector: "[data-tour='timeline-interaction-rect']",
content: () => (
<>
To navigate the timeline you can zoom in by pressing the alt key (or
option) + the scroll wheel, and click and drag to pan the timeline.
<br />
Clicking on the timeline will also move the playhead to that date.
<br />
Go ahead and try it out!
</>
)
},
{
title: 'AOI tools',
selector: '.mapboxgl-ctrl-top-left',
content: () => (
<>
These tools allow you to draw or upload an AOI to run an analysis on the
selected datasets.
</>
),
stepInteraction: false
}
];

export function TourManager() {
const { setIsOpen, setSteps } = useTour();
const datasets = useAtomValue(timelineDatasetsAtom);

const datasetCount = datasets.length;
const prevDatasetCount = usePreviousValue(datasetCount);

useEffect(() => {
if (!prevDatasetCount && datasetCount > 0) {
setSteps?.(tourSteps);
setTimeout(() => {
setIsOpen(true);
}, 1000);
}
}, [prevDatasetCount, datasetCount, setIsOpen]);

return null;
}

interface ExtendedPopoverContentProps extends PopoverContentProps {
steps: (StepType & { title: string })[];
}

export function PopoverTourComponent(props: ExtendedPopoverContentProps) {
const { currentStep, steps, setIsOpen, setCurrentStep } = props;

const isLastStep = currentStep === steps.length - 1;
const { content, title } = steps[currentStep];
return (
<Popover>
<CloseButton
variation='base-text'
size='small'
fitting='skinny'
onClick={() => setIsOpen(false)}
>
<CollecticonXmark size='small' meaningful title='Close tour' />
</CloseButton>
<Heading as='strong' size='xsmall'>
{title}
</Heading>
<PopoverBody>
<>
{/* Check if the step.content is a function or a string */}
{typeof content === 'function' ? content({ ...props }) : content}
</>
</PopoverBody>
<PopoverFooter>
<Button
variation='base-text'
size='small'
fitting='skinny'
disabled={currentStep === 0}
onClick={() => {
setCurrentStep((s) => s - 1);
}}
>
<CollecticonChevronLeftSmall meaningful title='Previous step' />
</Button>
<small>
{currentStep + 1} / {steps.length}
</small>
<Button
variation='base-text'
size='small'
fitting='skinny'
disabled={isLastStep}
onClick={() => {
setCurrentStep((s) => s + 1);
}}
>
<CollecticonChevronRightSmall meaningful title='Next step' />
</Button>
</PopoverFooter>
{(currentStep === 0 || isLastStep) && (
<a
href='#'
onClick={(e) => {
e.preventDefault();
setIsOpen(false);
}}
>
Dismiss and do not show again
</a>
)}
</Popover>
);
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
"@mapbox/mapbox-gl-draw": "^1.3.0",
"@mapbox/mapbox-gl-geocoder": "^5.0.1",
"@parcel/transformer-raw": "~2.7.0",
"@reactour/tour": "^3.6.1",
"@tanstack/react-query": "^4.3.9",
"@tanstack/react-query-devtools": "^4.3.9",
"@tanstack/react-table": "^8.9.3",
Expand Down
41 changes: 41 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2962,6 +2962,42 @@
resolved "http://verdaccio.ds.io:4873/@reach%2fobserve-rect/-/observe-rect-1.2.0.tgz#d7a6013b8aafcc64c778a0ccb83355a11204d3b2"
integrity sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ==

"@reactour/mask@*":
version "1.1.0"
resolved "http://verdaccio.ds.io:4873/@reactour%2fmask/-/mask-1.1.0.tgz#e327306585ee3510e80169a7fa811e9d0b9448bb"
integrity sha512-GkJMLuTs3vTsm4Ryq2uXcE4sMzRP1p4xSd6juSOMqbHa7IVD/UiLCLqJWHR9xGSQPbYhpZAZAORUG5cS0U5tBA==
dependencies:
"@reactour/utils" "*"

"@reactour/popover@*":
version "1.1.1"
resolved "http://verdaccio.ds.io:4873/@reactour%2fpopover/-/popover-1.1.1.tgz#c9b05ee31b8677d874f1479d724204b936e1128f"
integrity sha512-BouulO0sXfmuHSPX8FwCYI0fMeT+VsWqk7UTao3NQcUC5H903ZeKOV2GYpwSJtRUQhsyNEu1Q8cEruGRf1SOXQ==
dependencies:
"@reactour/utils" "*"

"@reactour/tour@^3.6.1":
version "3.6.1"
resolved "http://verdaccio.ds.io:4873/@reactour%2ftour/-/tour-3.6.1.tgz#7537c8faa48546fe4e312f125113459b7a19fa13"
integrity sha512-vzmgbm4T7n5gh0cjc4Zi4G3K29dXQyEdi/o7ZYLpNcisJ0hwP5jNKH7BgckrHWEGldBxYSWl34tsRmHcyxporQ==
dependencies:
"@reactour/mask" "*"
"@reactour/popover" "*"
"@reactour/utils" "*"

"@reactour/utils@*":
version "0.5.0"
resolved "http://verdaccio.ds.io:4873/@reactour%2futils/-/utils-0.5.0.tgz#8886872d78839187fd53399834a1b9688e98d754"
integrity sha512-yQs5Nm/Dg1xRM7d/S/UILBV5OInrTgrjGzgc81/RP5khqdO5KnpOaC46yF83kDtCalte8X3RCwp+F2YA509k1w==
dependencies:
"@rooks/use-mutation-observer" "^4.11.2"
resize-observer-polyfill "^1.5.1"

"@rooks/use-mutation-observer@^4.11.2":
version "4.11.2"
resolved "http://verdaccio.ds.io:4873/@rooks%2fuse-mutation-observer/-/use-mutation-observer-4.11.2.tgz#a0466c4338e0a4487ea19253c86bcd427c29f4af"
integrity sha512-vpsdrZdr6TkB1zZJcHx+fR1YC/pHs2BaqcuYiEGjBVbwY5xcC49+h0hAUtQKHth3oJqXfIX/Ng8S7s5HFHdM/A==

"@sinclair/typebox@^0.24.1":
version "0.24.20"
resolved "http://verdaccio.ds.io:4873/@sinclair%2ftypebox/-/typebox-0.24.20.tgz#11a657875de6008622d53f56e063a6347c51a6dd"
Expand Down Expand Up @@ -11303,6 +11339,11 @@ require-main-filename@^1.0.1:
resolved "http://verdaccio.ds.io:4873/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=

resize-observer-polyfill@^1.5.1:
version "1.5.1"
resolved "http://verdaccio.ds.io:4873/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==

resolve-cwd@^3.0.0:
version "3.0.0"
resolved "http://verdaccio.ds.io:4873/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
Expand Down

0 comments on commit 7737b0e

Please sign in to comment.