Skip to content
Open
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
51 changes: 51 additions & 0 deletions packages/graph-explorer/src/core/styling/sampleStyles.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, test } from "vitest";

import { createEdgeType, createVertexType } from "@/core/entities";
import { parseStylingFile } from "@/core/styling";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

function asFile(contents: string, name: string): File {
return new File([contents], name, { type: "application/json" });
}

describe("air_routes sample styles file", () => {
test("parses and differentiates air routes vertex and edge types", async () => {
const sampleStylesPath = join(
__dirname,
"../../../../../samples/air_routes/styles.json",
);
const sampleStyles = readFileSync(sampleStylesPath, "utf-8");

const parsed = await parseStylingFile(asFile(sampleStyles, "styles.json"));

const airport = parsed.vertexStyles.get(createVertexType("airport"));
const country = parsed.vertexStyles.get(createVertexType("country"));
const continent = parsed.vertexStyles.get(createVertexType("continent"));
const route = parsed.edgeStyles.get(createEdgeType("route"));
const contains = parsed.edgeStyles.get(createEdgeType("contains"));

expect(airport).toBeDefined();
expect(country).toBeDefined();
expect(continent).toBeDefined();
expect(route).toBeDefined();
expect(contains).toBeDefined();

expect(airport!.color).not.toBe(country!.color);
expect(airport!.color).not.toBe(continent!.color);
expect(country!.color).not.toBe(continent!.color);

expect(
new Set([airport!.shape, country!.shape, continent!.shape]).size,
).toBe(3);

expect(airport!.iconUrl).toMatch(/^lucide:/);

expect(route!.lineColor).not.toBe(contains!.lineColor);
expect(route!.lineStyle).not.toBe(contains!.lineStyle);
});
});
14 changes: 14 additions & 0 deletions samples/air_routes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,18 @@ This sample uses Gremlin Server 3.8 as the database pre-loaded with the [air rou
```
4. Open the browser and navigate to: [http://localhost:8080/explorer](http://localhost:8080/explorer)

## Loading the sample styles

1. Launch the sample and open [http://localhost:8080/explorer](http://localhost:8080/explorer).
2. Open **Settings** from the left sidebar.
3. Go to the **Styles** section.
4. Click **Load styles**.
5. Select the `samples/air_routes/styles.json` file from this repository on your machine. If you copied only `docker-compose.yaml`, download `styles.json` from the repository first: https://github.com/aws/graph-explorer/blob/main/samples/air_routes/styles.json
6. In the selective import modal, keep all five styles selected (or pick only the ones you want), then click **Load selected**.
7. Return to the **Graph** or **Schema** view. Airports, countries, and continents now render with distinct shapes, colors, and icons; routes and contains edges also have distinct line styles.

> **Note:** The browser file picker reads from your host filesystem, not the Docker container. No volume mount is needed — just ensure `styles.json` is available on your machine before loading it.

To customize the styles or create your own, see [STYLING-REFERENCE.md](./STYLING-REFERENCE.md) for the complete property reference and available values.

Once it is running, the [Getting Started tutorial](../../docs/getting-started/README.md) walks you through exploring the air routes data step by step.
168 changes: 168 additions & 0 deletions samples/air_routes/STYLING-REFERENCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
# Styling Reference

This document describes the styling file format used by Graph Explorer. The sample `styles.json` in this directory demonstrates a subset of these properties.

## File format

Styling files use the File Envelope format with `kind: "styling-export"` and `version: 1`. The payload is a JSON object with two top-level keys: `vertices` and `edges`.

## Vertex style properties

Each vertex type can have the following optional properties:

- `color` (string): Hex color code, e.g., `"#128EE5"`
- `shape` (string): Node shape (see Shape values below)
- `icon` (string): Icon reference (see Icon formats below)
- `iconImageType` (string): MIME type for the icon, e.g., `"image/svg+xml"`
- `displayLabel` (string): Override label text
- `displayNameAttribute` (string): Attribute to use as the label
- `longDisplayNameAttribute` (string): Attribute to use as the description
- `backgroundOpacity` (number): Opacity from 0 to 1
- `borderWidth` (number): Border width in pixels
- `borderColor` (string): Hex color code for the border
- `borderStyle` (string): Border line style (see Line style values below)

**Example:**

```json
{
"airport": {
"color": "#E63946",
"shape": "round-rectangle",
"icon": "lucide:plane",
"iconImageType": "image/svg+xml",
"displayNameAttribute": "code",
"longDisplayNameAttribute": "desc"
}
}
```

## Edge style properties

Each edge type can have the following optional properties:

- `lineColor` (string): Hex color code, e.g., `"#264653"`
- `lineThickness` (number): Line width in pixels
- `lineStyle` (string): Line style (see Line style values below)
- `sourceArrowStyle` (string): Arrow style at the source (see Arrow style values below)
- `targetArrowStyle` (string): Arrow style at the target (see Arrow style values below)
- `displayLabel` (string): Override label text
- `displayNameAttribute` (string): Attribute to use as the label
- `labelColor` (string): Hex color code for the label text
- `labelBackgroundOpacity` (number): Label background opacity from 0 to 1
- `labelBorderColor` (string): Hex color code for the label border
- `labelBorderStyle` (string): Label border line style (see Line style values below)
- `labelBorderWidth` (number): Label border width in pixels

**Example:**

```json
{
"route": {
"lineColor": "#264653",
"lineThickness": 2,
"lineStyle": "solid",
"targetArrowStyle": "triangle",
"displayNameAttribute": "dist"
}
}
```

## Icon formats

Icons can be specified in two formats:

### Lucide icons (recommended)

Use the `lucide:` prefix followed by the icon name:

```json
{
"icon": "lucide:plane",
"iconImageType": "image/svg+xml"
}
```

Lucide icons are bundled with Graph Explorer and always render. See [Lucide icons](https://lucide.dev/icons/) for the full list.

### Base64 data URIs

Embed an image as a base64-encoded data URI:

```json
{
"icon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...",
"iconImageType": "image/png"
}
```

The subtype can be any image type (e.g., `image/png`, `image/jpeg`, `image/svg+xml`).

**Security note:** Remote URLs (e.g., `https://example.com/icon.png`) are rejected for security reasons. Only Lucide references and base64 data URIs are allowed.

## Shape values

The following shapes are available:

- `rectangle`
- `roundrectangle`
- `ellipse`
- `triangle`
- `pentagon`
- `hexagon`
- `heptagon`
- `octagon`
- `star`
- `barrel`
- `diamond`
- `vee`
- `rhomboid`
- `tag`
- `round-rectangle`
- `round-triangle`
- `round-diamond`
- `round-pentagon`
- `round-hexagon`
- `round-heptagon`
- `round-octagon`
- `round-tag`
- `cut-rectangle`
- `concave-hexagon`

## Line style values

The following line styles are available:

- `solid`
- `dashed`
- `dotted`

## Arrow style values

The following arrow styles are available:

- `triangle`
- `triangle-tee`
- `circle-triangle`
- `triangle-cross`
- `triangle-backcurve`
- `tee`
- `vee`
- `square`
- `circle`
- `diamond`
- `none`

## All properties are optional

You only need to specify the properties you want to override. Unspecified properties use the app defaults. For example, this is a valid minimal entry:

```json
{
"airport": {
"color": "#E63946"
}
}
```

This sets only the color; all other properties (shape, icon, labels, etc.) use the defaults.
52 changes: 52 additions & 0 deletions samples/air_routes/styles.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"meta": {
"kind": "styling-export",
"version": 1,
"timestamp": "2026-08-17T00:00:00.000Z",
"source": "Graph Explorer",
"sourceVersion": "3.2.2"
},
"data": {
"vertices": {
"airport": {
"displayNameAttribute": "code",
"longDisplayNameAttribute": "desc",
"icon": "lucide:plane",
"iconImageType": "image/svg+xml",
"color": "#e66412",
"shape": "round-rectangle"
},
"country": {
"displayNameAttribute": "code",
"longDisplayNameAttribute": "desc",
"icon": "lucide:flag",
"iconImageType": "image/svg+xml",
"color": "#e612b8",
"shape": "hexagon"
},
"continent": {
"displayNameAttribute": "code",
"longDisplayNameAttribute": "desc",
"icon": "lucide:globe",
"iconImageType": "image/svg+xml",
"color": "#128EE5",
"shape": "diamond"
}
},
"edges": {
"route": {
"displayNameAttribute": "dist",
"lineColor": "#0c4a6e",
"lineThickness": 2,
"lineStyle": "solid",
"targetArrowStyle": "triangle"
},
"contains": {
"lineColor": "#b3b3b3",
"lineThickness": 1,
"lineStyle": "dotted",
"targetArrowStyle": "triangle"
}
}
}
}
Loading