Upstream ClosedXML/ClosedXML state of the touched files before f8af6b544a
This commit is contained in:
commit
8d5ee56081
5 files changed
+315
No files matched your search
@@ -0,0 +1,233 @@
|
||||
using System;
|
||||
using ClosedXML.Excel;
|
||||
using NUnit.Framework;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace ClosedXML.Tests.Excel.ConditionalFormats
|
||||
{
|
||||
[TestFixture]
|
||||
public class ConditionalFormatTests
|
||||
{
|
||||
[Test]
|
||||
public void MaintainConditionalFormattingOrder()
|
||||
{
|
||||
// The input file contains duplicates of same dxf and thus the output also contains some duplicate dxf.
|
||||
TestHelper.LoadSaveAndCompare(
|
||||
@"Other\StyleReferenceFiles\ConditionalFormattingOrder\inputfile.xlsx",
|
||||
@"Other\StyleReferenceFiles\ConditionalFormattingOrder\ConditionalFormattingOrder.xlsx");
|
||||
}
|
||||
|
||||
[TestCase(true, 7)]
|
||||
[TestCase(false, 8)]
|
||||
public void SaveOptionAffectsConsolidationConditionalFormatRanges(bool consolidateConditionalFormatRanges, int expectedCount)
|
||||
{
|
||||
var options = new SaveOptions
|
||||
{
|
||||
ConsolidateConditionalFormatRanges = consolidateConditionalFormatRanges
|
||||
};
|
||||
|
||||
var wb = new XLWorkbook();
|
||||
var ws = wb.AddWorksheet("Sheet");
|
||||
|
||||
ws.Range("D2:D3").AddConditionalFormat().DataBar(XLColor.Red).LowestValue().HighestValue();
|
||||
ws.Range("B2:B3").AddConditionalFormat().DataBar(XLColor.Red).LowestValue().HighestValue();
|
||||
ws.Range("E2:E6").AddConditionalFormat().ColorScale().LowestValue(XLColor.Red).HighestValue(XLColor.Blue);
|
||||
ws.Range("F2:F6").AddConditionalFormat().ColorScale().LowestValue(XLColor.Red).HighestValue(XLColor.Blue);
|
||||
ws.Range("G2:G7").AddConditionalFormat().WhenIsUnique().Fill.SetBackgroundColor(XLColor.Blue);
|
||||
ws.Range("H2:H7").AddConditionalFormat().WhenIsUnique().Fill.SetBackgroundColor(XLColor.Blue);
|
||||
ws.Range("I2:I6").AddConditionalFormat().WhenContains("test");
|
||||
ws.Range("J2:J6").AddConditionalFormat().WhenContains("test");
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
wb.SaveAs(ms, options);
|
||||
var wb_saved = new XLWorkbook(ms);
|
||||
Assert.AreEqual(expectedCount, wb_saved.Worksheet("Sheet").ConditionalFormats.Count());
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(true, 1)]
|
||||
[TestCase(false, 2)]
|
||||
public void SaveOptionAffectsConsolidationDataValidationRanges(bool consolidateDataValidationRanges, int expectedCount)
|
||||
{
|
||||
var options = new SaveOptions
|
||||
{
|
||||
ConsolidateDataValidationRanges = consolidateDataValidationRanges
|
||||
};
|
||||
|
||||
var wb = new XLWorkbook();
|
||||
var ws = wb.AddWorksheet("Sheet");
|
||||
ws.Range("C2:C5").CreateDataValidation().Decimal.Between(1, 5);
|
||||
ws.Range("D2:D5").CreateDataValidation().Decimal.Between(1, 5);
|
||||
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
wb.SaveAs(ms, options);
|
||||
var wb_saved = new XLWorkbook(ms);
|
||||
Assert.AreEqual(expectedCount, wb_saved.Worksheet("Sheet").DataValidations.Count());
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase("en-US")]
|
||||
[TestCase("fr-FR")]
|
||||
[TestCase("ru-RU")]
|
||||
public void SaveConditionalFormat_CultureIndependent(string culture)
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
var expectedValue = 1.5;
|
||||
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(culture);
|
||||
using (var wb = new XLWorkbook())
|
||||
{
|
||||
var ws = wb.AddWorksheet();
|
||||
var i = 1;
|
||||
ws.Cell(i++, 1).AddConditionalFormat().WhenEquals(expectedValue).Fill.SetBackgroundColor(XLColor.Red);
|
||||
ws.Cell(i++, 1).AddConditionalFormat().WhenNotEquals(expectedValue).Fill.SetBackgroundColor(XLColor.Red);
|
||||
ws.Cell(i++, 1).AddConditionalFormat().WhenGreaterThan(expectedValue).Fill.SetBackgroundColor(XLColor.Red);
|
||||
ws.Cell(i++, 1).AddConditionalFormat().WhenLessThan(expectedValue).Fill.SetBackgroundColor(XLColor.Red);
|
||||
ws.Cell(i++, 1).AddConditionalFormat().WhenEqualOrGreaterThan(expectedValue).Fill.SetBackgroundColor(XLColor.Red);
|
||||
ws.Cell(i++, 1).AddConditionalFormat().WhenEqualOrLessThan(expectedValue).Fill.SetBackgroundColor(XLColor.Red);
|
||||
ws.Cell(i++, 1).AddConditionalFormat().WhenBetween(expectedValue, expectedValue).Fill.SetBackgroundColor(XLColor.Red);
|
||||
ws.Cell(i++, 1).AddConditionalFormat().WhenNotBetween(expectedValue, expectedValue).Fill.SetBackgroundColor(XLColor.Red);
|
||||
|
||||
wb.SaveAs(ms);
|
||||
}
|
||||
|
||||
using (var wb = new XLWorkbook(ms))
|
||||
{
|
||||
var ws = wb.Worksheets.First();
|
||||
|
||||
var conditionalFormatValues = ws.ConditionalFormats
|
||||
.SelectMany(cf => cf.Values.Values)
|
||||
.Select(v => v.Value)
|
||||
.Distinct();
|
||||
|
||||
Assert.AreEqual(1, conditionalFormatValues.Count());
|
||||
Assert.AreEqual("1.5", conditionalFormatValues.Single());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CellIs_type_reads_only_required_formula_arguments()
|
||||
{
|
||||
// The CellIs uses formula tags as arguments. Some producers generate extra empty
|
||||
// formula tags and ClosedXml should be able to load CellIs conditional formatting
|
||||
// with such extra tags without an exception. The test file has been modified to
|
||||
// include extra formula tags and test checks that extra tags are ignored.
|
||||
TestHelper.LoadAndAssert((_, ws) =>
|
||||
{
|
||||
AssertFormulaArgs(ws, XLCFOperator.Between, "$D$2", "$E$2");
|
||||
AssertFormulaArgs(ws, XLCFOperator.NotBetween, "$D$3", "$E$3");
|
||||
AssertFormulaArgs(ws, XLCFOperator.GreaterThan, "$D$4");
|
||||
AssertFormulaArgs(ws, XLCFOperator.LessThan, "$D$5");
|
||||
AssertFormulaArgs(ws, XLCFOperator.Equal, "$D$6");
|
||||
}, @"Other\ConditionalFormats\Extra_formulas_CellIs_type.xlsx");
|
||||
|
||||
static void AssertFormulaArgs(IXLWorksheet ws, XLCFOperator cfOperator, params string[] expectedFormulas)
|
||||
{
|
||||
var cf = ws.ConditionalFormats.Single(cf => cf.ConditionalFormatType == XLConditionalFormatType.CellIs && cf.Operator == cfOperator);
|
||||
Assert.AreEqual(expectedFormulas.Length, cf.Values.Count);
|
||||
CollectionAssert.AreEqual(expectedFormulas, cf.Values.Select(v => v.Value.Value));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Expression_type_skips_empty_formula_tags()
|
||||
{
|
||||
// The Expression uses formula tag as arguments. Some producers generate extra empty
|
||||
// formula tags and ClosedXml should be able to load Expression conditional formatting
|
||||
// with such extra tags without an exception. The test file has been modified to
|
||||
// include extra formula tags and test checks that extra tags are ignored.
|
||||
TestHelper.LoadAndAssert((_, ws) =>
|
||||
{
|
||||
AssertFormulaArgs(ws, "A1:A1", "$C$1=5");
|
||||
AssertFormulaArgs(ws, "A2:A2", "$C$2=4");
|
||||
}, @"Other\ConditionalFormats\Extra_formulas_Expression_type.xlsx");
|
||||
|
||||
static void AssertFormulaArgs(IXLWorksheet ws, string range, string expectedFormula)
|
||||
{
|
||||
var cf = ws.ConditionalFormats.Single(cf => cf.ConditionalFormatType == XLConditionalFormatType.Expression && cf.Range.RangeAddress.ToString() == range);
|
||||
Assert.AreEqual(1, cf.Values.Count);
|
||||
CollectionAssert.AreEqual(expectedFormula, cf.Values[1].Value);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Range_setter_throws_on_range_from_different_worksheet()
|
||||
{
|
||||
using var wb = new XLWorkbook();
|
||||
var ws1 = wb.AddWorksheet();
|
||||
var cf = ws1.Range("A1").AddConditionalFormat();
|
||||
var ws2 = wb.AddWorksheet();
|
||||
var differentSheetRange = ws2.Range("B5");
|
||||
|
||||
Assert.Throws<ArgumentException>(() => cf.Range = differentSheetRange);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Ranges_setter_throws_on_range_from_different_worksheet()
|
||||
{
|
||||
using var wb = new XLWorkbook();
|
||||
var ws1 = wb.AddWorksheet();
|
||||
var cf = ws1.Range("A1").AddConditionalFormat();
|
||||
|
||||
var ranges = ws1.Ranges("B1");
|
||||
var differentSheetRange = wb.AddWorksheet().Range("C1");
|
||||
ranges.Add(differentSheetRange);
|
||||
|
||||
Assert.That(() => cf.Ranges = ranges, Throws.TypeOf<ArgumentException>().With.Message.Contains("must be from worksheet"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Ranges_setter_throws_on_empty_ranges()
|
||||
{
|
||||
using var wb = new XLWorkbook();
|
||||
var ws1 = wb.AddWorksheet();
|
||||
var cf = ws1.Range("A1").AddConditionalFormat();
|
||||
|
||||
var emptyRanges = ws1.Ranges("");
|
||||
|
||||
Assert.That(() => cf.Ranges = emptyRanges, Throws.TypeOf<ArgumentException>().With.Message.Contains("empty"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Text_formats_are_loaded_as_value()
|
||||
{
|
||||
// Issue #2690: Ensure that CF for text are loaded as a value and not as a formula.
|
||||
TestHelper.CreateSaveLoadAssert(
|
||||
(_, ws) =>
|
||||
{
|
||||
ws.AddConditionalFormat().WhenContains("Contains")
|
||||
.Fill.SetBackgroundColor(XLColor.Red);
|
||||
|
||||
ws.AddConditionalFormat().WhenNotContains("Not Contains")
|
||||
.Fill.SetBackgroundColor(XLColor.Green);
|
||||
|
||||
ws.AddConditionalFormat().WhenStartsWith("Starts With")
|
||||
.Fill.SetBackgroundColor(XLColor.Blue);
|
||||
|
||||
ws.AddConditionalFormat().WhenEndsWith("Ends With")
|
||||
.Fill.SetBackgroundColor(XLColor.Black);
|
||||
},
|
||||
(_, ws) =>
|
||||
{
|
||||
AssertTextCfValue(ws, XLConditionalFormatType.ContainsText, "Contains");
|
||||
AssertTextCfValue(ws, XLConditionalFormatType.NotContainsText, "Not Contains");
|
||||
AssertTextCfValue(ws, XLConditionalFormatType.StartsWith, "Starts With");
|
||||
AssertTextCfValue(ws, XLConditionalFormatType.EndsWith, "Ends With");
|
||||
});
|
||||
return;
|
||||
|
||||
static void AssertTextCfValue(IXLWorksheet ws, XLConditionalFormatType type, string text)
|
||||
{
|
||||
var cf = ws.ConditionalFormats.Single(x => x.ConditionalFormatType == type);
|
||||
Assert.AreEqual(1, cf.Values.Count);
|
||||
Assert.AreEqual(text, cf.Values[1].Value);
|
||||
Assert.IsFalse(cf.Values[1].IsFormula);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,82 @@
|
||||
using ClosedXML.Extensions;
|
||||
using DocumentFormat.OpenXml.Office.Excel;
|
||||
using DocumentFormat.OpenXml.Office2010.Excel;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace ClosedXML.Excel
|
||||
{
|
||||
internal class XLCFDataBarConverterExtension : IXLCFConverterExtension
|
||||
{
|
||||
private static readonly IReadOnlyDictionary<DocumentFormat.OpenXml.Spreadsheet.ConditionalFormatValueObjectValues, ConditionalFormattingValueObjectTypeValues> CFValueToTypeMap =
|
||||
new Dictionary<DocumentFormat.OpenXml.Spreadsheet.ConditionalFormatValueObjectValues, ConditionalFormattingValueObjectTypeValues>
|
||||
{
|
||||
{ DocumentFormat.OpenXml.Spreadsheet.ConditionalFormatValueObjectValues.Max, ConditionalFormattingValueObjectTypeValues.AutoMax },
|
||||
{ DocumentFormat.OpenXml.Spreadsheet.ConditionalFormatValueObjectValues.Min, ConditionalFormattingValueObjectTypeValues.AutoMin },
|
||||
{ DocumentFormat.OpenXml.Spreadsheet.ConditionalFormatValueObjectValues.Number, ConditionalFormattingValueObjectTypeValues.Numeric },
|
||||
{ DocumentFormat.OpenXml.Spreadsheet.ConditionalFormatValueObjectValues.Percent, ConditionalFormattingValueObjectTypeValues.Percent },
|
||||
{ DocumentFormat.OpenXml.Spreadsheet.ConditionalFormatValueObjectValues.Percentile, ConditionalFormattingValueObjectTypeValues.Percentile },
|
||||
{ DocumentFormat.OpenXml.Spreadsheet.ConditionalFormatValueObjectValues.Formula, ConditionalFormattingValueObjectTypeValues.Formula },
|
||||
};
|
||||
|
||||
public ConditionalFormattingRule Convert(IXLConditionalFormat cf, XLWorkbook.SaveContext context)
|
||||
{
|
||||
ConditionalFormattingRule conditionalFormattingRule = new ConditionalFormattingRule()
|
||||
{
|
||||
Type = DocumentFormat.OpenXml.Spreadsheet.ConditionalFormatValues.DataBar,
|
||||
Id = ((XLConditionalFormat)cf).Id.WrapInBraces()
|
||||
};
|
||||
|
||||
DataBar dataBar = new DataBar()
|
||||
{
|
||||
MinLength = 0,
|
||||
MaxLength = 100,
|
||||
Gradient = true,
|
||||
ShowValue = !cf.ShowBarOnly
|
||||
};
|
||||
|
||||
var cfMinType = cf.ContentTypes.TryGetValue(1, out var contentType1)
|
||||
? GetCFType(contentType1.ToOpenXml())
|
||||
: ConditionalFormattingValueObjectTypeValues.AutoMin;
|
||||
var cfMin = new ConditionalFormattingValueObject { Type = cfMinType };
|
||||
if (cf.Values.Any() && cf.Values[1]?.Value != null)
|
||||
{
|
||||
cfMin.Type = ConditionalFormattingValueObjectTypeValues.Numeric;
|
||||
cfMin.Append(new Formula() { Text = cf.Values[1].Value });
|
||||
}
|
||||
|
||||
var cfMaxType = cf.ContentTypes.TryGetValue(2, out var contentType2)
|
||||
? GetCFType(contentType2.ToOpenXml())
|
||||
: ConditionalFormattingValueObjectTypeValues.AutoMax;
|
||||
var cfMax = new ConditionalFormattingValueObject { Type = cfMaxType };
|
||||
if (cf.Values.Count >= 2 && cf.Values[2]?.Value != null)
|
||||
{
|
||||
cfMax.Type = ConditionalFormattingValueObjectTypeValues.Numeric;
|
||||
cfMax.Append(new Formula() { Text = cf.Values[2].Value });
|
||||
}
|
||||
|
||||
var barAxisColor = new BarAxisColor { Rgb = XLColor.Black.Color.ToHex() };
|
||||
|
||||
var negativeFillColor = new NegativeFillColor { Rgb = cf.Colors[1].Color.ToHex() };
|
||||
if (cf.Colors.Count == 2)
|
||||
{
|
||||
negativeFillColor = new NegativeFillColor { Rgb = cf.Colors[2].Color.ToHex() };
|
||||
}
|
||||
|
||||
dataBar.Append(cfMin);
|
||||
dataBar.Append(cfMax);
|
||||
|
||||
dataBar.Append(negativeFillColor);
|
||||
dataBar.Append(barAxisColor);
|
||||
|
||||
conditionalFormattingRule.Append(dataBar);
|
||||
|
||||
return conditionalFormattingRule;
|
||||
}
|
||||
|
||||
private static ConditionalFormattingValueObjectTypeValues GetCFType(DocumentFormat.OpenXml.Spreadsheet.ConditionalFormatValueObjectValues value)
|
||||
{
|
||||
return CFValueToTypeMap[value];
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user