Support update conditional formatting on inserting/deleting columns/rows (#1717)

Return error for unsupported conditional formatting rule types
This commit is contained in:
ByteFlyCoding 2023-11-10 09:25:59 +08:00 committed by GitHub
parent 134865d9d2
commit e014a8bb23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 86 additions and 12 deletions

View File

@ -30,7 +30,10 @@ const (
)
// adjustHelperFunc defines functions to adjust helper.
var adjustHelperFunc = [7]func(*File, *xlsxWorksheet, string, adjustDirection, int, int, int) error{
var adjustHelperFunc = [8]func(*File, *xlsxWorksheet, string, adjustDirection, int, int, int) error{
func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
return f.adjustConditionalFormats(ws, sheet, dir, num, offset, sheetID)
},
func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
return f.adjustDefinedNames(ws, sheet, dir, num, offset, sheetID)
},
@ -231,15 +234,19 @@ func (f *File) adjustSingleRowFormulas(sheet, sheetN string, r *xlsxRow, num, of
}
// adjustCellRef provides a function to adjust cell reference.
func (f *File) adjustCellRef(ref string, dir adjustDirection, num, offset int) (string, error) {
func (f *File) adjustCellRef(ref string, dir adjustDirection, num, offset int) (string, bool, error) {
if !strings.Contains(ref, ":") {
ref += ":" + ref
}
var delete bool
coordinates, err := rangeRefToCoordinates(ref)
if err != nil {
return ref, err
return ref, delete, err
}
if dir == columns {
if offset < 0 && coordinates[0] == coordinates[2] {
delete = true
}
if coordinates[0] >= num {
coordinates[0] += offset
}
@ -247,6 +254,9 @@ func (f *File) adjustCellRef(ref string, dir adjustDirection, num, offset int) (
coordinates[2] += offset
}
} else {
if offset < 0 && coordinates[1] == coordinates[3] {
delete = true
}
if coordinates[1] >= num {
coordinates[1] += offset
}
@ -254,7 +264,8 @@ func (f *File) adjustCellRef(ref string, dir adjustDirection, num, offset int) (
coordinates[3] += offset
}
}
return f.coordinatesToRangeRef(coordinates)
ref, err = f.coordinatesToRangeRef(coordinates)
return ref, delete, err
}
// adjustFormula provides a function to adjust formula reference and shared
@ -265,7 +276,7 @@ func (f *File) adjustFormula(sheet, sheetN string, formula *xlsxF, dir adjustDir
}
var err error
if formula.Ref != "" && sheet == sheetN {
if formula.Ref, err = f.adjustCellRef(formula.Ref, dir, num, offset); err != nil {
if formula.Ref, _, err = f.adjustCellRef(formula.Ref, dir, num, offset); err != nil {
return err
}
if si && formula.Si != nil {
@ -770,6 +781,29 @@ func (f *File) adjustVolatileDeps(ws *xlsxWorksheet, sheet string, dir adjustDir
return nil
}
// adjustConditionalFormats updates the cell reference of the worksheet
// conditional formatting when inserting or deleting rows or columns.
func (f *File) adjustConditionalFormats(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
for i := 0; i < len(ws.ConditionalFormatting); i++ {
cf := ws.ConditionalFormatting[i]
if cf == nil {
continue
}
ref, del, err := f.adjustCellRef(cf.SQRef, dir, num, offset)
if err != nil {
return err
}
if del {
ws.ConditionalFormatting = append(ws.ConditionalFormatting[:i],
ws.ConditionalFormatting[i+1:]...)
i--
continue
}
ws.ConditionalFormatting[i].SQRef = ref
}
return nil
}
// adjustDrawings updates the starting anchor of the two cell anchor pictures
// and charts object when inserting or deleting rows or columns.
func (from *xlsxFrom) adjustDrawings(dir adjustDirection, num, offset int, editAs string) (bool, error) {

View File

@ -962,6 +962,37 @@ func TestAdjustVolatileDeps(t *testing.T) {
f.volatileDepsWriter()
}
func TestAdjustConditionalFormats(t *testing.T) {
f := NewFile()
assert.NoError(t, f.SetSheetRow("Sheet1", "B1", &[]interface{}{1, nil, 1, 1}))
formatID, err := f.NewConditionalStyle(&Style{Font: &Font{Color: "09600B"}, Fill: Fill{Type: "pattern", Color: []string{"C7EECF"}, Pattern: 1}})
assert.NoError(t, err)
format := []ConditionalFormatOptions{
{
Type: "cell",
Criteria: "greater than",
Format: formatID,
Value: "0",
},
}
for _, ref := range []string{"B1", "D1:E1"} {
assert.NoError(t, f.SetConditionalFormat("Sheet1", ref, format))
}
assert.NoError(t, f.RemoveCol("Sheet1", "B"))
opts, err := f.GetConditionalFormats("Sheet1")
assert.NoError(t, err)
assert.Len(t, format, 1)
assert.Equal(t, format, opts["C1:D1"])
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
assert.True(t, ok)
ws.(*xlsxWorksheet).ConditionalFormatting[0].SQRef = "-"
assert.Equal(t, newCellNameToCoordinatesError("-", newInvalidCellNameError("-")), f.RemoveCol("Sheet1", "B"))
ws.(*xlsxWorksheet).ConditionalFormatting[0] = nil
assert.NoError(t, f.RemoveCol("Sheet1", "B"))
}
func TestAdjustDrawings(t *testing.T) {
f := NewFile()
// Test add pictures to sheet with positioning

View File

@ -67,12 +67,6 @@ func TestDataValidation(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, dataValidations, 1)
dv = NewDataValidation(true)
dv.Sqref = "A4:A5"
assert.NoError(t, dv.SetRange("Sheet2!$A$2:$A$3", "", DataValidationTypeList, DataValidationOperatorBetween))
dv.SetError(DataValidationErrorStyleStop, "error title", "error body")
assert.NoError(t, f.AddDataValidation("Sheet2", dv))
dv = NewDataValidation(true)
dv.Sqref = "A5:B6"
for _, listValid := range [][]string{

View File

@ -1223,7 +1223,7 @@ func TestConditionalFormat(t *testing.T) {
},
))
// Set conditional format with illegal criteria type
assert.NoError(t, f.SetConditionalFormat(sheet1, "K1:K10",
assert.Equal(t, ErrParameterInvalid, f.SetConditionalFormat(sheet1, "K1:K10",
[]ConditionalFormatOptions{
{
Type: "data_bar",

View File

@ -2664,8 +2664,10 @@ func (f *File) SetConditionalFormat(sheet, rangeRef string, opts []ConditionalFo
f.addSheetNameSpace(sheet, NameSpaceSpreadSheetX14)
}
cfRule = append(cfRule, rule)
continue
}
}
return ErrParameterInvalid
}
}

View File

@ -191,6 +191,19 @@ func TestSetConditionalFormat(t *testing.T) {
assert.EqualError(t, f.SetConditionalFormat("Sheet1", "A1:A2", condFmts), "XML syntax error on line 1: element <conditionalFormattings> closed by </conditionalFormatting>")
// Test creating a conditional format with invalid icon set style
assert.EqualError(t, f.SetConditionalFormat("Sheet1", "A1:A2", []ConditionalFormatOptions{{Type: "icon_set", IconStyle: "unknown"}}), ErrParameterInvalid.Error())
// Test unsupported conditional formatting rule types
for _, val := range []string{
"date",
"time",
"text",
"time_period",
"blanks",
"no_blanks",
"errors",
"no_errors",
} {
assert.Equal(t, ErrParameterInvalid, f.SetConditionalFormat("Sheet1", "A1", []ConditionalFormatOptions{{Type: val}}))
}
}
func TestGetConditionalFormats(t *testing.T) {