The createPopup API opens a popup inside Studio that shows a component of your own. Studio owns the popup and closes it when Studio is destroyed.
Use createPopup when the content belongs with the report: a detail view of something in Studio, or a short form about it. The popup is rendered inside Studio, can be made movable and resizable, and does not block the report, so the user can keep working in Studio while it is open and your application can update it as they do. Use your application's own modal instead when the content must block the rest of the page or must stay open after Studio is destroyed.
import {
AgPopupInstance,
AgReportState,
AgStudioApi,
AgStudioProperties,
createStudio,
enableStudioDevValidations,
} from "ag-studio";
import { PopupContentParams, SportDetail } from "./interfaces.ts";
import { PopupContent } from "./popupContent.ts";
if (process.env.NODE_ENV !== "production") {
// Enable extended validations only for development
enableStudioDevValidations();
}
const initialState: AgReportState = {
pages: [
{
id: "summary",
widgets: {
"medals-by-sport": {
type: "grid",
dataMapping: {
cols: [
{ id: "medals.sport" },
{ id: "medals.gold", aggregation: "sum" },
{ id: "medals.silver", aggregation: "sum" },
{ id: "medals.bronze", aggregation: "sum" },
{ id: "medals.total", aggregation: "sum" },
],
},
format: {
title: {
enabled: true,
text: "Medals by Sport",
},
},
sort: [
{
field: { id: "medals.total", aggregation: "sum" },
direction: "desc",
},
],
},
"medals-by-country": {
type: "bar-chart-grouped",
dataMapping: {
categoryKey: [{ id: "medals.country" }],
valueKey: [{ id: "medals.total", aggregation: "sum" }],
},
format: {
title: {
enabled: true,
text: "Medals by Country",
},
},
sort: [
{
field: { id: "medals.total", aggregation: "sum" },
direction: "desc",
},
],
},
},
widgetLayout: {
"medals-by-sport": { xTrack: 0, yTrack: 0, xSpan: 24, ySpan: 18 },
"medals-by-country": { xTrack: 0, yTrack: 18, xSpan: 24, ySpan: 14 },
},
filter: {
page: [],
widget: {
"medals-by-country": [
{
field: { id: "medals.total" },
view: { expanded: true, viewTypeId: "rank" },
model: { operator: "rank", value: [10, null] },
},
],
},
},
},
],
selectedPageId: "summary",
panels: {
filters: {
collapsed: true,
},
data: {
collapsed: true,
},
edit: {
collapsed: true,
},
},
};
const studioProperties: AgStudioProperties = {
mode: "edit",
initialState,
onStateUpdated: ({ state }) => {
refreshPopup(state);
},
};
let studioApi: AgStudioApi;
let medalRows: any[] = [];
let popup: AgPopupInstance<PopupContentParams> | undefined;
let shownSport: string | undefined;
// The popup computes its detail view from the same rows the report is given.
function keepMedalRows(rows: any[]): any[] {
medalRows = rows;
// A popup opened while the rows were loading has an empty detail view, so fill it in now.
if (popup) {
popup.refresh(buildParams(shownSport, popup.closePopup));
}
return rows;
}
function getSportDetail(sport: string): SportDetail {
const detail: SportDetail = {
sport,
gold: 0,
silver: 0,
bronze: 0,
total: 0,
athletes: 0,
topCountries: [],
};
const athletes = new Set<string>();
const countryTotals = new Map<string, number>();
for (const row of medalRows) {
if (row.sport !== sport) {
continue;
}
detail.gold += row.gold;
detail.silver += row.silver;
detail.bronze += row.bronze;
detail.total += row.total;
athletes.add(row.athlete);
countryTotals.set(
row.country,
(countryTotals.get(row.country) ?? 0) + row.total,
);
}
detail.athletes = athletes.size;
detail.topCountries = [...countryTotals]
.map(([country, total]) => ({ country, total }))
.sort((a, b) => b.total - a.total)
.slice(0, 3);
return detail;
}
function getTopSport(): string {
const sportTotals = new Map<string, number>();
for (const row of medalRows) {
sportTotals.set(row.sport, (sportTotals.get(row.sport) ?? 0) + row.total);
}
return [...sportTotals].sort((a, b) => b[1] - a[1])[0]?.[0] ?? "";
}
// A row clicked in the Medals by Sport grid is published as that widget's cross-filter selection.
// Ctrl/Cmd-click adds to the selection, so the most recently added sport is the last combination.
function getSelectedSport(state: AgReportState): string | undefined {
const page = state.pages.find((p) => p.id === state.selectedPageId);
const selection = page?.crossFilter?.values?.["medals-by-sport"]?.find(
(s) => s.type === "multiValue",
);
if (selection?.type !== "multiValue") {
return undefined;
}
const combination = selection.values[selection.values.length - 1] ?? [];
return combination.find((pair) => pair.field.id === "medals.sport")?.value;
}
function buildParams(
sport: string | undefined,
closePopup: () => void,
): PopupContentParams {
return {
detail: getSportDetail(sport ?? getTopSport()),
hint: sport
? undefined
: "Select a sport in Medals by Sport to see its details.",
closePopup,
};
}
function openPopup() {
if (popup) {
return;
}
shownSport = getSelectedSport(studioApi.getState());
popup = studioApi.createPopup<PopupContentParams>({
comp: PopupContent,
title: "Sport Details",
movable: true,
resizable: true,
width: 320,
height: 360,
compParams: ({ closePopup }) => buildParams(shownSport, closePopup),
closedCallback: () => {
popup = undefined;
},
});
}
function refreshPopup(state: AgReportState) {
if (!popup) {
return;
}
const sport = getSelectedSport(state);
if (sport === shownSport) {
return;
}
shownSport = sport;
popup.refresh(buildParams(sport, popup.closePopup));
}
// setup Studio after the page has finished loading
const studioDiv = document.querySelector<HTMLElement>("#myStudio")!;
studioApi = createStudio(studioDiv, studioProperties);
fetch("https://www.ag-grid.com/studio/example-assets/olympic-winners.json")
.then((response) => response.json())
.then((data) =>
studioApi.setProperty("data", {
sources: [{ id: "medals", data: keepMedalRows(data) }],
}),
);
(window as any).openPopup = openPopup;
if (typeof window !== "undefined") {
// Attach external event handlers to window so they can be called from index.html
(<any>window).openPopup = openPopup;
}
The example above opens a Sport Details popup from a button outside Studio. The popup shows the medal counts, number of athletes and top countries for one sport. Click a row in Medals by Sport and the open popup updates to show that sport. Until a sport is selected, or when the selection is cleared, the popup shows the sport with the most medals. The popup's component has its own Close button.
Opening a Popup Copy Link
Call createPopup with the component to show and the params to pass to it:
api.createPopup({
comp: PopupContent,
compParams: { detail: getSportDetail('Swimming') },
title: 'Sport Details',
});comp is a class implementing AgTypeScriptComponent. Studio calls its init method with compParams, then shows the element returned by getGui.
Component names registered on the components property cannot be used as comp.
The popup is rendered inside Studio, or inside the element set on the popupParent property.
Popup Options Copy Link
titlesets the text shown in the title bar.movablelets the user drag the popup by its title bar.resizablelets the user resize the popup by dragging its edges and corners.maximizableadds a title bar button that expands the popup to fill Studio.
movable, resizable and maximizable are off by default.
The popup opens at 600 by 400 pixels and does not grow to fit its content. Set width and height when your component needs a different size. When the popup is resizable, minWidth and minHeight set how small the user can make it.
Closing the Popup Copy Link
The popup stays open when the user clicks outside it, and the rest of the page stays interactive. It closes when:
- The user presses Escape while focus is inside the popup.
- The user clicks the close button in the title bar.
closePopupis called, either on the instance returned bycreatePopupor from the component.- Studio is destroyed.
To let the component close the popup, for example from its own button, supply compParams as a callback. The callback receives a closePopup function, which you can pass on to the component:
api.createPopup({
comp: PopupContent,
compParams: ({ closePopup }) => ({ detail: getSportDetail('Swimming'), closePopup }),
title: 'Sport Details',
});closedCallback runs once each time the popup closes, however it was closed. It receives the keyboard event when Escape closed the popup, and undefined otherwise. The example uses it to forget the closed instance, so the next click on Open Popup opens a new one.
Several popups can be open at the same time. Each one stays open until it is closed.
Updating the Popup Copy Link
createPopup returns an instance with two methods:
refresh(params)passes new params to the component.closePopup()closes the popup.
To receive the new params, implement the optional refresh(params) method on the component. Without it, refresh has no effect.
The example keeps the instance and refreshes the popup from onStateUpdated when the sport selected in Medals by Sport changes. refresh takes the params themselves, not a callback. If the component needs closePopup, pass the instance's closePopup in the new params:
const popup = api.createPopup({
comp: PopupContent,
compParams: ({ closePopup }) => ({ detail: getSportDetail('Swimming'), closePopup }),
title: 'Sport Details',
});
popup.refresh({ detail: getSportDetail('Athletics'), closePopup: popup.closePopup }); API Reference Copy Link
Open a popup showing a component of your own. The popup is rendered inside Studio, or inside
the element given by the popupParent property, and Studio owns it: the popup closes and
your component is destroyed when Studio is destroyed.
The popup stays open until something closes it: clicking outside it leaves it open, nothing
is dimmed, and the rest of the page stays interactive. The Escape key and the close button in
its title bar close it. To close it from inside your component, supply compParams as a
callback and call the closePopup() function it receives.
closedCallback runs once each time the popup closes, whichever of those closed it.
The popup is sized as it opens and does not grow to fit its content, so set width and
height when your component needs more or less room than the default 600 by 400 pixels.
Several popups can be open at once, each staying open until it is closed. |
Properties available on the AgCreatePopupParams<TParams> interface.
The component shown inside the popup. Either a TypeScript class implementing AgTypeScriptComponent, or a component of the framework you are using (React, Angular or Vue). Component names registered on the components property are not supported here.
|
The parameters passed to the component when it is created. Either the parameters themselves, or a callback that returns them. The callback is given a closePopup() function, so that the component can close the popup itself, for example from its own Close button.
|
The text shown in the popup title bar.
|
Whether the user can move the popup by dragging its title bar. |
Whether the title bar offers a button that expands the popup to fill Studio. |
Whether the user can resize the popup by dragging its edges and corners. |
The width of the popup in pixels when it opens. The popup is sized as it opens and does not grow to fit its content, so set this when your component needs more or less room than the default. The user can still change the size afterwards if resizable or maximizable is enabled. |
The height of the popup in pixels when it opens. The popup is sized as it opens and does not grow to fit its content, so set this when your component needs more or less room than the default. The user can still change the size afterwards if resizable or maximizable is enabled. |
The smallest width in pixels the user can resize the popup down to. The default never exceeds width, so a popup opened narrower than 250 pixels is not forced back up to 250 the first time it is resized. |
The smallest height in pixels the user can resize the popup down to. The default never exceeds height, so a popup opened shorter than 250 pixels is not forced back up to 250 the first time it is resized. |
Called once when the popup closes, whatever closed it. The argument is the keyboard event when the Escape key closed the popup, and undefined otherwise: the title bar close button, closePopup() and Studio being destroyed all report no event.
|