diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md
index 5c37f0360..398449902 100644
--- a/ci/release/changelogs/next.md
+++ b/ci/release/changelogs/next.md
@@ -12,3 +12,4 @@
#### Bugfixes ⛑️
- Shadow is cut off when `--pad` is 0. Thank you @LeonardsonCC ! [#1326](https://github.com/terrastruct/d2/pull/1326)
+- Fixes grid layout overwriting label placements for nested objects. [#1345](https://github.com/terrastruct/d2/pull/1345)
diff --git a/d2graph/d2graph.go b/d2graph/d2graph.go
index 006bfae85..301cc5bd8 100644
--- a/d2graph/d2graph.go
+++ b/d2graph/d2graph.go
@@ -1817,90 +1817,6 @@ func (g *Graph) ApplyTheme(themeID int64) error {
return nil
}
-func (obj *Object) MoveWithDescendants(dx, dy float64) {
- obj.TopLeft.X += dx
- obj.TopLeft.Y += dy
- for _, child := range obj.ChildrenArray {
- child.MoveWithDescendants(dx, dy)
- }
-}
-
-func (obj *Object) MoveWithDescendantsTo(x, y float64) {
- dx := x - obj.TopLeft.X
- dy := y - obj.TopLeft.Y
- obj.MoveWithDescendants(dx, dy)
-}
-
-func (parent *Object) removeChild(child *Object) {
- delete(parent.Children, strings.ToLower(child.ID))
- for i := 0; i < len(parent.ChildrenArray); i++ {
- if parent.ChildrenArray[i] == child {
- parent.ChildrenArray = append(parent.ChildrenArray[:i], parent.ChildrenArray[i+1:]...)
- break
- }
- }
-}
-
-// remove obj and all descendants from graph, as a new Graph
-func (g *Graph) ExtractAsNestedGraph(obj *Object) *Graph {
- descendantObjects, edges := pluckObjAndEdges(g, obj)
-
- tempGraph := NewGraph()
- tempGraph.Root.ChildrenArray = []*Object{obj}
- tempGraph.Root.Children[strings.ToLower(obj.ID)] = obj
-
- for _, descendantObj := range descendantObjects {
- descendantObj.Graph = tempGraph
- }
- tempGraph.Objects = descendantObjects
- tempGraph.Edges = edges
-
- obj.Parent.removeChild(obj)
- obj.Parent = tempGraph.Root
-
- return tempGraph
-}
-
-func pluckObjAndEdges(g *Graph, obj *Object) (descendantsObjects []*Object, edges []*Edge) {
- for i := 0; i < len(g.Edges); i++ {
- edge := g.Edges[i]
- if edge.Src == obj || edge.Dst == obj {
- edges = append(edges, edge)
- g.Edges = append(g.Edges[:i], g.Edges[i+1:]...)
- i--
- }
- }
-
- for i := 0; i < len(g.Objects); i++ {
- temp := g.Objects[i]
- if temp.AbsID() == obj.AbsID() {
- descendantsObjects = append(descendantsObjects, obj)
- g.Objects = append(g.Objects[:i], g.Objects[i+1:]...)
- for _, child := range obj.ChildrenArray {
- subObjects, subEdges := pluckObjAndEdges(g, child)
- descendantsObjects = append(descendantsObjects, subObjects...)
- edges = append(edges, subEdges...)
- }
- break
- }
- }
-
- return descendantsObjects, edges
-}
-
-func (g *Graph) InjectNestedGraph(tempGraph *Graph, parent *Object) {
- obj := tempGraph.Root.ChildrenArray[0]
- obj.MoveWithDescendantsTo(0, 0)
- obj.Parent = parent
- for _, obj := range tempGraph.Objects {
- obj.Graph = g
- }
- g.Objects = append(g.Objects, tempGraph.Objects...)
- parent.Children[strings.ToLower(obj.ID)] = obj
- parent.ChildrenArray = append(parent.ChildrenArray, obj)
- g.Edges = append(g.Edges, tempGraph.Edges...)
-}
-
func (g *Graph) PrintString() string {
buf := &bytes.Buffer{}
fmt.Fprint(buf, "Objects: [")
@@ -1918,54 +1834,6 @@ func (obj *Object) IterDescendants(apply func(parent, child *Object)) {
}
}
-// ShiftDescendants moves Object's descendants (not including itself)
-// descendants' edges are also moved by the same dx and dy (the whole route is moved if both ends are a descendant)
-func (obj *Object) ShiftDescendants(dx, dy float64) {
- // also need to shift edges of descendants that are shifted
- movedEdges := make(map[*Edge]struct{})
- for _, e := range obj.Graph.Edges {
- isSrcDesc := e.Src.IsDescendantOf(obj)
- isDstDesc := e.Dst.IsDescendantOf(obj)
-
- if isSrcDesc && isDstDesc {
- movedEdges[e] = struct{}{}
- for _, p := range e.Route {
- p.X += dx
- p.Y += dy
- }
- }
- }
-
- obj.IterDescendants(func(_, curr *Object) {
- curr.TopLeft.X += dx
- curr.TopLeft.Y += dy
- for _, e := range obj.Graph.Edges {
- if _, ok := movedEdges[e]; ok {
- continue
- }
- isSrc := e.Src == curr
- isDst := e.Dst == curr
-
- if isSrc && isDst {
- for _, p := range e.Route {
- p.X += dx
- p.Y += dy
- }
- } else if isSrc {
- e.Route[0].X += dx
- e.Route[0].Y += dy
- } else if isDst {
- e.Route[len(e.Route)-1].X += dx
- e.Route[len(e.Route)-1].Y += dy
- }
-
- if isSrc || isDst {
- movedEdges[e] = struct{}{}
- }
- }
- })
-}
-
func (obj *Object) IsMultiple() bool {
return obj.Style.Multiple != nil && obj.Style.Multiple.Value == "true"
}
@@ -1973,19 +1841,3 @@ func (obj *Object) IsMultiple() bool {
func (obj *Object) Is3D() bool {
return obj.Style.ThreeDee != nil && obj.Style.ThreeDee.Value == "true"
}
-
-// GetModifierElementAdjustments returns width/height adjustments to account for shapes with 3d or multiple
-func (obj *Object) GetModifierElementAdjustments() (dx, dy float64) {
- if obj.Is3D() {
- if obj.Shape.Value == d2target.ShapeHexagon {
- dy = d2target.THREE_DEE_OFFSET / 2
- } else {
- dy = d2target.THREE_DEE_OFFSET
- }
- dx = d2target.THREE_DEE_OFFSET
- } else if obj.IsMultiple() {
- dy = d2target.MULTIPLE_OFFSET
- dx = d2target.MULTIPLE_OFFSET
- }
- return dx, dy
-}
diff --git a/d2graph/layout.go b/d2graph/layout.go
new file mode 100644
index 000000000..43342daa1
--- /dev/null
+++ b/d2graph/layout.go
@@ -0,0 +1,191 @@
+package d2graph
+
+import (
+ "strings"
+
+ "oss.terrastruct.com/d2/d2target"
+ "oss.terrastruct.com/d2/lib/geo"
+ "oss.terrastruct.com/d2/lib/label"
+ "oss.terrastruct.com/d2/lib/shape"
+)
+
+func (obj *Object) MoveWithDescendants(dx, dy float64) {
+ obj.TopLeft.X += dx
+ obj.TopLeft.Y += dy
+ for _, child := range obj.ChildrenArray {
+ child.MoveWithDescendants(dx, dy)
+ }
+}
+
+func (obj *Object) MoveWithDescendantsTo(x, y float64) {
+ dx := x - obj.TopLeft.X
+ dy := y - obj.TopLeft.Y
+ obj.MoveWithDescendants(dx, dy)
+}
+
+func (parent *Object) removeChild(child *Object) {
+ delete(parent.Children, strings.ToLower(child.ID))
+ for i := 0; i < len(parent.ChildrenArray); i++ {
+ if parent.ChildrenArray[i] == child {
+ parent.ChildrenArray = append(parent.ChildrenArray[:i], parent.ChildrenArray[i+1:]...)
+ break
+ }
+ }
+}
+
+// remove obj and all descendants from graph, as a new Graph
+func (g *Graph) ExtractAsNestedGraph(obj *Object) *Graph {
+ descendantObjects, edges := pluckObjAndEdges(g, obj)
+
+ tempGraph := NewGraph()
+ tempGraph.Root.ChildrenArray = []*Object{obj}
+ tempGraph.Root.Children[strings.ToLower(obj.ID)] = obj
+
+ for _, descendantObj := range descendantObjects {
+ descendantObj.Graph = tempGraph
+ }
+ tempGraph.Objects = descendantObjects
+ tempGraph.Edges = edges
+
+ obj.Parent.removeChild(obj)
+ obj.Parent = tempGraph.Root
+
+ return tempGraph
+}
+
+func pluckObjAndEdges(g *Graph, obj *Object) (descendantsObjects []*Object, edges []*Edge) {
+ for i := 0; i < len(g.Edges); i++ {
+ edge := g.Edges[i]
+ if edge.Src == obj || edge.Dst == obj {
+ edges = append(edges, edge)
+ g.Edges = append(g.Edges[:i], g.Edges[i+1:]...)
+ i--
+ }
+ }
+
+ for i := 0; i < len(g.Objects); i++ {
+ temp := g.Objects[i]
+ if temp.AbsID() == obj.AbsID() {
+ descendantsObjects = append(descendantsObjects, obj)
+ g.Objects = append(g.Objects[:i], g.Objects[i+1:]...)
+ for _, child := range obj.ChildrenArray {
+ subObjects, subEdges := pluckObjAndEdges(g, child)
+ descendantsObjects = append(descendantsObjects, subObjects...)
+ edges = append(edges, subEdges...)
+ }
+ break
+ }
+ }
+
+ return descendantsObjects, edges
+}
+
+func (g *Graph) InjectNestedGraph(tempGraph *Graph, parent *Object) {
+ obj := tempGraph.Root.ChildrenArray[0]
+ obj.MoveWithDescendantsTo(0, 0)
+ obj.Parent = parent
+ for _, obj := range tempGraph.Objects {
+ obj.Graph = g
+ }
+ g.Objects = append(g.Objects, tempGraph.Objects...)
+ parent.Children[strings.ToLower(obj.ID)] = obj
+ parent.ChildrenArray = append(parent.ChildrenArray, obj)
+ g.Edges = append(g.Edges, tempGraph.Edges...)
+}
+
+// ShiftDescendants moves Object's descendants (not including itself)
+// descendants' edges are also moved by the same dx and dy (the whole route is moved if both ends are a descendant)
+func (obj *Object) ShiftDescendants(dx, dy float64) {
+ // also need to shift edges of descendants that are shifted
+ movedEdges := make(map[*Edge]struct{})
+ for _, e := range obj.Graph.Edges {
+ isSrcDesc := e.Src.IsDescendantOf(obj)
+ isDstDesc := e.Dst.IsDescendantOf(obj)
+
+ if isSrcDesc && isDstDesc {
+ movedEdges[e] = struct{}{}
+ for _, p := range e.Route {
+ p.X += dx
+ p.Y += dy
+ }
+ }
+ }
+
+ obj.IterDescendants(func(_, curr *Object) {
+ curr.TopLeft.X += dx
+ curr.TopLeft.Y += dy
+ for _, e := range obj.Graph.Edges {
+ if _, ok := movedEdges[e]; ok {
+ continue
+ }
+ isSrc := e.Src == curr
+ isDst := e.Dst == curr
+
+ if isSrc && isDst {
+ for _, p := range e.Route {
+ p.X += dx
+ p.Y += dy
+ }
+ } else if isSrc {
+ e.Route[0].X += dx
+ e.Route[0].Y += dy
+ } else if isDst {
+ e.Route[len(e.Route)-1].X += dx
+ e.Route[len(e.Route)-1].Y += dy
+ }
+
+ if isSrc || isDst {
+ movedEdges[e] = struct{}{}
+ }
+ }
+ })
+}
+
+// GetModifierElementAdjustments returns width/height adjustments to account for shapes with 3d or multiple
+func (obj *Object) GetModifierElementAdjustments() (dx, dy float64) {
+ if obj.Is3D() {
+ if obj.Shape.Value == d2target.ShapeHexagon {
+ dy = d2target.THREE_DEE_OFFSET / 2
+ } else {
+ dy = d2target.THREE_DEE_OFFSET
+ }
+ dx = d2target.THREE_DEE_OFFSET
+ } else if obj.IsMultiple() {
+ dy = d2target.MULTIPLE_OFFSET
+ dx = d2target.MULTIPLE_OFFSET
+ }
+ return dx, dy
+}
+
+func (obj *Object) ToShape() shape.Shape {
+ tl := obj.TopLeft
+ if tl == nil {
+ tl = geo.NewPoint(0, 0)
+ }
+ dslShape := strings.ToLower(obj.Shape.Value)
+ shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[dslShape]
+ contentBox := geo.NewBox(tl, obj.Width, obj.Height)
+ return shape.NewShape(shapeType, contentBox)
+}
+
+func (obj *Object) GetLabelTopLeft() *geo.Point {
+ if obj.LabelPosition == nil {
+ return nil
+ }
+
+ s := obj.ToShape()
+ labelPosition := label.Position(*obj.LabelPosition)
+
+ var box *geo.Box
+ if labelPosition.IsOutside() {
+ box = s.GetBox()
+ } else {
+ box = s.GetInnerBox()
+ }
+
+ labelTL := labelPosition.GetPointOnBox(box, label.PADDING,
+ float64(obj.LabelDimensions.Width),
+ float64(obj.LabelDimensions.Height),
+ )
+ return labelTL
+}
diff --git a/d2layouts/d2dagrelayout/layout.go b/d2layouts/d2dagrelayout/layout.go
index db9c4f4b3..e033c61e7 100644
--- a/d2layouts/d2dagrelayout/layout.go
+++ b/d2layouts/d2dagrelayout/layout.go
@@ -119,9 +119,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
}
if obj.Icon != nil && obj.Shape.Value != d2target.ShapeImage {
- contentBox := geo.NewBox(geo.NewPoint(0, 0), float64(obj.Width), float64(obj.Height))
- shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[obj.Shape.Value]
- s := shape.NewShape(shapeType, contentBox)
+ s := obj.ToShape()
iconSize := d2target.GetIconSize(s.GetInnerBox(), string(label.InsideTopLeft))
// Since dagre container labels are pushed up, we don't want a child container to collide
maxContainerLabelHeight = go2.Max(maxContainerLabelHeight, (iconSize+label.PADDING*2)*2)
@@ -240,7 +238,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
obj.Width = math.Ceil(dn.Width)
obj.Height = math.Ceil(dn.Height)
- if obj.HasLabel() {
+ if obj.HasLabel() && obj.LabelPosition == nil {
if len(obj.ChildrenArray) > 0 {
obj.LabelPosition = go2.Pointer(string(label.OutsideTopCenter))
} else if obj.HasOutsideBottomLabel() {
@@ -253,7 +251,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
obj.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
}
}
- if obj.Icon != nil {
+ if obj.Icon != nil && obj.IconPosition == nil {
if len(obj.ChildrenArray) > 0 {
obj.IconPosition = go2.Pointer(string(label.OutsideTopLeft))
obj.LabelPosition = go2.Pointer(string(label.OutsideTopRight))
@@ -491,8 +489,8 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
}
}
- srcShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Src.Shape.Value)], edge.Src.Box)
- dstShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Dst.Shape.Value)], edge.Dst.Box)
+ srcShape := edge.Src.ToShape()
+ dstShape := edge.Dst.ToShape()
// trace the edge to the specific shape's border
points[startIndex] = shape.TraceToShapeBorder(srcShape, start, points[startIndex+1])
diff --git a/d2layouts/d2elklayout/layout.go b/d2layouts/d2elklayout/layout.go
index 5c29020a2..2fb5e160d 100644
--- a/d2layouts/d2elklayout/layout.go
+++ b/d2layouts/d2elklayout/layout.go
@@ -407,7 +407,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
obj.Width = math.Ceil(n.Width)
obj.Height = math.Ceil(n.Height)
- if obj.HasLabel() {
+ if obj.HasLabel() && obj.LabelPosition == nil {
if len(obj.ChildrenArray) > 0 {
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
} else if obj.HasOutsideBottomLabel() {
@@ -419,7 +419,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
obj.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
}
}
- if obj.Icon != nil {
+ if obj.Icon != nil && obj.IconPosition == nil {
if len(obj.ChildrenArray) > 0 {
obj.IconPosition = go2.Pointer(string(label.InsideTopLeft))
obj.LabelPosition = go2.Pointer(string(label.InsideTopRight))
@@ -501,8 +501,8 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
}
}
- srcShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Src.Shape.Value)], edge.Src.Box)
- dstShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Dst.Shape.Value)], edge.Dst.Box)
+ srcShape := edge.Src.ToShape()
+ dstShape := edge.Dst.ToShape()
// trace the edge to the specific shape's border
points[startIndex] = shape.TraceToShapeBorder(srcShape, points[startIndex], points[startIndex+1])
diff --git a/d2layouts/d2grid/layout.go b/d2layouts/d2grid/layout.go
index 5ddc75839..390470583 100644
--- a/d2layouts/d2grid/layout.go
+++ b/d2layouts/d2grid/layout.go
@@ -6,13 +6,11 @@ import (
"fmt"
"math"
"sort"
- "strings"
"oss.terrastruct.com/d2/d2graph"
"oss.terrastruct.com/d2/d2target"
"oss.terrastruct.com/d2/lib/geo"
"oss.terrastruct.com/d2/lib/label"
- "oss.terrastruct.com/d2/lib/shape"
"oss.terrastruct.com/util-go/go2"
)
@@ -104,42 +102,99 @@ func withoutGridDiagrams(ctx context.Context, g *d2graph.Graph, layout d2graph.L
if obj.GridGap != nil || obj.VerticalGap != nil {
verticalPadding = gd.verticalGap
}
+
// size shape according to grid
- obj.SizeToContent(float64(gd.width), float64(gd.height), float64(2*horizontalPadding), float64(2*verticalPadding))
+ obj.SizeToContent(gd.width, gd.height, float64(2*horizontalPadding), float64(2*verticalPadding))
// compute where the grid should be placed inside shape
- dslShape := strings.ToLower(obj.Shape.Value)
- shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[dslShape]
- s := shape.NewShape(shapeType, geo.NewBox(geo.NewPoint(0, 0), obj.Width, obj.Height))
+ s := obj.ToShape()
innerBox := s.GetInnerBox()
if innerBox.TopLeft.X != 0 || innerBox.TopLeft.Y != 0 {
gd.shift(innerBox.TopLeft.X, innerBox.TopLeft.Y)
}
+ // compute how much space the label and icon occupy
+ var occupiedWidth, occupiedHeight float64
+ if obj.Icon != nil {
+ iconSpace := float64(d2target.MAX_ICON_SIZE + 2*label.PADDING)
+ occupiedWidth = iconSpace
+ occupiedHeight = iconSpace
+ }
+
var dx, dy float64
- if obj.LabelDimensions.Width != 0 {
- labelWidth := float64(obj.LabelDimensions.Width) + 2*label.PADDING
- if labelWidth > obj.Width {
- dx = (labelWidth - obj.Width) / 2
- obj.Width = labelWidth
- }
- }
if obj.LabelDimensions.Height != 0 {
- labelHeight := float64(obj.LabelDimensions.Height) + 2*label.PADDING
- if labelHeight > float64(verticalPadding) {
- // if the label doesn't fit within the padding, we need to add more
- grow := labelHeight - float64(verticalPadding)
- dy = grow
- obj.Height += grow
+ occupiedHeight = math.Max(
+ occupiedHeight,
+ float64(obj.LabelDimensions.Height)+2*label.PADDING,
+ )
+ }
+ if obj.LabelDimensions.Width != 0 {
+ // . ├────┤───────├────┤
+ // . icon label icon
+ // with an icon in top left we need 2x the space to fit the label in the center
+ occupiedWidth *= 2
+ occupiedWidth += float64(obj.LabelDimensions.Width) + 2*label.PADDING
+ if occupiedWidth > obj.Width {
+ dx = (occupiedWidth - obj.Width) / 2
+ obj.Width = occupiedWidth
}
}
+
+ // also check for grid cells with outside top labels or icons
+ // the first grid object is at the top (and always exists)
+ topY := gd.objects[0].TopLeft.Y
+ highestOutside := topY
+ for _, o := range gd.objects {
+ // we only want to compute label positions for objects at the top of the grid
+ if o.TopLeft.Y > topY {
+ if gd.rowDirected {
+ // if the grid is rowDirected (row1, row2, etc) we can stop after finishing the first row
+ break
+ } else {
+ // otherwise we continue until the next column
+ continue
+ }
+ }
+ if o.LabelPosition != nil {
+ labelPosition := label.Position(*o.LabelPosition)
+ if labelPosition.IsOutside() {
+ labelTL := o.GetLabelTopLeft()
+ if labelTL.Y < highestOutside {
+ highestOutside = labelTL.Y
+ }
+ }
+ }
+ if o.IconPosition != nil {
+ switch label.Position(*o.IconPosition) {
+ case label.OutsideTopLeft, label.OutsideTopCenter, label.OutsideTopRight:
+ iconSpace := float64(d2target.MAX_ICON_SIZE + label.PADDING)
+ if topY-iconSpace < highestOutside {
+ highestOutside = topY - iconSpace
+ }
+ }
+ }
+ }
+ if highestOutside < topY {
+ occupiedHeight += topY - highestOutside + 2*label.PADDING
+ }
+ if occupiedHeight > float64(verticalPadding) {
+ // if the label doesn't fit within the padding, we need to add more
+ dy = occupiedHeight - float64(verticalPadding)
+ obj.Height += dy
+ }
+
// we need to center children if we have to expand to fit the container label
if dx != 0 || dy != 0 {
gd.shift(dx, dy)
}
}
- obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
+ if obj.HasLabel() {
+ obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
+ }
+ if obj.Icon != nil {
+ obj.IconPosition = go2.Pointer(string(label.InsideTopLeft))
+ }
gridDiagrams[obj.AbsID()] = gd
for _, o := range gd.objects {
@@ -191,10 +246,17 @@ func layoutGrid(g *d2graph.Graph, obj *d2graph.Object) (*gridDiagram, error) {
// position labels and icons
for _, o := range gd.objects {
if o.Icon != nil {
- o.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
- o.IconPosition = go2.Pointer(string(label.InsideMiddleCenter))
+ // don't overwrite position if nested graph layout positioned label/icon
+ if o.LabelPosition == nil {
+ o.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
+ }
+ if o.IconPosition == nil {
+ o.IconPosition = go2.Pointer(string(label.InsideMiddleCenter))
+ }
} else {
- o.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
+ if o.LabelPosition == nil {
+ o.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
+ }
}
}
diff --git a/e2etests/stable_test.go b/e2etests/stable_test.go
index f88f82fb5..ff25dfd6d 100644
--- a/e2etests/stable_test.go
+++ b/e2etests/stable_test.go
@@ -2723,6 +2723,7 @@ scenarios: {
loadFromFile(t, "grid_large_checkered"),
loadFromFile(t, "grid_nested"),
loadFromFile(t, "grid_nested_gap0"),
+ loadFromFile(t, "grid_icon"),
loadFromFile(t, "multiple_offset"),
loadFromFile(t, "multiple_offset_left"),
}
diff --git a/e2etests/testdata/files/grid_icon.d2 b/e2etests/testdata/files/grid_icon.d2
new file mode 100644
index 000000000..40fdce41e
--- /dev/null
+++ b/e2etests/testdata/files/grid_icon.d2
@@ -0,0 +1,67 @@
+classes: {
+ 2x2: {
+ grid-rows: 2
+ grid-columns: 2
+ }
+}
+
+grid w/ container + icon: {
+ class: 2x2
+
+ a
+ b: {
+ b child
+
+ icon: https://icons.terrastruct.com/dev%2Fgithub.svg
+ }
+ c
+ d
+}
+
+grid + icon: {
+ class: 2x2
+
+ icon: https://icons.terrastruct.com/dev%2Fgithub.svg
+
+ a
+ b
+ c
+ d
+}
+
+grid + icon w/ container: {
+ class: 2x2
+
+ icon: https://icons.terrastruct.com/dev%2Fgithub.svg
+
+ a
+ b: {
+ b child
+ }
+ c
+ d
+}
+
+no label grid w/ container + icon: "" {
+ class: 2x2
+
+ a
+ b: {
+ b child
+
+ icon: https://icons.terrastruct.com/dev%2Fgithub.svg
+ }
+ c
+ d
+}
+
+no label grid + icon: "" {
+ class: 2x2
+
+ icon: https://icons.terrastruct.com/dev%2Fgithub.svg
+
+ a
+ b
+ c
+ d
+}
diff --git a/e2etests/testdata/stable/grid_icon/dagre/board.exp.json b/e2etests/testdata/stable/grid_icon/dagre/board.exp.json
new file mode 100644
index 000000000..4ad3f73ef
--- /dev/null
+++ b/e2etests/testdata/stable/grid_icon/dagre/board.exp.json
@@ -0,0 +1,1271 @@
+{
+ "name": "",
+ "isFolderOnly": false,
+ "fontFamily": "SourceSansPro",
+ "shapes": [
+ {
+ "id": "grid w/ container + icon",
+ "type": "rectangle",
+ "classes": [
+ "2x2"
+ ],
+ "pos": {
+ "x": 0,
+ "y": 18
+ },
+ "width": 384,
+ "height": 421,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B4",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "grid w/ container + icon",
+ "fontSize": 28,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 273,
+ "labelHeight": 36,
+ "labelPosition": "INSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "grid w/ container + icon.a",
+ "type": "rectangle",
+ "pos": {
+ "x": 60,
+ "y": 143
+ },
+ "width": 53,
+ "height": 130,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "a",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid w/ container + icon.b",
+ "type": "rectangle",
+ "pos": {
+ "x": 153,
+ "y": 143
+ },
+ "width": 171,
+ "height": 130,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": {
+ "Scheme": "https",
+ "Opaque": "",
+ "User": null,
+ "Host": "icons.terrastruct.com",
+ "Path": "/dev/github.svg",
+ "RawPath": "/dev%2Fgithub.svg",
+ "OmitHost": false,
+ "ForceQuery": false,
+ "RawQuery": "",
+ "Fragment": "",
+ "RawFragment": ""
+ },
+ "iconPosition": "OUTSIDE_TOP_LEFT",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b",
+ "fontSize": 24,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 12,
+ "labelHeight": 31,
+ "labelPosition": "OUTSIDE_TOP_RIGHT",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid w/ container + icon.b.b child",
+ "type": "rectangle",
+ "pos": {
+ "x": 193,
+ "y": 175
+ },
+ "width": 91,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b child",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 46,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 3
+ },
+ {
+ "id": "grid w/ container + icon.c",
+ "type": "rectangle",
+ "pos": {
+ "x": 60,
+ "y": 313
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "c",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid w/ container + icon.d",
+ "type": "rectangle",
+ "pos": {
+ "x": 153,
+ "y": 313
+ },
+ "width": 171,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "d",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid + icon",
+ "type": "rectangle",
+ "classes": [
+ "2x2"
+ ],
+ "pos": {
+ "x": 444,
+ "y": 55
+ },
+ "width": 278,
+ "height": 347,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B4",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": {
+ "Scheme": "https",
+ "Opaque": "",
+ "User": null,
+ "Host": "icons.terrastruct.com",
+ "Path": "/dev/github.svg",
+ "RawPath": "/dev%2Fgithub.svg",
+ "OmitHost": false,
+ "ForceQuery": false,
+ "RawQuery": "",
+ "Fragment": "",
+ "RawFragment": ""
+ },
+ "iconPosition": "INSIDE_TOP_LEFT",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "grid + icon",
+ "fontSize": 28,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 120,
+ "labelHeight": 36,
+ "labelPosition": "INSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "grid + icon.a",
+ "type": "rectangle",
+ "pos": {
+ "x": 509,
+ "y": 129
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "a",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid + icon.b",
+ "type": "rectangle",
+ "pos": {
+ "x": 602,
+ "y": 129
+ },
+ "width": 54,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid + icon.c",
+ "type": "rectangle",
+ "pos": {
+ "x": 509,
+ "y": 235
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "c",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid + icon.d",
+ "type": "rectangle",
+ "pos": {
+ "x": 602,
+ "y": 235
+ },
+ "width": 54,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "d",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid + icon w/ container",
+ "type": "rectangle",
+ "classes": [
+ "2x2"
+ ],
+ "pos": {
+ "x": 782,
+ "y": 0
+ },
+ "width": 433,
+ "height": 457,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B4",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": {
+ "Scheme": "https",
+ "Opaque": "",
+ "User": null,
+ "Host": "icons.terrastruct.com",
+ "Path": "/dev/github.svg",
+ "RawPath": "/dev%2Fgithub.svg",
+ "OmitHost": false,
+ "ForceQuery": false,
+ "RawQuery": "",
+ "Fragment": "",
+ "RawFragment": ""
+ },
+ "iconPosition": "INSIDE_TOP_LEFT",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "grid + icon w/ container",
+ "fontSize": 28,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 275,
+ "labelHeight": 36,
+ "labelPosition": "INSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "grid + icon w/ container.a",
+ "type": "rectangle",
+ "pos": {
+ "x": 866,
+ "y": 120
+ },
+ "width": 53,
+ "height": 130,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "a",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid + icon w/ container.b",
+ "type": "rectangle",
+ "pos": {
+ "x": 959,
+ "y": 120
+ },
+ "width": 171,
+ "height": 130,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b",
+ "fontSize": 24,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 12,
+ "labelHeight": 31,
+ "labelPosition": "OUTSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid + icon w/ container.b.b child",
+ "type": "rectangle",
+ "pos": {
+ "x": 999,
+ "y": 152
+ },
+ "width": 91,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b child",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 46,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 3
+ },
+ {
+ "id": "grid + icon w/ container.c",
+ "type": "rectangle",
+ "pos": {
+ "x": 866,
+ "y": 290
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "c",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid + icon w/ container.d",
+ "type": "rectangle",
+ "pos": {
+ "x": 959,
+ "y": 290
+ },
+ "width": 171,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "d",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "no label grid w/ container + icon",
+ "type": "rectangle",
+ "classes": [
+ "2x2"
+ ],
+ "pos": {
+ "x": 1275,
+ "y": 41
+ },
+ "width": 384,
+ "height": 375,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B4",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "",
+ "fontSize": 28,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "INSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "no label grid w/ container + icon.a",
+ "type": "rectangle",
+ "pos": {
+ "x": 1335,
+ "y": 120
+ },
+ "width": 53,
+ "height": 130,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "a",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "no label grid w/ container + icon.b",
+ "type": "rectangle",
+ "pos": {
+ "x": 1428,
+ "y": 120
+ },
+ "width": 171,
+ "height": 130,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": {
+ "Scheme": "https",
+ "Opaque": "",
+ "User": null,
+ "Host": "icons.terrastruct.com",
+ "Path": "/dev/github.svg",
+ "RawPath": "/dev%2Fgithub.svg",
+ "OmitHost": false,
+ "ForceQuery": false,
+ "RawQuery": "",
+ "Fragment": "",
+ "RawFragment": ""
+ },
+ "iconPosition": "OUTSIDE_TOP_LEFT",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b",
+ "fontSize": 24,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 12,
+ "labelHeight": 31,
+ "labelPosition": "OUTSIDE_TOP_RIGHT",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "no label grid w/ container + icon.b.b child",
+ "type": "rectangle",
+ "pos": {
+ "x": 1468,
+ "y": 152
+ },
+ "width": 91,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b child",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 46,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 3
+ },
+ {
+ "id": "no label grid w/ container + icon.c",
+ "type": "rectangle",
+ "pos": {
+ "x": 1335,
+ "y": 290
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "c",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "no label grid w/ container + icon.d",
+ "type": "rectangle",
+ "pos": {
+ "x": 1428,
+ "y": 290
+ },
+ "width": 171,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "d",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "no label grid + icon",
+ "type": "rectangle",
+ "classes": [
+ "2x2"
+ ],
+ "pos": {
+ "x": 1719,
+ "y": 76
+ },
+ "width": 267,
+ "height": 306,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B4",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": {
+ "Scheme": "https",
+ "Opaque": "",
+ "User": null,
+ "Host": "icons.terrastruct.com",
+ "Path": "/dev/github.svg",
+ "RawPath": "/dev%2Fgithub.svg",
+ "OmitHost": false,
+ "ForceQuery": false,
+ "RawQuery": "",
+ "Fragment": "",
+ "RawFragment": ""
+ },
+ "iconPosition": "INSIDE_TOP_LEFT",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "",
+ "fontSize": 28,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "INSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "no label grid + icon.a",
+ "type": "rectangle",
+ "pos": {
+ "x": 1779,
+ "y": 150
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "a",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "no label grid + icon.b",
+ "type": "rectangle",
+ "pos": {
+ "x": 1872,
+ "y": 150
+ },
+ "width": 54,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "no label grid + icon.c",
+ "type": "rectangle",
+ "pos": {
+ "x": 1779,
+ "y": 256
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "c",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "no label grid + icon.d",
+ "type": "rectangle",
+ "pos": {
+ "x": 1872,
+ "y": 256
+ },
+ "width": 54,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "d",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ }
+ ],
+ "connections": [],
+ "root": {
+ "id": "",
+ "type": "",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "width": 0,
+ "height": 0,
+ "opacity": 0,
+ "strokeDash": 0,
+ "strokeWidth": 0,
+ "borderRadius": 0,
+ "fill": "N7",
+ "stroke": "",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "zIndex": 0,
+ "level": 0
+ }
+}
diff --git a/e2etests/testdata/stable/grid_icon/dagre/sketch.exp.svg b/e2etests/testdata/stable/grid_icon/dagre/sketch.exp.svg
new file mode 100644
index 000000000..92e20b3c2
--- /dev/null
+++ b/e2etests/testdata/stable/grid_icon/dagre/sketch.exp.svg
@@ -0,0 +1,102 @@
+
\ No newline at end of file
diff --git a/e2etests/testdata/stable/grid_icon/elk/board.exp.json b/e2etests/testdata/stable/grid_icon/elk/board.exp.json
new file mode 100644
index 000000000..be644853a
--- /dev/null
+++ b/e2etests/testdata/stable/grid_icon/elk/board.exp.json
@@ -0,0 +1,1271 @@
+{
+ "name": "",
+ "isFolderOnly": false,
+ "fontFamily": "SourceSansPro",
+ "shapes": [
+ {
+ "id": "grid w/ container + icon",
+ "type": "rectangle",
+ "classes": [
+ "2x2"
+ ],
+ "pos": {
+ "x": 12,
+ "y": 27
+ },
+ "width": 404,
+ "height": 416,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B4",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "grid w/ container + icon",
+ "fontSize": 28,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 273,
+ "labelHeight": 36,
+ "labelPosition": "INSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "grid w/ container + icon.a",
+ "type": "rectangle",
+ "pos": {
+ "x": 72,
+ "y": 87
+ },
+ "width": 53,
+ "height": 190,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "a",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid w/ container + icon.b",
+ "type": "rectangle",
+ "pos": {
+ "x": 165,
+ "y": 87
+ },
+ "width": 191,
+ "height": 190,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": {
+ "Scheme": "https",
+ "Opaque": "",
+ "User": null,
+ "Host": "icons.terrastruct.com",
+ "Path": "/dev/github.svg",
+ "RawPath": "/dev%2Fgithub.svg",
+ "OmitHost": false,
+ "ForceQuery": false,
+ "RawQuery": "",
+ "Fragment": "",
+ "RawFragment": ""
+ },
+ "iconPosition": "INSIDE_TOP_LEFT",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b",
+ "fontSize": 24,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 12,
+ "labelHeight": 31,
+ "labelPosition": "INSIDE_TOP_RIGHT",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid w/ container + icon.b.b child",
+ "type": "rectangle",
+ "pos": {
+ "x": 215,
+ "y": 161
+ },
+ "width": 91,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b child",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 46,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 3
+ },
+ {
+ "id": "grid w/ container + icon.c",
+ "type": "rectangle",
+ "pos": {
+ "x": 72,
+ "y": 317
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "c",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid w/ container + icon.d",
+ "type": "rectangle",
+ "pos": {
+ "x": 165,
+ "y": 317
+ },
+ "width": 191,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "d",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid + icon",
+ "type": "rectangle",
+ "classes": [
+ "2x2"
+ ],
+ "pos": {
+ "x": 436,
+ "y": 62
+ },
+ "width": 278,
+ "height": 347,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B4",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": {
+ "Scheme": "https",
+ "Opaque": "",
+ "User": null,
+ "Host": "icons.terrastruct.com",
+ "Path": "/dev/github.svg",
+ "RawPath": "/dev%2Fgithub.svg",
+ "OmitHost": false,
+ "ForceQuery": false,
+ "RawQuery": "",
+ "Fragment": "",
+ "RawFragment": ""
+ },
+ "iconPosition": "INSIDE_TOP_LEFT",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "grid + icon",
+ "fontSize": 28,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 120,
+ "labelHeight": 36,
+ "labelPosition": "INSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "grid + icon.a",
+ "type": "rectangle",
+ "pos": {
+ "x": 501,
+ "y": 136
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "a",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid + icon.b",
+ "type": "rectangle",
+ "pos": {
+ "x": 594,
+ "y": 136
+ },
+ "width": 54,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid + icon.c",
+ "type": "rectangle",
+ "pos": {
+ "x": 501,
+ "y": 242
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "c",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid + icon.d",
+ "type": "rectangle",
+ "pos": {
+ "x": 594,
+ "y": 242
+ },
+ "width": 54,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "d",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid + icon w/ container",
+ "type": "rectangle",
+ "classes": [
+ "2x2"
+ ],
+ "pos": {
+ "x": 734,
+ "y": 12
+ },
+ "width": 433,
+ "height": 447,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B4",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": {
+ "Scheme": "https",
+ "Opaque": "",
+ "User": null,
+ "Host": "icons.terrastruct.com",
+ "Path": "/dev/github.svg",
+ "RawPath": "/dev%2Fgithub.svg",
+ "OmitHost": false,
+ "ForceQuery": false,
+ "RawQuery": "",
+ "Fragment": "",
+ "RawFragment": ""
+ },
+ "iconPosition": "INSIDE_TOP_LEFT",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "grid + icon w/ container",
+ "fontSize": 28,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 275,
+ "labelHeight": 36,
+ "labelPosition": "INSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "grid + icon w/ container.a",
+ "type": "rectangle",
+ "pos": {
+ "x": 808,
+ "y": 86
+ },
+ "width": 53,
+ "height": 166,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "a",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid + icon w/ container.b",
+ "type": "rectangle",
+ "pos": {
+ "x": 901,
+ "y": 86
+ },
+ "width": 191,
+ "height": 166,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b",
+ "fontSize": 24,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 12,
+ "labelHeight": 31,
+ "labelPosition": "INSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid + icon w/ container.b.b child",
+ "type": "rectangle",
+ "pos": {
+ "x": 951,
+ "y": 136
+ },
+ "width": 91,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b child",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 46,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 3
+ },
+ {
+ "id": "grid + icon w/ container.c",
+ "type": "rectangle",
+ "pos": {
+ "x": 808,
+ "y": 292
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "c",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "grid + icon w/ container.d",
+ "type": "rectangle",
+ "pos": {
+ "x": 901,
+ "y": 292
+ },
+ "width": 191,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "d",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "no label grid w/ container + icon",
+ "type": "rectangle",
+ "classes": [
+ "2x2"
+ ],
+ "pos": {
+ "x": 1187,
+ "y": 27
+ },
+ "width": 404,
+ "height": 416,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B4",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "",
+ "fontSize": 28,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "INSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "no label grid w/ container + icon.a",
+ "type": "rectangle",
+ "pos": {
+ "x": 1247,
+ "y": 87
+ },
+ "width": 53,
+ "height": 190,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "a",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "no label grid w/ container + icon.b",
+ "type": "rectangle",
+ "pos": {
+ "x": 1340,
+ "y": 87
+ },
+ "width": 191,
+ "height": 190,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": {
+ "Scheme": "https",
+ "Opaque": "",
+ "User": null,
+ "Host": "icons.terrastruct.com",
+ "Path": "/dev/github.svg",
+ "RawPath": "/dev%2Fgithub.svg",
+ "OmitHost": false,
+ "ForceQuery": false,
+ "RawQuery": "",
+ "Fragment": "",
+ "RawFragment": ""
+ },
+ "iconPosition": "INSIDE_TOP_LEFT",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b",
+ "fontSize": 24,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 12,
+ "labelHeight": 31,
+ "labelPosition": "INSIDE_TOP_RIGHT",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "no label grid w/ container + icon.b.b child",
+ "type": "rectangle",
+ "pos": {
+ "x": 1390,
+ "y": 161
+ },
+ "width": 91,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b child",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 46,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 3
+ },
+ {
+ "id": "no label grid w/ container + icon.c",
+ "type": "rectangle",
+ "pos": {
+ "x": 1247,
+ "y": 317
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "c",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "no label grid w/ container + icon.d",
+ "type": "rectangle",
+ "pos": {
+ "x": 1340,
+ "y": 317
+ },
+ "width": 191,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "d",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "no label grid + icon",
+ "type": "rectangle",
+ "classes": [
+ "2x2"
+ ],
+ "pos": {
+ "x": 1611,
+ "y": 82
+ },
+ "width": 267,
+ "height": 306,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B4",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": {
+ "Scheme": "https",
+ "Opaque": "",
+ "User": null,
+ "Host": "icons.terrastruct.com",
+ "Path": "/dev/github.svg",
+ "RawPath": "/dev%2Fgithub.svg",
+ "OmitHost": false,
+ "ForceQuery": false,
+ "RawQuery": "",
+ "Fragment": "",
+ "RawFragment": ""
+ },
+ "iconPosition": "INSIDE_TOP_LEFT",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "",
+ "fontSize": 28,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "INSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "no label grid + icon.a",
+ "type": "rectangle",
+ "pos": {
+ "x": 1671,
+ "y": 156
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "a",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "no label grid + icon.b",
+ "type": "rectangle",
+ "pos": {
+ "x": 1764,
+ "y": 156
+ },
+ "width": 54,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "no label grid + icon.c",
+ "type": "rectangle",
+ "pos": {
+ "x": 1671,
+ "y": 262
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "c",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "no label grid + icon.d",
+ "type": "rectangle",
+ "pos": {
+ "x": 1764,
+ "y": 262
+ },
+ "width": 54,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "d",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 2
+ }
+ ],
+ "connections": [],
+ "root": {
+ "id": "",
+ "type": "",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "width": 0,
+ "height": 0,
+ "opacity": 0,
+ "strokeDash": 0,
+ "strokeWidth": 0,
+ "borderRadius": 0,
+ "fill": "N7",
+ "stroke": "",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "zIndex": 0,
+ "level": 0
+ }
+}
diff --git a/e2etests/testdata/stable/grid_icon/elk/sketch.exp.svg b/e2etests/testdata/stable/grid_icon/elk/sketch.exp.svg
new file mode 100644
index 000000000..ab9421789
--- /dev/null
+++ b/e2etests/testdata/stable/grid_icon/elk/sketch.exp.svg
@@ -0,0 +1,102 @@
+grid w/ container + icongrid + icongrid + icon w/ containerabcdabcdabcdabcdabcdb childb childb child
+
+
+
\ No newline at end of file
diff --git a/e2etests/testdata/stable/grid_nested/dagre/board.exp.json b/e2etests/testdata/stable/grid_nested/dagre/board.exp.json
index 7a47434c9..539206801 100644
--- a/e2etests/testdata/stable/grid_nested/dagre/board.exp.json
+++ b/e2etests/testdata/stable/grid_nested/dagre/board.exp.json
@@ -14,7 +14,7 @@
"y": 150
},
"width": 384,
- "height": 356,
+ "height": 388,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
@@ -52,7 +52,7 @@
"type": "rectangle",
"pos": {
"x": 60,
- "y": 210
+ "y": 242
},
"width": 53,
"height": 130,
@@ -93,7 +93,7 @@
"type": "rectangle",
"pos": {
"x": 153,
- "y": 210
+ "y": 242
},
"width": 171,
"height": 130,
@@ -125,7 +125,7 @@
"underline": false,
"labelWidth": 12,
"labelHeight": 31,
- "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPosition": "OUTSIDE_TOP_CENTER",
"zIndex": 0,
"level": 2
},
@@ -134,7 +134,7 @@
"type": "rectangle",
"pos": {
"x": 193,
- "y": 242
+ "y": 274
},
"width": 91,
"height": 66,
@@ -175,7 +175,7 @@
"type": "rectangle",
"pos": {
"x": 60,
- "y": 380
+ "y": 412
},
"width": 53,
"height": 66,
@@ -216,7 +216,7 @@
"type": "rectangle",
"pos": {
"x": 153,
- "y": 380
+ "y": 412
},
"width": 171,
"height": 66,
@@ -263,7 +263,7 @@
"y": 0
},
"width": 692,
- "height": 656,
+ "height": 688,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
@@ -301,7 +301,7 @@
"type": "rectangle",
"pos": {
"x": 504,
- "y": 60
+ "y": 92
},
"width": 53,
"height": 430,
@@ -342,7 +342,7 @@
"type": "rectangle",
"pos": {
"x": 597,
- "y": 60
+ "y": 92
},
"width": 479,
"height": 430,
@@ -374,7 +374,7 @@
"underline": false,
"labelWidth": 12,
"labelHeight": 31,
- "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPosition": "OUTSIDE_TOP_CENTER",
"zIndex": 0,
"level": 2
},
@@ -383,7 +383,7 @@
"type": "rectangle",
"pos": {
"x": 617,
- "y": 123
+ "y": 155
},
"width": 439,
"height": 335,
@@ -424,7 +424,7 @@
"type": "rectangle",
"pos": {
"x": 637,
- "y": 183
+ "y": 215
},
"width": 186,
"height": 240,
@@ -465,7 +465,7 @@
"type": "rectangle",
"pos": {
"x": 657,
- "y": 246
+ "y": 278
},
"width": 146,
"height": 140,
@@ -506,7 +506,7 @@
"type": "rectangle",
"pos": {
"x": 697,
- "y": 283
+ "y": 315
},
"width": 66,
"height": 66,
@@ -547,7 +547,7 @@
"type": "rectangle",
"pos": {
"x": 843,
- "y": 183
+ "y": 215
},
"width": 193,
"height": 240,
@@ -588,7 +588,7 @@
"type": "rectangle",
"pos": {
"x": 863,
- "y": 246
+ "y": 278
},
"width": 153,
"height": 140,
@@ -629,7 +629,7 @@
"type": "rectangle",
"pos": {
"x": 903,
- "y": 283
+ "y": 315
},
"width": 73,
"height": 66,
@@ -670,7 +670,7 @@
"type": "rectangle",
"pos": {
"x": 504,
- "y": 530
+ "y": 562
},
"width": 53,
"height": 66,
@@ -711,7 +711,7 @@
"type": "rectangle",
"pos": {
"x": 597,
- "y": 530
+ "y": 562
},
"width": 479,
"height": 66,
@@ -755,7 +755,7 @@
],
"pos": {
"x": 1196,
- "y": 69
+ "y": 85
},
"width": 480,
"height": 518,
@@ -796,7 +796,7 @@
"type": "rectangle",
"pos": {
"x": 1256,
- "y": 129
+ "y": 145
},
"width": 53,
"height": 292,
@@ -840,7 +840,7 @@
],
"pos": {
"x": 1349,
- "y": 129
+ "y": 145
},
"width": 267,
"height": 292,
@@ -881,7 +881,7 @@
"type": "rectangle",
"pos": {
"x": 1409,
- "y": 189
+ "y": 205
},
"width": 53,
"height": 66,
@@ -922,7 +922,7 @@
"type": "rectangle",
"pos": {
"x": 1502,
- "y": 189
+ "y": 205
},
"width": 54,
"height": 66,
@@ -963,7 +963,7 @@
"type": "rectangle",
"pos": {
"x": 1409,
- "y": 295
+ "y": 311
},
"width": 53,
"height": 66,
@@ -1004,7 +1004,7 @@
"type": "rectangle",
"pos": {
"x": 1502,
- "y": 295
+ "y": 311
},
"width": 54,
"height": 66,
@@ -1045,7 +1045,7 @@
"type": "rectangle",
"pos": {
"x": 1256,
- "y": 461
+ "y": 477
},
"width": 53,
"height": 66,
@@ -1086,7 +1086,7 @@
"type": "rectangle",
"pos": {
"x": 1349,
- "y": 461
+ "y": 477
},
"width": 267,
"height": 66,
@@ -1127,7 +1127,7 @@
"type": "rectangle",
"pos": {
"x": 1736,
- "y": 38
+ "y": 54
},
"width": 321,
"height": 581,
@@ -1168,7 +1168,7 @@
"type": "rectangle",
"pos": {
"x": 1736,
- "y": 84
+ "y": 100
},
"width": 53,
"height": 469,
@@ -1209,7 +1209,7 @@
"type": "rectangle",
"pos": {
"x": 1789,
- "y": 84
+ "y": 100
},
"width": 268,
"height": 469,
@@ -1250,7 +1250,7 @@
"type": "rectangle",
"pos": {
"x": 1789,
- "y": 125
+ "y": 141
},
"width": 214,
"height": 66,
@@ -1291,7 +1291,7 @@
"type": "rectangle",
"pos": {
"x": 2003,
- "y": 125
+ "y": 141
},
"width": 54,
"height": 66,
@@ -1332,7 +1332,7 @@
"type": "rectangle",
"pos": {
"x": 1789,
- "y": 191
+ "y": 207
},
"width": 214,
"height": 362,
@@ -1373,7 +1373,7 @@
"type": "rectangle",
"pos": {
"x": 1789,
- "y": 227
+ "y": 243
},
"width": 53,
"height": 66,
@@ -1414,7 +1414,7 @@
"type": "rectangle",
"pos": {
"x": 1842,
- "y": 227
+ "y": 243
},
"width": 161,
"height": 66,
@@ -1455,7 +1455,7 @@
"type": "rectangle",
"pos": {
"x": 1789,
- "y": 293
+ "y": 309
},
"width": 53,
"height": 260,
@@ -1496,7 +1496,7 @@
"type": "rectangle",
"pos": {
"x": 1842,
- "y": 293
+ "y": 309
},
"width": 161,
"height": 260,
@@ -1537,7 +1537,7 @@
"type": "rectangle",
"pos": {
"x": 1842,
- "y": 324
+ "y": 340
},
"width": 107,
"height": 163,
@@ -1578,7 +1578,7 @@
"type": "rectangle",
"pos": {
"x": 1842,
- "y": 355
+ "y": 371
},
"width": 53,
"height": 66,
@@ -1619,7 +1619,7 @@
"type": "rectangle",
"pos": {
"x": 1895,
- "y": 355
+ "y": 371
},
"width": 54,
"height": 66,
@@ -1660,7 +1660,7 @@
"type": "rectangle",
"pos": {
"x": 1842,
- "y": 421
+ "y": 437
},
"width": 53,
"height": 66,
@@ -1701,7 +1701,7 @@
"type": "rectangle",
"pos": {
"x": 1895,
- "y": 421
+ "y": 437
},
"width": 54,
"height": 66,
@@ -1742,7 +1742,7 @@
"type": "rectangle",
"pos": {
"x": 1949,
- "y": 324
+ "y": 340
},
"width": 54,
"height": 163,
@@ -1783,7 +1783,7 @@
"type": "rectangle",
"pos": {
"x": 1842,
- "y": 487
+ "y": 503
},
"width": 107,
"height": 66,
@@ -1824,7 +1824,7 @@
"type": "rectangle",
"pos": {
"x": 1949,
- "y": 487
+ "y": 503
},
"width": 54,
"height": 66,
@@ -1865,7 +1865,7 @@
"type": "rectangle",
"pos": {
"x": 2003,
- "y": 191
+ "y": 207
},
"width": 54,
"height": 362,
@@ -1906,7 +1906,7 @@
"type": "rectangle",
"pos": {
"x": 1736,
- "y": 553
+ "y": 569
},
"width": 53,
"height": 66,
@@ -1947,7 +1947,7 @@
"type": "rectangle",
"pos": {
"x": 1789,
- "y": 553
+ "y": 569
},
"width": 268,
"height": 66,
diff --git a/e2etests/testdata/stable/grid_nested/dagre/sketch.exp.svg b/e2etests/testdata/stable/grid_nested/dagre/sketch.exp.svg
index 136a16b78..7812cc589 100644
--- a/e2etests/testdata/stable/grid_nested/dagre/sketch.exp.svg
+++ b/e2etests/testdata/stable/grid_nested/dagre/sketch.exp.svg
@@ -1,16 +1,16 @@
-grid w/ containergrid w/ nested containersgrid in gridgrid w/ grid w/ gridabcdabcdabcdabcdb childb 1abcdabcdb 2b 2aabcdb 3b 3aabcdb 4b 3aabcd
-
+ .d2-2603497423 .fill-N1{fill:#0A0F25;}
+ .d2-2603497423 .fill-N2{fill:#676C7E;}
+ .d2-2603497423 .fill-N3{fill:#9499AB;}
+ .d2-2603497423 .fill-N4{fill:#CFD2DD;}
+ .d2-2603497423 .fill-N5{fill:#DEE1EB;}
+ .d2-2603497423 .fill-N6{fill:#EEF1F8;}
+ .d2-2603497423 .fill-N7{fill:#FFFFFF;}
+ .d2-2603497423 .fill-B1{fill:#0D32B2;}
+ .d2-2603497423 .fill-B2{fill:#0D32B2;}
+ .d2-2603497423 .fill-B3{fill:#E3E9FD;}
+ .d2-2603497423 .fill-B4{fill:#E3E9FD;}
+ .d2-2603497423 .fill-B5{fill:#EDF0FD;}
+ .d2-2603497423 .fill-B6{fill:#F7F8FE;}
+ .d2-2603497423 .fill-AA2{fill:#4A6FF3;}
+ .d2-2603497423 .fill-AA4{fill:#EDF0FD;}
+ .d2-2603497423 .fill-AA5{fill:#F7F8FE;}
+ .d2-2603497423 .fill-AB4{fill:#EDF0FD;}
+ .d2-2603497423 .fill-AB5{fill:#F7F8FE;}
+ .d2-2603497423 .stroke-N1{stroke:#0A0F25;}
+ .d2-2603497423 .stroke-N2{stroke:#676C7E;}
+ .d2-2603497423 .stroke-N3{stroke:#9499AB;}
+ .d2-2603497423 .stroke-N4{stroke:#CFD2DD;}
+ .d2-2603497423 .stroke-N5{stroke:#DEE1EB;}
+ .d2-2603497423 .stroke-N6{stroke:#EEF1F8;}
+ .d2-2603497423 .stroke-N7{stroke:#FFFFFF;}
+ .d2-2603497423 .stroke-B1{stroke:#0D32B2;}
+ .d2-2603497423 .stroke-B2{stroke:#0D32B2;}
+ .d2-2603497423 .stroke-B3{stroke:#E3E9FD;}
+ .d2-2603497423 .stroke-B4{stroke:#E3E9FD;}
+ .d2-2603497423 .stroke-B5{stroke:#EDF0FD;}
+ .d2-2603497423 .stroke-B6{stroke:#F7F8FE;}
+ .d2-2603497423 .stroke-AA2{stroke:#4A6FF3;}
+ .d2-2603497423 .stroke-AA4{stroke:#EDF0FD;}
+ .d2-2603497423 .stroke-AA5{stroke:#F7F8FE;}
+ .d2-2603497423 .stroke-AB4{stroke:#EDF0FD;}
+ .d2-2603497423 .stroke-AB5{stroke:#F7F8FE;}
+ .d2-2603497423 .background-color-N1{background-color:#0A0F25;}
+ .d2-2603497423 .background-color-N2{background-color:#676C7E;}
+ .d2-2603497423 .background-color-N3{background-color:#9499AB;}
+ .d2-2603497423 .background-color-N4{background-color:#CFD2DD;}
+ .d2-2603497423 .background-color-N5{background-color:#DEE1EB;}
+ .d2-2603497423 .background-color-N6{background-color:#EEF1F8;}
+ .d2-2603497423 .background-color-N7{background-color:#FFFFFF;}
+ .d2-2603497423 .background-color-B1{background-color:#0D32B2;}
+ .d2-2603497423 .background-color-B2{background-color:#0D32B2;}
+ .d2-2603497423 .background-color-B3{background-color:#E3E9FD;}
+ .d2-2603497423 .background-color-B4{background-color:#E3E9FD;}
+ .d2-2603497423 .background-color-B5{background-color:#EDF0FD;}
+ .d2-2603497423 .background-color-B6{background-color:#F7F8FE;}
+ .d2-2603497423 .background-color-AA2{background-color:#4A6FF3;}
+ .d2-2603497423 .background-color-AA4{background-color:#EDF0FD;}
+ .d2-2603497423 .background-color-AA5{background-color:#F7F8FE;}
+ .d2-2603497423 .background-color-AB4{background-color:#EDF0FD;}
+ .d2-2603497423 .background-color-AB5{background-color:#F7F8FE;}
+ .d2-2603497423 .color-N1{color:#0A0F25;}
+ .d2-2603497423 .color-N2{color:#676C7E;}
+ .d2-2603497423 .color-N3{color:#9499AB;}
+ .d2-2603497423 .color-N4{color:#CFD2DD;}
+ .d2-2603497423 .color-N5{color:#DEE1EB;}
+ .d2-2603497423 .color-N6{color:#EEF1F8;}
+ .d2-2603497423 .color-N7{color:#FFFFFF;}
+ .d2-2603497423 .color-B1{color:#0D32B2;}
+ .d2-2603497423 .color-B2{color:#0D32B2;}
+ .d2-2603497423 .color-B3{color:#E3E9FD;}
+ .d2-2603497423 .color-B4{color:#E3E9FD;}
+ .d2-2603497423 .color-B5{color:#EDF0FD;}
+ .d2-2603497423 .color-B6{color:#F7F8FE;}
+ .d2-2603497423 .color-AA2{color:#4A6FF3;}
+ .d2-2603497423 .color-AA4{color:#EDF0FD;}
+ .d2-2603497423 .color-AA5{color:#F7F8FE;}
+ .d2-2603497423 .color-AB4{color:#EDF0FD;}
+ .d2-2603497423 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--color-danger-fg:red;}.sketch-overlay-B1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]>grid w/ containergrid w/ nested containersgrid in gridgrid w/ grid w/ gridabcdabcdabcdabcdb childb 1abcdabcdb 2b 2aabcdb 3b 3aabcdb 4b 3aabcd
+
\ No newline at end of file
diff --git a/e2etests/testdata/stable/grid_nested/elk/board.exp.json b/e2etests/testdata/stable/grid_nested/elk/board.exp.json
index 09aabada3..08a18ac92 100644
--- a/e2etests/testdata/stable/grid_nested/elk/board.exp.json
+++ b/e2etests/testdata/stable/grid_nested/elk/board.exp.json
@@ -125,7 +125,7 @@
"underline": false,
"labelWidth": 12,
"labelHeight": 31,
- "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 2
},
@@ -374,7 +374,7 @@
"underline": false,
"labelWidth": 12,
"labelHeight": 31,
- "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 2
},
diff --git a/e2etests/testdata/stable/grid_nested/elk/sketch.exp.svg b/e2etests/testdata/stable/grid_nested/elk/sketch.exp.svg
index cb2d63c26..9a8649b41 100644
--- a/e2etests/testdata/stable/grid_nested/elk/sketch.exp.svg
+++ b/e2etests/testdata/stable/grid_nested/elk/sketch.exp.svg
@@ -1,16 +1,16 @@
-grid w/ containergrid w/ nested containersgrid in gridgrid w/ grid w/ gridabcdabcdabcdabcdb childb 1abcdabcdb 2b 2aabcdb 3b 3aabcdb 4b 3aabcd
+ .d2-511948305 .fill-N1{fill:#0A0F25;}
+ .d2-511948305 .fill-N2{fill:#676C7E;}
+ .d2-511948305 .fill-N3{fill:#9499AB;}
+ .d2-511948305 .fill-N4{fill:#CFD2DD;}
+ .d2-511948305 .fill-N5{fill:#DEE1EB;}
+ .d2-511948305 .fill-N6{fill:#EEF1F8;}
+ .d2-511948305 .fill-N7{fill:#FFFFFF;}
+ .d2-511948305 .fill-B1{fill:#0D32B2;}
+ .d2-511948305 .fill-B2{fill:#0D32B2;}
+ .d2-511948305 .fill-B3{fill:#E3E9FD;}
+ .d2-511948305 .fill-B4{fill:#E3E9FD;}
+ .d2-511948305 .fill-B5{fill:#EDF0FD;}
+ .d2-511948305 .fill-B6{fill:#F7F8FE;}
+ .d2-511948305 .fill-AA2{fill:#4A6FF3;}
+ .d2-511948305 .fill-AA4{fill:#EDF0FD;}
+ .d2-511948305 .fill-AA5{fill:#F7F8FE;}
+ .d2-511948305 .fill-AB4{fill:#EDF0FD;}
+ .d2-511948305 .fill-AB5{fill:#F7F8FE;}
+ .d2-511948305 .stroke-N1{stroke:#0A0F25;}
+ .d2-511948305 .stroke-N2{stroke:#676C7E;}
+ .d2-511948305 .stroke-N3{stroke:#9499AB;}
+ .d2-511948305 .stroke-N4{stroke:#CFD2DD;}
+ .d2-511948305 .stroke-N5{stroke:#DEE1EB;}
+ .d2-511948305 .stroke-N6{stroke:#EEF1F8;}
+ .d2-511948305 .stroke-N7{stroke:#FFFFFF;}
+ .d2-511948305 .stroke-B1{stroke:#0D32B2;}
+ .d2-511948305 .stroke-B2{stroke:#0D32B2;}
+ .d2-511948305 .stroke-B3{stroke:#E3E9FD;}
+ .d2-511948305 .stroke-B4{stroke:#E3E9FD;}
+ .d2-511948305 .stroke-B5{stroke:#EDF0FD;}
+ .d2-511948305 .stroke-B6{stroke:#F7F8FE;}
+ .d2-511948305 .stroke-AA2{stroke:#4A6FF3;}
+ .d2-511948305 .stroke-AA4{stroke:#EDF0FD;}
+ .d2-511948305 .stroke-AA5{stroke:#F7F8FE;}
+ .d2-511948305 .stroke-AB4{stroke:#EDF0FD;}
+ .d2-511948305 .stroke-AB5{stroke:#F7F8FE;}
+ .d2-511948305 .background-color-N1{background-color:#0A0F25;}
+ .d2-511948305 .background-color-N2{background-color:#676C7E;}
+ .d2-511948305 .background-color-N3{background-color:#9499AB;}
+ .d2-511948305 .background-color-N4{background-color:#CFD2DD;}
+ .d2-511948305 .background-color-N5{background-color:#DEE1EB;}
+ .d2-511948305 .background-color-N6{background-color:#EEF1F8;}
+ .d2-511948305 .background-color-N7{background-color:#FFFFFF;}
+ .d2-511948305 .background-color-B1{background-color:#0D32B2;}
+ .d2-511948305 .background-color-B2{background-color:#0D32B2;}
+ .d2-511948305 .background-color-B3{background-color:#E3E9FD;}
+ .d2-511948305 .background-color-B4{background-color:#E3E9FD;}
+ .d2-511948305 .background-color-B5{background-color:#EDF0FD;}
+ .d2-511948305 .background-color-B6{background-color:#F7F8FE;}
+ .d2-511948305 .background-color-AA2{background-color:#4A6FF3;}
+ .d2-511948305 .background-color-AA4{background-color:#EDF0FD;}
+ .d2-511948305 .background-color-AA5{background-color:#F7F8FE;}
+ .d2-511948305 .background-color-AB4{background-color:#EDF0FD;}
+ .d2-511948305 .background-color-AB5{background-color:#F7F8FE;}
+ .d2-511948305 .color-N1{color:#0A0F25;}
+ .d2-511948305 .color-N2{color:#676C7E;}
+ .d2-511948305 .color-N3{color:#9499AB;}
+ .d2-511948305 .color-N4{color:#CFD2DD;}
+ .d2-511948305 .color-N5{color:#DEE1EB;}
+ .d2-511948305 .color-N6{color:#EEF1F8;}
+ .d2-511948305 .color-N7{color:#FFFFFF;}
+ .d2-511948305 .color-B1{color:#0D32B2;}
+ .d2-511948305 .color-B2{color:#0D32B2;}
+ .d2-511948305 .color-B3{color:#E3E9FD;}
+ .d2-511948305 .color-B4{color:#E3E9FD;}
+ .d2-511948305 .color-B5{color:#EDF0FD;}
+ .d2-511948305 .color-B6{color:#F7F8FE;}
+ .d2-511948305 .color-AA2{color:#4A6FF3;}
+ .d2-511948305 .color-AA4{color:#EDF0FD;}
+ .d2-511948305 .color-AA5{color:#F7F8FE;}
+ .d2-511948305 .color-AB4{color:#EDF0FD;}
+ .d2-511948305 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--color-danger-fg:red;}.sketch-overlay-B1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]>grid w/ containergrid w/ nested containersgrid in gridgrid w/ grid w/ gridabcdabcdabcdabcdb childb 1abcdabcdb 2b 2aabcdb 3b 3aabcdb 4b 3aabcd
\ No newline at end of file