Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(hub-common): adding view for content edit #1116

Closed
wants to merge 5 commits into from
Closed
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
15 changes: 15 additions & 0 deletions packages/common/src/content/_internal/ContentSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { PROJECT_STATUSES, IConfigurationSchema } from "../../core";
import { HubItemEntitySchema } from "../../core/schemas/shared/HubItemEntitySchema";

export type ContentEditorType = (typeof ContentEditorTypes)[number];
export const ContentEditorTypes = ["hub:content:edit"] as const;

/**
* defines the JSON schema for a Hub Project's editable fields
*/
export const ContentSchema: IConfigurationSchema = {
...HubItemEntitySchema,
properties: {
...HubItemEntitySchema.properties,
},
} as IConfigurationSchema;
119 changes: 119 additions & 0 deletions packages/common/src/content/_internal/ContentUiSchemaEdit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { IUiSchema } from "../../core";

export const uiSchema: IUiSchema = {
type: "Layout",
elements: [
{
type: "Section",
labelKey: "{{i18nScope}}.sections.basic.title",
elements: [
// title
{
labelKey: "{{i18nScope}}.fields.title.label",
scope: "/properties/name",
type: "Control",
options: {
helperText: {
labelKey: "{{i18nScope}}.fields.title.hint",
},
messages: [
{
type: "ERROR",
keyword: "required",
icon: true,
labelKey: "{{i18nScope}}.fields.title.requiredError",
},
],
},
},
// summary
{
labelKey: "{{i18nScope}}.fields.summary.label",
scope: "/properties/summary",
type: "Control",
options: {
control: "hub-field-input-input",
type: "textarea",
helperText: {
labelKey: "{{i18nScope}}.fields.summary.hint",
},
},
},
// description
{
labelKey: "{{i18nScope}}.fields.description.label",
scope: "/properties/description",
type: "Control",
options: {
control: "hub-field-input-input",
type: "textarea",
helperText: {
labelKey: "{{i18nScope}}.fields.description.hint",
},
},
},
// thumbnail image
{
labelKey: "{{i18nScope}}.fields._thumbnail.label",
scope: "/properties/_thumbnail",
type: "Control",
options: {
control: "hub-field-input-image-picker",
maxWidth: 727,
maxHeight: 484,
aspectRatio: 1.5,
helperText: {
labelKey: "{{i18nScope}}.fields._thumbnail.helperText",
},
},
},
// tags
{
labelKey: "{{i18nScope}}.fields.tags.label",
scope: "/properties/tags",
type: "Control",
options: {
control: "hub-field-input-combobox",
allowCustomValues: true,
selectionMode: "multiple",
placeholderIcon: "label",
helperText: { labelKey: "{{i18nScope}}.fields.tags.hint" },
},
},
// categories
{
labelKey: "{{i18nScope}}.fields.categories.label",
scope: "/properties/categories",
type: "Control",
options: {
control: "hub-field-input-combobox",
allowCustomValues: false,
selectionMode: "multiple",
placeholderIcon: "select-category",
helperText: {
labelKey: "{{i18nScope}}.fields.categories.agolHint", // TODO: hint should describe whether it can be set on Enterprise or Online
},
},
},
],
},
// { // TODO: in another PR
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good to be forward thinking. I suspect that it will need to be added to ContentSchema as well?

// type: "Section",
// labelKey: "{{i18nScope}}.sections.location.label",
// options: {
// helperText: {
// labelKey: "{{i18nScope}}.sections.location.helperText",
// },
// },
// elements: [
// {
// scope: "/properties/location",
// type: "Control",
// options: {
// control: "hub-field-input-location-picker",
// },
// },
// ],
// },
],
};
21 changes: 21 additions & 0 deletions packages/common/src/content/_internal/computeProps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { IRequestOptions } from "@esri/arcgis-rest-request";
import { UserSession } from "@esri/arcgis-rest-auth";
import { getItemThumbnailUrl } from "../../resources";
import { IHubEditableContent } from "../../core";
import { IModel } from "../../types";

export function computeProps(
model: IModel,
content: Partial<IHubEditableContent>,
requestOptions: IRequestOptions
): IHubEditableContent {
let token: string;
if (requestOptions.authentication) {
const session: UserSession = requestOptions.authentication as UserSession;
token = session.token;
}
// thumbnail url
content.thumbnailUrl = getItemThumbnailUrl(model.item, requestOptions, token);

return content as IHubEditableContent;
}
6 changes: 6 additions & 0 deletions packages/common/src/content/_internal/getPropertyMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ export function getPropertyMap(): IPropertyMap[] {
storeKey: "data.settings.capabilities",
});

