Merge pull request #899 from alixander/prevent-bad-sequence

fixes 3 bugs: near sequence child, class font, class in sequence
This commit is contained in:
Alexander Wang 2023-02-25 08:13:43 -08:00 committed by GitHub
commit 3e039debe3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 352 additions and 31 deletions

View file

@ -4,7 +4,11 @@
#### Improvements 🧹 #### Improvements 🧹
- `near` key set to sequence diagram children get an appropriate error message. [#899](https://github.com/terrastruct/d2/issues/899)
- `class` and `sql_table` shape respect `font-color` styling as header font color. [#899](https://github.com/terrastruct/d2/issues/899)
#### Bugfixes ⛑️ #### Bugfixes ⛑️
- Error reported when no actors are declared in sequence diagram. [#886](https://github.com/terrastruct/d2/pull/886) - Error reported when no actors are declared in sequence diagram. [#886](https://github.com/terrastruct/d2/pull/886)
- Fixed img bundling on image shapes. [#889](https://github.com/terrastruct/d2/issues/889) - Fixed img bundling on image shapes. [#889](https://github.com/terrastruct/d2/issues/889)
- `class` shape as sequence diagram actors had wrong colors. [#899](https://github.com/terrastruct/d2/issues/899)

View file

@ -678,6 +678,10 @@ func (c *compiler) validateNear(g *d2graph.Graph) {
c.errorf(obj.Attributes.NearKey, "near keys cannot be set to an descendant") c.errorf(obj.Attributes.NearKey, "near keys cannot be set to an descendant")
continue continue
} }
if nearObj.OuterSequenceDiagram() != nil {
c.errorf(obj.Attributes.NearKey, "near keys cannot be set to an object within sequence diagrams")
continue
}
} else if isConst { } else if isConst {
is := false is := false
for _, e := range g.Edges { for _, e := range g.Edges {

View file

@ -1774,6 +1774,17 @@ dst.id <-> src.dst_id
assert.String(t, "sequence_diagram", g.Objects[0].Attributes.Shape.Value) assert.String(t, "sequence_diagram", g.Objects[0].Attributes.Shape.Value)
}, },
}, },
{
name: "near_sequence",
text: `x: {
shape: sequence_diagram
a
}
b.near: x.a
`,
expErr: `d2/testdata/d2compiler/TestCompile/near_sequence.d2:5:9: near keys cannot be set to an object within sequence diagrams`,
},
{ {
name: "sequence-timestamp", name: "sequence-timestamp",

View file

@ -338,6 +338,12 @@ func (l ContainerLevel) LabelSize() int {
func (obj *Object) GetFill() string { func (obj *Object) GetFill() string {
level := int(obj.Level()) level := int(obj.Level())
shape := obj.Attributes.Shape.Value
if strings.EqualFold(shape, d2target.ShapeSQLTable) || strings.EqualFold(shape, d2target.ShapeClass) {
return color.N1
}
if obj.IsSequenceDiagramNote() { if obj.IsSequenceDiagramNote() {
return color.N7 return color.N7
} else if obj.IsSequenceDiagramGroup() { } else if obj.IsSequenceDiagramGroup() {
@ -366,8 +372,6 @@ func (obj *Object) GetFill() string {
return color.N7 return color.N7
} }
shape := obj.Attributes.Shape.Value
if shape == "" || strings.EqualFold(shape, d2target.ShapeSquare) || strings.EqualFold(shape, d2target.ShapeCircle) || strings.EqualFold(shape, d2target.ShapeOval) || strings.EqualFold(shape, d2target.ShapeRectangle) { if shape == "" || strings.EqualFold(shape, d2target.ShapeSquare) || strings.EqualFold(shape, d2target.ShapeCircle) || strings.EqualFold(shape, d2target.ShapeOval) || strings.EqualFold(shape, d2target.ShapeRectangle) {
if level == 1 { if level == 1 {
if !obj.IsContainer() { if !obj.IsContainer() {
@ -409,10 +413,6 @@ func (obj *Object) GetFill() string {
return color.N5 return color.N5
} }
if strings.EqualFold(shape, d2target.ShapeSQLTable) || strings.EqualFold(shape, d2target.ShapeClass) {
return color.N1
}
return color.N7 return color.N7
} }

View file

@ -382,7 +382,7 @@ func Table(r *Runner, shape d2target.Shape) (string, error) {
textEl := d2themes.NewThemableElement("text") textEl := d2themes.NewThemableElement("text")
textEl.X = tl.X textEl.X = tl.X
textEl.Y = tl.Y + float64(shape.LabelHeight)*3/4 textEl.Y = tl.Y + float64(shape.LabelHeight)*3/4
textEl.Fill = shape.Stroke textEl.Fill = shape.GetFontColor()
textEl.ClassName = "text" textEl.ClassName = "text"
textEl.Style = fmt.Sprintf("text-anchor:%s;font-size:%vpx", textEl.Style = fmt.Sprintf("text-anchor:%s;font-size:%vpx",
"start", 4+shape.FontSize, "start", 4+shape.FontSize,
@ -532,7 +532,7 @@ func Class(r *Runner, shape d2target.Shape) (string, error) {
textEl := d2themes.NewThemableElement("text") textEl := d2themes.NewThemableElement("text")
textEl.X = tl.X + float64(shape.LabelWidth)/2 textEl.X = tl.X + float64(shape.LabelWidth)/2
textEl.Y = tl.Y + float64(shape.LabelHeight)*3/4 textEl.Y = tl.Y + float64(shape.LabelHeight)*3/4
textEl.Fill = shape.Stroke textEl.Fill = shape.GetFontColor()
textEl.ClassName = "text-mono" textEl.ClassName = "text-mono"
textEl.Style = fmt.Sprintf("text-anchor:%s;font-size:%vpx", textEl.Style = fmt.Sprintf("text-anchor:%s;font-size:%vpx",
"middle", "middle",

View file

@ -30,7 +30,7 @@ func classHeader(shape d2target.Shape, box *geo.Box, text string, textWidth, tex
textEl := d2themes.NewThemableElement("text") textEl := d2themes.NewThemableElement("text")
textEl.X = tl.X + textWidth/2 textEl.X = tl.X + textWidth/2
textEl.Y = tl.Y + textHeight*3/4 textEl.Y = tl.Y + textHeight*3/4
textEl.Fill = shape.Stroke textEl.Fill = shape.GetFontColor()
textEl.ClassName = "text-mono" textEl.ClassName = "text-mono"
textEl.Style = fmt.Sprintf(`text-anchor:%s;font-size:%vpx;`, textEl.Style = fmt.Sprintf(`text-anchor:%s;font-size:%vpx;`,
"middle", 4+fontSize, "middle", 4+fontSize,

View file

@ -562,11 +562,6 @@ func drawConnection(writer io.Writer, labelMaskID string, connection d2target.Co
} else if connection.Italic { } else if connection.Italic {
fontClass += "-italic" fontClass += "-italic"
} }
fontColor := color.N1
if connection.Color != color.Empty {
fontColor = connection.Color
}
if connection.Fill != color.Empty { if connection.Fill != color.Empty {
rectEl := d2themes.NewThemableElement("rect") rectEl := d2themes.NewThemableElement("rect")
rectEl.X, rectEl.Y = labelTL.X, labelTL.Y rectEl.X, rectEl.Y = labelTL.X, labelTL.Y
@ -578,7 +573,7 @@ func drawConnection(writer io.Writer, labelMaskID string, connection d2target.Co
textEl := d2themes.NewThemableElement("text") textEl := d2themes.NewThemableElement("text")
textEl.X = labelTL.X + float64(connection.LabelWidth)/2 textEl.X = labelTL.X + float64(connection.LabelWidth)/2
textEl.Y = labelTL.Y + float64(connection.FontSize) textEl.Y = labelTL.Y + float64(connection.FontSize)
textEl.Fill = fontColor textEl.Fill = connection.GetFontColor()
textEl.ClassName = fontClass textEl.ClassName = fontClass
textEl.Style = fmt.Sprintf("text-anchor:%s;font-size:%vpx", "middle", connection.FontSize) textEl.Style = fmt.Sprintf("text-anchor:%s;font-size:%vpx", "middle", connection.FontSize)
textEl.Content = RenderText(connection.Label, textEl.X, float64(connection.LabelHeight)) textEl.Content = RenderText(connection.Label, textEl.X, float64(connection.LabelHeight))
@ -1112,10 +1107,6 @@ func drawShape(writer io.Writer, targetShape d2target.Shape, sketchRunner *d2ske
fmt.Fprint(writer, mdEl.Render()) fmt.Fprint(writer, mdEl.Render())
fmt.Fprint(writer, `</foreignObject></g>`) fmt.Fprint(writer, `</foreignObject></g>`)
} else { } else {
fontColor := color.N1
if targetShape.Color != color.Empty {
fontColor = targetShape.Color
}
if targetShape.LabelFill != "" { if targetShape.LabelFill != "" {
rectEl := d2themes.NewThemableElement("rect") rectEl := d2themes.NewThemableElement("rect")
rectEl.X = labelTL.X rectEl.X = labelTL.X
@ -1129,7 +1120,7 @@ func drawShape(writer io.Writer, targetShape d2target.Shape, sketchRunner *d2ske
textEl.X = labelTL.X + float64(targetShape.LabelWidth)/2 textEl.X = labelTL.X + float64(targetShape.LabelWidth)/2
// text is vertically positioned at its baseline which is at labelTL+FontSize // text is vertically positioned at its baseline which is at labelTL+FontSize
textEl.Y = labelTL.Y + float64(targetShape.FontSize) textEl.Y = labelTL.Y + float64(targetShape.FontSize)
textEl.Fill = fontColor textEl.Fill = targetShape.GetFontColor()
textEl.ClassName = fontClass textEl.ClassName = fontClass
textEl.Style = fmt.Sprintf("text-anchor:%s;font-size:%vpx", "middle", targetShape.FontSize) textEl.Style = fmt.Sprintf("text-anchor:%s;font-size:%vpx", "middle", targetShape.FontSize)
textEl.Content = RenderText(targetShape.Label, textEl.X, float64(targetShape.LabelHeight)) textEl.Content = RenderText(targetShape.Label, textEl.X, float64(targetShape.LabelHeight))

View file

@ -31,7 +31,7 @@ func tableHeader(shape d2target.Shape, box *geo.Box, text string, textWidth, tex
textEl := d2themes.NewThemableElement("text") textEl := d2themes.NewThemableElement("text")
textEl.X = tl.X textEl.X = tl.X
textEl.Y = tl.Y + textHeight*3/4 textEl.Y = tl.Y + textHeight*3/4
textEl.Fill = shape.Stroke textEl.Fill = shape.GetFontColor()
textEl.ClassName = "text" textEl.ClassName = "text"
textEl.Style = fmt.Sprintf("text-anchor:%s;font-size:%vpx", textEl.Style = fmt.Sprintf("text-anchor:%s;font-size:%vpx",
"start", 4+fontSize, "start", 4+fontSize,

View file

@ -197,6 +197,19 @@ type Shape struct {
NeutralAccentColor string `json:"neutralAccentColor,omitempty"` NeutralAccentColor string `json:"neutralAccentColor,omitempty"`
} }
func (s Shape) GetFontColor() string {
if s.Type == ShapeClass || s.Type == ShapeSQLTable {
if !color.IsThemeColor(s.Color) {
return s.Color
}
return s.Stroke
}
if s.Color != color.Empty {
return s.Color
}
return color.N1
}
func (s Shape) CSSStyle() string { func (s Shape) CSSStyle() string {
out := "" out := ""
@ -302,6 +315,13 @@ func BaseConnection() *Connection {
} }
} }
func (c Connection) GetFontColor() string {
if c.Color != color.Empty {
return c.Color
}
return color.N1
}
func (c Connection) CSSStyle() string { func (c Connection) CSSStyle() string {
out := "" out := ""

View file

@ -39,6 +39,17 @@ group.nested: {
`, `,
expErr: "no actors declared in sequence diagram", expErr: "no actors declared in sequence diagram",
}, },
{
name: "class_font_style_sequence",
script: `shape: sequence_diagram
a: {
shape: class
style: {
font-color: red
}
}
`,
},
{ {
name: "nested_steps", name: "nested_steps",
script: `a: { script: `a: {

View file

@ -0,0 +1,91 @@
{
"name": "",
"fontFamily": "SourceSansPro",
"shapes": [
{
"id": "a",
"type": "class",
"pos": {
"x": 12,
"y": 52
},
"width": 117,
"height": 92,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "N1",
"stroke": "N7",
"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": 20,
"fontFamily": "DEFAULT",
"language": "",
"color": "red",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 12,
"labelHeight": 31,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1,
"primaryAccentColor": "B2",
"secondaryAccentColor": "AA2",
"neutralAccentColor": "N2"
}
],
"connections": [
{
"id": "(a -- )[0]",
"src": "a",
"srcArrow": "none",
"srcLabel": "",
"dst": "a-lifeline-end-2251863791",
"dstArrow": "none",
"dstLabel": "",
"opacity": 1,
"strokeDash": 6,
"strokeWidth": 2,
"stroke": "B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 70.5,
"y": 144
},
{
"x": 70.5,
"y": 214
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 1
}
]
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 186 KiB

View file

@ -0,0 +1,91 @@
{
"name": "",
"fontFamily": "SourceSansPro",
"shapes": [
{
"id": "a",
"type": "class",
"pos": {
"x": 12,
"y": 52
},
"width": 117,
"height": 92,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "N1",
"stroke": "N7",
"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": 20,
"fontFamily": "DEFAULT",
"language": "",
"color": "red",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 12,
"labelHeight": 31,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1,
"primaryAccentColor": "B2",
"secondaryAccentColor": "AA2",
"neutralAccentColor": "N2"
}
],
"connections": [
{
"id": "(a -- )[0]",
"src": "a",
"srcArrow": "none",
"srcLabel": "",
"dst": "a-lifeline-end-2251863791",
"dstArrow": "none",
"dstLabel": "",
"opacity": 1,
"strokeDash": 6,
"strokeWidth": 2,
"stroke": "B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 70.5,
"y": 144
},
{
"x": 70.5,
"y": 214
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 1
}
]
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 186 KiB

View file

@ -97,7 +97,7 @@
"strokeDash": 0, "strokeDash": 0,
"strokeWidth": 2, "strokeWidth": 2,
"borderRadius": 0, "borderRadius": 0,
"fill": "B5", "fill": "N1",
"stroke": "N7", "stroke": "N7",
"shadow": false, "shadow": false,
"3d": false, "3d": false,
@ -819,7 +819,7 @@
"strokeDash": 0, "strokeDash": 0,
"strokeWidth": 2, "strokeWidth": 2,
"borderRadius": 0, "borderRadius": 0,
"fill": "B5", "fill": "N1",
"stroke": "N7", "stroke": "N7",
"shadow": false, "shadow": false,
"3d": false, "3d": false,

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 849 KiB

After

Width:  |  Height:  |  Size: 849 KiB

View file

@ -97,7 +97,7 @@
"strokeDash": 0, "strokeDash": 0,
"strokeWidth": 2, "strokeWidth": 2,
"borderRadius": 0, "borderRadius": 0,
"fill": "B5", "fill": "N1",
"stroke": "N7", "stroke": "N7",
"shadow": false, "shadow": false,
"3d": false, "3d": false,
@ -819,7 +819,7 @@
"strokeDash": 0, "strokeDash": 0,
"strokeWidth": 2, "strokeWidth": 2,
"borderRadius": 0, "borderRadius": 0,
"fill": "B5", "fill": "N1",
"stroke": "N7", "stroke": "N7",
"shadow": false, "shadow": false,
"3d": false, "3d": false,

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 849 KiB

After

Width:  |  Height:  |  Size: 849 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 517 KiB

After

Width:  |  Height:  |  Size: 517 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 517 KiB

After

Width:  |  Height:  |  Size: 517 KiB

12
testdata/d2compiler/TestCompile/near_sequence.exp.json generated vendored Normal file
View file

@ -0,0 +1,12 @@
{
"graph": null,
"err": {
"ioerr": null,
"errs": [
{
"range": "d2/testdata/d2compiler/TestCompile/near_sequence.d2,4:8:45-4:11:48",
"errmsg": "d2/testdata/d2compiler/TestCompile/near_sequence.d2:5:9: near keys cannot be set to an object within sequence diagrams"
}
]
}
}