@hrforte/react-pivottable 2.2.1 → 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/__tests__/PivotTableUI-processing-test.js"],"names":["global","IS_REACT_ACT_ENVIRONMENT","records","name","gender","colour","trials","baseProps","data","rows","cols","aggregatorName","vals","rendererName","rerenderPivot","root","props","render","React","createElement","PivotTableUI","describe","containers","beforeEach","afterEach","forEach","container","unmount","node","remove","mount","document","body","appendChild","entry","push","it","expect","querySelector","toBeNull","not","Object","assign","showProcessing","overlay","textContent","toBe","PivotLoadingOverlay","wait","Promise","setTimeout","resolve","ms","actAsync","reject","callback","then","workerInstances","FakeWorker","url","posted","onmessage","message","previousWorker","Worker","workerThreshold","restore","worker","toBeDefined","computeMsg","find","m","type","result","id","catch","error"],"mappings":"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","file":"PivotTableUI-processing-test.js","sourcesContent":["import React, {act} from 'react';\nimport {createRoot} from 'react-dom/client';\nimport PivotTableUI, {PivotLoadingOverlay} from '../PivotTableUI';\nimport {\n computePivotState,\n createWorkerConfig,\n materializeData,\n} from '../pivotCompute';\n/* eslint-disable no-magic-numbers */\n\n// React 19 needs to be told that this environment supports `act`.\nglobal.IS_REACT_ACT_ENVIRONMENT = true;\n\n// `showProcessing` is a debugging/testing hook that forces the loading\n// overlay on top of the rendered table without running a real worker\n// computation, so the overlay can be inspected while consuming the package.\nconst records = [\n {name: 'Nick', gender: 'male', colour: 'blue', trials: 103},\n {name: 'Jane', gender: 'female', colour: 'red', trials: 95},\n {name: 'John', gender: 'male', colour: 'blue', trials: 112},\n {name: 'Carol', gender: 'female', colour: 'yellow', trials: 102},\n];\n\nconst baseProps = {\n data: records,\n rows: ['name'],\n cols: [],\n aggregatorName: 'Count',\n vals: [],\n rendererName: 'Table',\n};\n\nfunction rerenderPivot(root, props) {\n act(() => {\n root.render(React.createElement(PivotTableUI, props));\n });\n}\n\ndescribe(' PivotTableUI showProcessing', function() {\n let containers;\n\n beforeEach(function() {\n containers = [];\n });\n\n afterEach(function() {\n containers.forEach(container => {\n act(() => {\n container.root.unmount();\n });\n container.node.remove();\n });\n });\n\n const mount = props => {\n const node = document.createElement('div');\n document.body.appendChild(node);\n const root = createRoot(node);\n act(() => {\n root.render(React.createElement(PivotTableUI, props));\n });\n const entry = {node, root};\n containers.push(entry);\n return entry;\n };\n\n it('does not render the overlay by default', function() {\n const {node} = mount(baseProps);\n expect(node.querySelector('.pvtLoadingOverlay')).toBeNull();\n expect(node.querySelector('.pvtTable')).not.toBeNull();\n });\n\n it('renders the overlay on top of the table when showProcessing is true', function() {\n const {node, root} = mount(baseProps);\n // Table is rendered from the real data first (no overlay).\n expect(node.querySelector('.pvtTable')).not.toBeNull();\n expect(node.querySelector('.pvtLoadingOverlay')).toBeNull();\n\n rerenderPivot(root, Object.assign({}, baseProps, {showProcessing: true}));\n\n const overlay = node.querySelector('.pvtLoadingOverlay');\n expect(overlay).not.toBeNull();\n // The overlay is layered on top of the table: the table must survive.\n expect(node.querySelector('.pvtTable')).not.toBeNull();\n // Only a spinner and a label: progress percentages were removed, the\n // overlay simply stays until the worker posts its result event.\n expect(overlay.querySelector('.pvtLoadingSpinner')).not.toBeNull();\n expect(overlay.querySelector('.pvtLoadingText').textContent).toBe(\n 'Processing…'\n );\n expect(overlay.querySelector('.pvtLoadingBar')).toBeNull();\n expect(overlay.querySelector('.pvtLoadingPercent')).toBeNull();\n\n // Turning the flag back off removes the overlay.\n rerenderPivot(root, baseProps);\n expect(node.querySelector('.pvtLoadingOverlay')).toBeNull();\n });\n\n it('renders a spinner without any percentage markup', function() {\n const node = document.createElement('div');\n act(() => {\n createRoot(node).render(\n React.createElement(PivotLoadingOverlay, null)\n );\n });\n expect(node.querySelector('.pvtLoadingOverlay')).not.toBeNull();\n expect(node.querySelector('.pvtLoadingSpinner')).not.toBeNull();\n expect(node.querySelector('.pvtLoadingBarWrap')).toBeNull();\n expect(node.querySelector('.pvtLoadingPercent')).toBeNull();\n expect(node.querySelector('.pvtLoadingText').textContent).toBe(\n 'Processing…'\n );\n });\n\n it('hides the overlay only after the worker responds and the table renders', function() {\n const wait = ms => new Promise(resolve => setTimeout(resolve, ms));\n // React's act() returns a non-standard thenable whose .then() does not\n // return a promise, so it cannot be chained directly - wrap it first.\n const actAsync = callback =>\n new Promise((resolve, reject) => act(callback).then(resolve, reject));\n const workerInstances = [];\n class FakeWorker {\n constructor(url) {\n this.url = url;\n this.posted = [];\n this.onmessage = null;\n workerInstances.push(this);\n }\n\n postMessage(message) {\n this.posted.push(message);\n }\n\n terminate() {}\n }\n\n const previousWorker = global.Worker;\n global.Worker = FakeWorker;\n\n // workerThreshold: 2 makes this small dataset worker-eligible.\n const props = Object.assign({}, baseProps, {workerThreshold: 2});\n const {node} = mount(props);\n\n const restore = () => {\n global.Worker = previousWorker;\n };\n\n // The overlay is up while the computation is pending.\n expect(node.querySelector('.pvtLoadingOverlay')).not.toBeNull();\n\n // Wait out the dispatch debounce so the request reaches the worker.\n return actAsync(() => wait(80))\n .then(() => {\n const worker = workerInstances[0];\n expect(worker).toBeDefined();\n const computeMsg = worker.posted.find(m => m.type === 'pivot:compute');\n expect(computeMsg).toBeDefined();\n // The worker has not answered yet: the overlay must stay up.\n expect(node.querySelector('.pvtLoadingOverlay')).not.toBeNull();\n\n // Worker finishes and posts its result event.\n const result = computePivotState(\n materializeData(records, {}),\n createWorkerConfig(props)\n );\n return actAsync(() => {\n worker.onmessage({\n data: {type: 'pivot:result', id: computeMsg.id, result},\n });\n return wait(0);\n });\n })\n .then(() => {\n // The overlay is gone only now - and the freshly computed table is\n // already on screen in the same state where the overlay disappeared.\n expect(node.querySelector('.pvtLoadingOverlay')).toBeNull();\n expect(node.querySelector('.pvtTable')).not.toBeNull();\n restore();\n })\n .catch(error => {\n restore();\n throw error;\n });\n });\n});\n"]}
1
+ {"version":3,"sources":["../../src/__tests__/PivotTableUI-processing-test.js"],"names":["global","IS_REACT_ACT_ENVIRONMENT","records","name","gender","colour","trials","baseProps","data","rows","cols","aggregatorName","vals","rendererName","rerenderPivot","root","props","render","React","createElement","PivotTableUI","describe","containers","beforeEach","afterEach","forEach","container","unmount","node","remove","mount","document","body","appendChild","entry","push","it","expect","querySelector","toBeNull","not","Object","assign","showProcessing","overlay","textContent","toBe","PivotLoadingOverlay","wait","Promise","setTimeout","resolve","ms","actAsync","reject","callback","then","workerInstances","FakeWorker","url","posted","onmessage","message","previousWorker","Worker","workerThreshold","restore","WORKER_DEBOUNCE_MS","worker","toBeDefined","computeMsg","find","m","type","result","id","catch","error"],"mappings":";;;;AAAA;;;;AACA;;AACA;;;;AACA;;;;;;AAMA;;AAEA;AACAA,OAAOC,wBAAP,GAAkC,IAAlC;;AAEA;AACA;AACA;AACA,IAAMC,UAAU,CACd,EAACC,MAAM,MAAP,EAAeC,QAAQ,MAAvB,EAA+BC,QAAQ,MAAvC,EAA+CC,QAAQ,GAAvD,EADc,EAEd,EAACH,MAAM,MAAP,EAAeC,QAAQ,QAAvB,EAAiCC,QAAQ,KAAzC,EAAgDC,QAAQ,EAAxD,EAFc,EAGd,EAACH,MAAM,MAAP,EAAeC,QAAQ,MAAvB,EAA+BC,QAAQ,MAAvC,EAA+CC,QAAQ,GAAvD,EAHc,EAId,EAACH,MAAM,OAAP,EAAgBC,QAAQ,QAAxB,EAAkCC,QAAQ,QAA1C,EAAoDC,QAAQ,GAA5D,EAJc,CAAhB;;AAOA,IAAMC,YAAY;AAChBC,QAAMN,OADU;AAEhBO,QAAM,CAAC,MAAD,CAFU;AAGhBC,QAAM,EAHU;AAIhBC,kBAAgB,OAJA;AAKhBC,QAAM,EALU;AAMhBC,gBAAc;AANE,CAAlB;;AASA,SAASC,aAAT,CAAuBC,IAAvB,EAA6BC,KAA7B,EAAoC;AAClC,kBAAI,YAAM;AACRD,SAAKE,MAAL,CAAYC,gBAAMC,aAAN,CAAoBC,sBAApB,EAAkCJ,KAAlC,CAAZ;AACD,GAFD;AAGD;;AAEDK,SAAS,+BAAT,EAA0C,YAAW;AACnD,MAAIC,mBAAJ;;AAEAC,aAAW,YAAW;AACpBD,iBAAa,EAAb;AACD,GAFD;;AAIAE,YAAU,YAAW;AACnBF,eAAWG,OAAX,CAAmB,qBAAa;AAC9B,sBAAI,YAAM;AACRC,kBAAUX,IAAV,CAAeY,OAAf;AACD,OAFD;AAGAD,gBAAUE,IAAV,CAAeC,MAAf;AACD,KALD;AAMD,GAPD;;AASA,MAAMC,QAAQ,SAARA,KAAQ,QAAS;AACrB,QAAMF,OAAOG,SAASZ,aAAT,CAAuB,KAAvB,CAAb;AACAY,aAASC,IAAT,CAAcC,WAAd,CAA0BL,IAA1B;AACA,QAAMb,OAAO,wBAAWa,IAAX,CAAb;AACA,oBAAI,YAAM;AACRb,WAAKE,MAAL,CAAYC,gBAAMC,aAAN,CAAoBC,sBAApB,EAAkCJ,KAAlC,CAAZ;AACD,KAFD;AAGA,QAAMkB,QAAQ,EAACN,UAAD,EAAOb,UAAP,EAAd;AACAO,eAAWa,IAAX,CAAgBD,KAAhB;AACA,WAAOA,KAAP;AACD,GAVD;;AAYAE,KAAG,wCAAH,EAA6C,YAAW;AAAA,iBACvCN,MAAMvB,SAAN,CADuC;AAAA,QAC/CqB,IAD+C,UAC/CA,IAD+C;;AAEtDS,WAAOT,KAAKU,aAAL,CAAmB,oBAAnB,CAAP,EAAiDC,QAAjD;AACAF,WAAOT,KAAKU,aAAL,CAAmB,WAAnB,CAAP,EAAwCE,GAAxC,CAA4CD,QAA5C;AACD,GAJD;;AAMAH,KAAG,qEAAH,EAA0E,YAAW;AAAA,kBAC9DN,MAAMvB,SAAN,CAD8D;AAAA,QAC5EqB,IAD4E,WAC5EA,IAD4E;AAAA,QACtEb,IADsE,WACtEA,IADsE;AAEnF;;;AACAsB,WAAOT,KAAKU,aAAL,CAAmB,WAAnB,CAAP,EAAwCE,GAAxC,CAA4CD,QAA5C;AACAF,WAAOT,KAAKU,aAAL,CAAmB,oBAAnB,CAAP,EAAiDC,QAAjD;;AAEAzB,kBAAcC,IAAd,EAAoB0B,OAAOC,MAAP,CAAc,EAAd,EAAkBnC,SAAlB,EAA6B,EAACoC,gBAAgB,IAAjB,EAA7B,CAApB;;AAEA,QAAMC,UAAUhB,KAAKU,aAAL,CAAmB,oBAAnB,CAAhB;AACAD,WAAOO,OAAP,EAAgBJ,GAAhB,CAAoBD,QAApB;AACA;AACAF,WAAOT,KAAKU,aAAL,CAAmB,WAAnB,CAAP,EAAwCE,GAAxC,CAA4CD,QAA5C;AACA;AACA;AACAF,WAAOO,QAAQN,aAAR,CAAsB,oBAAtB,CAAP,EAAoDE,GAApD,CAAwDD,QAAxD;AACAF,WAAOO,QAAQN,aAAR,CAAsB,iBAAtB,EAAyCO,WAAhD,EAA6DC,IAA7D,CACE,aADF;AAGAT,WAAOO,QAAQN,aAAR,CAAsB,gBAAtB,CAAP,EAAgDC,QAAhD;AACAF,WAAOO,QAAQN,aAAR,CAAsB,oBAAtB,CAAP,EAAoDC,QAApD;;AAEA;AACAzB,kBAAcC,IAAd,EAAoBR,SAApB;AACA8B,WAAOT,KAAKU,aAAL,CAAmB,oBAAnB,CAAP,EAAiDC,QAAjD;AACD,GAxBD;;AA0BAH,KAAG,iDAAH,EAAsD,YAAW;AAC/D,QAAMR,OAAOG,SAASZ,aAAT,CAAuB,KAAvB,CAAb;AACA,oBAAI,YAAM;AACR,8BAAWS,IAAX,EAAiBX,MAAjB,CAAwBC,gBAAMC,aAAN,CAAoB4B,iCAApB,EAAyC,IAAzC,CAAxB;AACD,KAFD;AAGAV,WAAOT,KAAKU,aAAL,CAAmB,oBAAnB,CAAP,EAAiDE,GAAjD,CAAqDD,QAArD;AACAF,WAAOT,KAAKU,aAAL,CAAmB,oBAAnB,CAAP,EAAiDE,GAAjD,CAAqDD,QAArD;AACAF,WAAOT,KAAKU,aAAL,CAAmB,oBAAnB,CAAP,EAAiDC,QAAjD;AACAF,WAAOT,KAAKU,aAAL,CAAmB,oBAAnB,CAAP,EAAiDC,QAAjD;AACAF,WAAOT,KAAKU,aAAL,CAAmB,iBAAnB,EAAsCO,WAA7C,EAA0DC,IAA1D,CACE,aADF;AAGD,GAZD;;AAcAV,KAAG,wEAAH,EAA6E,YAAW;AACtF,QAAMY,OAAO,SAAPA,IAAO;AAAA,aAAM,IAAIC,OAAJ,CAAY;AAAA,eAAWC,WAAWC,OAAX,EAAoBC,EAApB,CAAX;AAAA,OAAZ,CAAN;AAAA,KAAb;AACA;AACA;AACA,QAAMC,WAAW,SAAXA,QAAW;AAAA,aACf,IAAIJ,OAAJ,CAAY,UAACE,OAAD,EAAUG,MAAV;AAAA,eAAqB,gBAAIC,QAAJ,EAAcC,IAAd,CAAmBL,OAAnB,EAA4BG,MAA5B,CAArB;AAAA,OAAZ,CADe;AAAA,KAAjB;AAEA,QAAMG,kBAAkB,EAAxB;;AANsF,QAOhFC,UAPgF;AAQpF,0BAAYC,GAAZ,EAAiB;AAAA;;AACf,aAAKA,GAAL,GAAWA,GAAX;AACA,aAAKC,MAAL,GAAc,EAAd;AACA,aAAKC,SAAL,GAAiB,IAAjB;AACAJ,wBAAgBtB,IAAhB,CAAqB,IAArB;AACD;;AAbmF;AAAA;AAAA,oCAexE2B,OAfwE,EAe/D;AACnB,eAAKF,MAAL,CAAYzB,IAAZ,CAAiB2B,OAAjB;AACD;AAjBmF;AAAA;AAAA,oCAmBxE,CAAE;AAnBsE;;AAAA;AAAA;;AAsBtF,QAAMC,iBAAiB/D,OAAOgE,MAA9B;AACAhE,WAAOgE,MAAP,GAAgBN,UAAhB;;AAEA;AACA,QAAM1C,QAAQyB,OAAOC,MAAP,CAAc,EAAd,EAAkBnC,SAAlB,EAA6B,EAAC0D,iBAAiB,CAAlB,EAA7B,CAAd;;AA1BsF,kBA2BvEnC,MAAMd,KAAN,CA3BuE;AAAA,QA2B/EY,IA3B+E,WA2B/EA,IA3B+E;;AA6BtF,QAAMsC,UAAU,SAAVA,OAAU,GAAM;AACpBlE,aAAOgE,MAAP,GAAgBD,cAAhB;AACD,KAFD;;AAIA;AACA1B,WAAOT,KAAKU,aAAL,CAAmB,oBAAnB,CAAP,EAAiDE,GAAjD,CAAqDD,QAArD;;AAEA;AACA,WAAOc,SAAS;AAAA,aAAML,KAAKmB,mCAAqB,EAA1B,CAAN;AAAA,KAAT,EACJX,IADI,CACC,YAAM;AACV,UAAMY,SAASX,gBAAgB,CAAhB,CAAf;AACApB,aAAO+B,MAAP,EAAeC,WAAf;AACA,UAAMC,aAAaF,OAAOR,MAAP,CAAcW,IAAd,CAAmB;AAAA,eAAKC,EAAEC,IAAF,KAAW,eAAhB;AAAA,OAAnB,CAAnB;AACApC,aAAOiC,UAAP,EAAmBD,WAAnB;AACA;AACAhC,aAAOT,KAAKU,aAAL,CAAmB,oBAAnB,CAAP,EAAiDE,GAAjD,CAAqDD,QAArD;;AAEA;AACA,UAAMmC,SAAS,qCACb,mCAAgBxE,OAAhB,EAAyB,EAAzB,CADa,EAEb,sCAAmBc,KAAnB,CAFa,CAAf;AAIA,aAAOqC,SAAS,YAAM;AACpBe,eAAOP,SAAP,CAAiB;AACfrD,gBAAM,EAACiE,MAAM,cAAP,EAAuBE,IAAIL,WAAWK,EAAtC,EAA0CD,cAA1C;AADS,SAAjB;AAGA,eAAO1B,KAAK,CAAL,CAAP;AACD,OALM,CAAP;AAMD,KApBI,EAqBJQ,IArBI,CAqBC,YAAM;AACV;AACA;AACAnB,aAAOT,KAAKU,aAAL,CAAmB,oBAAnB,CAAP,EAAiDC,QAAjD;AACAF,aAAOT,KAAKU,aAAL,CAAmB,WAAnB,CAAP,EAAwCE,GAAxC,CAA4CD,QAA5C;AACA2B;AACD,KA3BI,EA4BJU,KA5BI,CA4BE,iBAAS;AACdV;AACA,YAAMW,KAAN;AACD,KA/BI,CAAP;AAgCD,GArED;AAsED,CAhJD","file":"PivotTableUI-processing-test.js","sourcesContent":["import React, {act} from 'react';\nimport {createRoot} from 'react-dom/client';\nimport PivotTableUI, {PivotLoadingOverlay} from '../PivotTableUI';\nimport {\n computePivotState,\n createWorkerConfig,\n materializeData,\n WORKER_DEBOUNCE_MS,\n} from '../pivotCompute';\n/* eslint-disable no-magic-numbers */\n\n// React 19 needs to be told that this environment supports `act`.\nglobal.IS_REACT_ACT_ENVIRONMENT = true;\n\n// `showProcessing` is a debugging/testing hook that forces the loading\n// overlay on top of the rendered table without running a real worker\n// computation, so the overlay can be inspected while consuming the package.\nconst records = [\n {name: 'Nick', gender: 'male', colour: 'blue', trials: 103},\n {name: 'Jane', gender: 'female', colour: 'red', trials: 95},\n {name: 'John', gender: 'male', colour: 'blue', trials: 112},\n {name: 'Carol', gender: 'female', colour: 'yellow', trials: 102},\n];\n\nconst baseProps = {\n data: records,\n rows: ['name'],\n cols: [],\n aggregatorName: 'Count',\n vals: [],\n rendererName: 'Table',\n};\n\nfunction rerenderPivot(root, props) {\n act(() => {\n root.render(React.createElement(PivotTableUI, props));\n });\n}\n\ndescribe(' PivotTableUI showProcessing', function() {\n let containers;\n\n beforeEach(function() {\n containers = [];\n });\n\n afterEach(function() {\n containers.forEach(container => {\n act(() => {\n container.root.unmount();\n });\n container.node.remove();\n });\n });\n\n const mount = props => {\n const node = document.createElement('div');\n document.body.appendChild(node);\n const root = createRoot(node);\n act(() => {\n root.render(React.createElement(PivotTableUI, props));\n });\n const entry = {node, root};\n containers.push(entry);\n return entry;\n };\n\n it('does not render the overlay by default', function() {\n const {node} = mount(baseProps);\n expect(node.querySelector('.pvtLoadingOverlay')).toBeNull();\n expect(node.querySelector('.pvtTable')).not.toBeNull();\n });\n\n it('renders the overlay on top of the table when showProcessing is true', function() {\n const {node, root} = mount(baseProps);\n // Table is rendered from the real data first (no overlay).\n expect(node.querySelector('.pvtTable')).not.toBeNull();\n expect(node.querySelector('.pvtLoadingOverlay')).toBeNull();\n\n rerenderPivot(root, Object.assign({}, baseProps, {showProcessing: true}));\n\n const overlay = node.querySelector('.pvtLoadingOverlay');\n expect(overlay).not.toBeNull();\n // The overlay is layered on top of the table: the table must survive.\n expect(node.querySelector('.pvtTable')).not.toBeNull();\n // Only a spinner and a label: progress percentages were removed, the\n // overlay simply stays until the worker posts its result event.\n expect(overlay.querySelector('.pvtLoadingSpinner')).not.toBeNull();\n expect(overlay.querySelector('.pvtLoadingText').textContent).toBe(\n 'Processing…'\n );\n expect(overlay.querySelector('.pvtLoadingBar')).toBeNull();\n expect(overlay.querySelector('.pvtLoadingPercent')).toBeNull();\n\n // Turning the flag back off removes the overlay.\n rerenderPivot(root, baseProps);\n expect(node.querySelector('.pvtLoadingOverlay')).toBeNull();\n });\n\n it('renders a spinner without any percentage markup', function() {\n const node = document.createElement('div');\n act(() => {\n createRoot(node).render(React.createElement(PivotLoadingOverlay, null));\n });\n expect(node.querySelector('.pvtLoadingOverlay')).not.toBeNull();\n expect(node.querySelector('.pvtLoadingSpinner')).not.toBeNull();\n expect(node.querySelector('.pvtLoadingBarWrap')).toBeNull();\n expect(node.querySelector('.pvtLoadingPercent')).toBeNull();\n expect(node.querySelector('.pvtLoadingText').textContent).toBe(\n 'Processing…'\n );\n });\n\n it('hides the overlay only after the worker responds and the table renders', function() {\n const wait = ms => new Promise(resolve => setTimeout(resolve, ms));\n // React's act() returns a non-standard thenable whose .then() does not\n // return a promise, so it cannot be chained directly - wrap it first.\n const actAsync = callback =>\n new Promise((resolve, reject) => act(callback).then(resolve, reject));\n const workerInstances = [];\n class FakeWorker {\n constructor(url) {\n this.url = url;\n this.posted = [];\n this.onmessage = null;\n workerInstances.push(this);\n }\n\n postMessage(message) {\n this.posted.push(message);\n }\n\n terminate() {}\n }\n\n const previousWorker = global.Worker;\n global.Worker = FakeWorker;\n\n // workerThreshold: 2 makes this small dataset worker-eligible.\n const props = Object.assign({}, baseProps, {workerThreshold: 2});\n const {node} = mount(props);\n\n const restore = () => {\n global.Worker = previousWorker;\n };\n\n // The overlay is up while the computation is pending.\n expect(node.querySelector('.pvtLoadingOverlay')).not.toBeNull();\n\n // Wait out the dispatch debounce so the request reaches the worker.\n return actAsync(() => wait(WORKER_DEBOUNCE_MS + 50))\n .then(() => {\n const worker = workerInstances[0];\n expect(worker).toBeDefined();\n const computeMsg = worker.posted.find(m => m.type === 'pivot:compute');\n expect(computeMsg).toBeDefined();\n // The worker has not answered yet: the overlay must stay up.\n expect(node.querySelector('.pvtLoadingOverlay')).not.toBeNull();\n\n // Worker finishes and posts its result event.\n const result = computePivotState(\n materializeData(records, {}),\n createWorkerConfig(props)\n );\n return actAsync(() => {\n worker.onmessage({\n data: {type: 'pivot:result', id: computeMsg.id, result},\n });\n return wait(0);\n });\n })\n .then(() => {\n // The overlay is gone only now - and the freshly computed table is\n // already on screen in the same state where the overlay disappeared.\n expect(node.querySelector('.pvtLoadingOverlay')).toBeNull();\n expect(node.querySelector('.pvtTable')).not.toBeNull();\n restore();\n })\n .catch(error => {\n restore();\n throw error;\n });\n });\n});\n"]}
@@ -0,0 +1,314 @@
1
+ 'use strict';
2
+
3
+ var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
4
+
5
+ var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
6
+
7
+ var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
8
+
9
+ var _react = require('react');
10
+
11
+ var _react2 = _interopRequireDefault(_react);
12
+
13
+ var _client = require('react-dom/client');
14
+
15
+ var _PivotTableUI = require('../PivotTableUI');
16
+
17
+ var _PivotTableUI2 = _interopRequireDefault(_PivotTableUI);
18
+
19
+ var _pivotCompute = require('../pivotCompute');
20
+
21
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
22
+
23
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
24
+
25
+ function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
26
+
27
+ function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
28
+
29
+ /* eslint-disable no-magic-numbers */
30
+
31
+ // React 19 needs to be told that this environment supports `act`.
32
+ global.IS_REACT_ACT_ENVIRONMENT = true;
33
+
34
+ /*
35
+ * Regression tests for the "drag & drop freezes the browser" bug on large
36
+ * datasets.
37
+ *
38
+ * The worker keeps the *aggregation* off the main thread, but the rendered
39
+ * table itself cannot be: a table with 10k rows x 25 row attributes is ~270k
40
+ * cells. Every prop that changes identity on a parent re-render (`onColSort`,
41
+ * `onRowSort`, `colSorts`, `rowSorts`) defeated PivotTable's PureComponent
42
+ * check and made React re-render that whole table - on every drag event, every
43
+ * keystroke and even while a worker computation was still pending. These tests
44
+ * count renderer invocations to pin that behaviour down.
45
+ */
46
+ var records = [{ name: 'Nick', gender: 'male', colour: 'blue', trials: 103 }, { name: 'Jane', gender: 'female', colour: 'red', trials: 95 }, { name: 'John', gender: 'male', colour: 'blue', trials: 112 }, { name: 'Carol', gender: 'female', colour: 'yellow', trials: 102 }];
47
+
48
+ var renders = void 0;
49
+ var WorkerInstances = [];
50
+ var nextWorkerUrl = 1;
51
+
52
+ // Stands in for the real Table renderer: a PureComponent, exactly like the
53
+ // renderers built by `makeRenderer`, recording every render it is asked for.
54
+ // Rendering the real table is what costs seconds on a 10k-row pivot, so the
55
+ // number of calls here is the metric that matters.
56
+
57
+ var CountingRenderer = function (_React$PureComponent) {
58
+ _inherits(CountingRenderer, _React$PureComponent);
59
+
60
+ function CountingRenderer() {
61
+ _classCallCheck(this, CountingRenderer);
62
+
63
+ return _possibleConstructorReturn(this, (CountingRenderer.__proto__ || Object.getPrototypeOf(CountingRenderer)).apply(this, arguments));
64
+ }
65
+
66
+ _createClass(CountingRenderer, [{
67
+ key: 'render',
68
+ value: function render() {
69
+ renders.push({
70
+ rows: (this.props.rows || []).join('|'),
71
+ rowKeys: this.props.precomputedData ? this.props.precomputedData.rowKeys.length : null
72
+ });
73
+ return _react2.default.createElement('table', { className: 'pvtTable' });
74
+ }
75
+ }]);
76
+
77
+ return CountingRenderer;
78
+ }(_react2.default.PureComponent);
79
+
80
+ // Stable identities, mirroring what a consumer must do for the PureComponent
81
+ // bail-out to work (see the `renderers` constant in examples/App.jsx).
82
+
83
+
84
+ var stubRenderers = { Stub: CountingRenderer };
85
+
86
+ var baseProps = function baseProps(extra) {
87
+ return Object.assign({
88
+ data: records,
89
+ rows: ['name'],
90
+ cols: [],
91
+ aggregatorName: 'Count',
92
+ vals: [],
93
+ rendererName: 'Stub',
94
+ renderers: stubRenderers
95
+ }, extra);
96
+ };
97
+
98
+ // Emulates how examples/App.jsx drives PivotTableUI: the component is
99
+ // controlled, so `onChange` feeds the next state back in as props.
100
+ var harnessRef = { setState: null };
101
+
102
+ function Harness(props) {
103
+ var _React$useState = _react2.default.useState(props.initialState),
104
+ _React$useState2 = _slicedToArray(_React$useState, 2),
105
+ state = _React$useState2[0],
106
+ setState = _React$useState2[1];
107
+
108
+ harnessRef.setState = setState;
109
+ return _react2.default.createElement(_PivotTableUI2.default, _extends({}, state, {
110
+ renderers: stubRenderers,
111
+ rendererName: 'Stub',
112
+ workerThreshold: props.workerThreshold,
113
+ onChange: function onChange(next) {
114
+ return setState(next);
115
+ }
116
+ }));
117
+ }
118
+
119
+ var FakeWorker = function () {
120
+ function FakeWorker(url) {
121
+ _classCallCheck(this, FakeWorker);
122
+
123
+ this.url = url;
124
+ this.posted = [];
125
+ this.onmessage = null;
126
+ this.onerror = null;
127
+ WorkerInstances.push(this);
128
+ }
129
+
130
+ _createClass(FakeWorker, [{
131
+ key: 'postMessage',
132
+ value: function postMessage(message) {
133
+ this.posted.push(message);
134
+ }
135
+ }, {
136
+ key: 'terminate',
137
+ value: function terminate() {}
138
+ }]);
139
+
140
+ return FakeWorker;
141
+ }();
142
+
143
+ var wait = function wait(ms) {
144
+ return new Promise(function (resolve) {
145
+ return setTimeout(resolve, ms);
146
+ });
147
+ };
148
+ // React's act() returns a non-standard thenable whose .then() does not return
149
+ // a promise, so it cannot be chained directly - wrap it first.
150
+ var actAsync = function actAsync(callback) {
151
+ return new Promise(function (resolve, reject) {
152
+ return (0, _react.act)(callback).then(resolve, reject);
153
+ });
154
+ };
155
+
156
+ // The worker client keeps one shared worker per module, so before every test
157
+ // the client is pointed at a new URL: that discards the worker of the previous
158
+ // test and lets the next one create (and answer) its own fake worker.
159
+ var freshWorkerClient = function freshWorkerClient() {
160
+ var client = require('../PivotDataWorker');
161
+ client.configureWorkerUrl('pivot-worker.js?test=' + nextWorkerUrl++);
162
+ return client;
163
+ };
164
+
165
+ describe(' PivotTableUI re-render guards', function () {
166
+ var mounted = void 0;
167
+
168
+ beforeEach(function () {
169
+ renders = [];
170
+ WorkerInstances = [];
171
+ freshWorkerClient();
172
+ global.Worker = FakeWorker;
173
+ mounted = null;
174
+ });
175
+
176
+ afterEach(function () {
177
+ if (mounted) {
178
+ (0, _react.act)(function () {
179
+ mounted.root.unmount();
180
+ });
181
+ mounted.node.remove();
182
+ mounted = null;
183
+ }
184
+ global.Worker = undefined;
185
+ });
186
+
187
+ var mount = function mount(element) {
188
+ var node = document.createElement('div');
189
+ document.body.appendChild(node);
190
+ var root = (0, _client.createRoot)(node);
191
+ (0, _react.act)(function () {
192
+ root.render(element);
193
+ });
194
+ mounted = { node: node, root: root };
195
+ return mounted;
196
+ };
197
+
198
+ // Computes and posts the result the worker would have produced.
199
+ var answerCompute = function answerCompute(config, id) {
200
+ var worker = WorkerInstances[0];
201
+ expect(worker).toBeDefined();
202
+ var result = (0, _pivotCompute.computePivotState)((0, _pivotCompute.materializeData)(records, {}), (0, _pivotCompute.createWorkerConfig)(config));
203
+ (0, _react.act)(function () {
204
+ worker.onmessage({ data: { type: 'pivot:result', id: id, result: result } });
205
+ });
206
+ return result;
207
+ };
208
+
209
+ // Finds the request the component just dispatched and answers it.
210
+ var answerNextCompute = function answerNextCompute(config) {
211
+ return actAsync(function () {
212
+ return wait(150);
213
+ }).then(function () {
214
+ var computeMsg = WorkerInstances[0].posted.filter(function (m) {
215
+ return m.type === 'pivot:compute';
216
+ }).pop();
217
+ expect(computeMsg).toBeDefined();
218
+ var result = answerCompute(config, computeMsg.id);
219
+ return actAsync(function () {
220
+ return wait(0);
221
+ }).then(function () {
222
+ return result;
223
+ });
224
+ });
225
+ };
226
+
227
+ it('does not re-render the table while a worker computation is pending', function () {
228
+ var props = baseProps({ workerThreshold: 2 });
229
+
230
+ var _mount = mount(_react2.default.createElement(Harness, { initialState: props, workerThreshold: props.workerThreshold })),
231
+ node = _mount.node;
232
+
233
+ var rendersBefore = 0;
234
+
235
+ // Nothing to show yet: the first computation has no previous table.
236
+ expect(node.querySelector('.pvtLoadingOverlay')).not.toBeNull();
237
+ expect(node.querySelector('.pvtTable')).toBeNull();
238
+
239
+ return answerNextCompute(props).then(function () {
240
+ expect(node.querySelector('.pvtLoadingOverlay')).toBeNull();
241
+ rendersBefore = renders.length;
242
+ expect(rendersBefore).toBeGreaterThan(0);
243
+ expect(renders[rendersBefore - 1].rows).toBe('name');
244
+ expect(renders[rendersBefore - 1].rowKeys).toBe(4);
245
+
246
+ // Simulate a drop: the parent applies the new row axis, exactly like
247
+ // examples/App.jsx does from PivotTableUI's onChange.
248
+ (0, _react.act)(function () {
249
+ harnessRef.setState(function (prev) {
250
+ return Object.assign({}, prev, { rows: ['gender'] });
251
+ });
252
+ });
253
+
254
+ // The overlay is up over the previous table and, crucially, the table
255
+ // was NOT re-rendered with the new axis while the result is missing:
256
+ // neither the pending render itself nor the state updates that follow
257
+ // it may touch the rendered table.
258
+ expect(node.querySelector('.pvtLoadingOverlay')).not.toBeNull();
259
+ expect(node.querySelector('.pvtTable')).not.toBeNull();
260
+ expect(renders.length).toBe(rendersBefore);
261
+
262
+ return actAsync(function () {
263
+ return wait(150);
264
+ });
265
+ }).then(function () {
266
+ expect(renders.length).toBe(rendersBefore);
267
+ var computeMsg = WorkerInstances[0].posted.filter(function (m) {
268
+ return m.type === 'pivot:compute';
269
+ }).pop();
270
+ answerCompute(Object.assign({}, props, { rows: ['gender'] }), computeMsg.id);
271
+ return actAsync(function () {
272
+ return wait(0);
273
+ });
274
+ }).then(function () {
275
+ // Exactly one render for the new result: the render that follows it
276
+ // (overlay removal) must bail out through the PureComponent check.
277
+ expect(renders.length).toBe(rendersBefore + 1);
278
+ expect(renders[rendersBefore].rows).toBe('gender');
279
+ expect(renders[rendersBefore].rowKeys).toBe(2);
280
+ expect(node.querySelector('.pvtLoadingOverlay')).toBeNull();
281
+ expect(node.querySelector('.pvtTable')).not.toBeNull();
282
+ });
283
+ });
284
+
285
+ it('does not re-render the table when the parent re-renders unchanged props', function () {
286
+ var props = baseProps();
287
+
288
+ var _mount2 = mount(_react2.default.createElement(_PivotTableUI2.default, props)),
289
+ node = _mount2.node,
290
+ root = _mount2.root;
291
+
292
+ // Small datasets are not worker-eligible: the classic main-thread path
293
+ // renders the table synchronously on mount (no overlay, no pending state).
294
+
295
+
296
+ return actAsync(function () {
297
+ return wait(0);
298
+ }).then(function () {
299
+ var rendersBefore = renders.length;
300
+ expect(rendersBefore).toBeGreaterThan(0);
301
+
302
+ // A parent re-render with the very same props (an unrelated App state
303
+ // change) must not touch the table.
304
+ (0, _react.act)(function () {
305
+ root.render(_react2.default.createElement(_PivotTableUI2.default, props));
306
+ });
307
+
308
+ expect(renders.length).toBe(rendersBefore);
309
+ expect(node.querySelector('.pvtLoadingOverlay')).toBeNull();
310
+ expect(node.querySelector('.pvtTable')).not.toBeNull();
311
+ });
312
+ });
313
+ });
314
+ //# sourceMappingURL=PivotTableUI-rerender-test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/__tests__/PivotTableUI-rerender-test.js"],"names":["global","IS_REACT_ACT_ENVIRONMENT","records","name","gender","colour","trials","renders","WorkerInstances","nextWorkerUrl","CountingRenderer","push","rows","props","join","rowKeys","precomputedData","length","React","PureComponent","stubRenderers","Stub","baseProps","Object","assign","data","cols","aggregatorName","vals","rendererName","renderers","extra","harnessRef","setState","Harness","useState","initialState","state","workerThreshold","next","FakeWorker","url","posted","onmessage","onerror","message","wait","Promise","setTimeout","resolve","ms","actAsync","reject","callback","then","freshWorkerClient","client","require","configureWorkerUrl","describe","mounted","beforeEach","Worker","afterEach","root","unmount","node","remove","undefined","mount","document","createElement","body","appendChild","render","element","answerCompute","config","id","worker","expect","toBeDefined","result","type","answerNextCompute","computeMsg","filter","m","pop","it","rendersBefore","querySelector","not","toBeNull","toBeGreaterThan","toBe","prev"],"mappings":"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","file":"PivotTableUI-rerender-test.js","sourcesContent":["import React, {act} from 'react';\nimport {createRoot} from 'react-dom/client';\nimport PivotTableUI from '../PivotTableUI';\nimport {\n computePivotState,\n createWorkerConfig,\n materializeData,\n} from '../pivotCompute';\n/* eslint-disable no-magic-numbers */\n\n// React 19 needs to be told that this environment supports `act`.\nglobal.IS_REACT_ACT_ENVIRONMENT = true;\n\n/*\n * Regression tests for the \"drag & drop freezes the browser\" bug on large\n * datasets.\n *\n * The worker keeps the *aggregation* off the main thread, but the rendered\n * table itself cannot be: a table with 10k rows x 25 row attributes is ~270k\n * cells. Every prop that changes identity on a parent re-render (`onColSort`,\n * `onRowSort`, `colSorts`, `rowSorts`) defeated PivotTable's PureComponent\n * check and made React re-render that whole table - on every drag event, every\n * keystroke and even while a worker computation was still pending. These tests\n * count renderer invocations to pin that behaviour down.\n */\nconst records = [\n {name: 'Nick', gender: 'male', colour: 'blue', trials: 103},\n {name: 'Jane', gender: 'female', colour: 'red', trials: 95},\n {name: 'John', gender: 'male', colour: 'blue', trials: 112},\n {name: 'Carol', gender: 'female', colour: 'yellow', trials: 102},\n];\n\nlet renders;\nlet WorkerInstances = [];\nlet nextWorkerUrl = 1;\n\n// Stands in for the real Table renderer: a PureComponent, exactly like the\n// renderers built by `makeRenderer`, recording every render it is asked for.\n// Rendering the real table is what costs seconds on a 10k-row pivot, so the\n// number of calls here is the metric that matters.\nclass CountingRenderer extends React.PureComponent {\n render() {\n renders.push({\n rows: (this.props.rows || []).join('|'),\n rowKeys: this.props.precomputedData\n ? this.props.precomputedData.rowKeys.length\n : null,\n });\n return <table className=\"pvtTable\" />;\n }\n}\n\n// Stable identities, mirroring what a consumer must do for the PureComponent\n// bail-out to work (see the `renderers` constant in examples/App.jsx).\nconst stubRenderers = {Stub: CountingRenderer};\n\nconst baseProps = extra =>\n Object.assign(\n {\n data: records,\n rows: ['name'],\n cols: [],\n aggregatorName: 'Count',\n vals: [],\n rendererName: 'Stub',\n renderers: stubRenderers,\n },\n extra\n );\n\n// Emulates how examples/App.jsx drives PivotTableUI: the component is\n// controlled, so `onChange` feeds the next state back in as props.\nconst harnessRef = {setState: null};\n\nfunction Harness(props) {\n const [state, setState] = React.useState(props.initialState);\n harnessRef.setState = setState;\n return (\n <PivotTableUI\n {...state}\n renderers={stubRenderers}\n rendererName=\"Stub\"\n workerThreshold={props.workerThreshold}\n onChange={next => setState(next)}\n />\n );\n}\n\nclass FakeWorker {\n constructor(url) {\n this.url = url;\n this.posted = [];\n this.onmessage = null;\n this.onerror = null;\n WorkerInstances.push(this);\n }\n\n postMessage(message) {\n this.posted.push(message);\n }\n\n terminate() {}\n}\n\nconst wait = ms => new Promise(resolve => setTimeout(resolve, ms));\n// React's act() returns a non-standard thenable whose .then() does not return\n// a promise, so it cannot be chained directly - wrap it first.\nconst actAsync = callback =>\n new Promise((resolve, reject) => act(callback).then(resolve, reject));\n\n// The worker client keeps one shared worker per module, so before every test\n// the client is pointed at a new URL: that discards the worker of the previous\n// test and lets the next one create (and answer) its own fake worker.\nconst freshWorkerClient = () => {\n const client = require('../PivotDataWorker');\n client.configureWorkerUrl(`pivot-worker.js?test=${nextWorkerUrl++}`);\n return client;\n};\n\ndescribe(' PivotTableUI re-render guards', function() {\n let mounted;\n\n beforeEach(function() {\n renders = [];\n WorkerInstances = [];\n freshWorkerClient();\n global.Worker = FakeWorker;\n mounted = null;\n });\n\n afterEach(function() {\n if (mounted) {\n act(() => {\n mounted.root.unmount();\n });\n mounted.node.remove();\n mounted = null;\n }\n global.Worker = undefined;\n });\n\n const mount = element => {\n const node = document.createElement('div');\n document.body.appendChild(node);\n const root = createRoot(node);\n act(() => {\n root.render(element);\n });\n mounted = {node, root};\n return mounted;\n };\n\n // Computes and posts the result the worker would have produced.\n const answerCompute = (config, id) => {\n const worker = WorkerInstances[0];\n expect(worker).toBeDefined();\n const result = computePivotState(\n materializeData(records, {}),\n createWorkerConfig(config)\n );\n act(() => {\n worker.onmessage({data: {type: 'pivot:result', id, result}});\n });\n return result;\n };\n\n // Finds the request the component just dispatched and answers it.\n const answerNextCompute = config =>\n actAsync(() => wait(150)).then(() => {\n const computeMsg = WorkerInstances[0].posted\n .filter(m => m.type === 'pivot:compute')\n .pop();\n expect(computeMsg).toBeDefined();\n const result = answerCompute(config, computeMsg.id);\n return actAsync(() => wait(0)).then(() => result);\n });\n\n it('does not re-render the table while a worker computation is pending', function() {\n const props = baseProps({workerThreshold: 2});\n const {node} = mount(\n <Harness initialState={props} workerThreshold={props.workerThreshold} />\n );\n let rendersBefore = 0;\n\n // Nothing to show yet: the first computation has no previous table.\n expect(node.querySelector('.pvtLoadingOverlay')).not.toBeNull();\n expect(node.querySelector('.pvtTable')).toBeNull();\n\n return answerNextCompute(props)\n .then(() => {\n expect(node.querySelector('.pvtLoadingOverlay')).toBeNull();\n rendersBefore = renders.length;\n expect(rendersBefore).toBeGreaterThan(0);\n expect(renders[rendersBefore - 1].rows).toBe('name');\n expect(renders[rendersBefore - 1].rowKeys).toBe(4);\n\n // Simulate a drop: the parent applies the new row axis, exactly like\n // examples/App.jsx does from PivotTableUI's onChange.\n act(() => {\n harnessRef.setState(prev =>\n Object.assign({}, prev, {rows: ['gender']})\n );\n });\n\n // The overlay is up over the previous table and, crucially, the table\n // was NOT re-rendered with the new axis while the result is missing:\n // neither the pending render itself nor the state updates that follow\n // it may touch the rendered table.\n expect(node.querySelector('.pvtLoadingOverlay')).not.toBeNull();\n expect(node.querySelector('.pvtTable')).not.toBeNull();\n expect(renders.length).toBe(rendersBefore);\n\n return actAsync(() => wait(150));\n })\n .then(() => {\n expect(renders.length).toBe(rendersBefore);\n const computeMsg = WorkerInstances[0].posted\n .filter(m => m.type === 'pivot:compute')\n .pop();\n answerCompute(\n Object.assign({}, props, {rows: ['gender']}),\n computeMsg.id\n );\n return actAsync(() => wait(0));\n })\n .then(() => {\n // Exactly one render for the new result: the render that follows it\n // (overlay removal) must bail out through the PureComponent check.\n expect(renders.length).toBe(rendersBefore + 1);\n expect(renders[rendersBefore].rows).toBe('gender');\n expect(renders[rendersBefore].rowKeys).toBe(2);\n expect(node.querySelector('.pvtLoadingOverlay')).toBeNull();\n expect(node.querySelector('.pvtTable')).not.toBeNull();\n });\n });\n\n it('does not re-render the table when the parent re-renders unchanged props', function() {\n const props = baseProps();\n const {node, root} = mount(<PivotTableUI {...props} />);\n\n // Small datasets are not worker-eligible: the classic main-thread path\n // renders the table synchronously on mount (no overlay, no pending state).\n return actAsync(() => wait(0)).then(() => {\n const rendersBefore = renders.length;\n expect(rendersBefore).toBeGreaterThan(0);\n\n // A parent re-render with the very same props (an unrelated App state\n // change) must not touch the table.\n act(() => {\n root.render(<PivotTableUI {...props} />);\n });\n\n expect(renders.length).toBe(rendersBefore);\n expect(node.querySelector('.pvtLoadingOverlay')).toBeNull();\n expect(node.querySelector('.pvtTable')).not.toBeNull();\n });\n });\n});\n"]}
@@ -634,6 +634,7 @@ function canComputeInWorker(props) {
634
634
  if (!Array.isArray(data) || data.length < threshold) {
635
635
  return false;
636
636
  }
637
+
637
638
  return true;
638
639
  }
