Skip to content

Commit

Permalink
feat(app-page-builder): make website settings view configurable (#4145)
Browse files Browse the repository at this point in the history
  • Loading branch information
Pavel910 authored May 23, 2024
1 parent d711547 commit c11112d
Show file tree
Hide file tree
Showing 16 changed files with 432 additions and 260 deletions.
2 changes: 1 addition & 1 deletion packages/app-page-builder/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export * from "./templateEditor/editorConfig/TemplateEditorConfig";
export * from "./pageEditor/editorConfig/PageEditorConfig";

// Export extension components
export * from "./modules/WebsiteSettings/AddPbWebsiteSettings";
export { WebsiteSettingsConfig } from "./modules/WebsiteSettings/config/WebsiteSettingsConfig";

export * from "./plugins";

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@ import { WebsiteSettingsView } from "./WebsiteSettingsView";
import { FaviconAndLogo } from "./settingsGroups/FaviconAndLogo";
import { SocialMedia } from "./settingsGroups/SocialMedia";
import { HtmlTags } from "./settingsGroups/HtmlTags";
import { WebsiteSettingsWithConfig } from "./config/WebsiteSettingsConfig";

export const WebsiteSettings = () => {
return (
<Fragment>
<HasPermission name={"pb.settings"}>
<AddRoute path="/settings/page-builder/website">
<Layout title={"Page Builder - Website Settings"}>
<WebsiteSettingsView />
<WebsiteSettingsWithConfig>
<WebsiteSettingsView />
</WebsiteSettingsWithConfig>
</Layout>
</AddRoute>
</HasPermission>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import React from "react";
import { CenteredView, createVoidComponent, makeDecoratable } from "@webiny/app-admin";
import React, { Fragment } from "react";
import { CenteredView } from "@webiny/app-admin";
import { Form } from "@webiny/form";
import { ButtonPrimary } from "@webiny/ui/Button";
import { CircularProgress } from "@webiny/ui/Progress";
import { SimpleForm, SimpleFormFooter } from "@webiny/app-admin/components/SimpleForm";
import {
SimpleForm,
SimpleFormContent,
SimpleFormFooter,
SimpleFormHeader
} from "@webiny/app-admin/components/SimpleForm";
import { usePbWebsiteSettings } from "./usePbWebsiteSettings";

export const SettingsFields = makeDecoratable("SettingsFields", createVoidComponent());
import { WebsiteSettingsConfig } from "~/modules/WebsiteSettings/config/WebsiteSettingsConfig";
import { Cell, Grid } from "@webiny/ui/Grid";

export const WebsiteSettingsView = () => {
const { fetching, saving, settings, saveSettings } = usePbWebsiteSettings();
const { groups } = WebsiteSettingsConfig.useWebsiteSettingsConfig();

return (
<CenteredView>
Expand All @@ -18,7 +24,20 @@ export const WebsiteSettingsView = () => {
<SimpleForm>
{fetching && <CircularProgress label={"Loading settings..."} />}
{saving && <CircularProgress label={"Saving settings..."} />}
<SettingsFields />
{groups.map(group => (
<Fragment key={group.name}>
<SimpleFormHeader title={group.label} />
<SimpleFormContent>
<Grid>
{(group.elements || []).map(el => (
<Cell key={el.name} span={12}>
{el.element}
</Cell>
))}
</Grid>
</SimpleFormContent>
</Fragment>
))}
<SimpleFormFooter>
<ButtonPrimary
onClick={ev => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { DocumentNode } from "graphql";
import { ExecutableDefinitionNode, FieldNode, OperationDefinitionNode } from "graphql/language/ast";

declare module "graphql" {
interface DocumentNode {
__cacheKey: string;
}
}

let documentNodeModifier: DocumentNodeModifier;

class DocumentNodeModifier {
private cache = new Map<string, DocumentNode>();

public augmentDocument(
document: DocumentNode,
selectionPath: string,
selections: DocumentNode[]
) {
const cacheKey = this.createCacheKey(document, selections);
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey) as DocumentNode;
}

const NEW_QUERY = structuredClone(document);
selections.forEach(selection => {
this.addSelectionToQuery(NEW_QUERY, selectionPath, selection);
});

this.cache.set(cacheKey, NEW_QUERY);

return NEW_QUERY;
}

public addSelectionToQuery(
document: DocumentNode,
selectionPath: string,
addSelection: DocumentNode
): void {
const firstQueryDefinition = document.definitions[0] as ExecutableDefinitionNode;
if (!firstQueryDefinition) {
return;
} else if (!firstQueryDefinition.selectionSet) {
return;
}

let tree = firstQueryDefinition.selectionSet.selections as FieldNode[];
const fields = selectionPath.split(".");

fieldLoop: for (const field of fields) {
for (const selection of tree) {
if (!selection.selectionSet) {
continue;
}
if (selection.name.value === field) {
tree = selection.selectionSet.selections as FieldNode[];
continue fieldLoop;
}
}
// If we get here, it means we didn't find the necessary selection
return;
}
/**
* We must cast because there are a lot of types that are not intertwined and TS is complaining
*/
tree.push(
...((addSelection.definitions[0] as ExecutableDefinitionNode).selectionSet
.selections as FieldNode[])
);
}

private createCacheKey(document: DocumentNode, selections: DocumentNode[]) {
const def = document.definitions[0] as OperationDefinitionNode;
const operationName = def.name?.value;
return [operationName, ...selections.map(node => node.__cacheKey)].join(":");
}
}

export function createDocumentNodeModifier() {
if (!documentNodeModifier) {
documentNodeModifier = new DocumentNodeModifier();
}

return documentNodeModifier;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from "react";
import { DocumentNode } from "graphql";
import { makeDecoratable } from "@webiny/app-admin";
import { Property, useIdGenerator } from "@webiny/react-properties";

export interface ElementConfig {
name: string;
element: JSX.Element;
}

export interface ElementProps {
name: string;
element?: JSX.Element;
children?: React.ReactNode;
querySelection?: DocumentNode;
remove?: boolean;
before?: string;
after?: string;
}

export const Element = makeDecoratable(
"WebsiteSettingsElement",
({ name, remove, before, after, ...props }: ElementProps) => {
const getId = useIdGenerator("element");
const element = props.element ?? props.children;

const placeAfter = after !== undefined ? getId(after) : undefined;
const placeBefore = before !== undefined ? getId(before) : undefined;

return (
<Property
id={getId(name)}
name={"elements"}
remove={remove}
array={true}
before={placeBefore}
after={placeAfter}
>
<Property id={getId(name, "name")} name={"name"} value={name} />
{element ? (
<Property id={getId(name, "element")} name={"element"} value={element} />
) : null}
</Property>
);
}
);
Loading

0 comments on commit c11112d

Please sign in to comment.