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
13 changes: 10 additions & 3 deletions packages/terriajs/lib/ReactViews/Preview/DataPreview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import MappablePreview from "./MappablePreview";
import WarningBox from "./WarningBox";
import Styles from "./data-preview.scss";
import Icon from "../../Styled/Icon";
import { getCustomPreviewRenderer } from "./PreviewRenderers";

/**
* Data preview section, for the preview map see DataPreviewMap
Expand Down Expand Up @@ -61,9 +62,11 @@ class DataPreview extends Component {
</div>
);
} else if (previewed && previewed.isMappable) {
const MappablePreviewRenderer =
getCustomPreviewRenderer(previewed.type) ?? MappablePreview;
return (
<div className={Styles.previewInner}>
<MappablePreview
<MappablePreviewRenderer
previewed={previewed}
terria={this.props.terria}
viewState={this.props.viewState}
Expand All @@ -90,17 +93,21 @@ class DataPreview extends Component {
</div>
);
} else if (previewed && CatalogFunctionMixin.isMixedInto(previewed)) {
const FunctionRenderer =
getCustomPreviewRenderer(previewed.type) ?? InvokeFunction;
return (
<InvokeFunction
<FunctionRenderer
previewed={previewed}
terria={this.props.terria}
viewState={this.props.viewState}
/>
);
} else if (previewed && previewed.isGroup) {
const GroupPreviewRenderer =
getCustomPreviewRenderer(previewed.type) ?? GroupPreview;
return (
<div className={Styles.previewInner}>
<GroupPreview
<GroupPreviewRenderer
previewed={previewed}
terria={this.props.terria}
viewState={this.props.viewState}
Expand Down
54 changes: 54 additions & 0 deletions packages/terriajs/lib/ReactViews/Preview/PreviewRenderers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { observable, runInAction } from "mobx";
import { FC } from "react";
import CatalogMemberMixin from "../../ModelMixins/CatalogMemberMixin";

/**
* Custom previewe renderer type
*/
export type PreviewRendererType = FC<{
previewed: CatalogMemberMixin.Instance;
}>;

/**
* Custom preview renderer registry.
*/
const PreviewRenderers = observable(new Map<string, PreviewRendererType>());

/**
* Add a custom preview renderer for the given catalog item type.
*
* Preview renderers, render the `About data` view.
*
* @param type Catalog item type
* @param renderer Preview renderer component
*/
export function addCustomPreviewRenderer(
type: string,
renderer: PreviewRendererType
) {
runInAction(() => PreviewRenderers.set(type, renderer));
}

/**
* Remove custom preview renderer for the given catalog item type.
*
* The type falls back to its built-in preview renderer.
*
* @param type Catalog item type
* @returns `true` if a custom renderer was registered for the type, `false` otherwise.
*/
export function removeCustomPreviewRenderer(type: string): boolean {
return runInAction(() => PreviewRenderers.delete(type));
}

/**
* Get registered custom previewer renderer for the given catalog item type
*
* @param type Catalog item type
* @returns Registered custom renderer or `undefined` if no custom renderer is registered for the type.
*/
export function getCustomPreviewRenderer(
type: string
): PreviewRendererType | undefined {
return PreviewRenderers.get(type);
}
135 changes: 135 additions & 0 deletions packages/terriajs/test/ReactViews/Preview/DataPreviewSpec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { screen } from "@testing-library/react";
import { runInAction } from "mobx";
import CatalogMemberMixin from "../../../lib/ModelMixins/CatalogMemberMixin";
import CatalogGroup from "../../../lib/Models/Catalog/CatalogGroup";
import GeoJsonCatalogItem from "../../../lib/Models/Catalog/CatalogItems/GeoJsonCatalogItem";
import WebProcessingServiceCatalogFunction from "../../../lib/Models/Catalog/Ows/WebProcessingServiceCatalogFunction";
import CommonStrata from "../../../lib/Models/Definition/CommonStrata";
import Terria from "../../../lib/Models/Terria";
import ViewState from "../../../lib/ReactViewModels/ViewState";
import DataPreview from "../../../lib/ReactViews/Preview/DataPreview";
import {
PreviewRendererType,
addCustomPreviewRenderer,
removeCustomPreviewRenderer
} from "../../../lib/ReactViews/Preview/PreviewRenderers";
import { renderWithContexts } from "../withContext";

