Skip to content

Commit

Permalink
feat: follow card permission and editor support (#1394)
Browse files Browse the repository at this point in the history
  • Loading branch information
vivzhang authored Jan 26, 2024
1 parent 3137188 commit 23feb30
Show file tree
Hide file tree
Showing 9 changed files with 263 additions and 5 deletions.
16 changes: 15 additions & 1 deletion packages/common/src/core/schemas/getEditorConfig.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { EntityEditorType, IEditorConfig, StatCardEditorType } from "./types";
import {
EntityEditorType,
IEditorConfig,
StatCardEditorType,
FollowCardEditorType,
} from "./types";
import { IArcGISContext } from "../../ArcGISContext";
import {
EditorOptions,
EntityEditorOptions,
IStatCardEditorOptions,
CardEditorOptions,
} from "./internal/EditorOptions";
import { getEditorSchemas } from "./internal/getEditorSchemas";
import { EditorType } from "./types";
Expand Down Expand Up @@ -41,6 +47,14 @@ export async function getEditorConfig(
context: IArcGISContext
): Promise<IEditorConfig>;

// Follow card editor overload
export async function getEditorConfig(
i18nScope: string,
type: FollowCardEditorType,
options: CardEditorOptions,
context: IArcGISContext
): Promise<IEditorConfig>;

// General function
export async function getEditorConfig(
i18nScope: string,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { IArcGISContext } from "../../../../ArcGISContext";
import { CardEditorOptions } from "../EditorOptions";
import { IUiSchema } from "../../types";

/**
* @private
* Exports the uiSchema of the stat card
* @returns
*/
export const buildUiSchema = (
i18nScope: string,
config: CardEditorOptions,
context: IArcGISContext
): IUiSchema => {
return {
type: "Layout",
elements: [
{
label: "Select Entity to Follow",
scope: "/properties/followedItemId",
type: "Control",
options: {
control: "hub-field-input-gallery-picker",
targetEntity: "item",
catalogs: [
{
schemaVersion: 1,
title: "Esri",
scopes: {
item: {
targetEntity: "item",
filters: [
{
predicates: [
{
type: "Hub Project",
},
],
},
],
},
},
},
],
},
},
{
label: "Call-to-Action",
scope: "/properties/callToAction",
type: "Control",
},
{
label: "Alignment",
scope: "/properties/callToActionAlignment",
type: "Control",
options: {
control: "hub-field-input-alignment",
},
},
{
label: "Button State",
scope: "/properties/followStateText",
type: "Control",
options: {
control: "hub-field-input-input",
type: "textarea",
},
},
{
scope: "/properties/unfollowStateText",
type: "Control",
options: {
control: "hub-field-input-input",
type: "textarea",
},
},
{
label: "Button Alignment",
scope: "/properties/callToActionAlignment",
type: "Control",
options: {
control: "hub-field-input-alignment",
},
},
{
label: "Button Style",
scope: "/properties/buttonStyle",
type: "Control",
options: {
control: "hub-field-input-select",
labels: ["Solid Background", "Ounline"],
},
},
],
};
};
44 changes: 44 additions & 0 deletions packages/common/src/core/schemas/internal/follow/FollowSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { IConfigurationSchema } from "../../types";

export const FollowSchema: IConfigurationSchema = {
type: "object",
required: [],
properties: {
itemGalleryWithDefault: {
type: "array",
items: {
type: "string",
},
maxItems: 10,
default: [],
},
callToAction: {
type: "string",
default:
"By following this site you will get updates about new events, surveys, and tools that you can use to help us achieve our goals.",
},
callToActionAlignment: {
enum: ["start", "center", "end"],
default: "center",
type: "string",
},
followStateText: {
type: "string",
default: "Follow",
},
unfollowStateText: {
type: "string",
default: "Unfollow",
},
buttonAlignment: {
enum: ["start", "center", "end"],
default: "center",
type: "string",
},
buttonStyle: {
type: "string",
enum: ["solid", "outline"],
default: "solid",
},
},
};
33 changes: 30 additions & 3 deletions packages/common/src/core/schemas/internal/getCardEditorSchemas.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { CardEditorType, IEditorConfig, StatCardEditorType } from "../types";
import {
CardEditorType,
IEditorConfig,
StatCardEditorType,
FollowCardEditorType,
} from "../types";
import { getCardType } from "./getCardType";
import { filterSchemaToUiSchema } from "./filterSchemaToUiSchema";
import { CardEditorOptions } from "./EditorOptions";
Expand Down Expand Up @@ -29,12 +34,14 @@ export async function getCardEditorSchemas(

let schema;
let uiSchema;
let schemaPromise;
let uiSchemaPromise;

switch (cardType) {
case "stat":
// get correct module
const schemaPromise = import("./metrics/MetricSchema");
const uiSchemaPromise = {
schemaPromise = import("./metrics/MetricSchema");
uiSchemaPromise = {
"hub:card:stat": () => import("./metrics/StatCardUiSchema"),
}[type as StatCardEditorType];

Expand All @@ -51,6 +58,26 @@ export async function getCardEditorSchemas(
}
);
break;
case "follow":
// get correct module
schemaPromise = import("./follow/FollowSchema");
uiSchemaPromise = {
"hub:card:follow": () => import("./follow/FollowCardUiSchema"),
}[type as FollowCardEditorType];

// Allow imports to run in parallel
await Promise.all([schemaPromise, uiSchemaPromise()]).then(
([schemaModuleResolved, uiSchemaModuleResolved]) => {
const { FollowSchema: FollowSchema } = schemaModuleResolved;
schema = cloneObject(FollowSchema);
uiSchema = uiSchemaModuleResolved.buildUiSchema(
i18nScope,
options,
context
);
}
);
break;
}
// filter out properties not used in uiSchema
schema = filterSchemaToUiSchema(schema, uiSchema);
Expand Down
12 changes: 11 additions & 1 deletion packages/common/src/core/schemas/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,22 @@ export const validEntityEditorTypes = [
export type StatCardEditorType = (typeof validStatCardEditorTypes)[number];
export const validStatCardEditorTypes = ["hub:card:stat"] as const;

/** Defines the possible editor type values for a follow card. These
* correspond to the supported/defined uiSchema configurations. This should
* have its own signature in the getEditorConfig function.
*/
export type FollowCardEditorType = (typeof validFollowCardEditorTypes)[number];
export const validFollowCardEditorTypes = ["hub:card:follow"] as const;

/**
* Defines the possible editor type values for any layout card. These
* correspond to the supported/defined uiSchema configurations for cards.
*/
export type CardEditorType = (typeof validCardEditorTypes)[number];
export const validCardEditorTypes = [...validStatCardEditorTypes] as const;
export const validCardEditorTypes = [
...validStatCardEditorTypes,
...validFollowCardEditorTypes,
] as const;

/**
* All supported editor types - these "map"
Expand Down
8 changes: 8 additions & 0 deletions packages/common/src/permissions/HubPermissionPolicies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ const TempPermissionPolicies: IPermissionPolicy[] = [
* Highlevel Permission definitions for the Hub System as a whole
* Typically other permissions depend on these so a whole set of features
* can be enabled / disabled by changing a single permission
* MAKE SURE to add the permission string to the SystemPermissions array
* in Permissions.ts
*/
const SystemPermissionPolicies: IPermissionPolicy[] = [
{
Expand Down Expand Up @@ -110,6 +112,12 @@ const SystemPermissionPolicies: IPermissionPolicy[] = [
availability: ["alpha"],
environments: ["devext", "qaext"],
},
{
permission: "hub:card:follow",
environments: ["qaext"],
availability: ["alpha"],
licenses: ["hub-premium"],
},
];

/**
Expand Down
1 change: 1 addition & 0 deletions packages/common/src/permissions/types/Permission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const SystemPermissions = [
"hub:feature:workspace",
"hub:feature:gallery:map",
"hub:feature:user:preferences",
"hub:card:follow",
];

const validPermissions = [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { CardEditorOptions } from "../../../../../src/core/schemas/internal/EditorOptions";
import { buildUiSchema } from "../../../../../src/core/schemas/internal/follow/FollowCardUiSchema";
import { MOCK_CONTEXT } from "../../../../mocks/mock-auth";

describe("buildUiSchema: follow", () => {
it("returns the full follow card uiSchema", async () => {
const uiSchema = buildUiSchema("", {} as CardEditorOptions, MOCK_CONTEXT);

expect(uiSchema.type).toBe("Layout");
expect(uiSchema.elements?.length).toBe(7);
expect(uiSchema.elements![0].scope).toBe("/properties/followedItemId");
expect(uiSchema.elements![0].options!.control).toBe(
"hub-field-input-gallery-picker"
);
// TODO: add more explicit tests when we complete the ui schema
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as filterSchemaModule from "../../../../src/core/schemas/internal/filte
import { CardEditorType } from "../../../../src/core/schemas/types";

import * as statUiSchemaModule from "../../../../src/core/schemas/internal/metrics/StatCardUiSchema";
import * as followUiSchemaModule from "../../../../src/core/schemas/internal/follow/FollowCardUiSchema";

describe("getCardEditorSchemas", () => {
let uiSchemaBuildFnSpy: any;
Expand Down Expand Up @@ -49,4 +50,44 @@ describe("getCardEditorSchemas", () => {
expect(uiSchemaBuildFnSpy).toHaveBeenCalledTimes(1);
expect(filterSchemaToUiSchemaSpy).toHaveBeenCalledTimes(1);
});

[{ type: "hub:card:follow", buildFn: followUiSchemaModule }].forEach(
async ({ type, buildFn }) => {
it("returns a schema & uiSchema for a given card and card type", async () => {
uiSchemaBuildFnSpy = spyOn(buildFn, "buildUiSchema").and.returnValue({
type: "Layout",
});
const { schema, uiSchema } = await getCardEditorSchemas(
"some.scope",
type as CardEditorType,
{} as any,
{} as any
);

expect(uiSchemaBuildFnSpy).toHaveBeenCalledTimes(1);
expect(schema).toBeDefined();
expect(uiSchema).toEqual({ type: "Layout" });
});
}
);
it("filters the schemas to the uiSchema elements before returning", async () => {
const filterSchemaToUiSchemaSpy = spyOn(
filterSchemaModule,
"filterSchemaToUiSchema"
).and.callThrough();
uiSchemaBuildFnSpy = spyOn(
followUiSchemaModule,
"buildUiSchema"
).and.returnValue({});

await getCardEditorSchemas(
"some.scope",
"hub:card:follow",
{} as any,
{} as any
);

expect(uiSchemaBuildFnSpy).toHaveBeenCalledTimes(1);
expect(filterSchemaToUiSchemaSpy).toHaveBeenCalledTimes(1);
});
});

0 comments on commit 23feb30

Please sign in to comment.