From 5204e2d0809d45c4b9b5c7746b8a759cb3454ae3 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Fri, 15 Oct 2021 23:25:38 +0200 Subject: [PATCH] [GH-1368] Add empty / not empty table aggregations (#1557) This adds new aggregations for counting empty / not empty cards as well as computing the respective percentage. Fixes: #1368 --- .../calculations/calculations.test.tsx | 30 ++++++ .../components/calculations/calculations.ts | 26 ++++++ .../src/components/calculations/options.tsx | 7 +- .../__snapshots__/calculation.test.tsx.snap | 46 +++++++++- .../calculationOptions.test.tsx.snap | 92 ++++++++++++++++++- 5 files changed, 197 insertions(+), 4 deletions(-) diff --git a/webapp/src/components/calculations/calculations.test.tsx b/webapp/src/components/calculations/calculations.test.tsx index d4348ec6c..675967d17 100644 --- a/webapp/src/components/calculations/calculations.test.tsx +++ b/webapp/src/components/calculations/calculations.test.tsx @@ -151,6 +151,8 @@ describe('components/calculations/calculation logic', () => { updatedBy: {id: 'property_lastUpdatedBy', type: 'updatedBy', name: '', options: []}, } + const autofilledProperties = new Set([properties.createdBy, properties.createdTime, properties.updatedBy, properties.updatedTime]) + const intl = createIntl({locale: 'en-us'}) // testing count @@ -160,6 +162,34 @@ describe('components/calculations/calculation logic', () => { }) }) + // testing count empty + Object.values(properties).filter((p) => !autofilledProperties.has(p)).forEach((property) => { + it(`should correctly count empty for property type "${property.type}"`, function() { + expect(Calculations.countEmpty(cards, property, intl)).toBe('1') + }) + }) + + // testing percent empty + Object.values(properties).filter((p) => !autofilledProperties.has(p)).forEach((property) => { + it(`should correctly compute empty percent for property type "${property.type}"`, function() { + expect(Calculations.percentEmpty(cards, property, intl)).toBe('25%') + }) + }) + + // testing count not empty + Object.values(properties).filter((p) => !autofilledProperties.has(p)).forEach((property) => { + it(`should correctly count not empty for property type "${property.type}"`, function() { + expect(Calculations.countNotEmpty(cards, property, intl)).toBe('3') + }) + }) + + // testing percent not empty + Object.values(properties).filter((p) => !autofilledProperties.has(p)).forEach((property) => { + it(`should correctly compute not empty percent for property type "${property.type}"`, function() { + expect(Calculations.percentNotEmpty(cards, property, intl)).toBe('75%') + }) + }) + // testing countValues const countValueTests: Record = { text: '3', diff --git a/webapp/src/components/calculations/calculations.ts b/webapp/src/components/calculations/calculations.ts index 0c73d29c9..068a84353 100644 --- a/webapp/src/components/calculations/calculations.ts +++ b/webapp/src/components/calculations/calculations.ts @@ -53,6 +53,28 @@ function count(cards: readonly Card[], property: IPropertyTemplate): string { return String(cards.length) } +function countEmpty(cards: readonly Card[], property: IPropertyTemplate): string { + return String(cards.length - cardsWithValue(cards, property).length) +} + +function countNotEmpty(cards: readonly Card[], property: IPropertyTemplate): string { + return String(cardsWithValue(cards, property).length) +} + +function percentEmpty(cards: readonly Card[], property: IPropertyTemplate): string { + if (cards.length === 0) { + return '' + } + return String((((cards.length - cardsWithValue(cards, property).length) / cards.length) * 100).toFixed(0)) + '%' +} + +function percentNotEmpty(cards: readonly Card[], property: IPropertyTemplate): string { + if (cards.length === 0) { + return '' + } + return String(((cardsWithValue(cards, property).length / cards.length) * 100).toFixed(0)) + '%' +} + function countValueHelper(cards: readonly Card[], property: IPropertyTemplate): number { let values = 0 @@ -274,6 +296,10 @@ function dateRange(cards: readonly Card[], property: IPropertyTemplate): string const Calculations: Record string> = { count, + countEmpty, + countNotEmpty, + percentEmpty, + percentNotEmpty, countValue, countUniqueValue, countChecked, diff --git a/webapp/src/components/calculations/options.tsx b/webapp/src/components/calculations/options.tsx index b89ff19f6..e70f68073 100644 --- a/webapp/src/components/calculations/options.tsx +++ b/webapp/src/components/calculations/options.tsx @@ -19,6 +19,10 @@ type Option = { export const Options:Record = { none: {value: 'none', label: 'None', displayName: 'Calculate'}, count: {value: 'count', label: 'Count', displayName: 'Count'}, + countEmpty: {value: 'countEmpty', label: 'Count Empty', displayName: 'Empty'}, + countNotEmpty: {value: 'countNotEmpty', label: 'Count Not Empty', displayName: 'Not Empty'}, + percentEmpty: {value: 'percentEmpty', label: 'Percent Empty', displayName: 'Empty'}, + percentNotEmpty: {value: 'percentNotEmpty', label: 'Percent Not Empty', displayName: 'Not Empty'}, countValue: {value: 'countValue', label: 'Count Value', displayName: 'Values'}, countChecked: {value: 'countChecked', label: 'Count Checked', displayName: 'Checked'}, percentChecked: {value: 'percentChecked', label: 'Percent Checked', displayName: 'Checked'}, @@ -37,7 +41,8 @@ export const Options:Record = { } export const optionsByType: Map = new Map([ - ['common', [Options.none, Options.count, Options.countValue, Options.countUniqueValue]], + ['common', [Options.none, Options.count, Options.countEmpty, Options.countNotEmpty, Options.percentEmpty, + Options.percentNotEmpty, Options.countValue, Options.countUniqueValue]], ['checkbox', [Options.countChecked, Options.countUnchecked, Options.percentChecked, Options.percentUnchecked]], ['number', [Options.sum, Options.average, Options.median, Options.min, Options.max, Options.range]], ['date', [Options.earliest, Options.latest, Options.dateRange]], diff --git a/webapp/src/components/kanban/calculation/__snapshots__/calculation.test.tsx.snap b/webapp/src/components/kanban/calculation/__snapshots__/calculation.test.tsx.snap index 64a9bf790..b96d99647 100644 --- a/webapp/src/components/kanban/calculation/__snapshots__/calculation.test.tsx.snap +++ b/webapp/src/components/kanban/calculation/__snapshots__/calculation.test.tsx.snap @@ -45,7 +45,7 @@ exports[`components/kanban/calculation/KanbanCalculation calculations menu open - 3 results available. Use Up and Down to choose options, press Enter to select the currently focused option, press Escape to exit the menu, press Tab to select the option and exit the menu. + 7 results available. Use Up and Down to choose options, press Enter to select the currently focused option, press Escape to exit the menu, press Tab to select the option and exit the menu.
+
+ + Count Empty + + + +
+
+ + Count Not Empty + + + +
+
+ + Percent Empty + + + +
+
+ + Percent Not Empty + + + +
diff --git a/webapp/src/components/kanban/calculation/__snapshots__/calculationOptions.test.tsx.snap b/webapp/src/components/kanban/calculation/__snapshots__/calculationOptions.test.tsx.snap index abad9d2eb..97bef1fa4 100644 --- a/webapp/src/components/kanban/calculation/__snapshots__/calculationOptions.test.tsx.snap +++ b/webapp/src/components/kanban/calculation/__snapshots__/calculationOptions.test.tsx.snap @@ -99,7 +99,7 @@ exports[`components/kanban/calculations/KanbanCalculationOptions with menu open - 3 results available. Use Up and Down to choose options, press Enter to select the currently focused option, press Escape to exit the menu, press Tab to select the option and exit the menu. + 7 results available. Use Up and Down to choose options, press Enter to select the currently focused option, press Escape to exit the menu, press Tab to select the option and exit the menu.
+
+ + Count Empty + + + +
+
+ + Count Not Empty + + + +
+
+ + Percent Empty + + + +
+
+ + Percent Not Empty + + + +
@@ -219,7 +263,7 @@ exports[`components/kanban/calculations/KanbanCalculationOptions with submenu op - 3 results available. Use Up and Down to choose options, press Enter to select the currently focused option, press Escape to exit the menu, press Tab to select the option and exit the menu. + 7 results available. Use Up and Down to choose options, press Enter to select the currently focused option, press Escape to exit the menu, press Tab to select the option and exit the menu.
+
+ + Count Empty + + + +
+
+ + Count Not Empty + + + +
+
+ + Percent Empty + + + +
+
+ + Percent Not Empty + + + +