const CustomRenderer: PreviewRendererType = ({
previewed
}: {
previewed: CatalogMemberMixin.Instance;
}) => <div>Custom renderer for {previewed.name}</div>;

describe("DataPreview", function () {
describe("preview rendering", function () {
let terria: Terria;
let viewState: ViewState;
let mappableItem: GeoJsonCatalogItem;
let functionItem: WebProcessingServiceCatalogFunction;
let groupItem: CatalogGroup;

beforeEach(function () {
terria = new Terria({ baseUrl: "./" });
viewState = new ViewState({ terria });

mappableItem = new GeoJsonCatalogItem("test-geojson", terria);
functionItem = new WebProcessingServiceCatalogFunction(
"test-wps",
terria
);
groupItem = new CatalogGroup("test-group", terria);

runInAction(() => {
mappableItem.setTrait(CommonStrata.definition, "name", "Test GeoJSON");
// Avoid spinning up the preview map, which isn't what we're testing here.
mappableItem.setTrait(CommonStrata.definition, "disablePreview", true);
functionItem.setTrait(CommonStrata.definition, "name", "Test WPS");
groupItem.setTrait(CommonStrata.definition, "name", "Test Group");
});
});

// The renderer registry is module global, so leave it as we found it.
afterEach(function () {
removeCustomPreviewRenderer(mappableItem.type);
removeCustomPreviewRenderer(functionItem.type);
removeCustomPreviewRenderer(groupItem.type);
});

function renderPreview(previewed: CatalogMemberMixin.Instance) {
return renderWithContexts(
<DataPreview
terria={terria}
viewState={viewState}
previewed={previewed}
/>,
viewState
);
}

it("renders the built-in renderer when no custom renderer is registered", function () {
// MappablePreview, InvokeFunction and GroupPreview all title the preview
// with the catalog member name.
const mappablePreview = renderPreview(mappableItem);
expect(
screen.getByRole("heading", { name: "Test GeoJSON" })
).toBeVisible();
expect(screen.queryByText(/Custom renderer for/)).toBeNull();
mappablePreview.unmount();

const functionPreview = renderPreview(functionItem);
expect(screen.getByRole("heading", { name: "Test WPS" })).toBeVisible();
expect(screen.queryByText(/Custom renderer for/)).toBeNull();
functionPreview.unmount();

const groupPreview = renderPreview(groupItem);
expect(screen.getByRole("heading", { name: "Test Group" })).toBeVisible();
expect(screen.queryByText(/Custom renderer for/)).toBeNull();
groupPreview.unmount();
});

it("renders the custom renderer registered for the catalog member type", function () {
addCustomPreviewRenderer(mappableItem.type, CustomRenderer);
addCustomPreviewRenderer(functionItem.type, CustomRenderer);
addCustomPreviewRenderer(groupItem.type, CustomRenderer);

const mappablePreview = renderPreview(mappableItem);
expect(
screen.getByText("Custom renderer for Test GeoJSON")
).toBeVisible();
expect(
screen.queryByRole("heading", { name: "Test GeoJSON" })
).toBeNull();
mappablePreview.unmount();

const functionPreview = renderPreview(functionItem);
expect(screen.getByText("Custom renderer for Test WPS")).toBeVisible();
expect(screen.queryByRole("heading", { name: "Test WPS" })).toBeNull();
functionPreview.unmount();

const groupPreview = renderPreview(groupItem);
expect(screen.getByText("Custom renderer for Test Group")).toBeVisible();
expect(screen.queryByRole("heading", { name: "Test Group" })).toBeNull();
groupPreview.unmount();
});

it("passes the preview props to the custom renderer", function () {
let receivedProps: Record<string, unknown> | undefined;
const PropCapturingRenderer: PreviewRendererType = (props) => {
receivedProps = props;
return null;
};
addCustomPreviewRenderer(mappableItem.type, PropCapturingRenderer);

renderPreview(mappableItem);

expect(receivedProps).toEqual(
jasmine.objectContaining({
previewed: mappableItem,
terria,
viewState
})
);
});
});
});
Loading