Support update conditional formatting on inserting/deleting columns/rows (#1717)
Return error for unsupported conditional formatting rule types
This commit is contained in:
parent
134865d9d2
commit
e014a8bb23
44
adjust.go
44
adjust.go
|
@ -30,7 +30,10 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// adjustHelperFunc defines functions to adjust helper.
|
// 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 {
|
func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
||||||
return f.adjustDefinedNames(ws, sheet, dir, num, offset, sheetID)
|
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.
|
// 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, ":") {
|
if !strings.Contains(ref, ":") {
|
||||||
ref += ":" + ref
|
ref += ":" + ref
|
||||||
}
|
}
|
||||||
|
var delete bool
|
||||||
coordinates, err := rangeRefToCoordinates(ref)
|
coordinates, err := rangeRefToCoordinates(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ref, err
|
return ref, delete, err
|
||||||
}
|
}
|
||||||
if dir == columns {
|
if dir == columns {
|
||||||
|
if offset < 0 && coordinates[0] == coordinates[2] {
|
||||||
|
delete = true
|
||||||
|
}
|
||||||
if coordinates[0] >= num {
|
if coordinates[0] >= num {
|
||||||
coordinates[0] += offset
|
coordinates[0] += offset
|
||||||
}
|
}
|
||||||
|
@ -247,6 +254,9 @@ func (f *File) adjustCellRef(ref string, dir adjustDirection, num, offset int) (
|
||||||
coordinates[2] += offset
|
coordinates[2] += offset
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if offset < 0 && coordinates[1] == coordinates[3] {
|
||||||
|
delete = true
|
||||||
|
}
|
||||||
if coordinates[1] >= num {
|
if coordinates[1] >= num {
|
||||||
coordinates[1] += offset
|
coordinates[1] += offset
|
||||||
}
|
}
|
||||||
|
@ -254,7 +264,8 @@ func (f *File) adjustCellRef(ref string, dir adjustDirection, num, offset int) (
|
||||||
coordinates[3] += offset
|
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
|
// 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
|
var err error
|
||||||
if formula.Ref != "" && sheet == sheetN {
|
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
|
return err
|
||||||
}
|
}
|
||||||
if si && formula.Si != nil {
|
if si && formula.Si != nil {
|
||||||
|
@ -770,6 +781,29 @@ func (f *File) adjustVolatileDeps(ws *xlsxWorksheet, sheet string, dir adjustDir
|
||||||
return nil
|
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
|
// adjustDrawings updates the starting anchor of the two cell anchor pictures
|
||||||
// and charts object when inserting or deleting rows or columns.
|
// and charts object when inserting or deleting rows or columns.
|
||||||
func (from *xlsxFrom) adjustDrawings(dir adjustDirection, num, offset int, editAs string) (bool, error) {
|
func (from *xlsxFrom) adjustDrawings(dir adjustDirection, num, offset int, editAs string) (bool, error) {
|
||||||
|
|
|
@ -962,6 +962,37 @@ func TestAdjustVolatileDeps(t *testing.T) {
|
||||||
f.volatileDepsWriter()
|
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) {
|
func TestAdjustDrawings(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
// Test add pictures to sheet with positioning
|
// Test add pictures to sheet with positioning
|
||||||
|
|
|
@ -67,12 +67,6 @@ func TestDataValidation(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, dataValidations, 1)
|
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 = NewDataValidation(true)
|
||||||
dv.Sqref = "A5:B6"
|
dv.Sqref = "A5:B6"
|
||||||
for _, listValid := range [][]string{
|
for _, listValid := range [][]string{
|
||||||
|
|
|
@ -1223,7 +1223,7 @@ func TestConditionalFormat(t *testing.T) {
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
// Set conditional format with illegal criteria type
|
// 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{
|
[]ConditionalFormatOptions{
|
||||||
{
|
{
|
||||||
Type: "data_bar",
|
Type: "data_bar",
|
||||||
|
|
|
@ -2664,8 +2664,10 @@ func (f *File) SetConditionalFormat(sheet, rangeRef string, opts []ConditionalFo
|
||||||
f.addSheetNameSpace(sheet, NameSpaceSpreadSheetX14)
|
f.addSheetNameSpace(sheet, NameSpaceSpreadSheetX14)
|
||||||
}
|
}
|
||||||
cfRule = append(cfRule, rule)
|
cfRule = append(cfRule, rule)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return ErrParameterInvalid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>")
|
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
|
// 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())
|
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) {
|
func TestGetConditionalFormats(t *testing.T) {
|
||||||
|
|
Loading…
Reference in New Issue