Skip to content

Commit

Permalink
front: animate new intermediate op in stdcm
Browse files Browse the repository at this point in the history
- Add an animation when a new intermediate op is added
- Scroll to keep it in the viewport if it overflows
- Adapat e2e tests in stdcm to ensure buttons are clickable while it's animating

Signed-off-by: SharglutDev <p.filimon75@gmail.com>
  • Loading branch information
SharglutDev committed Dec 19, 2024
1 parent ea19025 commit eaa4315
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 3 deletions.
43 changes: 42 additions & 1 deletion front/src/applications/stdcm/components/StdcmForm/StdcmVias.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo } from 'react';
import { useLayoutEffect, useMemo, useState } from 'react';

import { Location } from '@osrd-project/ui-icons';
import { useTranslation } from 'react-i18next';
Expand Down Expand Up @@ -27,6 +27,8 @@ const StdcmVias = ({ disabled = false }: StdcmConfigCardProps) => {
useOsrdConfActions() as StdcmConfSliceActions;
const pathSteps = useSelector(getStdcmPathSteps);

const [newIntermediateOpIndex, setNewIntermediateOpIndex] = useState<number>();

const intermediatePoints = useMemo(() => pathSteps.slice(1, -1), [pathSteps]);

const updateStopType = (newStopType: StdcmStopTypes, pathStep: StdcmPathStep) => {
Expand All @@ -44,6 +46,44 @@ const StdcmVias = ({ disabled = false }: StdcmConfigCardProps) => {
);
};

/**
* As the new intermediateOp block animates, we want to scroll to keep the box in the viewport.
* To do so, we install an animation frame listener (requestAnimationFrame) which updates the scroll position
* each time an animation frame is triggered.
* An animation end listener is also installed to cancel the animation frame listener.
* To properly clean up when the component is unmounted, we return a cleanup function that removes both listeners.
*/
useLayoutEffect(() => {
if (!newIntermediateOpIndex) return undefined;

const newElement = document.querySelector(
`.stdcm-vias-bundle:nth-child(${newIntermediateOpIndex}) > :last-child`
);

if (!newElement) return undefined;

let requestId: number;

const scrollWithAnimation = () => {
newElement.scrollIntoView({
block: 'nearest',
behavior: 'auto',
});

requestId = requestAnimationFrame(scrollWithAnimation);
};

requestId = requestAnimationFrame(scrollWithAnimation);

const cancelListener = () => cancelAnimationFrame(requestId);

newElement.addEventListener('animationend', cancelListener);
return () => {
newElement.removeEventListener('animationend', cancelListener);
cancelListener();
};
}, [newIntermediateOpIndex]);

const updateStopDuration = (stopTime: string, pathStep: StdcmPathStep) => {
const stopFor = stopTime ? Number(stopTime) : undefined;
dispatch(
Expand All @@ -60,6 +100,7 @@ const StdcmVias = ({ disabled = false }: StdcmConfigCardProps) => {

const addViaOnClick = (pathStepIndex: number) => {
dispatch(addStdcmVia(pathStepIndex));
setNewIntermediateOpIndex(pathStepIndex);
};

return (
Expand Down
22 changes: 22 additions & 0 deletions front/src/styles/scss/applications/stdcm/_home.scss
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,28 @@
gap: 16px;
}

.stdcm-vias-bundle {
animation: bouncin-in 0.75s cubic-bezier(0.567, -0.475, 0, 1);

.stdcm-card {
scroll-margin-bottom: 15px;
}
}

@keyframes bouncin-in {
0% {
opacity: 0;
height: 0;
// To make the new block poping from above the clicked button
transform: translateY(-45px);
margin-bottom: -30px; // To make the button clicked bounce top
}
100% {
opacity: 1;
height: 296px; // height of add OP button + intermedita OP card
}
}

.stdcm-vias-list {
margin-block: 16px;

Expand Down
2 changes: 2 additions & 0 deletions front/tests/006-stdcm.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ test.use({
test.describe('Verify train schedule elements and filters', () => {
test.slow(); // Mark test as slow due to multiple steps

test.use({ viewport: { width: 1920, height: 1080 } });

let infra: Infra;
let OSRDLanguage: string;

Expand Down
5 changes: 3 additions & 2 deletions front/tests/pages/stdcm-page-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ class STDCMPage {
const fillVia = async (selectedSuggestion: Locator) => {
await this.addViaButton.nth(viaNumber - 1).click();
expect(await this.addViaButton.count()).toBe(viaNumber + 1);
await expect(this.getViaCi(viaNumber)).toBeVisible();
await this.getViaCi(viaNumber).fill(viaSearch);
await selectedSuggestion.click();
await expect(this.getViaCh(viaNumber)).toHaveValue('BV');
Expand Down Expand Up @@ -496,7 +497,7 @@ class STDCMPage {
// Wait for the download event
await this.page.waitForTimeout(500);
const downloadPromise = this.page.waitForEvent('download', { timeout: 120000 });
await this.downloadSimulationButton.click({ force: true });
await this.downloadSimulationButton.dispatchEvent('click');
const download = await downloadPromise.catch(() => {
throw new Error('Download event was not triggered.');
});
Expand All @@ -510,7 +511,7 @@ class STDCMPage {
}

async clickOnStartNewQueryButton() {
await this.startNewQueryButton.click();
await this.startNewQueryButton.dispatchEvent('click');
}

async mapMarkerVisibility() {
Expand Down

0 comments on commit eaa4315

Please sign in to comment.