mirror of
https://github.com/larksuite/cli.git
synced 2026-07-05 07:31:22 +08:00
Compare commits
1 Commits
feat/sheet
...
feat/lark-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3955bbee8c |
@@ -669,7 +669,8 @@ func writeSheetData(ctx context.Context, runtime *common.RuntimeContext, token,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := applyWorkbookCreateStylesToMatrix(matrix, styles, col0, baseRow, fmt.Sprintf("--styles for sheet %q", s.Name)); err != nil {
|
||||
matrix, err = applyWorkbookCreateStylesToMatrix(matrix, styles, col0, baseRow, fmt.Sprintf("--styles for sheet %q", s.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -681,11 +682,15 @@ func writeSheetData(ctx context.Context, runtime *common.RuntimeContext, token,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// styles can pad the matrix wider than the declared columns (cell_styles on
|
||||
// blank cells past the data extent), so the written width comes from the
|
||||
// padded matrix, not ncols.
|
||||
writeCols := len(matrix[0])
|
||||
startCol := columnIndexToLetter(col0)
|
||||
endCol := columnIndexToLetter(col0 + ncols - 1)
|
||||
endCol := columnIndexToLetter(col0 + writeCols - 1)
|
||||
allowOverwrite := s.AllowOverwrite == nil || *s.AllowOverwrite
|
||||
|
||||
rowsPerBatch := tablePutMaxCellsPerWrite / ncols
|
||||
rowsPerBatch := tablePutMaxCellsPerWrite / writeCols
|
||||
if rowsPerBatch < 1 {
|
||||
rowsPerBatch = 1
|
||||
}
|
||||
@@ -719,7 +724,7 @@ func writeSheetData(ctx context.Context, runtime *common.RuntimeContext, token,
|
||||
"sheet_id": sheetID,
|
||||
"range": fmt.Sprintf("%s%d:%s%d", startCol, baseRow+1, endCol, baseRow+len(matrix)),
|
||||
"data_rows": len(s.Rows),
|
||||
"columns": ncols,
|
||||
"columns": writeCols,
|
||||
"writes": writes,
|
||||
"mode": writeModeName(s),
|
||||
}, nil
|
||||
@@ -811,7 +816,7 @@ func writeTypedSheets(ctx context.Context, runtime *common.RuntimeContext, token
|
||||
s := &payload.Sheets[i]
|
||||
sheetID, ok := byName[s.Name]
|
||||
if !ok {
|
||||
rows, cols := sheetCreateDims(s)
|
||||
rows, cols := sheetCreateDims(s, styles.styleFor(i))
|
||||
sheetID, err = createSheet(ctx, runtime, token, s.Name, rows, cols)
|
||||
if err != nil {
|
||||
return written, fmt.Errorf("creating sheet %q failed: %w", s.Name, err) //nolint:forbidigo // intermediate error; surfaced as a partial_success message string via tablePutPartial, not a typed final error
|
||||
@@ -890,10 +895,12 @@ func createSheet(ctx context.Context, runtime *common.RuntimeContext, token, nam
|
||||
|
||||
// sheetCreateDims sizes a to-be-created sheet to the spec's write range so the
|
||||
// follow-up set_cell_range can't exceed sheet bounds. It accounts for the
|
||||
// start_cell offset and the optional header row. The backend's 20×200 defaults
|
||||
// are kept as floors (ordinary small tables are created exactly as before) and
|
||||
// its hard limits (200 cols, 50000 rows) as ceilings.
|
||||
func sheetCreateDims(s *tableSheetSpec) (rows, cols int) {
|
||||
// start_cell offset, the optional header row, and any --styles extent (so a
|
||||
// cell_styles / merge / resize op past the data still fits the grid). The
|
||||
// backend's 20×200 defaults are kept as floors (ordinary small tables are
|
||||
// created exactly as before) and its hard limits (200 cols, 50000 rows) as
|
||||
// ceilings.
|
||||
func sheetCreateDims(s *tableSheetSpec, styles *workbookCreateStylePayload) (rows, cols int) {
|
||||
_, col0, row0, _ := sheetAnchor(s)
|
||||
cols = col0 + len(s.Columns)
|
||||
rows = row0 + len(s.Rows)
|
||||
@@ -908,6 +915,19 @@ func sheetCreateDims(s *tableSheetSpec) (rows, cols int) {
|
||||
if headerOn(s) || (s.Mode == "append" && s.Header == nil) {
|
||||
rows++
|
||||
}
|
||||
// --styles can reach past the data (cell_styles on blank cells get padded
|
||||
// into the matrix and written; merges / resizes run as separate ops). Size
|
||||
// the grid to cover them too. workbookCreateStyleDimensions returns the
|
||||
// extent relative to the anchor, so add the anchor offset back.
|
||||
if styles != nil {
|
||||
styleRows, styleCols := workbookCreateStyleDimensions(styles, col0, row0)
|
||||
if col0+styleCols > cols {
|
||||
cols = col0 + styleCols
|
||||
}
|
||||
if row0+styleRows > rows {
|
||||
rows = row0 + styleRows
|
||||
}
|
||||
}
|
||||
if cols < 20 {
|
||||
cols = 20
|
||||
}
|
||||
@@ -1005,8 +1025,6 @@ func tablePutDryRun(runtime *common.RuntimeContext) *common.DryRunAPI {
|
||||
for i := range payload.Sheets {
|
||||
s := &payload.Sheets[i]
|
||||
matrix, _ := buildSheetMatrix(s, headerOn(s))
|
||||
desc := fmt.Sprintf("write sheet %q (%d data rows × %d cols, mode=%s) via set_cell_range",
|
||||
s.Name, len(s.Rows), len(s.Columns), writeModeName(s))
|
||||
rng := tablePutFullRange(s, len(matrix))
|
||||
if s.Mode == "append" {
|
||||
rng = "<append below existing data>"
|
||||
@@ -1014,10 +1032,23 @@ func tablePutDryRun(runtime *common.RuntimeContext) *common.DryRunAPI {
|
||||
// cell_styles are merged into the matrix only for overwrite mode,
|
||||
// where the anchor row is known statically; append's base row is
|
||||
// resolved at execute time, so the preview leaves the matrix bare
|
||||
// (the merges / sizes ops below still render).
|
||||
// (the merges / sizes ops below still render). Padding can widen /
|
||||
// lengthen the matrix past the data, so recompute the range from the
|
||||
// padded dims to match what Execute writes.
|
||||
_, col0, row0, _ := sheetAnchor(s)
|
||||
_ = applyWorkbookCreateStylesToMatrix(matrix, sheetStyles.styleFor(i), col0, row0, fmt.Sprintf("--styles for sheet %q", s.Name))
|
||||
matrix, _ = applyWorkbookCreateStylesToMatrix(matrix, sheetStyles.styleFor(i), col0, row0, fmt.Sprintf("--styles for sheet %q", s.Name))
|
||||
if len(matrix) > 0 {
|
||||
rng = fmt.Sprintf("%s%d:%s%d",
|
||||
columnIndexToLetter(col0), row0+1,
|
||||
columnIndexToLetter(col0+len(matrix[0])-1), row0+len(matrix))
|
||||
}
|
||||
}
|
||||
writeCols := len(s.Columns)
|
||||
if len(matrix) > 0 {
|
||||
writeCols = len(matrix[0])
|
||||
}
|
||||
desc := fmt.Sprintf("write sheet %q (%d data rows × %d cols, mode=%s) via set_cell_range",
|
||||
s.Name, len(s.Rows), writeCols, writeModeName(s))
|
||||
input := map[string]interface{}{
|
||||
"excel_id": token,
|
||||
"sheet_name": s.Name,
|
||||
|
||||
@@ -537,14 +537,15 @@ func TestTablePut_SheetCreateDims(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
spec tableSheetSpec
|
||||
styles *workbookCreateStylePayload
|
||||
wantRows, wantCols int
|
||||
}{
|
||||
{"small table keeps 20x200 floor", tableSheetSpec{Columns: cols(3), Rows: rows(5)}, 200, 20},
|
||||
{"wide table grows columns", tableSheetSpec{Columns: cols(37), Rows: rows(22)}, 200, 37},
|
||||
{"long table grows rows", tableSheetSpec{Columns: cols(3), Rows: rows(500)}, 501, 20},
|
||||
{"start_cell offset adds to both", tableSheetSpec{StartCell: "C5", Columns: cols(40), Rows: rows(5)}, 200, 42},
|
||||
{"header:false drops the header row", tableSheetSpec{Header: bp(false), Columns: cols(3), Rows: rows(500)}, 500, 20},
|
||||
{"columns clamp at backend max 200", tableSheetSpec{Columns: cols(250), Rows: rows(5)}, 200, 200},
|
||||
{"small table keeps 20x200 floor", tableSheetSpec{Columns: cols(3), Rows: rows(5)}, nil, 200, 20},
|
||||
{"wide table grows columns", tableSheetSpec{Columns: cols(37), Rows: rows(22)}, nil, 200, 37},
|
||||
{"long table grows rows", tableSheetSpec{Columns: cols(3), Rows: rows(500)}, nil, 501, 20},
|
||||
{"start_cell offset adds to both", tableSheetSpec{StartCell: "C5", Columns: cols(40), Rows: rows(5)}, nil, 200, 42},
|
||||
{"header:false drops the header row", tableSheetSpec{Header: bp(false), Columns: cols(3), Rows: rows(500)}, nil, 500, 20},
|
||||
{"columns clamp at backend max 200", tableSheetSpec{Columns: cols(250), Rows: rows(5)}, nil, 200, 200},
|
||||
// Default headerOn() is false for append mode, but writeSheetData forces
|
||||
// a header when append hits an empty sheet with no explicit Header
|
||||
// choice (so column names aren't lost). sheetCreateDims runs only on
|
||||
@@ -552,14 +553,22 @@ func TestTablePut_SheetCreateDims(t *testing.T) {
|
||||
// match: append + Header=nil ⇒ +1 row. Otherwise an append-near-50000
|
||||
// payload would be created one row short.
|
||||
{"append on new sheet sizes for the forced header row (49999 data rows + 1 header = 50000)",
|
||||
tableSheetSpec{Mode: "append", Columns: cols(3), Rows: rows(49999)}, 50000, 20},
|
||||
tableSheetSpec{Mode: "append", Columns: cols(3), Rows: rows(49999)}, nil, 50000, 20},
|
||||
{"append + Header=false (explicit) does NOT add the forced header row",
|
||||
tableSheetSpec{Mode: "append", Header: bp(false), Columns: cols(3), Rows: rows(50)}, 200, 20},
|
||||
tableSheetSpec{Mode: "append", Header: bp(false), Columns: cols(3), Rows: rows(50)}, nil, 200, 20},
|
||||
// --styles reaching past the data grows the grid so a cell_styles op on a
|
||||
// blank cell (or a merge / resize) still fits after the create.
|
||||
{"styles past the data grow the grid",
|
||||
tableSheetSpec{Columns: cols(3), Rows: rows(5)},
|
||||
&workbookCreateStylePayload{CellStyles: []workbookCreateCellStyleOp{{Range: "A1:Z400"}}}, 400, 26},
|
||||
{"styles inside the data don't shrink the grid",
|
||||
tableSheetSpec{Columns: cols(3), Rows: rows(5)},
|
||||
&workbookCreateStylePayload{CellStyles: []workbookCreateCellStyleOp{{Range: "A1:B2"}}}, 200, 20},
|
||||
}
|
||||
for _, tt := range cases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
gotRows, gotCols := sheetCreateDims(&tt.spec)
|
||||
gotRows, gotCols := sheetCreateDims(&tt.spec, tt.styles)
|
||||
if gotRows != tt.wantRows || gotCols != tt.wantCols {
|
||||
t.Errorf("sheetCreateDims = (%d rows, %d cols), want (%d, %d)", gotRows, gotCols, tt.wantRows, tt.wantCols)
|
||||
}
|
||||
|
||||
@@ -654,16 +654,26 @@ var WorkbookCreate = common.Shortcut{
|
||||
s := &payload.Sheets[i]
|
||||
matrix, _ := buildSheetMatrix(s, headerOn(s))
|
||||
_, col0, row0, _ := sheetAnchor(s)
|
||||
_ = applyWorkbookCreateStylesToMatrix(matrix, sheetStyles.styleFor(i), col0, row0, fmt.Sprintf("--styles for sheet %q", s.Name))
|
||||
matrix, _ = applyWorkbookCreateStylesToMatrix(matrix, sheetStyles.styleFor(i), col0, row0, fmt.Sprintf("--styles for sheet %q", s.Name))
|
||||
// Padding can widen / lengthen the matrix past the data, so build the
|
||||
// range from the padded dims to match what Execute writes.
|
||||
rng := tablePutFullRange(s, len(matrix))
|
||||
writeCols := len(s.Columns)
|
||||
if len(matrix) > 0 {
|
||||
writeCols = len(matrix[0])
|
||||
rng = fmt.Sprintf("%s%d:%s%d",
|
||||
columnIndexToLetter(col0), row0+1,
|
||||
columnIndexToLetter(col0+writeCols-1), row0+len(matrix))
|
||||
}
|
||||
input := map[string]interface{}{
|
||||
"excel_id": "<new-token>",
|
||||
"sheet_name": s.Name,
|
||||
"range": tablePutFullRange(s, len(matrix)),
|
||||
"range": rng,
|
||||
"cells": matrix,
|
||||
}
|
||||
wireBody, _ := buildToolBody("set_cell_range", input)
|
||||
dry.POST("/open-apis/sheet_ai/v2/spreadsheets/<new-token>/tools/invoke_write").
|
||||
Desc(fmt.Sprintf("write sheet %q (%d data rows × %d cols) via set_cell_range", s.Name, len(s.Rows), len(s.Columns))).
|
||||
Desc(fmt.Sprintf("write sheet %q (%d data rows × %d cols) via set_cell_range", s.Name, len(s.Rows), writeCols)).
|
||||
Body(wireBody)
|
||||
appendWorkbookCreateVisualOpsDryRun(dry, "<new-token>", "", s.Name, sheetStyles.styleFor(i))
|
||||
}
|
||||
@@ -1383,20 +1393,79 @@ func workbookCreateStyleDimensions(styles *workbookCreateStylePayload, baseCol,
|
||||
return rows, cols
|
||||
}
|
||||
|
||||
func applyWorkbookCreateStylesToMatrix(rows [][]interface{}, styles *workbookCreateStylePayload, baseCol, baseRow int, label string) error {
|
||||
// padMatrixForStyles grows the matrix down and right so it covers every
|
||||
// cell_styles range, appending empty cells for the positions the data doesn't
|
||||
// reach. cell_styles are applied by writing into the matrix in place (see
|
||||
// applyWorkbookCreateStylesToMatrix), so a style on a cell past the data extent
|
||||
// needs a real — if empty — cell to attach to. This lets a create/write set
|
||||
// styles on blank cells (reserved regions, decorative headers, empty borders).
|
||||
//
|
||||
// Only cell_styles contribute to the extent: cell_merges / row_sizes / col_sizes
|
||||
// run as separate API calls (see workbookCreateVisualOps) and never touch the
|
||||
// matrix. Ranges that start left of baseCol or above baseRow are skipped here
|
||||
// (the matrix can only extend down/right) and left for the caller to reject.
|
||||
// The (possibly reallocated) matrix is returned; callers must use the result.
|
||||
func padMatrixForStyles(rows [][]interface{}, styles *workbookCreateStylePayload, baseCol, baseRow int) [][]interface{} {
|
||||
if styles == nil {
|
||||
return nil
|
||||
return rows
|
||||
}
|
||||
needRows := len(rows)
|
||||
needCols := 0
|
||||
if len(rows) > 0 {
|
||||
needCols = len(rows[0])
|
||||
}
|
||||
for _, op := range styles.CellStyles {
|
||||
startCol, startRow, endCol, endRow, err := workbookCreateStyleRangeBounds(op.Range)
|
||||
if err != nil || startCol < baseCol || startRow < baseRow {
|
||||
continue // unparsable, or up/left of the anchor: not paddable
|
||||
}
|
||||
if endCol-baseCol+1 > needCols {
|
||||
needCols = endCol - baseCol + 1
|
||||
}
|
||||
if endRow-baseRow+1 > needRows {
|
||||
needRows = endRow - baseRow + 1
|
||||
}
|
||||
}
|
||||
// Widen existing rows to needCols.
|
||||
for r := range rows {
|
||||
for len(rows[r]) < needCols {
|
||||
rows[r] = append(rows[r], map[string]interface{}{})
|
||||
}
|
||||
}
|
||||
// Append full empty rows to reach needRows.
|
||||
for len(rows) < needRows {
|
||||
row := make([]interface{}, needCols)
|
||||
for c := range row {
|
||||
row[c] = map[string]interface{}{}
|
||||
}
|
||||
rows = append(rows, row)
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
// applyWorkbookCreateStylesToMatrix pads the matrix to cover the cell_styles
|
||||
// ranges (see padMatrixForStyles), merges each op's style into the covered
|
||||
// cells, and returns the padded matrix. A range that starts left of / above the
|
||||
// write anchor can't be padded to and is rejected.
|
||||
func applyWorkbookCreateStylesToMatrix(rows [][]interface{}, styles *workbookCreateStylePayload, baseCol, baseRow int, label string) ([][]interface{}, error) {
|
||||
if styles == nil {
|
||||
return rows, nil
|
||||
}
|
||||
rows = padMatrixForStyles(rows, styles, baseCol, baseRow)
|
||||
for i, op := range styles.CellStyles {
|
||||
startCol, startRow, endCol, endRow, err := workbookCreateStyleRangeBounds(op.Range)
|
||||
if err != nil {
|
||||
return common.ValidationErrorf("%s[%d].range %q: %v", label, i, op.Range, err)
|
||||
return rows, common.ValidationErrorf("%s[%d].range %q: %v", label, i, op.Range, err)
|
||||
}
|
||||
if startCol < baseCol || startRow < baseRow || endRow-baseRow >= len(rows) || len(rows) == 0 || endCol-baseCol >= len(rows[0]) {
|
||||
return common.ValidationErrorf("%s[%d].range %q is outside the write range %s%d:%s%d",
|
||||
// After padding, the matrix reaches every range that starts at or after
|
||||
// the anchor; a start left of / above it can't be covered. The endRow /
|
||||
// endCol checks stay as a defensive backstop (padding should have made
|
||||
// them unreachable).
|
||||
if startCol < baseCol || startRow < baseRow || len(rows) == 0 ||
|
||||
endRow-baseRow >= len(rows) || endCol-baseCol >= len(rows[0]) {
|
||||
return rows, common.ValidationErrorf("%s[%d].range %q starts outside the write range (its top-left must be at or after %s%d)",
|
||||
label, i, op.Range,
|
||||
columnIndexToLetter(baseCol), baseRow+1,
|
||||
columnIndexToLetter(baseCol+len(rows[0])-1), baseRow+len(rows))
|
||||
columnIndexToLetter(baseCol), baseRow+1)
|
||||
}
|
||||
for r := startRow - baseRow; r <= endRow-baseRow; r++ {
|
||||
for c := startCol - baseCol; c <= endCol-baseCol; c++ {
|
||||
@@ -1404,7 +1473,7 @@ func applyWorkbookCreateStylesToMatrix(rows [][]interface{}, styles *workbookCre
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func appendWorkbookCreateVisualOpsDryRun(dry *common.DryRunAPI, token, sheetID, sheetName string, styles *workbookCreateStylePayload) {
|
||||
|
||||
@@ -396,6 +396,37 @@ func TestWorkbookCreate_DryRun(t *testing.T) {
|
||||
t.Errorf("horizontal_alignment occurrences = %d, want 4 in 2x2 range; cells=%s", got, raw)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("cell style past the data pads empty cells so blank cells can be styled", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Data is a single cell (A1), but the style targets A1:C3 — the matrix is
|
||||
// padded to 3x3 with empty cells so the style range fits, letting blank
|
||||
// cells carry styling. The written range must reflect the padded extent.
|
||||
calls := parseDryRunAPI(t, WorkbookCreate, []string{
|
||||
"--title", "X",
|
||||
"--values", `[["a"]]`,
|
||||
"--styles", `{"styles":[{"name":"Sheet1","cell_styles":[{"range":"A1:C3","background_color":"#FFEEAA"}]}]}`,
|
||||
})
|
||||
body, _ := calls[1].(map[string]interface{})["body"].(map[string]interface{})
|
||||
input := decodeToolInput(t, body, "set_cell_range")
|
||||
if input["range"] != "A1:C3" {
|
||||
t.Errorf("range = %v, want A1:C3 (padded to the style extent)", input["range"])
|
||||
}
|
||||
cells, _ := input["cells"].([]interface{})
|
||||
if len(cells) != 3 {
|
||||
t.Fatalf("cells rows = %d, want 3 (padded); cells=%#v", len(cells), input["cells"])
|
||||
}
|
||||
lastRow, _ := cells[2].([]interface{})
|
||||
if len(lastRow) != 3 {
|
||||
t.Fatalf("last row width = %d, want 3 (padded)", len(lastRow))
|
||||
}
|
||||
// A blank padded cell (C3) still carries the style.
|
||||
c3, _ := lastRow[2].(map[string]interface{})
|
||||
c3s, _ := c3["cell_styles"].(map[string]interface{})
|
||||
if c3s["background_color"] != "#FFEEAA" {
|
||||
t.Errorf("padded blank cell C3 style = %#v, want background_color", c3)
|
||||
}
|
||||
})
|
||||
t.Run("style-only payload (cell_merges) still fills and emits merge_cells", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Previously workbookCreateStyleDimensions only counted cell_styles, so a
|
||||
@@ -595,3 +626,60 @@ func deepEqualJSON(a, b interface{}) bool {
|
||||
}
|
||||
return a == b
|
||||
}
|
||||
|
||||
// TestApplyWorkbookCreateStylesToMatrix covers the pad-then-style behavior
|
||||
// directly: a style range past the data grows the matrix with empty cells (so
|
||||
// blank cells can be styled), an in-range style leaves the matrix size alone,
|
||||
// and a range up/left of the anchor — which padding can't reach — is rejected.
|
||||
func TestApplyWorkbookCreateStylesToMatrix(t *testing.T) {
|
||||
t.Parallel()
|
||||
cell := func() interface{} { return map[string]interface{}{} }
|
||||
|
||||
t.Run("pads down and right for a style past the data", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
matrix := [][]interface{}{{cell()}} // 1x1 data
|
||||
styles := &workbookCreateStylePayload{CellStyles: []workbookCreateCellStyleOp{
|
||||
{Range: "A1:C3", Style: map[string]interface{}{"cell_styles": map[string]interface{}{"background_color": "#FFEEAA"}}},
|
||||
}}
|
||||
out, err := applyWorkbookCreateStylesToMatrix(matrix, styles, 0, 0, "--styles")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if len(out) != 3 || len(out[0]) != 3 || len(out[2]) != 3 {
|
||||
t.Fatalf("padded matrix = %d rows x %v cols, want 3x3", len(out), out)
|
||||
}
|
||||
// A blank padded corner (C3) carries the style.
|
||||
c3, _ := out[2][2].(map[string]interface{})
|
||||
c3s, _ := c3["cell_styles"].(map[string]interface{})
|
||||
if c3s["background_color"] != "#FFEEAA" {
|
||||
t.Errorf("padded cell C3 = %#v, want background_color", c3)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("no pad when the style is within the data", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
matrix := [][]interface{}{{cell(), cell()}, {cell(), cell()}} // 2x2 data
|
||||
styles := &workbookCreateStylePayload{CellStyles: []workbookCreateCellStyleOp{
|
||||
{Range: "A1:B2", Style: map[string]interface{}{"cell_styles": map[string]interface{}{"font_weight": "bold"}}},
|
||||
}}
|
||||
out, err := applyWorkbookCreateStylesToMatrix(matrix, styles, 0, 0, "--styles")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if len(out) != 2 || len(out[0]) != 2 {
|
||||
t.Errorf("matrix = %dx%d, want 2x2 (no pad)", len(out), len(out[0]))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("rejects a range up/left of the anchor", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
matrix := [][]interface{}{{cell()}} // anchored at C3 (col 2, row 2)
|
||||
styles := &workbookCreateStylePayload{CellStyles: []workbookCreateCellStyleOp{
|
||||
{Range: "A1", Style: map[string]interface{}{"cell_styles": map[string]interface{}{"font_weight": "bold"}}},
|
||||
}}
|
||||
_, err := applyWorkbookCreateStylesToMatrix(matrix, styles, 2, 2, "--styles")
|
||||
if err == nil || !strings.Contains(err.Error(), "starts outside the write range") {
|
||||
t.Errorf("err = %v, want 'starts outside the write range'", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user