@pie-element/hotspot 10.0.0-next.43 → 10.1.2-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1032 -2486
- package/configure/CHANGELOG.md +881 -2216
- package/configure/lib/defaults.js +3 -0
- package/configure/lib/defaults.js.map +1 -1
- package/configure/lib/hotspot-circle.js +6 -4
- package/configure/lib/hotspot-circle.js.map +1 -1
- package/configure/lib/hotspot-drawable.js +5 -5
- package/configure/lib/hotspot-drawable.js.map +1 -1
- package/configure/lib/hotspot-polygon.js +1 -2
- package/configure/lib/hotspot-polygon.js.map +1 -1
- package/configure/lib/hotspot-rectangle.js +6 -5
- package/configure/lib/hotspot-rectangle.js.map +1 -1
- package/configure/lib/utils.js +2 -3
- package/configure/lib/utils.js.map +1 -1
- package/configure/package.json +6 -6
- package/configure/src/__tests__/DeleteWidget.test.jsx +366 -0
- package/configure/src/__tests__/button.test.jsx +198 -0
- package/configure/src/__tests__/hotspot-circle.test.jsx +259 -0
- package/configure/src/__tests__/hotspot-palette.test.jsx +71 -0
- package/configure/src/__tests__/image-konva.test.jsx +226 -0
- package/configure/src/defaults.js +1 -0
- package/configure/src/hotspot-circle.jsx +4 -2
- package/configure/src/hotspot-drawable.jsx +1 -1
- package/configure/src/hotspot-polygon.jsx +0 -1
- package/configure/src/hotspot-rectangle.jsx +4 -3
- package/configure/src/utils.js +1 -1
- package/controller/CHANGELOG.md +600 -1532
- package/controller/lib/index.js +2 -2
- package/controller/lib/index.js.map +1 -1
- package/controller/lib/utils.js +3 -5
- package/controller/lib/utils.js.map +1 -1
- package/controller/package.json +3 -3
- package/controller/src/index.js +1 -1
- package/controller/src/utils.js +1 -2
- package/lib/hotspot/circle.js +1 -2
- package/lib/hotspot/circle.js.map +1 -1
- package/lib/hotspot/polygon.js +0 -1
- package/lib/hotspot/polygon.js.map +1 -1
- package/lib/hotspot/rectangle.js +0 -1
- package/lib/hotspot/rectangle.js.map +1 -1
- package/package.json +7 -7
- package/src/hotspot/__tests__/circle.test.jsx +464 -0
- package/src/hotspot/__tests__/container.test.jsx +546 -0
- package/src/hotspot/__tests__/image-konva-tooltip.test.jsx +510 -0
- package/src/hotspot/__tests__/polygon.test.jsx +502 -0
- package/src/hotspot/__tests__/rectangle.test.jsx +418 -0
- package/src/hotspot/circle.jsx +0 -1
- package/src/hotspot/polygon.jsx +0 -1
- package/src/hotspot/rectangle.jsx +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","names":["_cloneDeep","_interopRequireDefault","require","_shapes","updateImageDimensions","initialDim","nextDim","keepAspectRatio","resizeType","imageAspectRatio","width","height","exports","getDelta","referenceInitialValue","referenceNextValue","currentValue","getUpdatedRectangle","shape","x","y","getUpdatedCircles","radius","getUpdatedPolygon","points","map","point","getUpdatedShapes","shapes","group","SHAPE_GROUPS","RECTANGLES","POLYGONS","CIRCLES","getAllShapes","shapesMap","shapesArray","shapesKeys","Object","keys","length","reduce","acc","currentShapeKey","concat","index","groupShapes","rectangles","polygons","circles","shapeProps","cloneDeep","isPointInsidePolygon","polygon","inside","i","j","xi","yi","xj","yj","intersect","calculate","polygonPoints","xPoints","yPoints","minX","Math","min","minY","maxX","max","maxY","textX","textY","generateValidationMessage","config","minShapes","maxShapes","maxSelections","shapesMessage","selectionsMessage","message"],"sources":["../src/utils.js"],"sourcesContent":["import cloneDeep from 'lodash/cloneDeep';\nimport { SHAPE_GROUPS } from './shapes';\n\nconst updateImageDimensions = (initialDim, nextDim, keepAspectRatio, resizeType) => {\n // if we want to keep image aspect ratio\n if (keepAspectRatio) {\n const imageAspectRatio = initialDim.width / initialDim.height;\n\n if (resizeType === 'height') {\n // if we want to change image height => we update the width accordingly\n return {\n width: nextDim.height * imageAspectRatio,\n height: nextDim.height,\n };\n }\n\n // if we want to change image width => we update the height accordingly\n return {\n width: nextDim.width,\n height: nextDim.width / imageAspectRatio,\n };\n }\n\n // if we don't want to keep aspect ratio, we just update both values\n return {\n width: nextDim.width,\n height: nextDim.height,\n };\n};\n\n// referenceInitialValue = the initial value of the Stage\n// referenceNextValue = the next value of the Stage\n// currentValue = the value that has to be re-sized influenced by the changes that were made on the Stage\nconst getDelta = (referenceInitialValue, referenceNextValue, currentValue) =>\n (referenceNextValue / referenceInitialValue) * currentValue;\n\nconst getUpdatedRectangle = (initialDim, nextDim, shape) => ({\n ...shape,\n width: getDelta(initialDim.width, nextDim.width, shape.width),\n height: getDelta(initialDim.height, nextDim.height, shape.height),\n x: getDelta(initialDim.width, nextDim.width, shape.x),\n y: getDelta(initialDim.height, nextDim.height, shape.y),\n});\n\nconst getUpdatedCircles = (initialDim, nextDim, shape) => ({\n ...shape,\n radius: getDelta(initialDim.width, nextDim.width, shape.radius),\n x: getDelta(initialDim.width, nextDim.width, shape.x),\n y: getDelta(initialDim.height, nextDim.height, shape.y),\n});\n\nconst getUpdatedPolygon = (initialDim, nextDim, shape) => ({\n ...shape,\n points: shape.points.map((point) => ({\n x: getDelta(initialDim.width, nextDim.width, point.x),\n y: getDelta(initialDim.height, nextDim.height, point.y),\n })),\n});\n\n// initialDim = the initial dimensions: { width, height } of the Stage\n// nextDim = the next dimensions: { width, height } of the Stage\n// shapes = array of shapes that have to be re-sized and re-positioned\nconst getUpdatedShapes = (initialDim, nextDim, shapes) => {\n return shapes.map((shape) => {\n if (shape.group === SHAPE_GROUPS.RECTANGLES) {\n return getUpdatedRectangle(initialDim, nextDim, shape);\n }\n\n if (shape.group === SHAPE_GROUPS.POLYGONS) {\n return getUpdatedPolygon(initialDim, nextDim, shape);\n }\n\n if (shape.group === SHAPE_GROUPS.CIRCLES) {\n return getUpdatedCircles(initialDim, nextDim, shape);\n }\n });\n};\n\n// converts shapes map to shapes array\n// example:\n// from: { rectangles: [r1], polygons: [p1, p2]}\n// to: [{ ...r1, group: 'rectangles' }, { ...p1, group: 'polygons' }, { ...p2, group: 'polygons' }]\n// if a shape has index defined, keep it, otherwise initialize it\n// index is used for the UNDO function\nconst getAllShapes = (shapesMap) => {\n shapesMap = shapesMap || {};\n const shapesArray = [];\n const shapesKeys = Object.keys(shapesMap);\n\n return shapesKeys.length\n ? shapesKeys.reduce(\n (acc, currentShapeKey) =>\n acc.concat(\n shapesMap[currentShapeKey]\n ? shapesMap[currentShapeKey].map((shape, index) => ({\n ...shape,\n group: currentShapeKey,\n index: shape.index || acc.length + index,\n }))\n : [],\n ),\n shapesArray,\n )\n : shapesArray;\n};\n\n// converts shapes array to shapes map\n// is the reverse of getAllShapes function\n// example:\n// from: [{ ...r1, group: 'rectangles' }, { ...p1, group: 'polygons' }, { ...p2, group: 'polygons' }]\n// to: { rectangles: [r1], polygons: [p1, p2]}\nconst groupShapes = (shapesArray) => {\n shapesArray = shapesArray || [];\n const shapesMap = {\n rectangles: [],\n polygons: [],\n circles: [],\n };\n\n if (shapesArray.length) {\n return shapesArray.reduce((acc, { group, ...shapeProps }) => {\n acc[group] = [...(acc[group] || []), shapeProps];\n return acc;\n }, shapesMap);\n }\n\n return cloneDeep(shapesMap);\n};\n\nconst isPointInsidePolygon = (polygon, x, y) => {\n let inside = false;\n\n if (!polygon || polygon.length <= 0) {\n return inside;\n }\n\n for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {\n const xi = polygon[i].x;\n const yi = polygon[i].y;\n const xj = polygon[j].x;\n const yj = polygon[j].y;\n\n const intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;\n\n if (intersect) {\n inside = !inside;\n }\n }\n\n return inside;\n};\n\nconst calculate = (polygonPoints) => {\n if (!polygonPoints || polygonPoints.length <= 0) {\n return { x: 0, y: 0 };\n }\n\n const xPoints = polygonPoints.map((point) => point.x);\n const yPoints = polygonPoints.map((point) => point.y);\n const minX = Math.min(...xPoints);\n const minY = Math.min(...yPoints);\n const maxX = Math.max(...xPoints);\n const maxY = Math.max(...yPoints);\n\n // Find a suitable position for the text element within the polygon\n let textX, textY;\n\n for (let x = minX; x <= maxX - 20; x++) {\n for (let y = maxY - 20; y > minY; y--) {\n // Check if the text element's position (x, y) is within the polygon\n if (isPointInsidePolygon(polygonPoints, x, y)) {\n textX = x - 10;\n textY = y;\n break;\n }\n }\n }\n\n return { x: textX, y: textY };\n};\n\nconst generateValidationMessage = (config) => {\n const { minShapes, maxShapes, maxSelections } = config;\n\n const shapesMessage =\n `\\nThere should be at least ${minShapes} ` + (maxShapes ? `and at most ${maxShapes} ` : '') + 'shapes defined.';\n\n const selectionsMessage =\n '\\nThere should be at least 1 ' +\n (maxSelections ? `and at most ${maxSelections} ` : '') +\n 'shape' +\n (maxSelections ? 's' : '') +\n ' selected.';\n\n const message = 'Validation requirements:' + shapesMessage + selectionsMessage;\n\n return message;\n};\n\nexport {\n calculate,\n isPointInsidePolygon,\n updateImageDimensions,\n generateValidationMessage,\n getUpdatedShapes,\n getAllShapes,\n groupShapes,\n getUpdatedRectangle,\n getUpdatedPolygon,\n};\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"utils.js","names":["_lodashEs","require","_shapes","updateImageDimensions","initialDim","nextDim","keepAspectRatio","resizeType","imageAspectRatio","width","height","exports","getDelta","referenceInitialValue","referenceNextValue","currentValue","getUpdatedRectangle","shape","x","y","getUpdatedCircles","radius","getUpdatedPolygon","points","map","point","getUpdatedShapes","shapes","group","SHAPE_GROUPS","RECTANGLES","POLYGONS","CIRCLES","getAllShapes","shapesMap","shapesArray","shapesKeys","Object","keys","length","reduce","acc","currentShapeKey","concat","index","groupShapes","rectangles","polygons","circles","shapeProps","cloneDeep","isPointInsidePolygon","polygon","inside","i","j","xi","yi","xj","yj","intersect","calculate","polygonPoints","xPoints","yPoints","minX","Math","min","minY","maxX","max","maxY","textX","textY","generateValidationMessage","config","minShapes","maxShapes","maxSelections","shapesMessage","selectionsMessage","message"],"sources":["../src/utils.js"],"sourcesContent":["import { cloneDeep } from 'lodash-es';\nimport { SHAPE_GROUPS } from './shapes';\n\nconst updateImageDimensions = (initialDim, nextDim, keepAspectRatio, resizeType) => {\n // if we want to keep image aspect ratio\n if (keepAspectRatio) {\n const imageAspectRatio = initialDim.width / initialDim.height;\n\n if (resizeType === 'height') {\n // if we want to change image height => we update the width accordingly\n return {\n width: nextDim.height * imageAspectRatio,\n height: nextDim.height,\n };\n }\n\n // if we want to change image width => we update the height accordingly\n return {\n width: nextDim.width,\n height: nextDim.width / imageAspectRatio,\n };\n }\n\n // if we don't want to keep aspect ratio, we just update both values\n return {\n width: nextDim.width,\n height: nextDim.height,\n };\n};\n\n// referenceInitialValue = the initial value of the Stage\n// referenceNextValue = the next value of the Stage\n// currentValue = the value that has to be re-sized influenced by the changes that were made on the Stage\nconst getDelta = (referenceInitialValue, referenceNextValue, currentValue) =>\n (referenceNextValue / referenceInitialValue) * currentValue;\n\nconst getUpdatedRectangle = (initialDim, nextDim, shape) => ({\n ...shape,\n width: getDelta(initialDim.width, nextDim.width, shape.width),\n height: getDelta(initialDim.height, nextDim.height, shape.height),\n x: getDelta(initialDim.width, nextDim.width, shape.x),\n y: getDelta(initialDim.height, nextDim.height, shape.y),\n});\n\nconst getUpdatedCircles = (initialDim, nextDim, shape) => ({\n ...shape,\n radius: getDelta(initialDim.width, nextDim.width, shape.radius),\n x: getDelta(initialDim.width, nextDim.width, shape.x),\n y: getDelta(initialDim.height, nextDim.height, shape.y),\n});\n\nconst getUpdatedPolygon = (initialDim, nextDim, shape) => ({\n ...shape,\n points: shape.points.map((point) => ({\n x: getDelta(initialDim.width, nextDim.width, point.x),\n y: getDelta(initialDim.height, nextDim.height, point.y),\n })),\n});\n\n// initialDim = the initial dimensions: { width, height } of the Stage\n// nextDim = the next dimensions: { width, height } of the Stage\n// shapes = array of shapes that have to be re-sized and re-positioned\nconst getUpdatedShapes = (initialDim, nextDim, shapes) => {\n return shapes.map((shape) => {\n if (shape.group === SHAPE_GROUPS.RECTANGLES) {\n return getUpdatedRectangle(initialDim, nextDim, shape);\n }\n\n if (shape.group === SHAPE_GROUPS.POLYGONS) {\n return getUpdatedPolygon(initialDim, nextDim, shape);\n }\n\n if (shape.group === SHAPE_GROUPS.CIRCLES) {\n return getUpdatedCircles(initialDim, nextDim, shape);\n }\n });\n};\n\n// converts shapes map to shapes array\n// example:\n// from: { rectangles: [r1], polygons: [p1, p2]}\n// to: [{ ...r1, group: 'rectangles' }, { ...p1, group: 'polygons' }, { ...p2, group: 'polygons' }]\n// if a shape has index defined, keep it, otherwise initialize it\n// index is used for the UNDO function\nconst getAllShapes = (shapesMap) => {\n shapesMap = shapesMap || {};\n const shapesArray = [];\n const shapesKeys = Object.keys(shapesMap);\n\n return shapesKeys.length\n ? shapesKeys.reduce(\n (acc, currentShapeKey) =>\n acc.concat(\n shapesMap[currentShapeKey]\n ? shapesMap[currentShapeKey].map((shape, index) => ({\n ...shape,\n group: currentShapeKey,\n index: shape.index || acc.length + index,\n }))\n : [],\n ),\n shapesArray,\n )\n : shapesArray;\n};\n\n// converts shapes array to shapes map\n// is the reverse of getAllShapes function\n// example:\n// from: [{ ...r1, group: 'rectangles' }, { ...p1, group: 'polygons' }, { ...p2, group: 'polygons' }]\n// to: { rectangles: [r1], polygons: [p1, p2]}\nconst groupShapes = (shapesArray) => {\n shapesArray = shapesArray || [];\n const shapesMap = {\n rectangles: [],\n polygons: [],\n circles: [],\n };\n\n if (shapesArray.length) {\n return shapesArray.reduce((acc, { group, ...shapeProps }) => {\n acc[group] = [...(acc[group] || []), shapeProps];\n return acc;\n }, shapesMap);\n }\n\n return cloneDeep(shapesMap);\n};\n\nconst isPointInsidePolygon = (polygon, x, y) => {\n let inside = false;\n\n if (!polygon || polygon.length <= 0) {\n return inside;\n }\n\n for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {\n const xi = polygon[i].x;\n const yi = polygon[i].y;\n const xj = polygon[j].x;\n const yj = polygon[j].y;\n\n const intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;\n\n if (intersect) {\n inside = !inside;\n }\n }\n\n return inside;\n};\n\nconst calculate = (polygonPoints) => {\n if (!polygonPoints || polygonPoints.length <= 0) {\n return { x: 0, y: 0 };\n }\n\n const xPoints = polygonPoints.map((point) => point.x);\n const yPoints = polygonPoints.map((point) => point.y);\n const minX = Math.min(...xPoints);\n const minY = Math.min(...yPoints);\n const maxX = Math.max(...xPoints);\n const maxY = Math.max(...yPoints);\n\n // Find a suitable position for the text element within the polygon\n let textX, textY;\n\n for (let x = minX; x <= maxX - 20; x++) {\n for (let y = maxY - 20; y > minY; y--) {\n // Check if the text element's position (x, y) is within the polygon\n if (isPointInsidePolygon(polygonPoints, x, y)) {\n textX = x - 10;\n textY = y;\n break;\n }\n }\n }\n\n return { x: textX, y: textY };\n};\n\nconst generateValidationMessage = (config) => {\n const { minShapes, maxShapes, maxSelections } = config;\n\n const shapesMessage =\n `\\nThere should be at least ${minShapes} ` + (maxShapes ? `and at most ${maxShapes} ` : '') + 'shapes defined.';\n\n const selectionsMessage =\n '\\nThere should be at least 1 ' +\n (maxSelections ? `and at most ${maxSelections} ` : '') +\n 'shape' +\n (maxSelections ? 's' : '') +\n ' selected.';\n\n const message = 'Validation requirements:' + shapesMessage + selectionsMessage;\n\n return message;\n};\n\nexport {\n calculate,\n isPointInsidePolygon,\n updateImageDimensions,\n generateValidationMessage,\n getUpdatedShapes,\n getAllShapes,\n groupShapes,\n getUpdatedRectangle,\n getUpdatedPolygon,\n};\n"],"mappings":";;;;;;AAAA,IAAAA,SAAA,GAAAC,OAAA;AACA,IAAAC,OAAA,GAAAD,OAAA;AAEA,MAAME,qBAAqB,GAAGA,CAACC,UAAU,EAAEC,OAAO,EAAEC,eAAe,EAAEC,UAAU,KAAK;EAClF;EACA,IAAID,eAAe,EAAE;IACnB,MAAME,gBAAgB,GAAGJ,UAAU,CAACK,KAAK,GAAGL,UAAU,CAACM,MAAM;IAE7D,IAAIH,UAAU,KAAK,QAAQ,EAAE;MAC3B;MACA,OAAO;QACLE,KAAK,EAAEJ,OAAO,CAACK,MAAM,GAAGF,gBAAgB;QACxCE,MAAM,EAAEL,OAAO,CAACK;MAClB,CAAC;IACH;;IAEA;IACA,OAAO;MACLD,KAAK,EAAEJ,OAAO,CAACI,KAAK;MACpBC,MAAM,EAAEL,OAAO,CAACI,KAAK,GAAGD;IAC1B,CAAC;EACH;;EAEA;EACA,OAAO;IACLC,KAAK,EAAEJ,OAAO,CAACI,KAAK;IACpBC,MAAM,EAAEL,OAAO,CAACK;EAClB,CAAC;AACH,CAAC;;AAED;AACA;AACA;AAAAC,OAAA,CAAAR,qBAAA,GAAAA,qBAAA;AACA,MAAMS,QAAQ,GAAGA,CAACC,qBAAqB,EAAEC,kBAAkB,EAAEC,YAAY,KACtED,kBAAkB,GAAGD,qBAAqB,GAAIE,YAAY;AAE7D,MAAMC,mBAAmB,GAAGA,CAACZ,UAAU,EAAEC,OAAO,EAAEY,KAAK,MAAM;EAC3D,GAAGA,KAAK;EACRR,KAAK,EAAEG,QAAQ,CAACR,UAAU,CAACK,KAAK,EAAEJ,OAAO,CAACI,KAAK,EAAEQ,KAAK,CAACR,KAAK,CAAC;EAC7DC,MAAM,EAAEE,QAAQ,CAACR,UAAU,CAACM,MAAM,EAAEL,OAAO,CAACK,MAAM,EAAEO,KAAK,CAACP,MAAM,CAAC;EACjEQ,CAAC,EAAEN,QAAQ,CAACR,UAAU,CAACK,KAAK,EAAEJ,OAAO,CAACI,KAAK,EAAEQ,KAAK,CAACC,CAAC,CAAC;EACrDC,CAAC,EAAEP,QAAQ,CAACR,UAAU,CAACM,MAAM,EAAEL,OAAO,CAACK,MAAM,EAAEO,KAAK,CAACE,CAAC;AACxD,CAAC,CAAC;AAACR,OAAA,CAAAK,mBAAA,GAAAA,mBAAA;AAEH,MAAMI,iBAAiB,GAAGA,CAAChB,UAAU,EAAEC,OAAO,EAAEY,KAAK,MAAM;EACzD,GAAGA,KAAK;EACRI,MAAM,EAAET,QAAQ,CAACR,UAAU,CAACK,KAAK,EAAEJ,OAAO,CAACI,KAAK,EAAEQ,KAAK,CAACI,MAAM,CAAC;EAC/DH,CAAC,EAAEN,QAAQ,CAACR,UAAU,CAACK,KAAK,EAAEJ,OAAO,CAACI,KAAK,EAAEQ,KAAK,CAACC,CAAC,CAAC;EACrDC,CAAC,EAAEP,QAAQ,CAACR,UAAU,CAACM,MAAM,EAAEL,OAAO,CAACK,MAAM,EAAEO,KAAK,CAACE,CAAC;AACxD,CAAC,CAAC;AAEF,MAAMG,iBAAiB,GAAGA,CAAClB,UAAU,EAAEC,OAAO,EAAEY,KAAK,MAAM;EACzD,GAAGA,KAAK;EACRM,MAAM,EAAEN,KAAK,CAACM,MAAM,CAACC,GAAG,CAAEC,KAAK,KAAM;IACnCP,CAAC,EAAEN,QAAQ,CAACR,UAAU,CAACK,KAAK,EAAEJ,OAAO,CAACI,KAAK,EAAEgB,KAAK,CAACP,CAAC,CAAC;IACrDC,CAAC,EAAEP,QAAQ,CAACR,UAAU,CAACM,MAAM,EAAEL,OAAO,CAACK,MAAM,EAAEe,KAAK,CAACN,CAAC;EACxD,CAAC,CAAC;AACJ,CAAC,CAAC;;AAEF;AACA;AACA;AAAAR,OAAA,CAAAW,iBAAA,GAAAA,iBAAA;AACA,MAAMI,gBAAgB,GAAGA,CAACtB,UAAU,EAAEC,OAAO,EAAEsB,MAAM,KAAK;EACxD,OAAOA,MAAM,CAACH,GAAG,CAAEP,KAAK,IAAK;IAC3B,IAAIA,KAAK,CAACW,KAAK,KAAKC,oBAAY,CAACC,UAAU,EAAE;MAC3C,OAAOd,mBAAmB,CAACZ,UAAU,EAAEC,OAAO,EAAEY,KAAK,CAAC;IACxD;IAEA,IAAIA,KAAK,CAACW,KAAK,KAAKC,oBAAY,CAACE,QAAQ,EAAE;MACzC,OAAOT,iBAAiB,CAAClB,UAAU,EAAEC,OAAO,EAAEY,KAAK,CAAC;IACtD;IAEA,IAAIA,KAAK,CAACW,KAAK,KAAKC,oBAAY,CAACG,OAAO,EAAE;MACxC,OAAOZ,iBAAiB,CAAChB,UAAU,EAAEC,OAAO,EAAEY,KAAK,CAAC;IACtD;EACF,CAAC,CAAC;AACJ,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AAAAN,OAAA,CAAAe,gBAAA,GAAAA,gBAAA;AACA,MAAMO,YAAY,GAAIC,SAAS,IAAK;EAClCA,SAAS,GAAGA,SAAS,IAAI,CAAC,CAAC;EAC3B,MAAMC,WAAW,GAAG,EAAE;EACtB,MAAMC,UAAU,GAAGC,MAAM,CAACC,IAAI,CAACJ,SAAS,CAAC;EAEzC,OAAOE,UAAU,CAACG,MAAM,GACpBH,UAAU,CAACI,MAAM,CACf,CAACC,GAAG,EAAEC,eAAe,KACnBD,GAAG,CAACE,MAAM,CACRT,SAAS,CAACQ,eAAe,CAAC,GACtBR,SAAS,CAACQ,eAAe,CAAC,CAAClB,GAAG,CAAC,CAACP,KAAK,EAAE2B,KAAK,MAAM;IAChD,GAAG3B,KAAK;IACRW,KAAK,EAAEc,eAAe;IACtBE,KAAK,EAAE3B,KAAK,CAAC2B,KAAK,IAAIH,GAAG,CAACF,MAAM,GAAGK;EACrC,CAAC,CAAC,CAAC,GACH,EACN,CAAC,EACHT,WACF,CAAC,GACDA,WAAW;AACjB,CAAC;;AAED;AACA;AACA;AACA;AACA;AAAAxB,OAAA,CAAAsB,YAAA,GAAAA,YAAA;AACA,MAAMY,WAAW,GAAIV,WAAW,IAAK;EACnCA,WAAW,GAAGA,WAAW,IAAI,EAAE;EAC/B,MAAMD,SAAS,GAAG;IAChBY,UAAU,EAAE,EAAE;IACdC,QAAQ,EAAE,EAAE;IACZC,OAAO,EAAE;EACX,CAAC;EAED,IAAIb,WAAW,CAACI,MAAM,EAAE;IACtB,OAAOJ,WAAW,CAACK,MAAM,CAAC,CAACC,GAAG,EAAE;MAAEb,KAAK;MAAE,GAAGqB;IAAW,CAAC,KAAK;MAC3DR,GAAG,CAACb,KAAK,CAAC,GAAG,CAAC,IAAIa,GAAG,CAACb,KAAK,CAAC,IAAI,EAAE,CAAC,EAAEqB,UAAU,CAAC;MAChD,OAAOR,GAAG;IACZ,CAAC,EAAEP,SAAS,CAAC;EACf;EAEA,OAAO,IAAAgB,mBAAS,EAAChB,SAAS,CAAC;AAC7B,CAAC;AAACvB,OAAA,CAAAkC,WAAA,GAAAA,WAAA;AAEF,MAAMM,oBAAoB,GAAGA,CAACC,OAAO,EAAElC,CAAC,EAAEC,CAAC,KAAK;EAC9C,IAAIkC,MAAM,GAAG,KAAK;EAElB,IAAI,CAACD,OAAO,IAAIA,OAAO,CAACb,MAAM,IAAI,CAAC,EAAE;IACnC,OAAOc,MAAM;EACf;EAEA,KAAK,IAAIC,CAAC,GAAG,CAAC,EAAEC,CAAC,GAAGH,OAAO,CAACb,MAAM,GAAG,CAAC,EAAEe,CAAC,GAAGF,OAAO,CAACb,MAAM,EAAEgB,CAAC,GAAGD,CAAC,EAAE,EAAE;IACnE,MAAME,EAAE,GAAGJ,OAAO,CAACE,CAAC,CAAC,CAACpC,CAAC;IACvB,MAAMuC,EAAE,GAAGL,OAAO,CAACE,CAAC,CAAC,CAACnC,CAAC;IACvB,MAAMuC,EAAE,GAAGN,OAAO,CAACG,CAAC,CAAC,CAACrC,CAAC;IACvB,MAAMyC,EAAE,GAAGP,OAAO,CAACG,CAAC,CAAC,CAACpC,CAAC;IAEvB,MAAMyC,SAAS,GAAGH,EAAE,GAAGtC,CAAC,KAAKwC,EAAE,GAAGxC,CAAC,IAAID,CAAC,GAAI,CAACwC,EAAE,GAAGF,EAAE,KAAKrC,CAAC,GAAGsC,EAAE,CAAC,IAAKE,EAAE,GAAGF,EAAE,CAAC,GAAGD,EAAE;IAElF,IAAII,SAAS,EAAE;MACbP,MAAM,GAAG,CAACA,MAAM;IAClB;EACF;EAEA,OAAOA,MAAM;AACf,CAAC;AAAC1C,OAAA,CAAAwC,oBAAA,GAAAA,oBAAA;AAEF,MAAMU,SAAS,GAAIC,aAAa,IAAK;EACnC,IAAI,CAACA,aAAa,IAAIA,aAAa,CAACvB,MAAM,IAAI,CAAC,EAAE;IAC/C,OAAO;MAAErB,CAAC,EAAE,CAAC;MAAEC,CAAC,EAAE;IAAE,CAAC;EACvB;EAEA,MAAM4C,OAAO,GAAGD,aAAa,CAACtC,GAAG,CAAEC,KAAK,IAAKA,KAAK,CAACP,CAAC,CAAC;EACrD,MAAM8C,OAAO,GAAGF,aAAa,CAACtC,GAAG,CAAEC,KAAK,IAAKA,KAAK,CAACN,CAAC,CAAC;EACrD,MAAM8C,IAAI,GAAGC,IAAI,CAACC,GAAG,CAAC,GAAGJ,OAAO,CAAC;EACjC,MAAMK,IAAI,GAAGF,IAAI,CAACC,GAAG,CAAC,GAAGH,OAAO,CAAC;EACjC,MAAMK,IAAI,GAAGH,IAAI,CAACI,GAAG,CAAC,GAAGP,OAAO,CAAC;EACjC,MAAMQ,IAAI,GAAGL,IAAI,CAACI,GAAG,CAAC,GAAGN,OAAO,CAAC;;EAEjC;EACA,IAAIQ,KAAK,EAAEC,KAAK;EAEhB,KAAK,IAAIvD,CAAC,GAAG+C,IAAI,EAAE/C,CAAC,IAAImD,IAAI,GAAG,EAAE,EAAEnD,CAAC,EAAE,EAAE;IACtC,KAAK,IAAIC,CAAC,GAAGoD,IAAI,GAAG,EAAE,EAAEpD,CAAC,GAAGiD,IAAI,EAAEjD,CAAC,EAAE,EAAE;MACrC;MACA,IAAIgC,oBAAoB,CAACW,aAAa,EAAE5C,CAAC,EAAEC,CAAC,CAAC,EAAE;QAC7CqD,KAAK,GAAGtD,CAAC,GAAG,EAAE;QACduD,KAAK,GAAGtD,CAAC;QACT;MACF;IACF;EACF;EAEA,OAAO;IAAED,CAAC,EAAEsD,KAAK;IAAErD,CAAC,EAAEsD;EAAM,CAAC;AAC/B,CAAC;AAAC9D,OAAA,CAAAkD,SAAA,GAAAA,SAAA;AAEF,MAAMa,yBAAyB,GAAIC,MAAM,IAAK;EAC5C,MAAM;IAAEC,SAAS;IAAEC,SAAS;IAAEC;EAAc,CAAC,GAAGH,MAAM;EAEtD,MAAMI,aAAa,GACjB,8BAA8BH,SAAS,GAAG,IAAIC,SAAS,GAAG,eAAeA,SAAS,GAAG,GAAG,EAAE,CAAC,GAAG,iBAAiB;EAEjH,MAAMG,iBAAiB,GACrB,+BAA+B,IAC9BF,aAAa,GAAG,eAAeA,aAAa,GAAG,GAAG,EAAE,CAAC,GACtD,OAAO,IACNA,aAAa,GAAG,GAAG,GAAG,EAAE,CAAC,GAC1B,YAAY;EAEd,MAAMG,OAAO,GAAG,0BAA0B,GAAGF,aAAa,GAAGC,iBAAiB;EAE9E,OAAOC,OAAO;AAChB,CAAC;AAACtE,OAAA,CAAA+D,yBAAA,GAAAA,yBAAA","ignoreList":[]}
|
package/configure/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pie-element/hotspot-configure",
|
|
3
3
|
"private": true,
|
|
4
|
-
"version": "9.
|
|
4
|
+
"version": "9.1.2-next.1",
|
|
5
5
|
"description": "",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"module": "src/index.js",
|
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
"@mui/icons-material": "^7.3.4",
|
|
13
13
|
"@mui/material": "^7.3.4",
|
|
14
14
|
"@pie-framework/pie-configure-events": "^1.3.0",
|
|
15
|
-
"@pie-lib/config-ui": "12.
|
|
16
|
-
"@pie-lib/editable-html-tip-tap": "1.
|
|
15
|
+
"@pie-lib/config-ui": "12.2.0-next.11",
|
|
16
|
+
"@pie-lib/editable-html-tip-tap": "1.2.0-next.11",
|
|
17
17
|
"debug": "^4.1.1",
|
|
18
18
|
"konva": "8.3.0",
|
|
19
|
-
"lodash": "^4.17.
|
|
19
|
+
"lodash-es": "^4.17.23",
|
|
20
20
|
"prop-types": "^15.7.2",
|
|
21
|
-
"react": "18.
|
|
22
|
-
"react-dom": "18.
|
|
21
|
+
"react": "18.3.1",
|
|
22
|
+
"react-dom": "18.3.1",
|
|
23
23
|
"react-konva": "^18.2.14"
|
|
24
24
|
},
|
|
25
25
|
"license": "ISC"
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, fireEvent } from '@testing-library/react';
|
|
3
|
+
import Konva from 'konva';
|
|
4
|
+
import DeleteWidget from '../DeleteWidget';
|
|
5
|
+
|
|
6
|
+
Konva.isBrowser = false;
|
|
7
|
+
|
|
8
|
+
jest.mock('react-konva', () => {
|
|
9
|
+
const React = require('react');
|
|
10
|
+
return {
|
|
11
|
+
Group: ({ children, onClick, ...props }) => {
|
|
12
|
+
return React.createElement('div', { 'data-testid': 'group', onClick, ...props }, children);
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
jest.mock('../image-konva', () => {
|
|
18
|
+
return function ImageComponent({ src, x, y }) {
|
|
19
|
+
return <div data-testid="delete-icon" data-src={src} data-x={x} data-y={y} />;
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
jest.mock('../utils', () => ({
|
|
24
|
+
calculate: jest.fn((points) => {
|
|
25
|
+
const xValues = points.map(p => p.x);
|
|
26
|
+
const yValues = points.map(p => p.y);
|
|
27
|
+
return {
|
|
28
|
+
x: Math.max(...xValues),
|
|
29
|
+
y: Math.max(...yValues),
|
|
30
|
+
};
|
|
31
|
+
}),
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
describe('DeleteWidget', () => {
|
|
35
|
+
let defaultProps;
|
|
36
|
+
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
defaultProps = {
|
|
39
|
+
id: 'shape1',
|
|
40
|
+
x: 100,
|
|
41
|
+
y: 150,
|
|
42
|
+
handleWidgetClick: jest.fn(),
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('rendering', () => {
|
|
47
|
+
it('should render without crashing', () => {
|
|
48
|
+
const { container } = render(<DeleteWidget {...defaultProps} width={200} height={150} />);
|
|
49
|
+
expect(container).toBeTruthy();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should render Group component', () => {
|
|
53
|
+
const { getByTestId } = render(<DeleteWidget {...defaultProps} width={200} height={150} />);
|
|
54
|
+
expect(getByTestId('group')).toBeInTheDocument();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should render delete icon', () => {
|
|
58
|
+
const { getByTestId } = render(<DeleteWidget {...defaultProps} width={200} height={150} />);
|
|
59
|
+
expect(getByTestId('delete-icon')).toBeInTheDocument();
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('rectangle positioning', () => {
|
|
64
|
+
it('should position delete icon at bottom-right for rectangles', () => {
|
|
65
|
+
const { getByTestId } = render(
|
|
66
|
+
<DeleteWidget
|
|
67
|
+
{...defaultProps}
|
|
68
|
+
x={100}
|
|
69
|
+
y={150}
|
|
70
|
+
width={200}
|
|
71
|
+
height={150}
|
|
72
|
+
/>
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const icon = getByTestId('delete-icon');
|
|
76
|
+
// positionX = x + width - offset = 100 + 200 - 20 = 280
|
|
77
|
+
// positionY = y + height - offset = 150 + 150 - 20 = 280
|
|
78
|
+
expect(icon).toHaveAttribute('data-x', '280');
|
|
79
|
+
expect(icon).toHaveAttribute('data-y', '280');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should handle different rectangle dimensions', () => {
|
|
83
|
+
const { getByTestId } = render(
|
|
84
|
+
<DeleteWidget
|
|
85
|
+
{...defaultProps}
|
|
86
|
+
x={50}
|
|
87
|
+
y={75}
|
|
88
|
+
width={300}
|
|
89
|
+
height={200}
|
|
90
|
+
/>
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const icon = getByTestId('delete-icon');
|
|
94
|
+
// positionX = 50 + 300 - 20 = 330
|
|
95
|
+
// positionY = 75 + 200 - 20 = 255
|
|
96
|
+
expect(icon).toHaveAttribute('data-x', '330');
|
|
97
|
+
expect(icon).toHaveAttribute('data-y', '255');
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe('circle positioning', () => {
|
|
102
|
+
it('should position delete icon above circle', () => {
|
|
103
|
+
const { getByTestId } = render(
|
|
104
|
+
<DeleteWidget
|
|
105
|
+
{...defaultProps}
|
|
106
|
+
x={200}
|
|
107
|
+
y={200}
|
|
108
|
+
isCircle={true}
|
|
109
|
+
radius={50}
|
|
110
|
+
/>
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const icon = getByTestId('delete-icon');
|
|
114
|
+
// positionX = x + radius - offset = 200 + 50 - 20 = 230
|
|
115
|
+
// positionY = y = 200
|
|
116
|
+
expect(icon).toHaveAttribute('data-x', '230');
|
|
117
|
+
expect(icon).toHaveAttribute('data-y', '200');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should handle different circle sizes', () => {
|
|
121
|
+
const { getByTestId } = render(
|
|
122
|
+
<DeleteWidget
|
|
123
|
+
{...defaultProps}
|
|
124
|
+
x={100}
|
|
125
|
+
y={100}
|
|
126
|
+
isCircle={true}
|
|
127
|
+
radius={75}
|
|
128
|
+
/>
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const icon = getByTestId('delete-icon');
|
|
132
|
+
// positionX = 100 + 75 - 20 = 155
|
|
133
|
+
// positionY = 100
|
|
134
|
+
expect(icon).toHaveAttribute('data-x', '155');
|
|
135
|
+
expect(icon).toHaveAttribute('data-y', '100');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should handle small circles', () => {
|
|
139
|
+
const { getByTestId } = render(
|
|
140
|
+
<DeleteWidget
|
|
141
|
+
{...defaultProps}
|
|
142
|
+
x={150}
|
|
143
|
+
y={150}
|
|
144
|
+
isCircle={true}
|
|
145
|
+
radius={20}
|
|
146
|
+
/>
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const icon = getByTestId('delete-icon');
|
|
150
|
+
// positionX = 150 + 20 - 20 = 150
|
|
151
|
+
// positionY = 150
|
|
152
|
+
expect(icon).toHaveAttribute('data-x', '150');
|
|
153
|
+
expect(icon).toHaveAttribute('data-y', '150');
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe('polygon positioning', () => {
|
|
158
|
+
it('should position delete icon using calculate function for polygons', () => {
|
|
159
|
+
const { calculate } = require('../utils');
|
|
160
|
+
const points = [
|
|
161
|
+
{ x: 10, y: 10 },
|
|
162
|
+
{ x: 100, y: 20 },
|
|
163
|
+
{ x: 90, y: 90 },
|
|
164
|
+
{ x: 20, y: 80 },
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
const { getByTestId } = render(
|
|
168
|
+
<DeleteWidget
|
|
169
|
+
{...defaultProps}
|
|
170
|
+
points={points}
|
|
171
|
+
/>
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
expect(calculate).toHaveBeenCalledWith(points);
|
|
175
|
+
|
|
176
|
+
const icon = getByTestId('delete-icon');
|
|
177
|
+
// Based on mocked calculate function: max x = 100, max y = 90
|
|
178
|
+
expect(icon).toHaveAttribute('data-x', '100');
|
|
179
|
+
expect(icon).toHaveAttribute('data-y', '90');
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should handle triangular polygons', () => {
|
|
183
|
+
const { calculate } = require('../utils');
|
|
184
|
+
const points = [
|
|
185
|
+
{ x: 50, y: 0 },
|
|
186
|
+
{ x: 100, y: 100 },
|
|
187
|
+
{ x: 0, y: 100 },
|
|
188
|
+
];
|
|
189
|
+
|
|
190
|
+
render(
|
|
191
|
+
<DeleteWidget
|
|
192
|
+
{...defaultProps}
|
|
193
|
+
points={points}
|
|
194
|
+
/>
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
expect(calculate).toHaveBeenCalledWith(points);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('should handle complex polygons', () => {
|
|
201
|
+
const { calculate } = require('../utils');
|
|
202
|
+
const points = [
|
|
203
|
+
{ x: 10, y: 10 },
|
|
204
|
+
{ x: 50, y: 5 },
|
|
205
|
+
{ x: 90, y: 10 },
|
|
206
|
+
{ x: 100, y: 50 },
|
|
207
|
+
{ x: 90, y: 90 },
|
|
208
|
+
{ x: 50, y: 100 },
|
|
209
|
+
{ x: 10, y: 90 },
|
|
210
|
+
{ x: 0, y: 50 },
|
|
211
|
+
];
|
|
212
|
+
|
|
213
|
+
render(
|
|
214
|
+
<DeleteWidget
|
|
215
|
+
{...defaultProps}
|
|
216
|
+
points={points}
|
|
217
|
+
/>
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
expect(calculate).toHaveBeenCalledWith(points);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
describe('interactions', () => {
|
|
225
|
+
it('should call handleWidgetClick when clicked', () => {
|
|
226
|
+
const handleWidgetClick = jest.fn();
|
|
227
|
+
const { getByTestId } = render(
|
|
228
|
+
<DeleteWidget
|
|
229
|
+
{...defaultProps}
|
|
230
|
+
handleWidgetClick={handleWidgetClick}
|
|
231
|
+
width={200}
|
|
232
|
+
height={150}
|
|
233
|
+
/>
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
const group = getByTestId('group');
|
|
237
|
+
fireEvent.click(group);
|
|
238
|
+
|
|
239
|
+
expect(handleWidgetClick).toHaveBeenCalledWith('shape1');
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('should call handleWidgetClick with correct id for circles', () => {
|
|
243
|
+
const handleWidgetClick = jest.fn();
|
|
244
|
+
const { getByTestId } = render(
|
|
245
|
+
<DeleteWidget
|
|
246
|
+
{...defaultProps}
|
|
247
|
+
id="circle1"
|
|
248
|
+
handleWidgetClick={handleWidgetClick}
|
|
249
|
+
isCircle={true}
|
|
250
|
+
radius={50}
|
|
251
|
+
/>
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
const group = getByTestId('group');
|
|
255
|
+
fireEvent.click(group);
|
|
256
|
+
|
|
257
|
+
expect(handleWidgetClick).toHaveBeenCalledWith('circle1');
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('should call handleWidgetClick with correct id for polygons', () => {
|
|
261
|
+
const handleWidgetClick = jest.fn();
|
|
262
|
+
const points = [
|
|
263
|
+
{ x: 10, y: 10 },
|
|
264
|
+
{ x: 100, y: 20 },
|
|
265
|
+
{ x: 50, y: 100 },
|
|
266
|
+
];
|
|
267
|
+
const { getByTestId } = render(
|
|
268
|
+
<DeleteWidget
|
|
269
|
+
{...defaultProps}
|
|
270
|
+
id="polygon1"
|
|
271
|
+
handleWidgetClick={handleWidgetClick}
|
|
272
|
+
points={points}
|
|
273
|
+
/>
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
const group = getByTestId('group');
|
|
277
|
+
fireEvent.click(group);
|
|
278
|
+
|
|
279
|
+
expect(handleWidgetClick).toHaveBeenCalledWith('polygon1');
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
describe('edge cases', () => {
|
|
284
|
+
it('should handle zero dimensions for rectangles', () => {
|
|
285
|
+
const { getByTestId } = render(
|
|
286
|
+
<DeleteWidget
|
|
287
|
+
{...defaultProps}
|
|
288
|
+
x={0}
|
|
289
|
+
y={0}
|
|
290
|
+
width={0}
|
|
291
|
+
height={0}
|
|
292
|
+
/>
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
const icon = getByTestId('delete-icon');
|
|
296
|
+
// positionX = 0 + 0 - 20 = -20
|
|
297
|
+
// positionY = 0 + 0 - 20 = -20
|
|
298
|
+
expect(icon).toHaveAttribute('data-x', '-20');
|
|
299
|
+
expect(icon).toHaveAttribute('data-y', '-20');
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it('should handle zero radius for circles', () => {
|
|
303
|
+
const { getByTestId } = render(
|
|
304
|
+
<DeleteWidget
|
|
305
|
+
{...defaultProps}
|
|
306
|
+
x={100}
|
|
307
|
+
y={100}
|
|
308
|
+
isCircle={true}
|
|
309
|
+
radius={0}
|
|
310
|
+
/>
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
const icon = getByTestId('delete-icon');
|
|
314
|
+
// positionX = 100 + 0 - 20 = 80
|
|
315
|
+
// positionY = 100
|
|
316
|
+
expect(icon).toHaveAttribute('data-x', '80');
|
|
317
|
+
expect(icon).toHaveAttribute('data-y', '100');
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it('should handle negative coordinates', () => {
|
|
321
|
+
const { getByTestId } = render(
|
|
322
|
+
<DeleteWidget
|
|
323
|
+
{...defaultProps}
|
|
324
|
+
x={-50}
|
|
325
|
+
y={-75}
|
|
326
|
+
width={100}
|
|
327
|
+
height={100}
|
|
328
|
+
/>
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
const icon = getByTestId('delete-icon');
|
|
332
|
+
// positionX = -50 + 100 - 20 = 30
|
|
333
|
+
// positionY = -75 + 100 - 20 = 5
|
|
334
|
+
expect(icon).toHaveAttribute('data-x', '30');
|
|
335
|
+
expect(icon).toHaveAttribute('data-y', '5');
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it('should handle single point polygon', () => {
|
|
339
|
+
const points = [{ x: 50, y: 50 }];
|
|
340
|
+
|
|
341
|
+
const { container } = render(
|
|
342
|
+
<DeleteWidget
|
|
343
|
+
{...defaultProps}
|
|
344
|
+
points={points}
|
|
345
|
+
/>
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
expect(container).toBeTruthy();
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
describe('icon rendering', () => {
|
|
353
|
+
it('should pass correct src to ImageComponent', () => {
|
|
354
|
+
const { getByTestId } = render(
|
|
355
|
+
<DeleteWidget
|
|
356
|
+
{...defaultProps}
|
|
357
|
+
width={200}
|
|
358
|
+
height={150}
|
|
359
|
+
/>
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
const icon = getByTestId('delete-icon');
|
|
363
|
+
expect(icon).toHaveAttribute('data-src');
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
});
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, fireEvent } from '@testing-library/react';
|
|
3
|
+
import RawButton from '../button';
|
|
4
|
+
|
|
5
|
+
describe('RawButton', () => {
|
|
6
|
+
describe('rendering', () => {
|
|
7
|
+
it('should render without crashing', () => {
|
|
8
|
+
const { container } = render(<RawButton />);
|
|
9
|
+
expect(container).toBeTruthy();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should render with default label', () => {
|
|
13
|
+
const { getByText } = render(<RawButton />);
|
|
14
|
+
expect(getByText('Add')).toBeInTheDocument();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should render with custom label', () => {
|
|
18
|
+
const { getByText } = render(<RawButton label="Custom Label" />);
|
|
19
|
+
expect(getByText('Custom Label')).toBeInTheDocument();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should render as a button element', () => {
|
|
23
|
+
const { getByRole } = render(<RawButton label="Test Button" />);
|
|
24
|
+
expect(getByRole('button')).toBeInTheDocument();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should apply custom className', () => {
|
|
28
|
+
const { getByRole } = render(<RawButton label="Test" className="custom-class" />);
|
|
29
|
+
const button = getByRole('button');
|
|
30
|
+
expect(button).toHaveClass('custom-class');
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('interactions', () => {
|
|
35
|
+
it('should call onClick when clicked', () => {
|
|
36
|
+
const onClick = jest.fn();
|
|
37
|
+
const { getByRole } = render(<RawButton label="Click Me" onClick={onClick} />);
|
|
38
|
+
|
|
39
|
+
const button = getByRole('button');
|
|
40
|
+
fireEvent.click(button);
|
|
41
|
+
|
|
42
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should not call onClick when disabled', () => {
|
|
46
|
+
const onClick = jest.fn();
|
|
47
|
+
const { getByRole } = render(<RawButton label="Disabled" onClick={onClick} disabled={true} />);
|
|
48
|
+
|
|
49
|
+
const button = getByRole('button');
|
|
50
|
+
fireEvent.click(button);
|
|
51
|
+
|
|
52
|
+
expect(onClick).not.toHaveBeenCalled();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should call onClick multiple times', () => {
|
|
56
|
+
const onClick = jest.fn();
|
|
57
|
+
const { getByRole } = render(<RawButton label="Multi Click" onClick={onClick} />);
|
|
58
|
+
|
|
59
|
+
const button = getByRole('button');
|
|
60
|
+
fireEvent.click(button);
|
|
61
|
+
fireEvent.click(button);
|
|
62
|
+
fireEvent.click(button);
|
|
63
|
+
|
|
64
|
+
expect(onClick).toHaveBeenCalledTimes(3);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('disabled state', () => {
|
|
69
|
+
it('should be enabled by default', () => {
|
|
70
|
+
const { getByRole } = render(<RawButton label="Test" />);
|
|
71
|
+
const button = getByRole('button');
|
|
72
|
+
expect(button).not.toBeDisabled();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should be disabled when disabled prop is true', () => {
|
|
76
|
+
const { getByRole } = render(<RawButton label="Test" disabled={true} />);
|
|
77
|
+
const button = getByRole('button');
|
|
78
|
+
expect(button).toBeDisabled();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should be enabled when disabled prop is false', () => {
|
|
82
|
+
const { getByRole } = render(<RawButton label="Test" disabled={false} />);
|
|
83
|
+
const button = getByRole('button');
|
|
84
|
+
expect(button).not.toBeDisabled();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('default props', () => {
|
|
89
|
+
it('should use default onClick when not provided', () => {
|
|
90
|
+
const { getByRole } = render(<RawButton label="Test" />);
|
|
91
|
+
const button = getByRole('button');
|
|
92
|
+
|
|
93
|
+
// Should not throw error when clicked
|
|
94
|
+
expect(() => fireEvent.click(button)).not.toThrow();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should use default label "Add" when not provided', () => {
|
|
98
|
+
const { getByText } = render(<RawButton />);
|
|
99
|
+
expect(getByText('Add')).toBeInTheDocument();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should use empty className by default', () => {
|
|
103
|
+
const { getByRole } = render(<RawButton label="Test" />);
|
|
104
|
+
const button = getByRole('button');
|
|
105
|
+
expect(button.className).toBeTruthy(); // Will have MUI classes
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should be enabled by default', () => {
|
|
109
|
+
const { getByRole } = render(<RawButton label="Test" />);
|
|
110
|
+
const button = getByRole('button');
|
|
111
|
+
expect(button).not.toBeDisabled();
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('variant and size', () => {
|
|
116
|
+
it('should render with contained variant', () => {
|
|
117
|
+
const { getByRole } = render(<RawButton label="Test" />);
|
|
118
|
+
const button = getByRole('button');
|
|
119
|
+
expect(button).toHaveClass('MuiButton-contained');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should render with small size', () => {
|
|
123
|
+
const { getByRole } = render(<RawButton label="Test" />);
|
|
124
|
+
const button = getByRole('button');
|
|
125
|
+
expect(button).toHaveClass('MuiButton-sizeSmall');
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe('edge cases', () => {
|
|
130
|
+
it('should handle empty label', () => {
|
|
131
|
+
const { getByRole } = render(<RawButton label="" />);
|
|
132
|
+
const button = getByRole('button');
|
|
133
|
+
expect(button).toBeInTheDocument();
|
|
134
|
+
expect(button.textContent).toBe('');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should handle very long label', () => {
|
|
138
|
+
const longLabel = 'This is a very long button label that might wrap or overflow';
|
|
139
|
+
const { getByText } = render(<RawButton label={longLabel} />);
|
|
140
|
+
expect(getByText(longLabel)).toBeInTheDocument();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should handle special characters in label', () => {
|
|
144
|
+
const specialLabel = '!@#$%^&*()_+-=[]{}|;:",.<>?/~`';
|
|
145
|
+
const { getByText } = render(<RawButton label={specialLabel} />);
|
|
146
|
+
expect(getByText(specialLabel)).toBeInTheDocument();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should handle Unicode characters in label', () => {
|
|
150
|
+
const unicodeLabel = '🚀 Launch 你好 مرحبا';
|
|
151
|
+
const { getByText } = render(<RawButton label={unicodeLabel} />);
|
|
152
|
+
expect(getByText(unicodeLabel)).toBeInTheDocument();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('should handle null onClick gracefully with default', () => {
|
|
156
|
+
const { getByRole } = render(<RawButton label="Test" onClick={null} />);
|
|
157
|
+
const button = getByRole('button');
|
|
158
|
+
|
|
159
|
+
// Should use default onClick
|
|
160
|
+
expect(() => fireEvent.click(button)).not.toThrow();
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe('prop updates', () => {
|
|
165
|
+
it('should update label when prop changes', () => {
|
|
166
|
+
const { getByText, rerender } = render(<RawButton label="Initial" />);
|
|
167
|
+
expect(getByText('Initial')).toBeInTheDocument();
|
|
168
|
+
|
|
169
|
+
rerender(<RawButton label="Updated" />);
|
|
170
|
+
expect(getByText('Updated')).toBeInTheDocument();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('should update disabled state when prop changes', () => {
|
|
174
|
+
const { getByRole, rerender } = render(<RawButton label="Test" disabled={false} />);
|
|
175
|
+
const button = getByRole('button');
|
|
176
|
+
expect(button).not.toBeDisabled();
|
|
177
|
+
|
|
178
|
+
rerender(<RawButton label="Test" disabled={true} />);
|
|
179
|
+
expect(button).toBeDisabled();
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should update onClick handler when prop changes', () => {
|
|
183
|
+
const onClick1 = jest.fn();
|
|
184
|
+
const onClick2 = jest.fn();
|
|
185
|
+
const { getByRole, rerender } = render(<RawButton label="Test" onClick={onClick1} />);
|
|
186
|
+
|
|
187
|
+
const button = getByRole('button');
|
|
188
|
+
fireEvent.click(button);
|
|
189
|
+
expect(onClick1).toHaveBeenCalledTimes(1);
|
|
190
|
+
expect(onClick2).not.toHaveBeenCalled();
|
|
191
|
+
|
|
192
|
+
rerender(<RawButton label="Test" onClick={onClick2} />);
|
|
193
|
+
fireEvent.click(button);
|
|
194
|
+
expect(onClick1).toHaveBeenCalledTimes(1);
|
|
195
|
+
expect(onClick2).toHaveBeenCalledTimes(1);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
});
|