From e8d3e4fe33cda2d2a49afd3e73b520bb28dec762 Mon Sep 17 00:00:00 2001 From: Todd Hambley Date: Thu, 15 Apr 2021 15:33:31 -0400 Subject: [PATCH 1/2] add optional custom auto-filter to table --- lib/xlsx/xform/table/custom-filter-xform.js | 33 ++++++++ lib/xlsx/xform/table/filter-column-xform.js | 78 +++++++++++++++--- spec/integration/data/test-issue-1669.xlsx | Bin 0 -> 9393 bytes ...ptional-custom-autofilter-on-table.spec.js | 8 ++ .../xform/table/custom-filter-xform.spec.js | 30 +++++++ .../xform/table/filter-column-xform.spec.js | 19 +++++ 6 files changed, 155 insertions(+), 13 deletions(-) create mode 100644 lib/xlsx/xform/table/custom-filter-xform.js create mode 100644 spec/integration/data/test-issue-1669.xlsx create mode 100644 spec/integration/issues/issue-1669-optional-custom-autofilter-on-table.spec.js create mode 100644 spec/unit/xlsx/xform/table/custom-filter-xform.spec.js diff --git a/lib/xlsx/xform/table/custom-filter-xform.js b/lib/xlsx/xform/table/custom-filter-xform.js new file mode 100644 index 000000000..d0e9d272f --- /dev/null +++ b/lib/xlsx/xform/table/custom-filter-xform.js @@ -0,0 +1,33 @@ +const BaseXform = require('../base-xform'); + +class CustomFilterXform extends BaseXform { + get tag() { + return 'customFilter'; + } + + render(xmlStream, model) { + xmlStream.leafNode(this.tag, { + val: model.val, + operator: model.operator, + }); + } + + parseOpen(node) { + if (node.name === this.tag) { + this.model = { + val: node.attributes.val, + operator: node.attributes.operator, + }; + return true; + } + return false; + } + + parseText() {} + + parseClose() { + return false; + } +} + +module.exports = CustomFilterXform; diff --git a/lib/xlsx/xform/table/filter-column-xform.js b/lib/xlsx/xform/table/filter-column-xform.js index 20fd24628..34059a08a 100644 --- a/lib/xlsx/xform/table/filter-column-xform.js +++ b/lib/xlsx/xform/table/filter-column-xform.js @@ -1,6 +1,22 @@ const BaseXform = require('../base-xform'); +const ListXform = require('../list-xform'); + +const CustomFilterXform = require('./custom-filter-xform'); class FilterColumnXform extends BaseXform { + constructor() { + super(); + + this.map = { + customFilters: new ListXform({ + tag: 'customFilters', + count: false, + empty: true, + childXform: new CustomFilterXform(), + }), + }; + } + get tag() { return 'filterColumn'; } @@ -10,28 +26,64 @@ class FilterColumnXform extends BaseXform { } render(xmlStream, model) { - xmlStream.leafNode(this.tag, { - colId: model.colId, - hiddenButton: model.filterButton ? '0' : '1', - }); - return true; + if (model.customFilters) { + xmlStream.openNode(this.tag, { + colId: model.colId, + hiddenButton: model.filterButton ? '0' : '1', + }); + + this.map.customFilters.render(xmlStream, model.customFilters); + + xmlStream.closeNode(); + return true; + } + xmlStream.leafNode(this.tag, { + colId: model.colId, + hiddenButton: model.filterButton ? '0' : '1', + }); + return true; + } parseOpen(node) { - if (node.name === this.tag) { - const {attributes} = node; - this.model = { - filterButton: attributes.hiddenButton === '0', - }; + if (this.parser) { + this.parser.parseOpen(node); return true; } - return false; + const {attributes} = node; + switch (node.name) { + case this.tag: + this.model = { + filterButton: attributes.hiddenButton === '0', + }; + return true; + default: + this.parser = this.map[node.name]; + if (this.parser) { + this.parseOpen(node); + return true; + } + throw new Error(`Unexpected xml node in parseOpen: ${JSON.stringify(node)}`); + } } parseText() {} - parseClose() { - return false; + parseClose(name) { + if (this.parser) { + if (!this.parser.parseClose(name)) { + this.parser = undefined; + } + return true; + } + switch (name) { + case this.tag: + this.model.customFilters = this.map.customFilters.model; + return false; + default: + // could be some unrecognised tags + return true; + } } } diff --git a/spec/integration/data/test-issue-1669.xlsx b/spec/integration/data/test-issue-1669.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..720db9843c1b4a3916c65e653b2417466201606f GIT binary patch literal 9393 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 { + it('issue 1669 - optional custom autofilter on table', () => { + const wb = new ExcelJS.Workbook(); + return wb.xlsx.readFile('./spec/integration/data/test-issue-1669.xlsx'); + }).timeout(6000); +}); diff --git a/spec/unit/xlsx/xform/table/custom-filter-xform.spec.js b/spec/unit/xlsx/xform/table/custom-filter-xform.spec.js new file mode 100644 index 000000000..971ac53b7 --- /dev/null +++ b/spec/unit/xlsx/xform/table/custom-filter-xform.spec.js @@ -0,0 +1,30 @@ +const testXformHelper = require('../test-xform-helper'); + +const CustomFilterXform = verquire('xlsx/xform/table/custom-filter-xform'); + +const expectations = [ + { + title: 'custom filter', + create() { + return new CustomFilterXform(); + }, + preparedModel: {val: '*brandywine*'}, + xml: '', + parsedModel: {val: '*brandywine*'}, + tests: ['render', 'renderIn', 'parse'], + }, + { + title: 'custom filter with operator', + create() { + return new CustomFilterXform(); + }, + preparedModel: {operator: 'notEqual', val: '4'}, + xml: '', + parsedModel: {operator: 'notEqual', val: '4'}, + tests: ['render', 'renderIn', 'parse'], + }, +]; + +describe('CustomFilterXform', () => { + testXformHelper(expectations); +}); diff --git a/spec/unit/xlsx/xform/table/filter-column-xform.spec.js b/spec/unit/xlsx/xform/table/filter-column-xform.spec.js index 2d9900c17..c15f802fe 100644 --- a/spec/unit/xlsx/xform/table/filter-column-xform.spec.js +++ b/spec/unit/xlsx/xform/table/filter-column-xform.spec.js @@ -31,6 +31,25 @@ const expectations = [ tests: ['prepare', 'render', 'renderIn', 'parse'], options: {index: 1}, }, + { + title: 'with custom filter', + create() { + return new FilterColumnXform(); + }, + initialModel: {filterButton: false, customFilters: [{val: '*brandywine*'}]}, + preparedModel: { + colId: '0', + filterButton: false, + customFilters: [{val: '*brandywine*'}], + }, + xml: + '', + get parsedModel() { + return this.initialModel; + }, + tests: ['prepare', 'render', 'renderIn', 'parse'], + options: {index: 0}, + }, ]; describe('FilterColumnXform', () => { From 596ce33b7326335a5b7f3c706ad8b5ec8e114e29 Mon Sep 17 00:00:00 2001 From: Todd Hambley Date: Wed, 12 May 2021 16:24:07 -0400 Subject: [PATCH 2/2] filters may be customFilters or just filters --- lib/xlsx/xform/table/filter-column-xform.js | 18 ++++++---- lib/xlsx/xform/table/filter-xform.js | 31 ++++++++++++++++++ spec/integration/data/test-issue-1669.xlsx | Bin 9393 -> 11345 bytes ...ptional-custom-autofilter-on-table.spec.js | 2 +- 4 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 lib/xlsx/xform/table/filter-xform.js diff --git a/lib/xlsx/xform/table/filter-column-xform.js b/lib/xlsx/xform/table/filter-column-xform.js index 34059a08a..c900996d4 100644 --- a/lib/xlsx/xform/table/filter-column-xform.js +++ b/lib/xlsx/xform/table/filter-column-xform.js @@ -2,6 +2,7 @@ const BaseXform = require('../base-xform'); const ListXform = require('../list-xform'); const CustomFilterXform = require('./custom-filter-xform'); +const FilterXform = require('./filter-xform'); class FilterColumnXform extends BaseXform { constructor() { @@ -14,6 +15,12 @@ class FilterColumnXform extends BaseXform { empty: true, childXform: new CustomFilterXform(), }), + filters: new ListXform({ + tag: 'filters', + count: false, + empty: true, + childXform: new FilterXform(), + }), }; } @@ -37,12 +44,11 @@ class FilterColumnXform extends BaseXform { xmlStream.closeNode(); return true; } - xmlStream.leafNode(this.tag, { - colId: model.colId, - hiddenButton: model.filterButton ? '0' : '1', - }); - return true; - + xmlStream.leafNode(this.tag, { + colId: model.colId, + hiddenButton: model.filterButton ? '0' : '1', + }); + return true; } parseOpen(node) { diff --git a/lib/xlsx/xform/table/filter-xform.js b/lib/xlsx/xform/table/filter-xform.js new file mode 100644 index 000000000..91a620a02 --- /dev/null +++ b/lib/xlsx/xform/table/filter-xform.js @@ -0,0 +1,31 @@ +const BaseXform = require('../base-xform'); + +class FilterXform extends BaseXform { + get tag() { + return 'filter'; + } + + render(xmlStream, model) { + xmlStream.leafNode(this.tag, { + val: model.val, + }); + } + + parseOpen(node) { + if (node.name === this.tag) { + this.model = { + val: node.attributes.val, + }; + return true; + } + return false; + } + + parseText() {} + + parseClose() { + return false; + } +} + +module.exports = FilterXform; diff --git a/spec/integration/data/test-issue-1669.xlsx b/spec/integration/data/test-issue-1669.xlsx index 720db9843c1b4a3916c65e653b2417466201606f..cc9dab8f15c8504047cd7d39916bfe5b9c92c445 100644 GIT binary patch delta 5677 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 delta 4058 zcmZvfcQ~Bgy2eKty^LtXj51nuqxTY`M~@IebiyzgL?3nZD5Hl6LPSZJVUR=wi5^iy z^dO%e5;X`%zH6VIZ|`%~`e(h@`>em#?|$z48L@8A2b&s_kTL4MW>cJ$5?gssNimZLy6%_Lm9`R|U2O8q81ysx~UpE_6dIfTXa z3pd((HYF{r)^WH?&Bz7fqNBP$+os|(_1>}=LtYKO?^)t+W2MZ(*-Q*+iU&nG$+f*H z6b<5pe?g?QRwbUrW?SDJ791=h9}uaaPUDHBHC(quH<%k{G?$MQix%-*4_FP+^J}y;ynX@*v2SR|kTrIWx3GwX> zTOK7XhZWRlyr_1B_}>|jV7*l`REOK+;SjgGT}a$0@`=`pTZ>EMUM-;N86J$+VGG-j zz;MaCm!~?a)Mp-El=-I9FGa?tejE_HUckvc`kV~C^C-tQ z!XL=jBSO3E3DmPm;=GK zgI|}&I&w1O+er9HD9hNd=_C;Y08Q9UG6A$+jApe|BFnRf)lFVpB9jUoR1Pl6lx7nb zZ=|zbZXcNjYAtrD9iK(qUwvptu)MdOf75ncvXfODXKkcCv>BKhIWM+FiOUuUG&V59 zgE{gw^Y%WLxP`&wO*9drfGW#Ws=9~0eoSK4+T-ya*0hguGMGo;IbOQ5S(&!Xsq5(K z0F_DF;^bZS1B%RWdW?0Qg6p%#GszP#$un#D*w<=e$*7QIPzEU;4b6EP`u^)XG{jQ`#=q4)phxkhE0cXmY73t zn)!qk<*eIL%S`aH|ney+Y6@ye55-@N}L@anD(CL66!&7Av2%L!tV%0RHWk zj#eD9v2ih?z`jeo*@ZkT26xuPmj_7JO_$AKhzvLO%QA`q5?J+fr_}6Ix6wkJ{6YCf zvrHzt`y2gX(e=IXvKU0*ci|q5p5CL2r4ya$j@UVmfj!1F_oD+`+An8txP;CF#g?O@ z?Fs#s0-J4;7pR8x%rsR{phC$v`X`et>f*shlEn{f9x!44(rec(yDz=_°=Pd`(vkZM5aE}0)}IU| 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 { - it('issue 1669 - optional custom autofilter on table', () => { + it('issue 1669 - optional autofilter and custom autofilter on tables', () => { const wb = new ExcelJS.Workbook(); return wb.xlsx.readFile('./spec/integration/data/test-issue-1669.xlsx'); }).timeout(6000);