Skip to content

Commit

Permalink
feat(): add structure for getting card editor schemas (#1277)
Browse files Browse the repository at this point in the history
* feat(): add initial scaffolding for getCardConfig

* feat(): bring schema and uiSchema and update getEditorConfig

* test(): add testing

* refactor(): rename to metricSchema

* refactor(): change to metric schema

* fix(): add required array

* refactor(): implement EditorOptions type and move IEditorConfig to schemas

* refactor(): use EditorOptions instead of ConfigurableEntity

* refactor(): create EditorType from EntityEditorType and CardEditorType

* refactor(): getEntityEditorSchemas is now getEditorSchemas

* refactor(): make EntityEditorOptions into an entity partial

* refactor(): change test name to match refactor

* test(): update tests and promise.all implementation

* fix(): make typing stronger for CardEditorOptions

* fix(): make typing stronger for CardEditorOptions

* docs(): strengthen comments around option types

* refactor(): move alignments/corners/dropshadows to generic types folder

* fix(): fix typing exports

* docs(): change config

* refactor(): feedback and start expecting IEntityEditorOptions in buildUiSchema site

* refactor(): update all buildUiSchema in discussions to take Ientityeditoroptions

* refactor(): update project to options

* refactor(): update initaitive

* refactor(): update pages

* refactor(): update content

* refactor(): template

* refactor(): groups

* refactor(): init template

* fix(): update interfaces file

* refactor(): strengthen typing in initiative template and remove unnecessary tests

* refactor(): require only a partial hubentity from getTypeFromEntity

* docs(): update imports and comments

* refactor(): fix imports to import from file directly

* refactor(): fix other imports to be direct

* refactor(): implement function overloads and update options types

* test(): ensure tests use correct typing and not any

* test(): fix test options

* refactor(): entityeditoroptions is HubEntity and record

* test(): ensure typing for test

* test(): ensure typing for test

* test(): ensure typing for test
  • Loading branch information
jordantsanz authored Oct 20, 2023
1 parent d16a88d commit 6b82f03
Show file tree
Hide file tree
Showing 55 changed files with 1,449 additions and 232 deletions.
7 changes: 2 additions & 5 deletions packages/common/src/content/HubContent.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { IArcGISContext } from "../ArcGISContext";
import { HubItemEntity } from "../core/HubItemEntity";

import {
IEditorConfig,
IWithEditorBehavior,
} from "../core/behaviors/IWithEditorBehavior";
import { IEditorConfig } from "../core/schemas/types";
import { IWithEditorBehavior } from "../core/behaviors/IWithEditorBehavior";
import {
IHubContentEditor,
IHubEditableContent,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { IHubEditableContent } from "../../core/types";
import { IArcGISContext } from "../../ArcGISContext";
import { IUiSchema } from "../../core/schemas/types";
import { EntityEditorOptions } from "../../core/schemas/internal/EditorOptions";

/**
* @private
Expand All @@ -10,7 +10,7 @@ import { IUiSchema } from "../../core/schemas/types";
*/
export const buildUiSchema = async (
i18nScope: string,
entity: IHubEditableContent,
options: EntityEditorOptions,
context: IArcGISContext
): Promise<IUiSchema> => {
return {
Expand Down
12 changes: 6 additions & 6 deletions packages/common/src/content/_internal/ContentUiSchemaEdit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { getTagItems } from "../../core/schemas/internal/getTagItems";
import { getCategoryItems } from "../../core/schemas/internal/getCategoryItems";
import { getLocationExtent } from "../../core/schemas/internal/getLocationExtent";
import { getLocationOptions } from "../../core/schemas/internal/getLocationOptions";
import { IHubEditableContent } from "../../core/types/IHubEditableContent";
import { IUiSchema } from "../../core/schemas/types";
import { getThumbnailUiSchemaElement } from "../../core/schemas/internal/getThumbnailUiSchemaElement";
import { EntityEditorOptions } from "../../core/schemas/internal/EditorOptions";

/**
* @private
Expand All @@ -15,7 +15,7 @@ import { getThumbnailUiSchemaElement } from "../../core/schemas/internal/getThum
*/
export const buildUiSchema = async (
i18nScope: string,
entity: IHubEditableContent,
options: EntityEditorOptions,
context: IArcGISContext
): Promise<IUiSchema> => {
return {
Expand Down Expand Up @@ -71,7 +71,7 @@ export const buildUiSchema = async (
},
},
},
getThumbnailUiSchemaElement(i18nScope, entity),
getThumbnailUiSchemaElement(i18nScope, options),
// tags
{
labelKey: `${i18nScope}.fields.tags.label`,
Expand All @@ -80,7 +80,7 @@ export const buildUiSchema = async (
options: {
control: "hub-field-input-combobox",
items: await getTagItems(
entity,
options,
context.portal.id,
context.hubRequestOptions
),
Expand Down Expand Up @@ -139,11 +139,11 @@ export const buildUiSchema = async (
options: {
control: "hub-field-input-location-picker",
extent: await getLocationExtent(
entity,
options,
context.hubRequestOptions
),
options: await getLocationOptions(
entity,
options,
context.portal.name,
context.hubRequestOptions
),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { IArcGISContext } from "../../ArcGISContext";
import { EntityEditorOptions } from "../../core/schemas/internal/EditorOptions";
import { IUiSchema, UiSchemaRuleEffects } from "../../core/schemas/types";
import { IHubEditableContent } from "../../core/types/IHubEditableContent";
import { isHostedFeatureServiceEntity } from "../hostedServiceUtils";
Expand All @@ -11,14 +12,14 @@ import { isHostedFeatureServiceEntity } from "../hostedServiceUtils";
*/
export const buildUiSchema = async (
i18nScope: string,
entity: IHubEditableContent,
options: EntityEditorOptions,
_context: IArcGISContext
): Promise<IUiSchema> => {
const uiSchema: IUiSchema = {
type: "Layout",
elements: [],
};
if (isHostedFeatureServiceEntity(entity)) {
if (isHostedFeatureServiceEntity(options as IHubEditableContent)) {
uiSchema.elements.push({
type: "Section",
labelKey: `${i18nScope}.sections.downloads.label`,
Expand Down
9 changes: 6 additions & 3 deletions packages/common/src/core/EntityEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import { HubPage } from "../pages/HubPage";
import { HubProject } from "../projects/HubProject";
import { HubSite } from "../sites/HubSite";
import { HubTemplate } from "../templates";
import { IEditorConfig, IWithEditorBehavior } from "./behaviors";
import { IEditorConfig, EntityEditorType } from "./schemas/types";
import { IWithEditorBehavior } from "./behaviors";
import { getTypeFromEntity } from "./getTypeFromEntity";
import { EditorType } from "./schemas";
import { HubEntity } from "./types/HubEntity";
import { HubEntityEditor, IEntityEditorContext } from "./types/HubEntityEditor";

Expand Down Expand Up @@ -65,7 +65,10 @@ export class EntityEditor {
}
}

async getConfig(i18nScope: string, type: EditorType): Promise<IEditorConfig> {
async getConfig(
i18nScope: string,
type: EntityEditorType
): Promise<IEditorConfig> {
return this.instance.getEditorConfig(i18nScope, type);
}

Expand Down
7 changes: 1 addition & 6 deletions packages/common/src/core/behaviors/IWithEditorBehavior.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import { IConfigurationSchema, IUiSchema, EditorType } from "../schemas";
import { IEditorConfig, EditorType } from "../schemas/types";
import { HubEntity, HubEntityEditor, IEntityEditorContext } from "../types";

export interface IEditorConfig {
schema: IConfigurationSchema;
uiSchema: IUiSchema;
}

/**
* Functions that are used by the arcgis-hub-entity-editor component
*/
Expand Down
5 changes: 2 additions & 3 deletions packages/common/src/core/getTypeFromEntity.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { getFamily } from "../content/get-family";
import { ConfigurableEntity } from "./schemas/internal/ConfigurableEntity";
import { HubEntity, IHubItemEntity, HubEntityType } from "./types";

/**
* Given a HubEntity, return it's HubEntityType
* Given a HubEntity, return its HubEntityType
* @param entity
* @returns
*/
export function getTypeFromEntity(
entity: IHubItemEntity | HubEntity | ConfigurableEntity
entity: IHubItemEntity | Partial<HubEntity>
): HubEntityType {
let type: HubEntityType;
switch (entity.type) {
Expand Down
44 changes: 37 additions & 7 deletions packages/common/src/core/schemas/getEditorConfig.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,52 @@
import { IEditorConfig } from "../behaviors/IWithEditorBehavior";
import { EntityEditorType, IEditorConfig, StatCardEditorType } from "./types";
import { IArcGISContext } from "../../ArcGISContext";
import { ConfigurableEntity } from "./internal/ConfigurableEntity";
import { getEntityEditorSchemas } from "./internal/getEntityEditorSchemas";
import {
EditorOptions,
EntityEditorOptions,
IStatCardEditorOptions,
} from "./internal/EditorOptions";
import { getEditorSchemas } from "./internal/getEditorSchemas";
import { EditorType } from "./types";

/**
* Construct the Editor Configuration for a given entity type
* NOTE: We use the concept of function overloading to write getEditorConfig.
* In doing so, we create multiple signatures for the function.
* When the function is called, its types will need to agree with one of the signatures.
* This prevents a function call from having an EntityEditorType type, and IStatCardEditorOptions options, for example.
*/

/**
* Construct the Editor Configuration (schema + uiSchema)
* for a given entity editor type
* @param i18nScope
* @param type
* @param entity
* @param options - options to integrate into the schema + uiSchema
* @param context
* @returns
*/

// Entity editor overload
export async function getEditorConfig(
i18nScope: string,
type: EntityEditorType,
options: EntityEditorOptions,
context: IArcGISContext
): Promise<IEditorConfig>;

// Stat card editor overload
export async function getEditorConfig(
i18nScope: string,
type: StatCardEditorType,
options: IStatCardEditorOptions,
context: IArcGISContext
): Promise<IEditorConfig>;

// General function
export async function getEditorConfig(
i18nScope: string,
type: EditorType,
entity: ConfigurableEntity,
options: EditorOptions,
context: IArcGISContext
): Promise<IEditorConfig> {
return getEntityEditorSchemas(i18nScope, type, entity, context);
return getEditorSchemas(i18nScope, type, options, context);
}

This file was deleted.

28 changes: 28 additions & 0 deletions packages/common/src/core/schemas/internal/EditorOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { HubEntity } from "../../types";

/** Intersection type of all EditorOptions */
export type EditorOptions = EntityEditorOptions | CardEditorOptions;

/**
* Options to use when constructing a schema and uiSchema for
* an entity's editor. Often times, this can just be the entity
* object itself.
*
* However, it must always have "type" on the options, even if not the entire entity.
*/
export type EntityEditorOptions = HubEntity & Record<string, any>;

/**
* Options to use when constructing a schema and uiSchema for
* a layout card's editor. This should be a union of all of the different
* layout card editor options, i.e. IStatCardEditorOptions | ICountdownCardEditorOptions | ...
*/
export type CardEditorOptions = IStatCardEditorOptions;

/**
* Options to use when constructing a schema and uiSchema for
* a stat card editor.
*/
export interface IStatCardEditorOptions {
themeColors: string[];
}
59 changes: 59 additions & 0 deletions packages/common/src/core/schemas/internal/getCardEditorSchemas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { CardEditorType, IEditorConfig, StatCardEditorType } from "../types";
import { getCardType } from "./getCardType";
import { filterSchemaToUiSchema } from "./filterSchemaToUiSchema";
import { CardEditorOptions } from "./EditorOptions";
import { cloneObject } from "../../../util";
import { IArcGISContext } from "../../../ArcGISContext";

/**
* get the editor schema and uiSchema defined for a layout card.
* The schema and uiSchema that are returned can be used to
* render a form UI (using the configuration editor).
*
* @param i18nScope translation scope to be interpolated into the uiSchema
* @param type editor type - corresponds to the returned uiSchema
* @param options optional hash of dynamic uiSchema element options
* @param context
* @returns
*/
export async function getCardEditorSchemas(
i18nScope: string,
type: CardEditorType,
options: CardEditorOptions,
context: IArcGISContext
): Promise<IEditorConfig> {
const cardType = getCardType(type);

// schema and uiSchema are dynamically imported based
// on the previous editor types

let schema;
let uiSchema;

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

// Allow imports to run in parallel
await Promise.all([schemaPromise, uiSchemaPromise()]).then(
([schemaModuleResolved, statModuleResolved]) => {
const { MetricSchema } = schemaModuleResolved;
schema = cloneObject(MetricSchema);
uiSchema = statModuleResolved.buildUiSchema(
i18nScope,
options,
context
);
}
);
break;
}
// filter out properties not used in uiSchema
schema = filterSchemaToUiSchema(schema, uiSchema);

return Promise.resolve({ schema, uiSchema });
}
8 changes: 8 additions & 0 deletions packages/common/src/core/schemas/internal/getCardType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { CardEditorType } from "../types";

/**
* Helper function to get card type string from full type
*/
export function getCardType(type: CardEditorType): string {
return type && type.split(":")[2];
}
Loading

0 comments on commit 6b82f03

Please sign in to comment.