fix: block naming issue (#6899)

This commit is contained in:
Dhruwang Jariwala
2025-11-28 12:41:04 +05:30
committed by GitHub
parent 7a0da3a3e4
commit 527f8fd0d5
5 changed files with 89 additions and 36 deletions
+2 -2
View File
@@ -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");
}
+30 -5
View File
@@ -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,
});
};
+1 -12
View File
@@ -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) => {