639
640
 
@@ -248,6 +248,7 @@ function canComputeInWorker(props) {
248
248
  if (!Array.isArray(data) || data.length < threshold) {
249
249
  return false;
250
250
  }
251
+
251
252
  return true;
252
253
  }
253
254
  //# sourceMappingURL=pivotCompute.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/pivotCompute.js"],"names":["materializeData","computePivotState","serializePivotData","sortersHaveFunctions","sanitizeSorters","createWorkerConfig","createComputeSignature","canComputeInWorker","DEFAULT_WORKER_THRESHOLD","WORKER_DEBOUNCE_MS","data","derivedAttributes","attrValues","materializedInput","recordsProcessed","PivotData","forEachRecord","record","push","Object","keys","attr","null","value","totalRecords","materialized","config","pivotData","assign","getRowKeys","getColKeys","serializeCell","values","aggregations","forEach","instance","cell","getInstance","agg","key","tree","rowBranch","flatRowKey","serializedRow","flatColKey","serializeTotals","serialized","totals","flatKey","rowKeys","map","slice","colKeys","rowTotals","colTotals","allTotal","sorters","some","clean","sorter","Array","isArray","props","rows","cols","aggregatorName","vals","valueFilter","rowOrder","colOrder","rowSorts","colSorts","JSON","stringify","options","enabled","Worker","threshold","length"],"mappings":"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","file":"pivotCompute.js","sourcesContent":["/* eslint-disable no-console */\n/* eslint-disable valid-jsdoc */\n/*\n * Shared pivot-computation logic.\n *\n * This module contains the pure, DOM-free parts of the pivot computation so\n * that the exact same code paths can run:\n * - on the main thread (classic synchronous rendering path), and\n * - inside a Web Worker (src/pivotWorker.js) for large datasets.\n *\n * It must stay free of React and of any `window`/`document` references.\n */\nimport {PivotData} from './Utilities';\n\n// Datasets smaller than this are computed synchronously on the main thread;\n// larger ones are handed off to the Web Worker.\nexport const DEFAULT_WORKER_THRESHOLD = 200;\n\n// Trailing debounce before a worker computation is dispatched, so rapid\n// interactions do not queue one full computation per single change.\nexport const WORKER_DEBOUNCE_MS = 100;\n\n/*\n * Walks the raw input once and computes the attribute-value inventory plus a\n * \"materialized\" copy of the records (with derived attributes applied).\n * This is an exact extraction of the historical PivotTableUI behavior.\n */\nexport function materializeData(data, derivedAttributes) {\n const attrValues = {};\n const materializedInput = [];\n let recordsProcessed = 0;\n PivotData.forEachRecord(data, derivedAttributes || {}, function(record) {\n materializedInput.push(record);\n for (const attr of Object.keys(record)) {\n if (!(attr in attrValues)) {\n attrValues[attr] = {};\n if (recordsProcessed > 0) {\n attrValues[attr].null = recordsProcessed;\n }\n }\n }\n for (const attr in attrValues) {\n const value = attr in record ? record[attr] : 'null';\n if (!(value in attrValues[attr])) {\n attrValues[attr][value] = 0;\n }\n attrValues[attr][value]++;\n }\n recordsProcessed++;\n });\n return {attrValues, materializedInput, totalRecords: recordsProcessed};\n}\n\n/*\n * Aggregates an already-materialized dataset into a PivotData instance and\n * serializes the result into a plain (structured-cloneable) object that can\n * cross the worker boundary and later hydrate PivotData on the main thread.\n */\nexport function computePivotState(materialized, config) {\n const pivotData = new PivotData(\n Object.assign({}, config, {\n data: materialized.materializedInput,\n derivedAttributes: {},\n })\n );\n // Force key sorting to happen while we are (possibly) inside the worker.\n pivotData.getRowKeys();\n pivotData.getColKeys();\n return serializePivotData(pivotData, materialized);\n}\n\n/*\n * Serializes a fully-built PivotData instance into plain data. Only the final\n * aggregated value of every cell is kept; `value()` and `format()` behavior\n * are reconstructed on the main thread by the PivotData hydration step.\n */\nexport function serializePivotData(pivotData, materialized) {\n const serializeCell = cell => {\n const values = {};\n pivotData.aggregations.forEach(agg => {\n const instance = cell.getInstance(agg.key);\n if (instance) {\n values[agg.key] = instance.value();\n }\n });\n return {values};\n };\n\n const tree = {};\n Object.keys(pivotData.tree).forEach(flatRowKey => {\n const rowBranch = pivotData.tree[flatRowKey];\n const serializedRow = {};\n Object.keys(rowBranch).forEach(flatColKey => {\n serializedRow[flatColKey] = serializeCell(rowBranch[flatColKey]);\n });\n tree[flatRowKey] = serializedRow;\n });\n\n const serializeTotals = totals => {\n const serialized = {};\n Object.keys(totals).forEach(flatKey => {\n serialized[flatKey] = serializeCell(totals[flatKey]);\n });\n return serialized;\n };\n\n return {\n rowKeys: pivotData.rowKeys.map(key => key.slice()),\n colKeys: pivotData.colKeys.map(key => key.slice()),\n tree,\n rowTotals: serializeTotals(pivotData.rowTotals),\n colTotals: serializeTotals(pivotData.colTotals),\n allTotal: serializeCell(pivotData.allTotal),\n aggregations: pivotData.aggregations.map(agg => Object.assign({}, agg)),\n attrValues: materialized.attrValues,\n materializedInput: materialized.materializedInput,\n totalRecords: materialized.totalRecords,\n };\n}\n\n/*\n * Sorters containing functions cannot be structured-cloned into a worker.\n * Returns true when the sorters prop itself, or any of its entries, is a\n * function. In that case PivotData re-sorts on the main thread after\n * hydration (distinct keys are few, so this stays cheap).\n */\nexport function sortersHaveFunctions(sorters) {\n if (typeof sorters === 'function') {\n return true;\n }\n if (!sorters || typeof sorters !== 'object') {\n return false;\n }\n return Object.keys(sorters).some(attr => typeof sorters[attr] === 'function');\n}\n\n/*\n * Returns a structured-cloneable subset of the sorters prop: data-type names\n * and sample-value arrays survive the worker boundary, functions do not.\n */\nexport function sanitizeSorters(sorters) {\n if (\n !sorters ||\n typeof sorters !== 'object' ||\n typeof sorters === 'function'\n ) {\n return {};\n }\n const clean = {};\n Object.keys(sorters).forEach(attr => {\n const sorter = sorters[attr];\n if (typeof sorter === 'string' || Array.isArray(sorter)) {\n clean[attr] = sorter;\n }\n });\n return clean;\n}\n\n/*\n * Extracts exactly the props PivotData needs to compute results, with\n * everything non-serializable stripped, so the object can be posted to the\n * worker and fed straight into PivotData.\n */\nexport function createWorkerConfig(props) {\n return {\n rows: props.rows,\n cols: props.cols,\n aggregatorName: props.aggregatorName,\n aggregations: props.aggregations,\n vals: props.vals,\n valueFilter: props.valueFilter,\n sorters: sanitizeSorters(props.sorters),\n rowOrder: props.rowOrder,\n colOrder: props.colOrder,\n rowSorts: props.rowSorts,\n colSorts: props.colSorts,\n };\n}\n\n/*\n * Stable string signature of everything that influences a worker computation.\n * Data identity is NOT part of it; callers append their own data version.\n */\nexport function createComputeSignature(props) {\n return JSON.stringify(createWorkerConfig(props));\n}\n\n/*\n * Decides whether the current props can be computed inside the Web Worker.\n * Anything that cannot be structured-cloned (function data sources, custom\n * derivedAttributes, custom aggregator maps) forces the classic path.\n * Function-based sorters are allowed: they are re-applied on the main thread.\n */\nexport function canComputeInWorker(props, options = {}) {\n if (options.enabled === false) {\n return false;\n }\n if (typeof Worker === 'undefined') {\n return false;\n }\n const threshold =\n typeof options.threshold === 'number'\n ? options.threshold\n : DEFAULT_WORKER_THRESHOLD;\n\n const data = props.data;\n if (!Array.isArray(data) || data.length < threshold) {\n return false;\n }\n return true;\n}\n"]}
