Skip to content
This repository has been archived by the owner on Apr 24, 2024. It is now read-only.

Commit

Permalink
Feature/1130 timeline event representation (#1192)
Browse files Browse the repository at this point in the history
<!--
Check relevant points but **please do not remove entries**.
-->

## Basics

<!--
These points need to be fulfilled for every PR.
-->

- [ ] The PR is rebased with current master
- [ ] I added a line to [changelog.md](/doc/changelog.md)
- [ ] Details of what I changed are in the commit messages
- [ ] References to issues, e.g. `close #X`, are in the commit messages
and changelog
- [ ] The buildserver is happy

<!--
If you have any troubles fulfilling these criteria, please write about
the trouble as comment in the PR.
We will help you, but we cannot accept PRs that do not fulfill the
basics.
-->

## Checklist

<!--
For documentation fixes, spell checking, and similar none of these
points below need to be checked.
Otherwise please check these points when getting a PR done:
-->

- [ ] I fully described what my PR does in the documentation
- [ ] I fixed all affected documentation
- [ ] I fixed the introduction tour
- [ ] I wrote migrations in a way that they are compatible with already
present data
- [ ] I fixed all affected decisions
- [ ] I added automated tests or a [manual test
protocol](../doc/tests/manual/protocol.md)
- [ ] I added code comments, logging, and assertions as appropriate
- [ ] I translated all strings visible to the user
- [ ] I mentioned [every code or
binary](https://github.com/ElektraInitiative/PermaplanT/blob/master/.reuse/dep5)
not directly written or done by me in [reuse
syntax](https://reuse.software/)
- [ ] I created left-over issues for things that are still to be done
- [ ] Code is conforming to [our Architecture](/doc/architecture)
- [ ] Code is conforming to [our Guidelines](/doc/guidelines)
- [ ] Code is consistent to [our Design Decisions](/doc/decisions)
- [ ] Exceptions to any guidelines are documented

## First Time Checklist

<!--
These points are only relevant when creating a PR the first time.
-->

- [ ] I have installed and I am using [pre-commit
hooks](../doc/contrib/README.md#Hooks)
- [ ] I am using [Tailwind CSS
Linting](https://tailwindcss.com/blog/introducing-linting-for-tailwindcss-intellisense)

## Review

<!--
Reviewers can copy&check the following to their review.
Also the checklist above can be used.
But also the PR creator should check these points when getting a PR
done:
-->

- [ ] I've tested the code
- [ ] I've read through the whole code
- [ ] I've read through the whole documentation
- [ ] I've checked conformity to guidelines
- [ ] I've checked conformity to requirements
- [ ] I've checked that the requirements are tested
  • Loading branch information
markus2330 authored Mar 13, 2024
2 parents 5a1de57 + 7882454 commit 8cb3a87
Show file tree
Hide file tree
Showing 14 changed files with 748 additions and 251 deletions.
3 changes: 3 additions & 0 deletions doc/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ Syntax: `- short text describing the change _(Your Name)_`
- Migrate from Jest to Vitest, update Vite to v5, update Node to 20, .env should be .env.local _(Paul)_
- Fix broken .env reading for scraper _(Paul)_
- Add a dev env variable for react query when developing in offline env (e.g. plane) _(Paul)_
- Collect events from planting layer and add it to timeline _(Daniel Steinkogler)_
- _()_
- _()_
- Remove error messages in console if a seed was not found _(Moritz)_
- _()_
- Landing page: Added blog entries, updated team members, minor text changes _(Christoph Schreiner)_
Expand Down
9 changes: 5 additions & 4 deletions e2e/features/timeline.feature
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ Feature: Planting Timeline
When I change the plants removed date to yesterday
Then the plant disappears

Scenario: Unhide a plant by changing the map date by day
When I change the plants added date to tomorrow
And I change the map date to tomorrow
Then the plant appears
# TODO Fix Test (#1202)
#Scenario: Unhide a plant by changing the map date by day
# When I change the plants added date to tomorrow
# And I change the map date to tomorrow
# Then the plant appears

Scenario: Hide a plant by changing the map date by month
When I change the map date to last month
Expand Down
17 changes: 17 additions & 0 deletions frontend/src/features/map_planning/api/getTimelineEvents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { TimelineDto } from '@/api_types/definitions';
import { createAPI } from '@/config/axios';

export async function getTimelineEvents(mapId: number, startDate: string, endDate: string) {
const http = createAPI();

try {
const searchParams = new URLSearchParams();
searchParams.append('start', startDate);
searchParams.append('end', endDate);

const response = await http.get<TimelineDto>(`/api/maps/${mapId}/timeline?${searchParams}`);
return response.data;
} catch (error) {
throw error as Error;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const ItemSliderPicker = ({
const scrollPositionOnMouseDown = useRef<number | null>(null);
const isScrollingToIndex = useRef(false);

const detectAndNotifyIfBordersIsReached = () => {
const detectAndNotifyIfBorderIsReached = () => {
const container = sliderContainerRef.current;

if (!container) return;
Expand All @@ -56,7 +56,10 @@ const ItemSliderPicker = ({
const containerWidth = container.offsetWidth;
const scrollWidth = container.scrollWidth;

const threshold = 500;
const threshold = 1000;

if (isDragging.current) return;

if (scrollLeft < threshold) {
leftEndReached?.();
} else if (scrollWidth - scrollLeft - containerWidth < threshold) {
Expand All @@ -68,7 +71,7 @@ const ItemSliderPicker = ({
if (itemKey == selectedItemKey) return;

setSelectedItemKey(itemKey);
detectAndNotifyIfBordersIsReached();
detectAndNotifyIfBorderIsReached();
};

const getSelectedItemIndex = useCallback(() => {
Expand All @@ -87,6 +90,9 @@ const ItemSliderPicker = ({

const items = container.getElementsByClassName('item') as HTMLCollectionOf<HTMLElement>;
const selectedElement = items[idx];

if (!selectedElement) return;

const containerWidth = container.offsetWidth;
const itemWidth = selectedElement.offsetWidth;
const scrollLeft = selectedElement.offsetLeft + itemWidth / 2 - containerWidth / 2;
Expand Down Expand Up @@ -201,6 +207,8 @@ const ItemSliderPicker = ({

isDragging.current = false;
dragStartX.current = null;

detectAndNotifyIfBorderIsReached();
};

const handleMouseWheel = (e: WheelEvent) => {
Expand Down Expand Up @@ -240,13 +248,6 @@ const ItemSliderPicker = ({
};
});

// Add a useEffect hook to scroll to the selected item when items change from the outside.
useEffect(() => {
const selectedIndex = getSelectedItemIndex();
const selectedItemIndex = Math.min(Math.max(selectedIndex, 0), items.length - 1);
scrollToIndex(selectedItemIndex);
}, [items, getSelectedItemIndex]);

// Add a useEffect hook to update the selected item when the selected value changes from the outside.
useEffect(() => {
if (value === undefined) return;
Expand Down Expand Up @@ -296,11 +297,11 @@ const ItemSliderPicker = ({
return (
<div
key={index}
className={`item min-w-1/5 dark relative flex w-10 flex-col items-center justify-end border-4 border-white px-7 pb-1 pt-1 dark:border-neutral-200-dark
className={`item dark border-4 px-2 pt-1
${
getSelectedItemIndex() === index
? 'selected-item bg-gray-300 font-bold text-secondary-300 group-focus:border-blue-300 dark:bg-black '
: ''
? 'selected-item border-b border-t border-blue-200 bg-gray-100 font-bold text-secondary-300 group-focus:border-blue-400 dark:border-black dark:bg-gray-900 '
: 'border-white dark:border-neutral-200-dark'
}
`}
onClick={(e) => handleItemClick(e, index)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { render } from '@testing-library/react';
import ReactTestUtils, { act } from 'react-dom/test-utils';
import { createDays, createYearsAndMonths } from '../../hooks/useGetTimelineData';
import useMapStore from '../../store/MapStore';
import { UNTRACKED_DEFAULT_STATE, UntrackedMapSlice } from '../../store/MapStoreTypes';
import TimelineDatePicker from './TimelineDatePicker';

const onSelectChange = vi.fn();
Expand All @@ -11,19 +14,22 @@ describe('handleDayItemChange', () => {
});

beforeAll(() => {
setupTimeline();
vi.useFakeTimers({ shouldAdvanceTime: true });
});

it('should callback correct date when day item is changed', () => {
Element.prototype.scrollTo = vi.fn(() => void 0);
setupTimeline();

Element.prototype.scrollTo = vi.fn(() => void 0);
const timeline = render(
<TimelineDatePicker
defaultDate="2020-12-31"
onSelectDate={onSelectChange}
onLoading={onLoading}
/>,
);

act(() => {
ReactTestUtils.Simulate.keyDown(timeline.getByTestId('timeline__day-slider'), {
key: 'ArrowRight',
Expand Down Expand Up @@ -57,6 +63,8 @@ describe('handleMonthItemChange', () => {
});

it('should callback correct date when month item is changed', () => {
setupTimeline();

Element.prototype.scrollTo = vi.fn(() => void 0);
const onSelectChange = vi.fn();

Expand Down Expand Up @@ -93,6 +101,8 @@ describe('handleMonthItemChange', () => {
});

it('should automatically select last day of month if new month has less days than previous selected day', () => {
setupTimeline();

Element.prototype.scrollTo = vi.fn(() => void 0);
const onSelectChange = vi.fn();

Expand Down Expand Up @@ -137,12 +147,14 @@ describe('handleYearItemChange', () => {
});

it('should callback correct date when year item is changed', () => {
setupTimeline();

Element.prototype.scrollTo = vi.fn(() => void 0);
const onSelectChange = vi.fn();

const timeline = render(
<TimelineDatePicker
defaultDate="2000-01-31"
defaultDate="2020-01-31"
onSelectDate={onSelectChange}
onLoading={onLoading}
/>,
Expand All @@ -155,11 +167,11 @@ describe('handleYearItemChange', () => {
});

vi.runAllTimers();
expect(onSelectChange).toBeCalledWith('2001-01-31');
expect(onSelectChange).toBeCalledWith('2021-01-31');
expect(
timeline.getByTestId('timeline__year-slider').getElementsByClassName('selected-item')[0]
.textContent,
).toBe('2001');
).toBe('2021');
expect(
timeline.getByTestId('timeline__month-slider').getElementsByClassName('selected-item')[0]
.textContent,
Expand All @@ -170,3 +182,23 @@ describe('handleYearItemChange', () => {
).toBe('31');
});
});

function setupTimeline() {
useMapStore.setState(createStoreWithTimelineData());
}

function createStoreWithTimelineData(): Pick<UntrackedMapSlice, 'untrackedState'> {
const { years, months } = createYearsAndMonths(2000, 2021, {}, {});
const days = createDays(2000, 2021, {});

return {
untrackedState: {
...UNTRACKED_DEFAULT_STATE,
timeLineEvents: {
yearly: years,
monthly: months,
daily: days,
},
},
};
}
Loading

0 comments on commit 8cb3a87

Please sign in to comment.