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
14 changes: 14 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"permissions": {
"allow": [
"Bash(echo MISSING: $path:*)",
"WebFetch(domain:success.outsystems.com)",
"Bash(echo MISSING: $file:*)",
"WebFetch(domain:outsystemsui-qa.outsystemsenterprise.com)",
"WebFetch(domain:developers.google.com)",
"WebFetch(domain:leafletjs.com)",
"WebFetch(domain:unpkg.com)",
"WebFetch(domain:app.unpkg.com)"
]
}
}
213 changes: 188 additions & 25 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,210 @@
# Contributing to OUTSYSTEMS-MAPS
# Contributing to OutSystems Maps

Thank you for considering contributing to our project! 🚀

We hope that the contribution of this repo is made in a way that meets the OutSystems guidelines.
Thank you for contributing to OutSystems Maps. This guide covers the development workflow and standards for this TypeScript mapping component library.

## Code of Conduct

We must follow the code of conduct of OutSystems.

## Our Development Process
## Development Setup

We use GitHub to sync code to and from our internal repository.
We'll use JIRA to track issues and feature requests, and GitHub as well to accept pull requests.
**Prerequisites:**
- Node.js >= 12
- npm
- [Visual Studio Code](https://code.visualstudio.com/) (recommended)

## Pull Requests
**Recommended VS Code Extensions:**
- Document This (JSDoc comment templates)
- ESLint (code quality enforcement)
- Prettier - Code formatter

We actively welcome your pull requests.
**Setup:**
```bash
npm install
npm run dev # Starts development server at http://localhost:3000
```

1. Create your branch from `dev`.
1. If you've added code that should have tests and be tested.
1. Ensure the test suite passes.
1. Make sure your code lints.
1. The PR is compliant with all PR Checks.
1. Ensure the PR has the correct label on title, as below:
The development server watches for TypeScript changes and recompiles automatically with hot reload.

| Labels | Change | SemVer |
| --------------------------------------------------------------------- | :----: | ------- |
| breaking<br>major | Major | X+1.0.0 |
| feat<br>feature<br>minor | Minor | 0.X+1.0 |
| revert<br>perf<br>test<br>refactor<br>fix<br>bugfix<br>patch<br>chore | Patch | 0.0.X+1 |
| ci<br>none<br>docs<br>style<br>skip | None | 0.0.0 |
## Development Workflow

## Trusted Committers
### Branch Naming

Create branches from `dev` following the pattern `<JIRA-ID>` or `<JIRA-ID>-description`:

```bash
git checkout dev
git pull origin dev
git checkout -b ROU-1234
```

Examples: `ROU-12619`, `ROU-12504-fix-marker-clustering`

Branches named `release/*` or `merge/*` are exempt from PR title validation.

### Commit Messages

No strict commit format is enforced. Use clear, descriptive messages that explain the change.

### Pull Request Requirements

**PR Title Format:**

PR titles must match the regex `^([A-Z][A-Z0-9]*-\d+(:)?\s\w)` (enforced by CI):

```
<JIRA-ID> <description>
```

Examples:
- `ROU-12619 Fix marker clustering performance`
- `ROU-12558 Mark release as latest`

**Required Labels:**

PRs must have at least one of these labels (enforced by CI):
- `feature`
- `bug` or `bugfix`
- `dependencies` or `dependency`
- `chore`

PRs with `do not merge` label will be blocked from merging.

**PR Content Requirements:**

The PR template requires:
- Link to sample page demonstrating the change
- Problem description ("What was happening?")
- Solution description ("What was done?")
- Test steps to verify the fix
- Screenshots or animated GIFs
- Checklist confirmation (tested locally, documented, ESLint clean, etc.)

**Before Submitting:**
1. Test locally: `npm run dev`
2. Build succeeds: `npm run build`
3. No ESLint errors: `npm run lint`
4. Add JSDoc comments (type `/**` above functions)



## Building and Testing

| Command | Description |
|---------|-------------|
| `npm run setup` | Install dependencies and start dev server |
| `npm run dev` | Start development server with hot reload at http://localhost:3000 |
| `npm run build` | Production build: clean + transpile + lintfix + lint |
| `npm run lint` | Check code style (ESLint) |
| `npm run lintfix` | Auto-fix ESLint issues |
| `npm run prettier` | Format all JS/TS/CSS files |
| `npm run docs` | Generate TypeDoc documentation |

**Note:** `npm test` is not configured. Manual testing via the dev server and sample pages is required.

### Testing

Automated tests are maintained in a separate private repository: [outsystems-maps-tests](https://github.com/OutSystems/outsystems-maps-tests)

To run tests locally (requires `gh` CLI):
```bash
gh repo clone OutSystems/outsystems-maps-tests ../outsystems-maps-tests
cd ../outsystems-maps-tests
npm run local -- --browsers=chrome --environment=dev --map=web
```

**Manual Testing Resources:**
- Development server: `npm run dev` → http://localhost:3000
- Sample app: https://www.outsystems.com/forge/component-overview/10984/outsystems-maps-sample
- Living documentation: https://outsystemsui.outsystems.com/OutSystemsMapsSample/

## Code Standards

Our Trusted Committers are here to assist you with your contributions.
Feel free to reach out to them for guidance, reviews, and support.
### TypeScript

- **Target:** ES2017, compiled to AMD module
- **Output:** Single file at `dist/OutSystemsMaps.js` (configured in `tsconfig.json`)
- **Style:** Strict ESLint rules enforced (`.eslintrc.json`)

### Naming Conventions

Enforced by ESLint:
- **Exported functions:** `StrictPascalCase`
- **Classes:** `StrictPascalCase`
- **Interfaces:** `IPascalCase` (must start with `I`)
- **Public/protected properties:** `strictCamelCase` (no leading underscore)
- **Private properties:** `_strictCamelCase` (leading underscore required)
- **Public/protected methods:** `strictCamelCase` (no leading underscore)
- **Private methods:** `_strictCamelCase` (leading underscore required)

### Member Ordering

Class members must be alphabetically ordered within groups (enforced by ESLint `@typescript-eslint/member-ordering`):

1. Private fields
2. Protected fields
3. Public fields
4. Constructor
5. Private methods
6. Protected methods
7. Public methods

### Formatting

Enforced by Prettier (`.prettierrc.json`):
- Single quotes
- Semicolons required
- 120 character line width
- Tabs for indentation (width: 4)
- ES5 trailing commas

Run `npm run prettier` to format all files.

## Documentation

Document all public APIs using JSDoc comments. VS Code's "Document This" extension provides templates when you type `/**` above a function.

**Architectural Decision Records:**

Architectural decisions are documented in `docs/adr/`. Follow the template in `docs/adr/ADR-0000-Title-of-ADR.md` when creating new ADRs.

## Common Patterns

**Provider Abstraction:**

When modifying provider-specific behavior:
- Google Maps implementation: `src/Providers/Maps/Google/`
- Leaflet implementation: `src/Providers/Maps/Leaflet/`
- Never import provider types into `src/OSFramework/` or `src/OutSystems/` namespaces

See [ARCHITECTURE.md](../ARCHITECTURE.md) for details on the provider abstraction pattern.

**Adding New Features:**

See [CLAUDE.md](../CLAUDE.md#common-tasks) for step-by-step guidance on adding new map features or providers.

## Support and Communication

**External Contributors:**

Contact the UI Components team via [component support page](https://www.outsystems.com/forge/component-discussions/9909/OutSystems+Maps)

**Internal Contributors:**

Slack channel `#rd-uicomponents-contributors` (business days, 2-3 PM PT)
Trusted Committer: [UI Components team on support rotation](mailto:rd.uicomponents.team@outsystems.com)


## Trusted Committers

Our Trusted Committer will always be the [UI Components team member on support rotation](mailto:rd.uicomponents.team@outsystems.com).


## Trusted Committer Availability Schedule

Our Trusted Committees are available internally on Slack channel _#rd-uicomponents-contributors_ on business days from 2PM-3PM (PT time).


## License

This repos belongs to OutSystems and rights are reserved.
This repository is proprietary to OutSystems. All rights reserved.
102 changes: 102 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# OutSystems Maps Architecture

> **Repository:** outsystems-maps
> **Runtime Environment:** User Browser (JavaScript Library)
> **Last Updated:** 2026-03-04

## Overview

OutSystems Maps is a TypeScript library that compiles to a single AMD module providing a unified API for interactive maps in OutSystems Reactive Web applications. It abstracts Google Maps and Leaflet/OpenStreetMap behind framework interfaces, allowing developers to build map-based features without JavaScript knowledge.

## Architecture Diagram

```mermaid
graph TB
%% This repository
OSMaps["OutSystems Maps Library<br/>Runs on: User Browser"]

%% External services
GoogleMapsAPI[Google Maps JavaScript API<br/>EXTERNAL]
GoogleRoutesAPI[Google Routes API<br/>EXTERNAL]
OpenStreetMap[OpenStreetMap Tiles<br/>EXTERNAL]
OSPlatform[OutSystems Platform<br/>EXTERNAL]

%% Communication flows
OSPlatform -->|JavaScript API calls<br/>Synchronous| OSMaps
OSMaps -->|Load maps SDK, geocoding, places search<br/>Synchronous| GoogleMapsAPI
OSMaps -->|Compute routes<br/>Synchronous| GoogleRoutesAPI
OSMaps -->|Load map tiles<br/>Synchronous| OpenStreetMap

%% Styling
classDef thisRepo fill:#e0f2f1,stroke:#00796b,stroke-width:3px
classDef external fill:#ffe1e1,stroke:#d32f2f,stroke-width:2px,stroke-dasharray: 5 5

class OSMaps thisRepo
class GoogleMapsAPI,GoogleRoutesAPI,OpenStreetMap,OSPlatform external
```

## External Integrations

| External Service | Communication Type | Purpose |
|------------------|-------------------|---------|
| Google Maps JavaScript API | Sync (HTTPS/JavaScript) | Render Google Maps, geocoding, places search, drawing tools, visualization libraries (v3.61+) |
| Google Routes API | Sync (HTTPS) | Calculate directions and route polylines via `routes.googleapis.com/directions/v2:computeRoutes` |
| @googlemaps/markerclusterer | Sync (JavaScript Library) | Create and manage per-zoom-level clusters for large marker volumes (v2.5.3) |
| OpenStreetMap Tiles | Sync (HTTPS) | Render Leaflet-based maps with OSM tile layers from `tile.openstreetmap.org` |
| Leaflet.Draw | Sync (JavaScript Library) | Enable interactive geometry drawing toolbar on Leaflet maps (v1.0.4) |
| Leaflet.Editable | Sync (JavaScript Library) | Enable interactive geometry editing (polylines, polygons) on Leaflet maps (v1.3.0) |
| Leaflet.Path.Drag | Sync (JavaScript Library) | Add dragging capabilities to Leaflet vector features (v1.9.5) |
| Leaflet Routing Machine | Sync (JavaScript Library) | Calculate routes for Leaflet maps with pluggable backends (OSRM, GraphHopper, TomTom) (v3.2.12) |
| OutSystems Platform | Sync (JavaScript API) | Host application that consumes this library via block parameters and events |

## Architectural Tenets

### T1. Provider Abstraction Must Isolate External Dependencies

The library maintains strict separation between provider-agnostic framework code and provider-specific implementations. All map provider logic (Google Maps, Leaflet) is contained within the `Provider.Maps` namespace, while the `OSFramework.Maps` namespace defines interfaces and abstract classes that do not reference concrete provider types. This allows adding new map providers without modifying core framework code.

**Evidence:**
- `src/OSFramework/Maps/OSMap/AbstractMap.ts` (in `AbstractMap` class) - defines common map behavior independent of provider using generic type parameter for provider object
- `src/Providers/Maps/Google/OSMap/OSMap.ts` (in `Map` class) - extends `AbstractMap` with Google-specific implementation for `google.maps.Map`
- `src/Providers/Maps/Leaflet/OSMap/OSMap.ts` (in `Map` class) - extends `AbstractMap` with Leaflet-specific implementation for `L.Map`
- `src/OSFramework/Maps/OSMap/Factory.ts` (in `MapFactory.MakeMap`) - routes provider selection to appropriate factory, the only place in framework layer that references provider namespaces

### T2. Public API Must Return Framework Interfaces, Not Provider Types

The OutSystems-facing API in `OutSystems.Maps.MapAPI` operates exclusively on framework interfaces from the `OSFramework.Maps` namespace. Public methods return interfaces like `IMap` and `IMarker`, never concrete provider classes. This ensures the external API remains stable regardless of which provider is selected at runtime or how provider implementations evolve.

**Evidence:**
- `src/OutSystems/Maps/MapAPI/MapManager.ts` (in `CreateMap`) - returns `OSFramework.Maps.OSMap.IMap` interface, not provider-specific class
- `src/OutSystems/Maps/MapAPI/MapManager.ts` (in `GetMapById`) - all lookup methods return framework interfaces
- `src/OutSystems/Maps/MapAPI/MarkerManager.ts` - marker operations work with framework marker interfaces
- `src/OSFramework/Maps/OSMap/IMap.ts` - interface exposes provider as generic `any` type, not concrete provider class

### T3. Framework Layer Owns Lifecycle and State Management

The `OSFramework` layer controls all component lifecycle (creation, initialization, disposal) and maintains parent-child relationships (maps contain markers, shapes, file layers, heatmap layers, drawing tools). Provider implementations are passive adapters that respond to framework commands via the `build()` and `dispose()` methods defined by shared interfaces, but do not independently manage their lifecycle or relationships.

**Evidence:**
- `src/OSFramework/Maps/OSMap/AbstractMap.ts` (in `build`, `finishBuild`) - framework controls initialization sequence and triggers events
- `src/OSFramework/Maps/OSMap/AbstractMap.ts` (private fields `_markers`, `_shapes`, `_fileLayers`, `_heatmapLayers`) - framework maintains collections of child components
- `src/OSFramework/Maps/Interfaces/IBuilder.ts` and `IDisposable.ts` - shared interfaces that enforce lifecycle contract
- `src/OSFramework/Maps/Marker/AbstractMarker.ts` (in `finishBuild`) - framework triggers initialization event and adds marker to clusterer after provider completes setup

### T4. Configuration Transformation Occurs at Provider Boundaries

User-facing configuration uses OutSystems-friendly structures (JSON strings, integer enums, string coordinates), while provider-specific configuration matches native API requirements (typed objects, native enums, coordinate objects). The `Configuration` namespace implements `getProviderConfig()` to transform between representations without exposing provider details to the framework layer.

**Evidence:**
- `src/OSFramework/Maps/Configuration/AbstractConfiguration.ts` (in `getProviderConfig`) - defines contract for transforming framework config to provider config
- `src/Providers/Maps/Google/Configuration/OSMap/GoogleMapConfig.ts` (in `getProviderConfig`) - transforms framework config to Google Maps API options, including mapId handling for advanced markers
- `src/Providers/Maps/Leaflet/Configuration/OSMap/LeafletMapConfiguration.ts` (in `getProviderConfig`) - transforms framework config to Leaflet API options, sets editable flag for drawing tools
- `src/OutSystems/Maps/MapAPI/MapManager.ts` (in `CreateMap`) - accepts JSON config string from OutSystems, parses and passes to factory

### T5. Events Flow Through Framework Event Managers

All events (map clicks, marker interactions, provider-specific events) are normalized and dispatched through framework event managers that inherit from `AbstractEventsManager`. Provider implementations register native provider events but trigger framework events, which handle subscription management and callback execution consistently across providers. User callbacks never receive provider-specific event objects.

**Evidence:**
- `src/OSFramework/Maps/Event/AbstractEventsManager.ts` - provides centralized event subscription and dispatch mechanism used by all component types
- `src/OSFramework/Maps/Event/OSMap/MapEventsManager.ts` (in `getInstanceOfEventType`) - validates provider event types before creating event instances
- `src/Providers/Maps/Google/OSMap/OSMap.ts` (private method `_setMapEvents`) - Google provider adds listeners via native API but triggers framework events
- `src/Providers/Maps/Leaflet/OSMap/OSMap.ts` (private method `_setMapEvents`) - Leaflet provider converts native events to framework event format before triggering
Loading