Skip to content
5 changes: 5 additions & 0 deletions .changeset/brown-mice-rescue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@getodk/web-forms': minor
---

Remove long-press interactions in maps
4 changes: 2 additions & 2 deletions packages/web-forms/e2e/page-objects/controls/MapControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class MapControl {
await this.page.mouse.up();
}

async longPressMap(mapComponent: Locator, moveX: number, moveY: number) {
async tapToAddPoint(mapComponent: Locator, moveX: number, moveY: number) {
const box = await mapComponent.locator(this.MAP_CONTAINER_SELECTOR).boundingBox();
if (!box) {
return;
Expand All @@ -69,7 +69,7 @@ export class MapControl {

await this.page.mouse.move(centerX + moveX, centerY + moveY);
await this.page.mouse.down();
await this.page.waitForTimeout(1400); // Press and hold
await this.page.waitForTimeout(200); // Press
await this.page.mouse.up();
await this.page.waitForTimeout(300); // Process point
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ test.describe('All Question Types (Visual)', () => {
});

test('adds a new point elsewhere, removes saved point', async () => {
await formPage.map.longPressMap(mapComponent, 100, 100);
await formPage.map.tapToAddPoint(mapComponent, 100, 100);
await formPage.map.expectMapScreenshot(mapComponent, 'placement-map-new-point-saved.png');

await formPage.map.removeSavedPoint(mapComponent);
Expand Down Expand Up @@ -184,10 +184,10 @@ test.describe('All Question Types (Visual)', () => {
await formPage.map.expectMapScreenshot(mapComponent, 'geoshape-initial-state.png');
await formPage.waitForNetworkIdle();
await formPage.map.scrollMapIntoView(mapComponent);
await formPage.map.longPressMap(mapComponent, 100, 100);
await formPage.map.longPressMap(mapComponent, 150, -50);
await formPage.map.tapToAddPoint(mapComponent, 100, 100);
await formPage.map.tapToAddPoint(mapComponent, 150, -50);
await formPage.map.expectMapScreenshot(mapComponent, 'geoshape-uncompleted-polygon.png');
await formPage.map.longPressMap(mapComponent, 50, -150);
await formPage.map.tapToAddPoint(mapComponent, 50, -150);
await formPage.map.expectMapScreenshot(mapComponent, 'geoshape-autoclose-polygon.png');
});
});
Expand All @@ -213,10 +213,10 @@ test.describe('All Question Types (Visual)', () => {
await formPage.map.expectMapScreenshot(mapComponent, 'geotrace-initial-state.png');
await formPage.waitForNetworkIdle();
await formPage.map.scrollMapIntoView(mapComponent);
await formPage.map.longPressMap(mapComponent, 100, 100);
await formPage.map.longPressMap(mapComponent, 150, -50);
await formPage.map.tapToAddPoint(mapComponent, 100, 100);
await formPage.map.tapToAddPoint(mapComponent, 150, -50);
await formPage.map.expectMapScreenshot(mapComponent, 'geotrace-two-points.png');
await formPage.map.longPressMap(mapComponent, 50, -75);
await formPage.map.tapToAddPoint(mapComponent, 50, -75);
await formPage.map.expectMapScreenshot(mapComponent, 'geoshape-three-points.png');
});
});
Expand Down
14 changes: 10 additions & 4 deletions packages/web-forms/src/components/common/map/MapBlock.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const isFullScreen = ref(false);
const isAdvancedPanelOpen = ref(false);
const confirmDeleteAction = ref(false);
const isUpdateCoordsDialogOpen = ref(false);
const pointPlaced = ref(false);
const selectedVertex = ref<Coordinate | undefined>();
const showErrorStyle = inject<ComputedRef<boolean>>(
QUESTION_HAS_ERROR,
Expand All @@ -46,7 +47,7 @@ const showErrorStyle = inject<ComputedRef<boolean>>(
const mapHandler = useMapBlock(
{ mode: props.mode, singleFeatureType: props.singleFeatureType },
{
onFeaturePlacement: () => emitSavedFeature(),
onFeaturePlacement: () => onFeaturePlacement(),
onVertexSelect: (vertex) => (selectedVertex.value = vertex),
}
);
Expand Down Expand Up @@ -98,6 +99,11 @@ watch(
(newValue) => mapHandler.setupMapInteractions(newValue)
);

const onFeaturePlacement = () => {
pointPlaced.value = true;
emitSavedFeature();
};

const handleEscapeKey = (event: KeyboardEvent) => {
if (event.key === 'Escape' && isFullScreen.value) {
isFullScreen.value = false;
Expand Down Expand Up @@ -175,15 +181,15 @@ const saveAdvancedPanelCoords = (newCoords: Coordinate) => {
/>

<Message
v-if="!disabled && mapHandler.canLongPressAndDrag()"
v-if="!disabled && mapHandler.canTapToAddAndDrag()"
severity="contrast"
closable
size="small"
:class="{ 'map-message': true, 'above-secondary-controls': showSecondaryControls }"
>
<!-- TODO: translations -->
<span v-if="savedFeatureValue">Press and drag to move a point</span>
<span v-else>Long press to place a point</span>
<span v-if="pointPlaced">Press and drag to move a point</span>
<span v-else>Tap to place a point</span>
</Message>
</div>

Expand Down
34 changes: 17 additions & 17 deletions packages/web-forms/src/components/common/map/MapStatusBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,14 @@ const savedStatus = computed<StatusDetails | null>(() => {

return { message: 'Point saved', icon: 'mdiCheckCircle', highlight: true };
});

const displayState = computed(() => {
const baseState = savedStatus.value ?? noSavedStatus.value;
if (selectedVertexInfo.value.length) {
return { ...baseState, message: selectedVertexInfo.value };
}
return { ...baseState };
});
</script>

<template>
Expand All @@ -145,16 +153,16 @@ const savedStatus = computed<StatusDetails | null>(() => {
</div>
</div>

<div v-else-if="savedStatus" class="map-status-container">
<div v-if="selectedVertexInfo.length" class="map-status">
<IconSVG :name="savedStatus.icon" :variant="savedStatus.highlight ? 'success' : 'base'" />
<span>{{ selectedVertexInfo }}</span>
</div>
<div v-else class="map-status">
<IconSVG :name="savedStatus.icon" :variant="savedStatus.highlight ? 'success' : 'base'" />
<span>{{ savedStatus.message }}</span>
<div class="map-status-container">
<div class="map-status">
<IconSVG
:name="displayState.icon"
:variant="displayState.highlight ? 'success' : 'base'"
/>
<span>{{ displayState.message }}</span>
</div>
<div class="map-status-buttons">

<div v-if="savedStatus" class="map-status-buttons">
<Button v-if="canRemove" outlined severity="contrast" @click="emit('discard')">
<span>–</span>
<!-- TODO: translations -->
Expand All @@ -168,14 +176,6 @@ const savedStatus = computed<StatusDetails | null>(() => {
</div>
</div>

<div v-else class="map-status-container">
<div class="map-status">
<IconSVG :name="noSavedStatus.icon" />
<!-- TODO: translations -->
<span>{{ noSavedStatus.message }}</span>
</div>
</div>

<Button
v-if="canOpenAdvancedPanel"
class="advanced-button"
Expand Down
8 changes: 4 additions & 4 deletions packages/web-forms/src/components/common/map/getModeConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export interface ModeCapabilities {
interface ModeConfig {
interactions: {
select: boolean;
longPress: boolean;
tapToAdd: boolean;
dragFeature: boolean;
dragFeatureAndVertex: boolean;
};
Expand All @@ -48,7 +48,7 @@ export const getModeConfig = (mode: Mode): ModeConfig => {
const defaultConfig = {
interactions: {
select: false,
longPress: false,
tapToAdd: false,
dragFeature: false,
dragFeatureAndVertex: false,
},
Expand Down Expand Up @@ -98,7 +98,7 @@ export const getModeConfig = (mode: Mode): ModeConfig => {
return {
interactions: {
...defaultConfig.interactions,
longPress: true,
tapToAdd: true,
dragFeature: true,
},
capabilities: {
Expand All @@ -115,7 +115,7 @@ export const getModeConfig = (mode: Mode): ModeConfig => {
interactions: {
...defaultConfig.interactions,
select: true,
longPress: true,
tapToAdd: true,
dragFeatureAndVertex: true,
},
capabilities: {
Expand Down
16 changes: 9 additions & 7 deletions packages/web-forms/src/components/common/map/useMapBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,13 @@ import {
getVertexByIndex,
updateVertexCoordinate,
} from '@/components/common/map/vertex-geometry.ts';
import type { FeatureCollection, Feature as GeoJsonFeature } from 'geojson';
import type { Feature as GeoJsonFeature, FeatureCollection } from 'geojson';
import { Map, View } from 'ol';
import { Attribution, Zoom } from 'ol/control';
import type { Coordinate } from 'ol/coordinate';
import Feature from 'ol/Feature';
import { LineString, Polygon, SimpleGeometry } from 'ol/geom';
import { defaults as defaultInteractions } from 'ol/interaction';
import TileLayer from 'ol/layer/Tile';
import VectorLayer from 'ol/layer/Vector';
import WebGLVectorLayer from 'ol/layer/WebGLVector';
Expand Down Expand Up @@ -133,6 +134,7 @@ export function useMapBlock(config: MapBlockConfig, events: MapBlockEvents) {
extent: getProjection(DEFAULT_VIEW_PROJECTION)?.getExtent(),
}),
controls: [new Zoom(), new Attribution({ collapsible: false })],
interactions: defaultInteractions({ doubleClickZoom: false }),
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want tap to add only points/vertices (ref: 5th bullet point). The user can still pinch, scroll, and use the +/- buttons to zoom.

});

mapViewControls = useMapViewControls(mapInstance);
Expand Down Expand Up @@ -199,8 +201,8 @@ export function useMapBlock(config: MapBlockConfig, events: MapBlockEvents) {
);
}

if (currentMode.interactions.longPress) {
mapInteractions.setupLongPressPoint(featuresSource, (feature) =>
if (currentMode.interactions.tapToAdd) {
mapInteractions.setupTapToAddVertex(featuresSource, (feature) =>
handlePointPlacement(feature)
);

Expand Down Expand Up @@ -468,9 +470,9 @@ export function useMapBlock(config: MapBlockConfig, events: MapBlockEvents) {
return currentMode.capabilities.canRemoveCurrentLocation && !!mapFeatures?.getSavedFeature();
};

const canLongPressAndDrag = () => {
const { longPress, dragFeature, dragFeatureAndVertex } = currentMode.interactions;
return longPress && (dragFeature || dragFeatureAndVertex);
const canTapToAddAndDrag = () => {
const { tapToAdd, dragFeature, dragFeatureAndVertex } = currentMode.interactions;
return tapToAdd && (dragFeature || dragFeatureAndVertex);
};

const canOpenAdvancedPanel = () => {
Expand Down Expand Up @@ -554,7 +556,7 @@ export function useMapBlock(config: MapBlockConfig, events: MapBlockEvents) {
selectSavedFeature: () => mapFeatures?.selectFeature(mapFeatures?.getSavedFeature()),
unselectFeature,

canLongPressAndDrag,
canTapToAddAndDrag,
canViewProperties: () => currentMode.capabilities.canViewProperties,
canOpenAdvancedPanel,
shouldShowMapOverlay,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export function useMapFeatures(
const selectFeature = (feature: Feature | undefined, vertexIndex?: number) => {
if (capabilities.canSelectFeatureOrVertex) {
selectedFeature.value?.set(IS_SELECTED_PROPERTY, false);
selectedFeature.value?.set(SELECTED_VERTEX_INDEX_PROPERTY, undefined);
feature?.set(SELECTED_VERTEX_INDEX_PROPERTY, vertexIndex);
feature?.set(IS_SELECTED_PROPERTY, true);
}
Expand Down
Loading