// TODO: (Aaron) looking ahead for adding location, I'm pretty sure we'll want this in the next PR
// map.push({
// entityKey: "location",
// storeKey: "item.properties.location",
// });

// TODO: look into composeContent() for what we can add here

return map;
Expand Down
5 changes: 3 additions & 2 deletions packages/common/src/content/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { PropertyMapper } from "../core/_internal/PropertyMapper";
import { getPropertyMap } from "./_internal/getPropertyMap";
import { cloneObject } from "../util";
import { IModel } from "../types";
import { computeProps } from "./_internal/computeProps";

// TODO: move this to defaults?
const DEFAULT_CONTENT_MODEL: IModel = {
Expand Down Expand Up @@ -114,8 +115,8 @@ export async function updateContent(
// );
// }
// now map back into a project and return that
const updatedContent = mapper.storeToEntity(updatedModel, content);
// updatedContent = computeProps(model, updatedContent, requestOptions);
let updatedContent = mapper.storeToEntity(updatedModel, content);
updatedContent = computeProps(model, updatedContent, requestOptions);
// the casting is needed because modelToObject returns a `Partial<T>`
// where as this function returns a `T`
return updatedContent as IHubEditableContent;
Expand Down
3 changes: 2 additions & 1 deletion packages/common/src/content/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
import { IRequestOptions } from "@esri/arcgis-rest-request";
import { PropertyMapper } from "../core/_internal/PropertyMapper";
import { getPropertyMap } from "./_internal/getPropertyMap";
import { computeProps } from "./_internal/computeProps";

const hasFeatures = (contentType: string) =>
["Feature Layer", "Table"].includes(contentType);
Expand Down Expand Up @@ -252,5 +253,5 @@ export const fetchHubContent = async (
);
const content = mapper.storeToEntity(model, {}) as IHubEditableContent;
// TODO: computeProps if we end up using fetchItemAndEnrichments()
return content;
return computeProps(model, content, requestOptions);
};
16 changes: 16 additions & 0 deletions packages/common/src/core/schemas/getEntityEditorSchemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ import {
PageEditorType,
PageEditorTypes,
} from "../../pages/_internal/PageSchema";
import {
ContentEditorType,
ContentEditorTypes,
} from "../../content/_internal/ContentSchema";

/**
* defines the possible editor type values - these correspond
Expand All @@ -32,6 +36,7 @@ import {
export type EditorType = (typeof validEditorTypes)[number];
export const validEditorTypes = [
...ProjectEditorTypes,
...ContentEditorTypes,
...InitiativeEditorTypes,
...SiteEditorTypes,
...DiscussionEditorTypes,
Expand Down Expand Up @@ -73,6 +78,17 @@ export const getEntityEditorSchemas = async (
import("../../projects/_internal/ProjectUiSchemaCreate"),
}[type as ProjectEditorType]());
break;
case "content":
const { ContentSchema } = await import(
"../../content/_internal/ContentSchema"
);
schema = cloneObject(ContentSchema);

({ uiSchema } = await {
"hub:content:edit": () =>
import("../../content/_internal/ContentUiSchemaEdit"),
}[type as ContentEditorType]());
break;
case "initiative":
const { InitiativeSchema } = await import(
"../../initiatives/_internal/InitiativeSchema"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const HubItemEntitySchema: IConfigurationSchema = {
tags: ENTITY_TAGS_SCHEMA,
categories: ENTITY_CATEGORIES_SCHEMA,
isDiscussable: ENTITY_IS_DISCUSSABLE_SCHEMA,
_thumbnail: ENTITY_IMAGE_SCHEMA,
view: {
type: "object",
properties: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ProjectEditorTypes } from "../../../src/projects/_internal/ProjectSchem
import { InitiativeEditorTypes } from "../../../src/initiatives/_internal/InitiativeSchema";
import { SiteEditorTypes } from "../../../src/sites/_internal/SiteSchema";
import { DiscussionEditorTypes } from "../../../src/discussions/_internal/DiscussionSchema";
import { ContentEditorTypes } from "../../../src/content/_internal/ContentSchema";
import { PageEditorTypes } from "../../../src/pages/_internal/PageSchema";
import * as applyOptionsModule from "../../../src/core/schemas/internal/applyUiSchemaElementOptions";
import * as filterSchemaModule from "../../../src/core/schemas/internal/filterSchemaToUiSchema";
Expand All @@ -15,6 +16,7 @@ describe("getEntityEditorSchemas", () => {
...InitiativeEditorTypes,
...SiteEditorTypes,
...DiscussionEditorTypes,
...ContentEditorTypes,
...PageEditorTypes,
].forEach(async (type, idx) => {
const { schema, uiSchema } = await getEntityEditorSchemas(
Expand Down