# Databricks AI / BI browser client

Embed Databricks AI/BI dashboards into your external web application. This client library renders a
published Databricks dashboard inside any DOM element, with per-user data filtering and audit
support via scoped access tokens.

## Documentation

For full setup instructions — including how to create a service principal, configure permissions,
perform the token exchange, and filter data per user — see the official docs:

Embedding for external users → [AWS](https://docs.databricks.com/en/dashboards/embed.html) |
[Azure](https://learn.microsoft.com/en-us/azure/databricks/dashboards/embed) |
[GCP](https://docs.gcp.databricks.com/en/dashboards/embed.html)

The docs include working example applications in both Python and Node.js.

## Usage

### Basic Usage

```ts
const dashboard = new DatabricksDashboard({
  workspaceId: '1234567890123456',
  instanceUrl: 'https://my-databricks-instance.com',
  container: document.getElementById('dashboard-container'),
  dashboardId: 'abcdedf123456',
  pageId: 'ab12cd34',
  token: tokenThatCanBeSeenByViewers,
});

dashboard.initialize();
```

### Handling Token Expiration for Seamless Viewer Experience

To support long-running dashboard, you can pass a `getNewToken` function that automatically
refreshes the token when it is close to expiration.

```ts
const dashboard = new DatabricksDashboard({
  // ...other required options
  getNewToken: async () => {
    // Return a string literal token value to the API
  },
});
```

### Set up Color Scheme

You can configure the embedded dashboard's color scheme using the `colorScheme` option.

The `colorScheme` option maps to the
[CSS `color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/color-scheme) property, which
allows the embedded dashboard to respect or override the user's light or dark preference.

```ts
const dashboard = new DatabricksDashboard({
  // ...other required options
  colorScheme: 'light dark', // or "light", or "dark"
});
```

### Programmatic Navigation

You can navigate between dashboards or pages without reloading the iframe using the `navigate()`
method. This provides a smooth transition for users switching between different dashboards.

```ts
const dashboard = new DatabricksDashboard({
  // ...other required options
});

dashboard.initialize();

// Navigate to a different dashboard
await dashboard.navigate({ dashboardId: 'xyz789' });

// Navigate to a specific page within a dashboard
await dashboard.navigate({ dashboardId: 'xyz789', pageId: 'page123' });
```

**Important Notes:**

- The `navigate()` method can only be called after the dashboard has been initialized and loaded
- The method will throw an error if called before the dashboard is ready or after it has been
  destroyed

### Filter Controls

Read and control the embedded dashboard's filters from your host page. Filters are addressed by
their **URL identifier** (`FilterState.id`) - the stable handle you should persist and pass back,
not the human-readable `displayName`.

<details>
<summary>Finding and setting a filter's ID</summary>

Call `getFilters()` and read each filter's `id`. Use its `displayName` (the widget's title) to tell
which filter is which. IDs are of the form `<pageId>~<widgetId>` and do not change when the
dashboard is edited. They only change when an author updates them explicitly. See the public docs
for more info
([AWS](https://docs.databricks.com/aws/en/dashboards/manage/filters/#filters-in-published-dashboard-urls)
| [Azure](https://learn.microsoft.com/en-us/azure/databricks/dashboards/manage/filters/#filter-urls)
| [GCP](https://docs.databricks.com/gcp/en/dashboards/manage/filters/#filters-in-published-dashboard-urls))

To provide a human-readable ID, open a dashboard in draft mode, select a filter widget (or a page)
and use the **Update URL Identifier** button. The value you set there is exactly what `getFilters()`
returns and what `setFilters()` expects.

</details>

#### Detecting filter readiness

`onFiltersReady` fires at most once, when filter controls become available, with the initial filter
snapshot. Use this to determine when the embedded dashboard has finished rendering and can receive
filter updates. Late subscribers still fire. It never fires if the embedded dashboard never becomes
ready.

This isn't strictly necessary to know when it's safe to call `getFilters()` or `setFilters()`. Those
methods will buffer early requests until the embedded dashboard is ready.

```ts
dashboard.onFiltersReady(({ filters }) => {
  renderFilterControls(filters);
});
```

#### Reading filters

`getFilters()` returns the current state of every filter - its `id`, `widgetType`, `displayName`,
whether it is at its author-configured default (`isDefault`), and its current `value`.

```ts
const filters = await dashboard.getFilters();
```

#### Setting filters

`setFilters()` writes one or more values, each targeting a filter by `id`. Alongside concrete
values, some filters accept these built-in sentinels:

- `{ all: true }`
- `{ none: true }`
- `{ default: true }` (reset to the author-configured default)

```ts
await dashboard.setFilters([
  { id: 'sales~region', type: 'single-select', value: 'US' },
  { id: 'sales~category', type: 'multi-select', values: ['Books', 'Toys'] },
  { id: 'sales~date', type: 'date-range', min: { default: true }, max: { default: true } },
]);
```

On dashboards configured for **apply mode** (selections staged until the viewer clicks "Apply"),
pass `{ apply: false }` to stage values instead of committing them, then commit or discard the
staged set:

```ts
await dashboard.setFilters([{ id: 'sales~region', type: 'single-select', value: 'US' }], {
  apply: false,
});
await dashboard.applyStagedFilters(); // or dashboard.cancelStagedFilters()
```

`applyStagedFilters()` and `cancelStagedFilters()` are no-ops (still resolving) on dashboards that
are not in apply mode.

#### Reacting to changes

`onFiltersChanged` fires whenever a filter's value changes (from viewer interaction, an applied
`setFilters` call, or a URL restore) with a snapshot of every filter. It does not fire on subscribe.
Use `onFiltersReady` or `getFilters()` for the initial state.

```ts
const unsubscribe = dashboard.onFiltersChanged((filters) => {
  console.log('filters changed', filters);
});
// later: unsubscribe();
```