1
+ {"version":3,"sources":["../src/pivotCompute.js"],"names":["materializeData","computePivotState","serializePivotData","sortersHaveFunctions","sanitizeSorters","createWorkerConfig","createComputeSignature","canComputeInWorker","DEFAULT_WORKER_THRESHOLD","WORKER_DEBOUNCE_MS","data","derivedAttributes","attrValues","materializedInput","recordsProcessed","PivotData","forEachRecord","record","push","Object","keys","attr","null","value","totalRecords","materialized","config","pivotData","assign","getRowKeys","getColKeys","serializeCell","values","aggregations","forEach","instance","cell","getInstance","agg","key","tree","rowBranch","flatRowKey","serializedRow","flatColKey","serializeTotals","serialized","totals","flatKey","rowKeys","map","slice","colKeys","rowTotals","colTotals","allTotal","sorters","some","clean","sorter","Array","isArray","props","rows","cols","aggregatorName","vals","valueFilter","rowOrder","colOrder","rowSorts","colSorts","JSON","stringify","options","enabled","Worker","threshold","length"],"mappings":"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","file":"pivotCompute.js","sourcesContent":["/* eslint-disable no-console */\n/* eslint-disable valid-jsdoc */\n/*\n * Shared pivot-computation logic.\n *\n * This module contains the pure, DOM-free parts of the pivot computation so\n * that the exact same code paths can run:\n * - on the main thread (classic synchronous rendering path), and\n * - inside a Web Worker (src/pivotWorker.js) for large datasets.\n *\n * It must stay free of React and of any `window`/`document` references.\n */\nimport {PivotData} from './Utilities';\n\n// Datasets smaller than this are computed synchronously on the main thread;\n// larger ones are handed off to the Web Worker.\nexport const DEFAULT_WORKER_THRESHOLD = 200;\n\n// Trailing debounce before a worker computation is dispatched, so rapid\n// interactions do not queue one full computation per single change.\nexport const WORKER_DEBOUNCE_MS = 100;\n\n/*\n * Walks the raw input once and computes the attribute-value inventory plus a\n * \"materialized\" copy of the records (with derived attributes applied).\n * This is an exact extraction of the historical PivotTableUI behavior.\n */\nexport function materializeData(data, derivedAttributes) {\n const attrValues = {};\n const materializedInput = [];\n let recordsProcessed = 0;\n PivotData.forEachRecord(data, derivedAttributes || {}, function(record) {\n materializedInput.push(record);\n for (const attr of Object.keys(record)) {\n if (!(attr in attrValues)) {\n attrValues[attr] = {};\n if (recordsProcessed > 0) {\n attrValues[attr].null = recordsProcessed;\n }\n }\n }\n for (const attr in attrValues) {\n const value = attr in record ? record[attr] : 'null';\n if (!(value in attrValues[attr])) {\n attrValues[attr][value] = 0;\n }\n attrValues[attr][value]++;\n }\n recordsProcessed++;\n });\n return {attrValues, materializedInput, totalRecords: recordsProcessed};\n}\n\n/*\n * Aggregates an already-materialized dataset into a PivotData instance and\n * serializes the result into a plain (structured-cloneable) object that can\n * cross the worker boundary and later hydrate PivotData on the main thread.\n */\nexport function computePivotState(materialized, config) {\n const pivotData = new PivotData(\n Object.assign({}, config, {\n data: materialized.materializedInput,\n derivedAttributes: {},\n })\n );\n // Force key sorting to happen while we are (possibly) inside the worker.\n pivotData.getRowKeys();\n pivotData.getColKeys();\n return serializePivotData(pivotData, materialized);\n}\n\n/*\n * Serializes a fully-built PivotData instance into plain data. Only the final\n * aggregated value of every cell is kept; `value()` and `format()` behavior\n * are reconstructed on the main thread by the PivotData hydration step.\n */\nexport function serializePivotData(pivotData, materialized) {\n const serializeCell = cell => {\n const values = {};\n pivotData.aggregations.forEach(agg => {\n const instance = cell.getInstance(agg.key);\n if (instance) {\n values[agg.key] = instance.value();\n }\n });\n return {values};\n };\n\n const tree = {};\n Object.keys(pivotData.tree).forEach(flatRowKey => {\n const rowBranch = pivotData.tree[flatRowKey];\n const serializedRow = {};\n Object.keys(rowBranch).forEach(flatColKey => {\n serializedRow[flatColKey] = serializeCell(rowBranch[flatColKey]);\n });\n tree[flatRowKey] = serializedRow;\n });\n\n const serializeTotals = totals => {\n const serialized = {};\n Object.keys(totals).forEach(flatKey => {\n serialized[flatKey] = serializeCell(totals[flatKey]);\n });\n return serialized;\n };\n\n return {\n rowKeys: pivotData.rowKeys.map(key => key.slice()),\n colKeys: pivotData.colKeys.map(key => key.slice()),\n tree,\n rowTotals: serializeTotals(pivotData.rowTotals),\n colTotals: serializeTotals(pivotData.colTotals),\n allTotal: serializeCell(pivotData.allTotal),\n aggregations: pivotData.aggregations.map(agg => Object.assign({}, agg)),\n attrValues: materialized.attrValues,\n materializedInput: materialized.materializedInput,\n totalRecords: materialized.totalRecords,\n };\n}\n\n/*\n * Sorters containing functions cannot be structured-cloned into a worker.\n * Returns true when the sorters prop itself, or any of its entries, is a\n * function. In that case PivotData re-sorts on the main thread after\n * hydration (distinct keys are few, so this stays cheap).\n */\nexport function sortersHaveFunctions(sorters) {\n if (typeof sorters === 'function') {\n return true;\n }\n if (!sorters || typeof sorters !== 'object') {\n return false;\n }\n return Object.keys(sorters).some(attr => typeof sorters[attr] === 'function');\n}\n\n/*\n * Returns a structured-cloneable subset of the sorters prop: data-type names\n * and sample-value arrays survive the worker boundary, functions do not.\n */\nexport function sanitizeSorters(sorters) {\n if (\n !sorters ||\n typeof sorters !== 'object' ||\n typeof sorters === 'function'\n ) {\n return {};\n }\n const clean = {};\n Object.keys(sorters).forEach(attr => {\n const sorter = sorters[attr];\n if (typeof sorter === 'string' || Array.isArray(sorter)) {\n clean[attr] = sorter;\n }\n });\n return clean;\n}\n\n/*\n * Extracts exactly the props PivotData needs to compute results, with\n * everything non-serializable stripped, so the object can be posted to the\n * worker and fed straight into PivotData.\n */\nexport function createWorkerConfig(props) {\n return {\n rows: props.rows,\n cols: props.cols,\n aggregatorName: props.aggregatorName,\n aggregations: props.aggregations,\n vals: props.vals,\n valueFilter: props.valueFilter,\n sorters: sanitizeSorters(props.sorters),\n rowOrder: props.rowOrder,\n colOrder: props.colOrder,\n rowSorts: props.rowSorts,\n colSorts: props.colSorts,\n };\n}\n\n/*\n * Stable string signature of everything that influences a worker computation.\n * Data identity is NOT part of it; callers append their own data version.\n */\nexport function createComputeSignature(props) {\n return JSON.stringify(createWorkerConfig(props));\n}\n\n/*\n * Decides whether the current props can be computed inside the Web Worker.\n * Anything that cannot be structured-cloned (function data sources, custom\n * derivedAttributes, custom aggregator maps) forces the classic path.\n * Function-based sorters are allowed: they are re-applied on the main thread.\n */\nexport function canComputeInWorker(props, options = {}) {\n if (options.enabled === false) {\n return false;\n }\n if (typeof Worker === 'undefined') {\n return false;\n }\n const threshold =\n typeof options.threshold === 'number'\n ? options.threshold\n : DEFAULT_WORKER_THRESHOLD;\n\n const data = props.data;\n if (!Array.isArray(data) || data.length < threshold) {\n return false;\n }\n\n return true;\n}\n"]}
@@ -396,8 +396,9 @@ table.pvtTable tbody tr td {
396
396
  flex-direction: column;
397
397
  align-items: center;
398
398
  justify-content: center;
399
- gap: 10px;
400
- background: rgba(255, 255, 255, 0.85);
399
+ gap: 15px;
400
+ background: rgba(0, 0, 0, 0.1);
401
+ backdrop-filter: blur(3px);
401
402
  z-index: 20;
402
403
  cursor: wait;
403
404
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
@@ -423,8 +424,41 @@ table.pvtTable tbody tr td {
423
424
  }
424
425
 
425
426
  .pvtLoadingText {
426
- font-size: 14px;
427
- color: #333;
427
+ font-size: 14px;
428
+ /* color: #333; */
429
+ display: inline-flex;
430
+ color: #ff6600;
431
+ gap: 2px;
432
+ }
433
+
434
+ .pvtLoadingText span {
435
+ font-weight: 600;
436
+ display: inline-block;
437
+ animation: wave 1.2s ease-in-out infinite;
438
+ }
439
+
440
+ .pvtLoadingText span:nth-child(1) { animation-delay: 0s; }
441
+ .pvtLoadingText span:nth-child(2) { animation-delay: 0.08s; }
442
+ .pvtLoadingText span:nth-child(3) { animation-delay: 0.16s; }
443
+ .pvtLoadingText span:nth-child(4) { animation-delay: 0.24s; }
444
+ .pvtLoadingText span:nth-child(5) { animation-delay: 0.32s; }
445
+ .pvtLoadingText span:nth-child(6) { animation-delay: 0.40s; }
446
+ .pvtLoadingText span:nth-child(7) { animation-delay: 0.48s; }
447
+ .pvtLoadingText span:nth-child(8) { animation-delay: 0.56s; }
448
+ .pvtLoadingText span:nth-child(9) { animation-delay: 0.64s; }
449
+ .pvtLoadingText span:nth-child(10) { animation-delay: 0.72s; }
450
+ .pvtLoadingText span:nth-child(11) { animation-delay: 0.80s; }
451
+
452
+ @keyframes wave {
453
+ 0%,
454
+ 60%,
455
+ 100% {
456
+ transform: translateY(0);
457
+ }
458
+
459
+ 30% {
460
+ transform: translateY(-4px);
461
+ }
428
462
  }
429
463
 
430
464
  .pvtComputeError {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hrforte/react-pivottable",
3
- "version": "2.2.1",
3
+ "version": "2.2.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },