Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 46 additions & 1 deletion src/components/heightgraph.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ describe('HeightGraph', () => {

it('should show expand icon when collapsed', () => {
render(<HeightGraph data={createMockData()} width={400} />);
expect(screen.getByText('β–²')).toBeInTheDocument();
expect(screen.getByAltText('Height Graph')).toBeInTheDocument();
});

it('should not display chart container when collapsed', () => {
Expand Down Expand Up @@ -227,4 +227,49 @@ describe('HeightGraph', () => {
const svg = document.querySelector('svg');
expect(svg).toBeInTheDocument();
});

it('should not expand when disabled', async () => {
const user = userEvent.setup();
const onExpand = vi.fn();
render(
<HeightGraph
data={createMockData()}
width={400}
disabled={true}
onExpand={onExpand}
/>
);

await user.click(screen.getByTitle('Height Graph'));

expect(onExpand).not.toHaveBeenCalled();
expect(screen.getByAltText('Height Graph')).toBeInTheDocument();
});

it('should auto-collapse when becoming disabled', () => {
const onExpand = vi.fn();
const { rerender } = render(
<HeightGraph
data={createMockData()}
width={400}
disabled={false}
onExpand={onExpand}
/>
);

// First expand it manually by simulating the expanded state via rerender
// We need to click to expand first
// Since clicking is async, let's test the prop-change collapse path directly
rerender(
<HeightGraph
data={createMockData()}
width={400}
disabled={true}
onExpand={onExpand}
/>
);

// Should remain collapsed (was never expanded)
expect(screen.getByAltText('Height Graph')).toBeInTheDocument();
});
});
54 changes: 32 additions & 22 deletions src/components/heightgraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ import React, { useEffect, useRef, useState } from 'react';
import * as d3 from 'd3';
import { colorMappings } from '@/utils/heightgraph';
import makeResizable from '@/utils/resizable';
import { ToolButton } from '@/components/map/parts/tool-button';
import elevationIcon from '@/images/elevation.png';
import type { FeatureCollection, GeoJsonProperties, Geometry } from 'geojson';

interface HeightGraphProps {
data: FeatureCollection<Geometry, GeoJsonProperties>[];
width: number;
height?: number;
disabled?: boolean;
onExpand?: (expanded: boolean) => void;
onHighlight?: (index: number | null) => void;
}
Expand All @@ -16,6 +19,7 @@ const HeightGraph: React.FC<HeightGraphProps> = ({
data,
width,
height = 200,
disabled = false,
onExpand,
onHighlight,
}) => {
Expand Down Expand Up @@ -279,37 +283,43 @@ const HeightGraph: React.FC<HeightGraphProps> = ({
};
}, [isExpanded]);

// Auto-collapse when disabled
const [prevDisabled, setPrevDisabled] = useState(disabled);
if (disabled !== prevDisabled) {
setPrevDisabled(disabled);
if (disabled && isExpanded) {
setIsExpanded(false);
if (onExpand) onExpand(false);
}
}

const handleToggleExpand = () => {
if (disabled) return;
const newState = !isExpanded;
setIsExpanded(newState);
if (onExpand) onExpand(newState);
};

return (
<>
<div
className="heightgraph-toggle"
onClick={handleToggleExpand}
<ToolButton
title="Height Graph"
style={{
position: 'absolute',
bottom: '84px',
right: '10px',
width: '36px',
height: '36px',
background: 'white',
border: '2px solid rgba(0,0,0,0.2)',
borderRadius: '4px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '18px',
zIndex: 1001,
}}
>
{isExpanded ? 'βˆ’' : 'β–²'}
</div>
icon={
isExpanded ? (
<span style={{ fontSize: '18px' }}>βˆ’</span>
) : (
<img
src={elevationIcon}
alt="Height Graph"
style={{ width: '24px', height: '24px' }}
/>
)
}
onClick={handleToggleExpand}
disabled={disabled}
className="z-[1001]"
data-testid="heightgraph-toggle"
/>
<div
ref={containerRef}
className={`maplibre-heightgraph ${isExpanded ? 'heightgraph-expanded' : ''}`}
Expand Down
33 changes: 26 additions & 7 deletions src/components/map/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,28 @@ vi.mock('./parts/brand-logos', () => ({
}));

vi.mock('./parts/tool-button', () => ({
ToolButton: vi.fn(({ title, onClick, 'data-testid': testId }) => (
<button aria-label={title} onClick={onClick} data-testid={testId}>
{title}
</button>
)),
ToolButton: vi.fn(
({
title,
onClick,
disabled,
'data-testid': testId,
}: {
title: string;
onClick: () => void;
disabled?: boolean;
'data-testid'?: string;
}) => (
<button
aria-label={title}
onClick={onClick}
disabled={disabled}
data-testid={testId}
>
{title}
</button>
)
),
}));

vi.mock('./parts/map-info-popup', () => ({
Expand Down Expand Up @@ -456,9 +473,11 @@ describe('MapComponent', () => {
});
});

it('should not show heightgraph when directions are not successful', () => {
it('should show heightgraph toggle but disabled when directions are not successful', () => {
render(<MapComponent />);
expect(screen.queryByTestId('heightgraph')).not.toBeInTheDocument();
const toggle = screen.getByTestId('heightgraph-toggle');
expect(toggle).toBeInTheDocument();
expect(toggle).toBeDisabled();
});

it('should set initial view state from getInitialMapPosition', () => {
Expand Down
33 changes: 16 additions & 17 deletions src/components/map/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -896,24 +896,10 @@ export const MapComponent = () => {

<BrandLogos />

<ToolButton
title="Open on osm.org"
icon={
<img
src={OSMIcon}
width={28}
height={28}
alt="openstreetmap.org logo"
/>
}
onClick={handleOpenOSM}
className="absolute bottom-10 right-4 z-10"
data-testid="osm-button"
/>

{directionsSuccessful && (
<div className="absolute bottom-10 right-4 z-10 flex flex-col gap-2">
<HeightGraph
data={heightgraphData}
disabled={!directionsSuccessful}
width={
directionsPanelOpen
? window.innerWidth * 0.75
Expand All @@ -927,7 +913,20 @@ export const MapComponent = () => {
}}
onHighlight={throttledSetHeightgraphHoverDistance}
/>
)}
<ToolButton
title="Open on osm.org"
icon={
<img
src={OSMIcon}
width={28}
height={28}
alt="openstreetmap.org link"
/>
}
onClick={handleOpenOSM}
data-testid="osm-button"
/>
</div>
</Map>

<div
Expand Down
29 changes: 29 additions & 0 deletions src/components/map/parts/tool-button.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,33 @@ describe('ToolButton', () => {
);
expect(screen.getByRole('button', { name: 'Test' })).toBeInTheDocument();
});

it('should be disabled when disabled prop is true', () => {
render(
<ToolButton
title="Test"
icon={<span>icon</span>}
onClick={vi.fn()}
disabled={true}
/>
);
expect(screen.getByRole('button', { name: 'Test' })).toBeDisabled();
});

it('should not call onClick when disabled', async () => {
const user = userEvent.setup();
const onClick = vi.fn();
render(
<ToolButton
title="Test"
icon={<span>icon</span>}
onClick={onClick}
disabled={true}
/>
);

await user.click(screen.getByRole('button', { name: 'Test' }));

expect(onClick).not.toHaveBeenCalled();
});
});
6 changes: 5 additions & 1 deletion src/components/map/parts/tool-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ interface ToolButtonProps {
icon: ReactNode;
onClick: () => void;
className?: string;
disabled?: boolean;
'data-testid'?: string;
}

Expand All @@ -13,6 +14,7 @@ export function ToolButton({
icon,
onClick,
className,
disabled = false,
'data-testid': testId,
}: ToolButtonProps) {
return (
Expand All @@ -21,6 +23,7 @@ export function ToolButton({
aria-label={title}
title={title}
onClick={onClick}
disabled={disabled}
data-testid={testId}
className={className}
style={{
Expand All @@ -30,11 +33,12 @@ export function ToolButton({
borderRadius: '4px',
boxShadow: '0 0 0 2px rgba(0,0,0,0.1)',
border: 'none',
cursor: 'pointer',
cursor: disabled ? 'default' : 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: 0,
opacity: disabled ? 0.4 : 1,
}}
>
<span aria-hidden={true} style={{ display: 'flex' }}>
Expand Down
Binary file added src/images/elevation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading