mirror of
https://github.com/formbricks/formbricks.git
synced 2026-04-20 19:30:41 -05:00
fix: block naming issue (#6899)
This commit is contained in:
committed by
GitHub
parent
7a0da3a3e4
commit
527f8fd0d5
+2
-2
@@ -1245,8 +1245,8 @@ checksums:
|
||||
environments/surveys/edit/custom_hostname: bc2b1c8de3f9b8ef145b45aeba6ab429
|
||||
environments/surveys/edit/darken_or_lighten_background_of_your_choice: 304a64a8050ebf501d195e948cd25b6f
|
||||
environments/surveys/edit/date_format: e95dfc41ac944874868487457ddc057a
|
||||
environments/surveys/edit/delete_block: c00617cb0724557e486304276063807a
|
||||
environments/surveys/edit/days_before_showing_this_survey_again: 354fb28c5ff076f022d82a20c749ee46
|
||||
environments/surveys/edit/delete_block: c00617cb0724557e486304276063807a
|
||||
environments/surveys/edit/delete_choice: fd750208d414b9ad8c980c161a0199e1
|
||||
environments/surveys/edit/disable_the_visibility_of_survey_progress: 2af631010114307ac2a91612559c9618
|
||||
environments/surveys/edit/display_an_estimate_of_completion_time_for_survey: 03f0a816569399c1c61d08dbc913de06
|
||||
@@ -1564,8 +1564,8 @@ checksums:
|
||||
environments/surveys/edit/unlock_targeting_description: 8e315dc41c2849754839a1460643c5fb
|
||||
environments/surveys/edit/unlock_targeting_title: 6098caf969cac64cd54e217471ae42d4
|
||||
environments/surveys/edit/unsaved_changes_warning: a164f276c9f7344022aa4640b32abcf9
|
||||
environments/surveys/edit/untitled_block: fdaa045139deff5cc65fa027df0cc22e
|
||||
environments/surveys/edit/until_they_submit_a_response: 2a0fd5dcc6cc40a72ed9b974f22eaf68
|
||||
environments/surveys/edit/untitled_block: fdaa045139deff5cc65fa027df0cc22e
|
||||
environments/surveys/edit/upgrade_notice_description: 32b66a4f257ad8d38bc38dcc95fe23c4
|
||||
environments/surveys/edit/upgrade_notice_title: 40866066ebc558ad0c92a4f19f12090c
|
||||
environments/surveys/edit/upload: 4a6c84aa16db0f4e5697f49b45257bc7
|
||||
|
||||
@@ -45,6 +45,7 @@ import {
|
||||
findElementLocation,
|
||||
moveBlock as moveBlockHelper,
|
||||
moveElementInBlock,
|
||||
renumberBlocks,
|
||||
updateElementInBlock,
|
||||
} from "@/modules/survey/editor/lib/blocks";
|
||||
import { findElementUsedInLogic, isUsedInQuota, isUsedInRecall } from "@/modules/survey/editor/lib/utils";
|
||||
@@ -765,7 +766,10 @@ export const ElementsView = ({
|
||||
const [movedBlock] = blocks.splice(sourceBlockIndex, 1);
|
||||
blocks.splice(destBlockIndex, 0, movedBlock);
|
||||
|
||||
setLocalSurvey({ ...localSurvey, blocks });
|
||||
// Renumber blocks sequentially after drag-and-drop reordering
|
||||
const renumberedBlocks = renumberBlocks(blocks);
|
||||
|
||||
setLocalSurvey({ ...localSurvey, blocks: renumberedBlocks });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
isElementIdUnique,
|
||||
moveBlock,
|
||||
moveElementInBlock,
|
||||
renumberBlocks,
|
||||
updateBlock,
|
||||
updateElementInBlock,
|
||||
} from "./blocks";
|
||||
@@ -83,6 +84,50 @@ const createMockSurvey = (blocks: TSurveyBlock[] = []): TSurvey => ({
|
||||
metadata: {},
|
||||
});
|
||||
|
||||
describe("renumberBlocks", () => {
|
||||
test("should renumber blocks sequentially starting from 1", () => {
|
||||
const blocks = [
|
||||
createMockBlock("block-1", "Old Name 1"),
|
||||
createMockBlock("block-2", "Old Name 2"),
|
||||
createMockBlock("block-3", "Old Name 3"),
|
||||
];
|
||||
|
||||
const result = renumberBlocks(blocks);
|
||||
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result[0].name).toBe("Block 1");
|
||||
expect(result[1].name).toBe("Block 2");
|
||||
expect(result[2].name).toBe("Block 3");
|
||||
});
|
||||
|
||||
test("should preserve block IDs and other properties", () => {
|
||||
const blocks = [
|
||||
createMockBlock("block-1", "Old Name 1", [createMockElement("q1")]),
|
||||
createMockBlock("block-2", "Old Name 2", [createMockElement("q2")]),
|
||||
];
|
||||
|
||||
const result = renumberBlocks(blocks);
|
||||
|
||||
expect(result[0].id).toBe("block-1");
|
||||
expect(result[1].id).toBe("block-2");
|
||||
expect(result[0].elements).toHaveLength(1);
|
||||
expect(result[1].elements).toHaveLength(1);
|
||||
});
|
||||
|
||||
test("should handle empty array", () => {
|
||||
const result = renumberBlocks([]);
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("should handle single block", () => {
|
||||
const blocks = [createMockBlock("block-1", "Old Name")];
|
||||
const result = renumberBlocks(blocks);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].name).toBe("Block 1");
|
||||
});
|
||||
});
|
||||
|
||||
describe("isElementIdUnique", () => {
|
||||
test("should return true for a unique element ID", () => {
|
||||
const blocks = [
|
||||
@@ -151,12 +196,12 @@ describe("findElementLocation", () => {
|
||||
describe("addBlock", () => {
|
||||
test("should add a block to empty survey", () => {
|
||||
const survey = createMockSurvey([]);
|
||||
const result = addBlock(mockT, survey, { name: "New Block" });
|
||||
const result = addBlock(mockT, survey, { name: "Block 1" });
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
if (result.ok) {
|
||||
expect(result.data.blocks).toHaveLength(1);
|
||||
expect(result.data.blocks[0].name).toBe("New Block");
|
||||
expect(result.data.blocks[0].name).toBe("Block 1");
|
||||
expect(result.data.blocks[0].elements).toEqual([]);
|
||||
}
|
||||
});
|
||||
@@ -182,19 +227,9 @@ describe("addBlock", () => {
|
||||
expect(result.ok).toBe(true);
|
||||
if (result.ok) {
|
||||
expect(result.data.blocks).toHaveLength(3);
|
||||
expect(result.data.blocks[1].name).toBe("Block 1.5");
|
||||
expect(result.data.blocks[1].name).toBe("Block 2");
|
||||
expect(result.data.blocks[0].name).toBe("Block 1");
|
||||
expect(result.data.blocks[2].name).toBe("Block 2");
|
||||
}
|
||||
});
|
||||
|
||||
test("should use default name if not provided", () => {
|
||||
const survey = createMockSurvey([]);
|
||||
const result = addBlock(mockT, survey, {});
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
if (result.ok) {
|
||||
expect(result.data.blocks[0].name).toBe("environments.surveys.edit.untitled_block");
|
||||
expect(result.data.blocks[2].name).toBe("Block 3");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -321,7 +356,7 @@ describe("duplicateBlock", () => {
|
||||
expect(result.ok).toBe(true);
|
||||
if (result.ok) {
|
||||
expect(result.data.blocks).toHaveLength(2);
|
||||
expect(result.data.blocks[1].name).toBe("Block 1 (copy)");
|
||||
expect(result.data.blocks[1].name).toBe("Block 2");
|
||||
expect(result.data.blocks[1].id).not.toBe("block-1");
|
||||
expect(result.data.blocks[1].elements[0].id).not.toBe("q1");
|
||||
expect(result.data.blocks[1].elements[1].id).not.toBe("q2");
|
||||
@@ -366,7 +401,7 @@ describe("duplicateBlock", () => {
|
||||
expect(result.ok).toBe(true);
|
||||
if (result.ok) {
|
||||
expect(result.data.blocks).toHaveLength(4);
|
||||
expect(result.data.blocks[2].name).toBe("Block 2 (copy)");
|
||||
expect(result.data.blocks[2].name).toBe("Block 3");
|
||||
expect(result.data.blocks[1].id).toBe("block-2");
|
||||
expect(result.data.blocks[3].id).toBe("block-3");
|
||||
}
|
||||
|
||||
@@ -52,6 +52,19 @@ export const findElementLocation = (
|
||||
// BLOCK OPERATIONS
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* Renumbers all blocks sequentially (Block 1, Block 2, Block 3, etc.)
|
||||
* This ensures block names stay in sync with their positions
|
||||
* @param blocks - Array of blocks to renumber
|
||||
* @returns Array of blocks with updated sequential names
|
||||
*/
|
||||
export const renumberBlocks = (blocks: TSurveyBlock[]): TSurveyBlock[] => {
|
||||
return blocks.map((block, index) => ({
|
||||
...block,
|
||||
name: `Block ${index + 1}`,
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a new block to the survey. Always generates a new CUID for the block ID to prevent conflicts
|
||||
* @param survey - The survey to add the block to
|
||||
@@ -87,7 +100,10 @@ export const addBlock = (
|
||||
blocks.splice(index, 0, newBlock);
|
||||
}
|
||||
|
||||
updatedSurvey.blocks = blocks;
|
||||
// Renumber blocks sequentially after adding
|
||||
const renumberedBlocks = renumberBlocks(blocks);
|
||||
|
||||
updatedSurvey.blocks = renumberedBlocks;
|
||||
return ok(updatedSurvey);
|
||||
};
|
||||
|
||||
@@ -144,9 +160,12 @@ export const deleteBlock = (survey: TSurvey, blockId: string): Result<TSurvey, E
|
||||
return err(new Error(`Block with ID "${blockId}" not found`));
|
||||
}
|
||||
|
||||
// Renumber blocks sequentially after deletion
|
||||
const renumberedBlocks = renumberBlocks(filteredBlocks);
|
||||
|
||||
return ok({
|
||||
...survey,
|
||||
blocks: filteredBlocks,
|
||||
blocks: renumberedBlocks,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -173,7 +192,7 @@ export const duplicateBlock = (survey: TSurvey, blockId: string): Result<TSurvey
|
||||
|
||||
// Assign new IDs
|
||||
duplicatedBlock.id = createId();
|
||||
duplicatedBlock.name = `${blockToDuplicate.name} (copy)`;
|
||||
// Name will be set by renumberBlocks to maintain sequential naming
|
||||
|
||||
// Generate new element IDs to avoid conflicts
|
||||
duplicatedBlock.elements = duplicatedBlock.elements.map((element) => ({
|
||||
@@ -190,9 +209,12 @@ export const duplicateBlock = (survey: TSurvey, blockId: string): Result<TSurvey
|
||||
const updatedBlocks = [...blocks];
|
||||
updatedBlocks.splice(blockIndex + 1, 0, duplicatedBlock);
|
||||
|
||||
// Renumber blocks sequentially after duplication
|
||||
const renumberedBlocks = renumberBlocks(updatedBlocks);
|
||||
|
||||
return ok({
|
||||
...survey,
|
||||
blocks: updatedBlocks,
|
||||
blocks: renumberedBlocks,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -228,9 +250,12 @@ export const moveBlock = (
|
||||
// Swap using destructuring assignment
|
||||
[blocks[blockIndex], blocks[targetIndex]] = [blocks[targetIndex], blocks[blockIndex]];
|
||||
|
||||
// Renumber blocks sequentially after reordering
|
||||
const renumberedBlocks = renumberBlocks(blocks);
|
||||
|
||||
return ok({
|
||||
...survey,
|
||||
blocks,
|
||||
blocks: renumberedBlocks,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1307,18 +1307,7 @@ export const ZSurvey = z
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Validate block names are unique (for editor usability)
|
||||
const blockNames = blocks.map((b) => b.name);
|
||||
const uniqueBlockNames = new Set(blockNames);
|
||||
if (uniqueBlockNames.size !== blockNames.length) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Block names must be unique",
|
||||
path: ["blocks", blockNames.findIndex((name, index) => blockNames.indexOf(name) !== index), "name"],
|
||||
});
|
||||
}
|
||||
|
||||
// 3. Build map of all elements across all blocks
|
||||
// 2. Build map of all elements across all blocks
|
||||
const allElements = new Map<string, { block: number; element: number; data: TSurveyElement }>();
|
||||
blocks.forEach((block, blockIdx) => {
|
||||
block.elements.forEach((element, elemIdx) => {
|
||||
|
||||
Reference in New Issue
Block a user