From e512d3e259f736336e5b6f82b8f7ce98f022da1e Mon Sep 17 00:00:00 2001 From: Ben Loe Date: Fri, 6 May 2022 11:59:34 -0400 Subject: [PATCH 1/3] #3268 Improve error msg layout in Form component (#3325) --- src/components/form/Form.module.scss | 22 +++++++++-- src/components/form/Form.tsx | 41 +++++++++++++++++++- src/pageEditor/sidebar/CreateRecipeModal.tsx | 7 +++- 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/src/components/form/Form.module.scss b/src/components/form/Form.module.scss index 1d7751f46e..0ceddf520f 100644 --- a/src/components/form/Form.module.scss +++ b/src/components/form/Form.module.scss @@ -1,7 +1,21 @@ -$color-danger: #fc3939; -$color-danger-active: #e50303; +/* + * Copyright (C) 2022 PixieBrix, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ .status { - color: $color-danger; - margin-bottom: 1rem; + border: none; + border-radius: 0; } diff --git a/src/components/form/Form.tsx b/src/components/form/Form.tsx index 8c70721feb..33fc89bfd4 100644 --- a/src/components/form/Form.tsx +++ b/src/components/form/Form.tsx @@ -18,9 +18,11 @@ import styles from "./Form.module.scss"; import React, { ReactElement } from "react"; -import { Button, Form as BootstrapForm } from "react-bootstrap"; +import { Alert, Button, Form as BootstrapForm } from "react-bootstrap"; import { Formik, FormikHelpers, FormikValues } from "formik"; import * as yup from "yup"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faExclamationTriangle } from "@fortawesome/free-solid-svg-icons"; export type OnSubmit = ( values: TValues, @@ -43,13 +45,46 @@ export type RenderSubmit = (state: { export type RenderStatus = (state: { status: string }) => ReactElement; type FormProps = { + /** + * The starting formik field values for the form + */ initialValues: FormikValues; + + /** + * The yup validation schema for the form + */ validationSchema: yup.AnyObjectSchema; + + /** + * Should the form be validated on component mount? + */ validateOnMount?: boolean; + + /** + * (from Formik): Should Formik reset the form when new initialValues change? + */ enableReinitialize?: boolean; + + /** + * The render function for the body of the form + */ renderBody?: RenderBody; + + /** + * The render function for the submit button (and any other co-located buttons) + */ renderSubmit?: RenderSubmit; + + /** + * The render function for the top-level form status message + * + * Note: This currently defaults to an "error" style layout + */ renderStatus?: RenderStatus; + + /** + * The submission handler for the form + */ onSubmit: OnSubmit; }; @@ -60,7 +95,9 @@ const defaultRenderSubmit: RenderSubmit = ({ isSubmitting, isValid }) => ( ); const defaultRenderStatus: RenderStatus = ({ status }) => ( -
{status}
+ + {status} + ); const Form: React.FC = ({ diff --git a/src/pageEditor/sidebar/CreateRecipeModal.tsx b/src/pageEditor/sidebar/CreateRecipeModal.tsx index 559141d50f..07d683b2fb 100644 --- a/src/pageEditor/sidebar/CreateRecipeModal.tsx +++ b/src/pageEditor/sidebar/CreateRecipeModal.tsx @@ -244,13 +244,18 @@ function useInitialFormState({ function useFormSchema() { const newRecipeIds = useSelector(selectNewRecipeIds); + const { data: recipes } = useGetRecipesQuery(); + const savedRecipeIds: RegistryId[] = (recipes ?? []).map( + (x) => x.metadata.id + ); + const allRecipeIds = [...newRecipeIds, ...savedRecipeIds]; // TODO: This should be yup.SchemaOf but we can't set the `id` property to `RegistryId` // see: https://github.com/jquense/yup/issues/1183#issuecomment-749186432 return object({ id: string() .matches(PACKAGE_REGEX, "Invalid registry id") - .notOneOf(newRecipeIds, "This id is already in use") + .notOneOf(allRecipeIds, "This id is already in use") .required(), name: string().required(), version: string() From 323b12de02fdc0c035cc7c09552ce6f7f41ae24e Mon Sep 17 00:00:00 2001 From: Ben Loe Date: Fri, 6 May 2022 15:51:15 -0400 Subject: [PATCH 2/3] #3261 Fix issue where blueprints are showing as dirty when selected with no changes (#3329) --- .../widgets/TemplateToggleWidget.tsx | 4 + .../formBuilder/edit/FormEditor.tsx | 60 ++++++------ .../formBuilder/formBuilderHelpers.test.ts | 95 ++++++++----------- .../formBuilder/formBuilderHelpers.ts | 59 ++++-------- src/pageEditor/tabs/RecipeOptions.tsx | 19 ++-- 5 files changed, 108 insertions(+), 129 deletions(-) diff --git a/src/components/fields/schemaFields/widgets/TemplateToggleWidget.tsx b/src/components/fields/schemaFields/widgets/TemplateToggleWidget.tsx index 9fe63e4374..c5ccab2719 100644 --- a/src/components/fields/schemaFields/widgets/TemplateToggleWidget.tsx +++ b/src/components/fields/schemaFields/widgets/TemplateToggleWidget.tsx @@ -101,6 +101,10 @@ const TemplateToggleWidget: React.VFC = ({ inputModeOptions.some((option) => option.value === "string") ) { onModeChange("string"); + } else if ( + inputModeOptions.some((option) => option.value === "number") + ) { + onModeChange("number"); } else if ( inputModeOptions.some((option) => option.value === "var") ) { diff --git a/src/components/formBuilder/edit/FormEditor.tsx b/src/components/formBuilder/edit/FormEditor.tsx index 237c4722da..c4caaf61ea 100644 --- a/src/components/formBuilder/edit/FormEditor.tsx +++ b/src/components/formBuilder/edit/FormEditor.tsx @@ -30,9 +30,9 @@ import { DEFAULT_FIELD_TYPE, generateNewPropertyName, moveStringInArray, + normalizeSchema, normalizeUiOrder, replaceStringInArray, - updateRjsfSchemaWithDefaultsIfNeeded, } from "@/components/formBuilder/formBuilderHelpers"; import { UI_ORDER } from "@/components/formBuilder/schemaFieldNames"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; @@ -72,14 +72,6 @@ const FormEditor: React.FC = ({ const { schema, uiSchema } = rjsfSchema; - useEffect(() => { - // Set default values if needed - const nextRjsfSchema = updateRjsfSchemaWithDefaultsIfNeeded(rjsfSchema); - if (nextRjsfSchema !== null) { - setRjsfSchema(nextRjsfSchema); - } - }, [rjsfSchema, setRjsfSchema]); - // Select the active field when FormEditor field changes useEffect( () => { @@ -118,11 +110,7 @@ const FormEditor: React.FC = ({ return { titleFieldProps, descriptionFieldProps }; }, [name]); - if (!schema || !uiSchema) { - return null; - } - - const propertyKeys = Object.keys(schema.properties ?? {}); + const propertyKeys = Object.keys(schema?.properties ?? {}); const addProperty = () => { const propertyName = generateNewPropertyName(propertyKeys); @@ -145,12 +133,16 @@ const FormEditor: React.FC = ({ ); const nextRjsfSchema = produce(rjsfSchema, (draft) => { - draft.uiSchema[UI_ORDER] = nextUiOrder; - if (!draft.schema.properties) { - draft.schema.properties = {}; + draft.schema = normalizeSchema(schema); + // eslint-disable-next-line security/detect-object-injection -- prop name is generated + draft.schema.properties[propertyName] = newProperty; + + if (!uiSchema) { + draft.uiSchema = {}; } - draft.schema.properties[propertyName] = newProperty; + // eslint-disable-next-line security/detect-object-injection -- prop name is a constant + draft.uiSchema[UI_ORDER] = nextUiOrder; }); setRjsfSchema(nextRjsfSchema); setActiveField(propertyName); @@ -176,6 +168,8 @@ const FormEditor: React.FC = ({ setActiveField(nextActiveField); const nextRjsfSchema = produce(rjsfSchema, (draft) => { + draft.schema = normalizeSchema(schema); + if (schema.required?.length > 0) { draft.schema.required = replaceStringInArray( schema.required, @@ -183,29 +177,35 @@ const FormEditor: React.FC = ({ ); } - draft.uiSchema[UI_ORDER] = nextUiOrder; - // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete,security/detect-object-injection delete draft.schema.properties[propertyToRemove]; - // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + + if (!uiSchema) { + draft.uiSchema = {}; + } + + // eslint-disable-next-line security/detect-object-injection -- prop name is a constant + draft.uiSchema[UI_ORDER] = nextUiOrder; + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete,security/detect-object-injection delete draft.uiSchema[propertyToRemove]; }); setRjsfSchema(nextRjsfSchema); }; - // There's always at least 1 item in uiOrder array, "*". + // The uiOrder field may not be initialized yet + const order = uiOrder ?? ["*"]; const canMoveUp = Boolean(activeField) && - (uiOrder?.length > 2 - ? uiOrder[0] !== activeField + (order.length > 2 + ? order[0] !== activeField : propertyKeys[0] !== activeField); const canMoveDown = Boolean(activeField) && - (uiOrder?.length === propertyKeys.length + 1 - ? uiOrder[uiOrder.length - 2] !== activeField - : Array.isArray(uiOrder) && - findLast(propertyKeys, (key) => !uiOrder.includes(key)) !== - activeField); + (order.length === propertyKeys.length + 1 + ? order[order.length - 2] !== activeField + : Array.isArray(order) && + findLast(propertyKeys, (key) => !order.includes(key)) !== activeField); return ( <> @@ -242,7 +242,7 @@ const FormEditor: React.FC = ({ - {activeField && Boolean(schema.properties?.[activeField]) && ( + {activeField && Boolean(schema?.properties?.[activeField]) && ( { let array: string[]; @@ -66,58 +66,6 @@ describe("replaceStringInArray", () => { }); }); -describe("updateRjsfSchemaWithDefaultsIfNeeded", () => { - test("accepts the minimal schema", () => { - const rjsfSchema: RJSFSchema = { - schema: MINIMAL_SCHEMA, - uiSchema: MINIMAL_UI_SCHEMA, - }; - - const nextRjsfSchema = updateRjsfSchemaWithDefaultsIfNeeded(rjsfSchema); - expect(nextRjsfSchema).toBeNull(); - }); - - test("init schema and ui schema", () => { - const nextRjsfSchema = updateRjsfSchemaWithDefaultsIfNeeded( - {} as RJSFSchema - ); - expect(nextRjsfSchema.schema).toEqual(MINIMAL_SCHEMA); - expect(nextRjsfSchema.uiSchema).toEqual(MINIMAL_UI_SCHEMA); - }); - - test("accepts the schema it created", () => { - const rjsfSchema = updateRjsfSchemaWithDefaultsIfNeeded({} as RJSFSchema); - const nextRjsfSchema = updateRjsfSchemaWithDefaultsIfNeeded(rjsfSchema); - expect(nextRjsfSchema).toBeNull(); - }); - - test("init ui order", () => { - const rjsfSchema: RJSFSchema = { - schema: MINIMAL_SCHEMA, - uiSchema: {}, - }; - - const nextRjsfSchema = updateRjsfSchemaWithDefaultsIfNeeded(rjsfSchema); - expect(nextRjsfSchema.uiSchema[UI_ORDER]).toEqual(["*"]); - }); - - test.each([null, false, true, "firstName"])( - "fixes required field when it's %s", - (requiredFieldValue: any) => { - const rjsfSchema: RJSFSchema = { - schema: { - ...MINIMAL_SCHEMA, - required: requiredFieldValue, - }, - uiSchema: MINIMAL_UI_SCHEMA, - }; - - const nextRjsfSchema = updateRjsfSchemaWithDefaultsIfNeeded(rjsfSchema); - expect(nextRjsfSchema.schema.required).toEqual([]); - } - ); -}); - describe("produceSchemaOnPropertyNameChange", () => { test.each(initRenamingCases())( "renaming a field", @@ -185,6 +133,45 @@ describe("validateNextPropertyName", () => { }); }); +describe("normalizeSchema", () => { + test("init schema", () => { + // eslint-disable-next-line unicorn/no-useless-undefined + const actual = normalizeSchema(undefined); + expect(actual).toStrictEqual(MINIMAL_SCHEMA); + }); + + test("add properties", () => { + const actual = normalizeSchema({ + type: "object", + }); + expect(actual).toStrictEqual({ + type: "object", + properties: {}, + }); + }); + + test("fix required", () => { + const actual = normalizeSchema({ + type: "object", + properties: { + foo: { + type: "string", + }, + }, + required: null, + }); + expect(actual).toStrictEqual({ + type: "object", + properties: { + foo: { + type: "string", + }, + }, + required: [], + }); + }); +}); + describe("normalizeUiOrder", () => { test("init uiOrder", () => { const actual = normalizeUiOrder(["propA", "propB"], []); diff --git a/src/components/formBuilder/formBuilderHelpers.ts b/src/components/formBuilder/formBuilderHelpers.ts index cdd511300e..934bd33298 100644 --- a/src/components/formBuilder/formBuilderHelpers.ts +++ b/src/components/formBuilder/formBuilderHelpers.ts @@ -321,48 +321,31 @@ export const produceSchemaOnUiTypeChange = ( }); }; -export const updateRjsfSchemaWithDefaultsIfNeeded = ( - rjsfSchema: RJSFSchema = {} as RJSFSchema -) => { - const { schema, uiSchema } = rjsfSchema; - - // eslint-disable-next-line security/detect-object-injection -- UI_ORDER is a known property - const uiOrder = uiSchema?.[UI_ORDER]; - const needToUpdateRequired = - Boolean(schema) && - typeof schema.required !== "undefined" && - !Array.isArray(schema.required); - - if (!schema || !uiSchema || !uiOrder?.includes("*") || needToUpdateRequired) { - return produce(rjsfSchema, (draft) => { - if (!draft.schema) { - draft.schema = MINIMAL_SCHEMA; - } - - if (!draft.uiSchema) { - draft.uiSchema = MINIMAL_UI_SCHEMA; - } - - // Relying on Immer to protect against object injections - /* eslint-disable security/detect-object-injection */ - if (!draft.uiSchema[UI_ORDER]) { - const propertyKeys = Object.keys(draft.schema.properties || {}); - draft.uiSchema[UI_ORDER] = [...propertyKeys, "*"]; - } else if (!draft.uiSchema[UI_ORDER].includes("*")) { - draft.uiSchema[UI_ORDER].push("*"); - } - /* eslint-enable security/detect-object-injection */ - - if (needToUpdateRequired) { - draft.schema.required = []; - } - }); +export const normalizeSchema = (schema: Schema | undefined) => { + if (!schema) { + return MINIMAL_SCHEMA; } - return null; + return produce(schema, (draft) => { + // Should we initialize the 'required' field? + if ( + Boolean(schema) && + typeof schema.required !== "undefined" && + !Array.isArray(schema.required) + ) { + draft.required = []; + } + + if (!draft.properties) { + draft.properties = {}; + } + }); }; -export const normalizeUiOrder = (propertyKeys: string[], uiOrder: string[]) => { +export const normalizeUiOrder = ( + propertyKeys: string[], + uiOrder: string[] = [] +) => { // A naive check to see if all property keys are presenter in uiOrder if ( propertyKeys.length === uiOrder.length - 1 && diff --git a/src/pageEditor/tabs/RecipeOptions.tsx b/src/pageEditor/tabs/RecipeOptions.tsx index 823912f4c5..879407a371 100644 --- a/src/pageEditor/tabs/RecipeOptions.tsx +++ b/src/pageEditor/tabs/RecipeOptions.tsx @@ -29,7 +29,11 @@ import dataPanelStyles from "@/pageEditor/tabs/dataPanelTabs.module.scss"; import cx from "classnames"; import FormPreview from "@/components/formBuilder/preview/FormPreview"; import { RJSFSchema } from "@/components/formBuilder/formBuilderTypes"; -import { FIELD_TYPE_OPTIONS } from "@/components/formBuilder/formBuilderHelpers"; +import { + FIELD_TYPE_OPTIONS, + MINIMAL_SCHEMA, + MINIMAL_UI_SCHEMA, +} from "@/components/formBuilder/formBuilderHelpers"; import { useDispatch, useSelector } from "react-redux"; import { selectActiveRecipeId, @@ -52,6 +56,11 @@ const formRuntimeContext: RuntimeContext = { allowExpressions: false, }; +const emptyOptions: OptionsDefinition = { + schema: MINIMAL_SCHEMA, + uiSchema: MINIMAL_UI_SCHEMA, +}; + const RecipeOptions: React.VFC = () => { const [activeField, setActiveField] = useState(); const recipeId = useSelector(selectActiveRecipeId); @@ -60,13 +69,9 @@ const RecipeOptions: React.VFC = () => { const savedOptions = recipe?.options; const dirtyOptions = useSelector(selectDirtyOptionsForRecipeId(recipeId)); - const options = dirtyOptions ?? - savedOptions ?? { - schema: {}, - uiSchema: {}, - }; + const optionsDefinition = dirtyOptions ?? savedOptions ?? emptyOptions; - const initialValues = { optionsDefinition: options }; + const initialValues = { optionsDefinition }; const dispatch = useDispatch(); const updateRedux = useCallback( From 52ec942dfdba75bbc5d595cdf37899b478e28297 Mon Sep 17 00:00:00 2001 From: Alexandr Date: Fri, 6 May 2022 16:15:03 -0400 Subject: [PATCH 3/3] #3324 Include document builder brick by default in new sidebar extensions (#3342) * 3324 Add document builder to Side panel by default * 3324 createNewBlock helper * 3324 moving files around --- .../formBuilder/FormBuilder.test.tsx | 9 +++-- src/pageEditor/createNewBlock.ts | 35 +++++++++++++++++++ .../{tabs/editTab => }/exampleBlockConfigs.ts | 16 +++++---- src/pageEditor/exampleExtensionConfig.ts | 34 ++++++++++++++++++ .../panes/insert/GenericInsertPane.tsx | 13 +++++-- .../tabs/editTab/useBlockPipelineActions.ts | 10 ++---- 6 files changed, 95 insertions(+), 22 deletions(-) create mode 100644 src/pageEditor/createNewBlock.ts rename src/pageEditor/{tabs/editTab => }/exampleBlockConfigs.ts (89%) create mode 100644 src/pageEditor/exampleExtensionConfig.ts diff --git a/src/components/formBuilder/FormBuilder.test.tsx b/src/components/formBuilder/FormBuilder.test.tsx index 8ff59212c2..3aa7d765ea 100644 --- a/src/components/formBuilder/FormBuilder.test.tsx +++ b/src/components/formBuilder/FormBuilder.test.tsx @@ -15,8 +15,7 @@ * along with this program. If not, see . */ -import { IBlock } from "@/core"; -import { getExampleBlockConfig } from "@/pageEditor/tabs/editTab/exampleBlockConfigs"; +import { getExampleBlockConfig } from "@/pageEditor/exampleBlockConfigs"; import { createFormikTemplate, fireTextInput, @@ -37,9 +36,9 @@ let defaultFieldName: string; beforeAll(() => { registerDefaultWidgets(); - const { schema, uiSchema } = getExampleBlockConfig({ - id: validateRegistryId("@pixiebrix/form"), - } as IBlock); + const { schema, uiSchema } = getExampleBlockConfig( + validateRegistryId("@pixiebrix/form") + ); exampleFormSchema = { schema, uiSchema, diff --git a/src/pageEditor/createNewBlock.ts b/src/pageEditor/createNewBlock.ts new file mode 100644 index 0000000000..58bdc002c8 --- /dev/null +++ b/src/pageEditor/createNewBlock.ts @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 PixieBrix, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { BlockConfig } from "@/blocks/types"; +import { defaultBlockConfig } from "@/blocks/util"; +import { RegistryId, Schema } from "@/core"; +import { uuidv4 } from "@/types/helpers"; +import { getExampleBlockConfig } from "./exampleBlockConfigs"; + +export function createNewBlock( + blockId: RegistryId, + blockInputSchema?: Schema +): BlockConfig { + return { + id: blockId, + instanceId: uuidv4(), + config: + getExampleBlockConfig(blockId) ?? + (blockInputSchema == null ? {} : defaultBlockConfig(blockInputSchema)), + }; +} diff --git a/src/pageEditor/tabs/editTab/exampleBlockConfigs.ts b/src/pageEditor/exampleBlockConfigs.ts similarity index 89% rename from src/pageEditor/tabs/editTab/exampleBlockConfigs.ts rename to src/pageEditor/exampleBlockConfigs.ts index d02e5f1f43..4d641a96e3 100644 --- a/src/pageEditor/tabs/editTab/exampleBlockConfigs.ts +++ b/src/pageEditor/exampleBlockConfigs.ts @@ -16,18 +16,20 @@ */ import { UnknownObject } from "@/types"; -import { IBlock } from "@/core"; +import { RegistryId } from "@/core"; import { COMPONENT_READER_ID } from "@/blocks/transformers/component/ComponentReader"; -export function getExampleBlockConfig(block: IBlock): UnknownObject | null { - if (block.id === COMPONENT_READER_ID) { +export function getExampleBlockConfig( + blockId: RegistryId +): UnknownObject | null { + if (blockId === COMPONENT_READER_ID) { return { selector: "", optional: false, }; } - if (block.id === "@pixiebrix/jquery-reader") { + if (blockId === "@pixiebrix/jquery-reader") { return { selectors: { property: "", @@ -35,7 +37,7 @@ export function getExampleBlockConfig(block: IBlock): UnknownObject | null { }; } - if (block.id === "@pixiebrix/form-modal") { + if (blockId === "@pixiebrix/form-modal") { return { schema: { title: "Example Form", @@ -55,7 +57,7 @@ export function getExampleBlockConfig(block: IBlock): UnknownObject | null { }; } - if (block.id === "@pixiebrix/form") { + if (blockId === "@pixiebrix/form") { return { schema: { title: "Example Form", @@ -76,7 +78,7 @@ export function getExampleBlockConfig(block: IBlock): UnknownObject | null { }; } - if (block.id === "@pixiebrix/document") { + if (blockId === "@pixiebrix/document") { return { body: [ { diff --git a/src/pageEditor/exampleExtensionConfig.ts b/src/pageEditor/exampleExtensionConfig.ts new file mode 100644 index 0000000000..f25c4776b5 --- /dev/null +++ b/src/pageEditor/exampleExtensionConfig.ts @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 PixieBrix, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { BlockPipeline } from "@/blocks/types"; +import { ExtensionPointType } from "@/extensionPoints/types"; +import { createNewBlock } from "@/pageEditor/createNewBlock"; +import { validateRegistryId } from "@/types/helpers"; + +const documentBlockId = validateRegistryId("@pixiebrix/document"); + +export function getExampleBlockPipeline( + type: ExtensionPointType +): BlockPipeline { + if (type === "actionPanel") { + const documentBuilderBlock = createNewBlock(documentBlockId); + return [documentBuilderBlock]; + } + + return []; +} diff --git a/src/pageEditor/panes/insert/GenericInsertPane.tsx b/src/pageEditor/panes/insert/GenericInsertPane.tsx index f7e5a894cd..1d6765648d 100644 --- a/src/pageEditor/panes/insert/GenericInsertPane.tsx +++ b/src/pageEditor/panes/insert/GenericInsertPane.tsx @@ -37,6 +37,7 @@ import { updateDynamicElement, } from "@/contentScript/messenger/api"; import { FormState } from "@/pageEditor/pageEditorTypes"; +import { getExampleBlockPipeline } from "@/pageEditor/exampleExtensionConfig"; const { addElement } = editorSlice.actions; @@ -92,10 +93,18 @@ const GenericInsertPane: React.FunctionComponent<{ const url = await getCurrentURL(); const metadata = internalExtensionPointMetaFactory(); + const formState = config.fromNativeElement( + url, + metadata, + undefined, + [] + ) as FormState; - await start( - config.fromNativeElement(url, metadata, undefined, []) as FormState + formState.extension.blockPipeline = getExampleBlockPipeline( + formState.type ); + + await start(formState); } catch (error) { notify.error({ message: "Error using adding new element", error }); } diff --git a/src/pageEditor/tabs/editTab/useBlockPipelineActions.ts b/src/pageEditor/tabs/editTab/useBlockPipelineActions.ts index 23208002c1..47a1c6772e 100644 --- a/src/pageEditor/tabs/editTab/useBlockPipelineActions.ts +++ b/src/pageEditor/tabs/editTab/useBlockPipelineActions.ts @@ -21,8 +21,6 @@ import { generateFreshOutputKey } from "@/pageEditor/tabs/editTab/editHelpers"; import { compact } from "lodash"; import { BlockConfig, BlockPipeline } from "@/blocks/types"; import { uuidv4 } from "@/types/helpers"; -import { getExampleBlockConfig } from "@/pageEditor/tabs/editTab/exampleBlockConfigs"; -import { defaultBlockConfig } from "@/blocks/util"; import { produce } from "immer"; import { actions } from "@/pageEditor/slices/editorSlice"; import { FormState, RootState } from "@/pageEditor/pageEditorTypes"; @@ -32,6 +30,7 @@ import { reportEvent } from "@/telemetry/events"; import { selectSessionId } from "@/pageEditor/slices/sessionSelectors"; import { NodeId } from "@/pageEditor/tabs/editTab/editorNode/EditorNode"; import { FOUNDATION_NODE_ID } from "@/pageEditor/uiState/uiState"; +import { createNewBlock } from "@/pageEditor/createNewBlock"; type BlockPipelineActions = { addBlock: (block: IBlock, pipelineIndex: number) => void; @@ -63,12 +62,7 @@ function useBlockPipelineActions( ...blockPipeline.map((x) => x.outputKey), ]) ); - const newBlock: BlockConfig = { - id: block.id, - instanceId: uuidv4(), - config: - getExampleBlockConfig(block) ?? defaultBlockConfig(block.inputSchema), - }; + const newBlock = createNewBlock(block.id, block.inputSchema); if (outputKey) { newBlock.outputKey = outputKey; }