legend
This commit is contained in:
parent
27d60d9e7d
commit
79e7b69552
6 changed files with 1375 additions and 1 deletions
|
|
@ -96,6 +96,8 @@ func (c *compiler) compileBoard(g *d2graph.Graph, ir *d2ir.Map) *d2graph.Graph {
|
|||
c.validateEdges(g)
|
||||
c.validatePositionsCompatibility(g)
|
||||
|
||||
c.compileLegend(g, ir)
|
||||
|
||||
c.compileBoardsField(g, ir, "layers")
|
||||
c.compileBoardsField(g, ir, "scenarios")
|
||||
c.compileBoardsField(g, ir, "steps")
|
||||
|
|
@ -110,6 +112,42 @@ func (c *compiler) compileBoard(g *d2graph.Graph, ir *d2ir.Map) *d2graph.Graph {
|
|||
return g
|
||||
}
|
||||
|
||||
func (c *compiler) compileLegend(g *d2graph.Graph, m *d2ir.Map) {
|
||||
varsField := m.GetField(d2ast.FlatUnquotedString("vars"))
|
||||
if varsField == nil || varsField.Map() == nil {
|
||||
return
|
||||
}
|
||||
|
||||
legendField := varsField.Map().GetField(d2ast.FlatUnquotedString("d2-legend"))
|
||||
if legendField == nil || legendField.Map() == nil {
|
||||
return
|
||||
}
|
||||
|
||||
legendGraph := d2graph.NewGraph()
|
||||
|
||||
c.compileMap(legendGraph.Root, legendField.Map())
|
||||
c.setDefaultShapes(legendGraph)
|
||||
|
||||
objects := make([]*d2graph.Object, 0)
|
||||
for _, obj := range legendGraph.Objects {
|
||||
if obj.Style.Opacity != nil {
|
||||
if opacity, err := strconv.ParseFloat(obj.Style.Opacity.Value, 64); err == nil && opacity == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
objects = append(objects, obj)
|
||||
}
|
||||
|
||||
legend := &d2graph.Legend{
|
||||
Objects: objects,
|
||||
Edges: legendGraph.Edges,
|
||||
}
|
||||
|
||||
if len(legend.Objects) > 0 || len(legend.Edges) > 0 {
|
||||
g.Legend = legend
|
||||
}
|
||||
}
|
||||
|
||||
func (c *compiler) compileBoardsField(g *d2graph.Graph, ir *d2ir.Map, fieldName string) {
|
||||
boards := ir.GetField(d2ast.FlatUnquotedString(fieldName))
|
||||
if boards.Map() == nil {
|
||||
|
|
|
|||
|
|
@ -720,6 +720,142 @@ x: {
|
|||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "legend",
|
||||
|
||||
text: `
|
||||
vars: {
|
||||
d2-legend: {
|
||||
User: "A person who interacts with the system" {
|
||||
shape: person
|
||||
style: {
|
||||
fill: "#f5f5f5"
|
||||
}
|
||||
}
|
||||
|
||||
Database: "Stores application data" {
|
||||
shape: cylinder
|
||||
style.fill: "#b5d3ff"
|
||||
}
|
||||
|
||||
HiddenShape: "This should not appear in the legend" {
|
||||
style.opacity: 0
|
||||
}
|
||||
|
||||
User -> Database: "Reads data" {
|
||||
style.stroke: "blue"
|
||||
}
|
||||
|
||||
Database -> User: "Returns results" {
|
||||
style.stroke-dash: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
user: User
|
||||
db: Database
|
||||
user -> db: Uses
|
||||
`,
|
||||
assertions: func(t *testing.T, g *d2graph.Graph) {
|
||||
if g.Legend == nil {
|
||||
t.Fatal("Expected Legend to be non-nil")
|
||||
return
|
||||
}
|
||||
|
||||
// 2. Verify the correct objects are in the legend
|
||||
if len(g.Legend.Objects) != 2 {
|
||||
t.Errorf("Expected 2 objects in legend, got %d", len(g.Legend.Objects))
|
||||
}
|
||||
|
||||
// Check for User object
|
||||
hasUser := false
|
||||
hasDatabase := false
|
||||
for _, obj := range g.Legend.Objects {
|
||||
if obj.ID == "User" {
|
||||
hasUser = true
|
||||
if obj.Shape.Value != "person" {
|
||||
t.Errorf("User shape incorrect, expected 'person', got: %s", obj.Shape.Value)
|
||||
}
|
||||
} else if obj.ID == "Database" {
|
||||
hasDatabase = true
|
||||
if obj.Shape.Value != "cylinder" {
|
||||
t.Errorf("Database shape incorrect, expected 'cylinder', got: %s", obj.Shape.Value)
|
||||
}
|
||||
} else if obj.ID == "HiddenShape" {
|
||||
t.Errorf("HiddenShape should not be in legend due to opacity: 0")
|
||||
}
|
||||
}
|
||||
|
||||
if !hasUser {
|
||||
t.Errorf("User object missing from legend")
|
||||
}
|
||||
if !hasDatabase {
|
||||
t.Errorf("Database object missing from legend")
|
||||
}
|
||||
|
||||
// 3. Verify the correct edges are in the legend
|
||||
if len(g.Legend.Edges) != 2 {
|
||||
t.Errorf("Expected 2 edges in legend, got %d", len(g.Legend.Edges))
|
||||
}
|
||||
|
||||
// Check for expected edges
|
||||
hasReadsEdge := false
|
||||
hasReturnsEdge := false
|
||||
for _, edge := range g.Legend.Edges {
|
||||
if edge.Label.Value == "Reads data" {
|
||||
hasReadsEdge = true
|
||||
// Check edge properties
|
||||
if edge.Style.Stroke == nil {
|
||||
t.Errorf("Reads edge stroke is nil")
|
||||
} else if edge.Style.Stroke.Value != "blue" {
|
||||
t.Errorf("Reads edge stroke incorrect, expected 'blue', got: %s", edge.Style.Stroke.Value)
|
||||
}
|
||||
} else if edge.Label.Value == "Returns results" {
|
||||
hasReturnsEdge = true
|
||||
// Check edge properties
|
||||
if edge.Style.StrokeDash == nil {
|
||||
t.Errorf("Returns edge stroke-dash is nil")
|
||||
} else if edge.Style.StrokeDash.Value != "5" {
|
||||
t.Errorf("Returns edge stroke-dash incorrect, expected '5', got: %s", edge.Style.StrokeDash.Value)
|
||||
}
|
||||
} else if edge.Label.Value == "Hidden connection" {
|
||||
t.Errorf("Hidden connection should not be in legend due to opacity: 0")
|
||||
}
|
||||
}
|
||||
|
||||
if !hasReadsEdge {
|
||||
t.Errorf("'Reads data' edge missing from legend")
|
||||
}
|
||||
if !hasReturnsEdge {
|
||||
t.Errorf("'Returns results' edge missing from legend")
|
||||
}
|
||||
|
||||
// 4. Verify the regular diagram content is still there
|
||||
userObj, hasUserObj := g.Root.HasChild([]string{"user"})
|
||||
if !hasUserObj {
|
||||
t.Errorf("Main diagram missing 'user' object")
|
||||
} else if userObj.Label.Value != "User" {
|
||||
t.Errorf("User label incorrect, expected 'User', got: %s", userObj.Label.Value)
|
||||
}
|
||||
|
||||
dbObj, hasDBObj := g.Root.HasChild([]string{"db"})
|
||||
if !hasDBObj {
|
||||
t.Errorf("Main diagram missing 'db' object")
|
||||
} else if dbObj.Label.Value != "Database" {
|
||||
t.Errorf("DB label incorrect, expected 'Database', got: %s", dbObj.Label.Value)
|
||||
}
|
||||
|
||||
// Check the main edge
|
||||
if len(g.Edges) == 0 {
|
||||
t.Errorf("No edges found in main diagram")
|
||||
} else {
|
||||
mainEdge := g.Edges[0]
|
||||
if mainEdge.Label.Value != "Uses" {
|
||||
t.Errorf("Main edge label incorrect, expected 'Uses', got: %s", mainEdge.Label.Value)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "underscore_edge_nested",
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,26 @@ func Export(ctx context.Context, g *d2graph.Graph, fontFamily *d2fonts.FontFamil
|
|||
diagram.Connections[i] = toConnection(g.Edges[i], g.Theme)
|
||||
}
|
||||
|
||||
if g.Legend != nil {
|
||||
legend := &d2target.Legend{}
|
||||
|
||||
if len(g.Legend.Objects) > 0 {
|
||||
legend.Shapes = make([]d2target.Shape, len(g.Legend.Objects))
|
||||
for i, obj := range g.Legend.Objects {
|
||||
legend.Shapes[i] = toShape(obj, g)
|
||||
}
|
||||
}
|
||||
|
||||
if len(g.Legend.Edges) > 0 {
|
||||
legend.Connections = make([]d2target.Connection, len(g.Legend.Edges))
|
||||
for i, edge := range g.Legend.Edges {
|
||||
legend.Connections[i] = toConnection(edge, g.Theme)
|
||||
}
|
||||
}
|
||||
|
||||
diagram.Legend = legend
|
||||
}
|
||||
|
||||
return diagram, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ type Graph struct {
|
|||
BaseAST *d2ast.Map `json:"-"`
|
||||
|
||||
Root *Object `json:"root"`
|
||||
Legend *Legend `json:"legend,omitempty"`
|
||||
Edges []*Edge `json:"edges"`
|
||||
Objects []*Object `json:"objects"`
|
||||
|
||||
|
|
@ -67,6 +68,11 @@ type Graph struct {
|
|||
Data map[string]interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type Legend struct {
|
||||
Objects []*Object `json:"objects,omitempty"`
|
||||
Edges []*Edge `json:"edges,omitempty"`
|
||||
}
|
||||
|
||||
func NewGraph() *Graph {
|
||||
d := &Graph{}
|
||||
d.Root = &Object{
|
||||
|
|
|
|||
|
|
@ -86,7 +86,8 @@ type Diagram struct {
|
|||
Shapes []Shape `json:"shapes"`
|
||||
Connections []Connection `json:"connections"`
|
||||
|
||||
Root Shape `json:"root"`
|
||||
Root Shape `json:"root"`
|
||||
Legend *Legend `json:"legend,omitempty"`
|
||||
// Maybe Icon can be used as a watermark in the root shape
|
||||
|
||||
Layers []*Diagram `json:"layers,omitempty"`
|
||||
|
|
@ -94,6 +95,11 @@ type Diagram struct {
|
|||
Steps []*Diagram `json:"steps,omitempty"`
|
||||
}
|
||||
|
||||
type Legend struct {
|
||||
Shapes []Shape `json:"shapes,omitempty"`
|
||||
Connections []Connection `json:"connections,omitempty"`
|
||||
}
|
||||
|
||||
func (d *Diagram) GetBoard(boardPath []string) *Diagram {
|
||||
if len(boardPath) == 0 {
|
||||
return d
|
||||
|
|
|
|||
1168
testdata/d2compiler/TestCompile/legend.exp.json
generated
vendored
Normal file
1168
testdata/d2compiler/TestCompile/legend.exp.json
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue