From a0a361d4a2c6bfa2ce8f2ae0baffed716aa02cb2 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 10 Aug 2023 21:10:52 -0700 Subject: [PATCH 01/58] add story --- .../LeaderboardHeader.stories.svelte | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 web-common/src/features/dashboards/leaderboard/__stories__/LeaderboardHeader.stories.svelte diff --git a/web-common/src/features/dashboards/leaderboard/__stories__/LeaderboardHeader.stories.svelte b/web-common/src/features/dashboards/leaderboard/__stories__/LeaderboardHeader.stories.svelte new file mode 100644 index 00000000000..ea89de5f1fe --- /dev/null +++ b/web-common/src/features/dashboards/leaderboard/__stories__/LeaderboardHeader.stories.svelte @@ -0,0 +1,63 @@ + + + + + + + From bfca234de690e5c1f915751b1273c8f8ef9d36f6 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 10 Aug 2023 22:00:08 -0700 Subject: [PATCH 02/58] update story default props --- .../__stories__/LeaderboardHeader.stories.svelte | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web-common/src/features/dashboards/leaderboard/__stories__/LeaderboardHeader.stories.svelte b/web-common/src/features/dashboards/leaderboard/__stories__/LeaderboardHeader.stories.svelte index ea89de5f1fe..f09d4feb972 100644 --- a/web-common/src/features/dashboards/leaderboard/__stories__/LeaderboardHeader.stories.svelte +++ b/web-common/src/features/dashboards/leaderboard/__stories__/LeaderboardHeader.stories.svelte @@ -14,10 +14,10 @@ const defaultArgs = { displayName: "Leaderboard Name", dimensionDescription: "dimension description", - isFetching: true, + isFetching: false, hovered: false, - showTimeComparison: true, - showPercentOfTotal: true, + showTimeComparison: false, + showPercentOfTotal: false, filterExcludeMode: false, // referenceValue: 400, // unfilteredTotal: 1000, From 28124f14139345b0c18b868acfd39da50491f99d Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Wed, 16 Aug 2023 11:19:54 -0700 Subject: [PATCH 03/58] begin migrating sort info to dashboard store --- .../features/dashboards/dashboard-stores.ts | 84 ++++++++++++++++++- .../dimension-table/DimensionDisplay.svelte | 45 +++++----- 2 files changed, 105 insertions(+), 24 deletions(-) diff --git a/web-common/src/features/dashboards/dashboard-stores.ts b/web-common/src/features/dashboards/dashboard-stores.ts index d6c4205172e..0cf697d4f5a 100644 --- a/web-common/src/features/dashboards/dashboard-stores.ts +++ b/web-common/src/features/dashboards/dashboard-stores.ts @@ -11,7 +11,10 @@ import { getComparionRangeForScrub } from "@rilldata/web-common/lib/time/compari import { getDefaultTimeGrain } from "@rilldata/web-common/lib/time/grains"; import { convertTimeRangePreset } from "@rilldata/web-common/lib/time/ranges"; import { TimeRangePreset } from "@rilldata/web-common/lib/time/types"; -import type { DashboardTimeControls } from "@rilldata/web-common/lib/time/types"; +import type { + DashboardTimeControls, + ScrubRange, +} from "@rilldata/web-common/lib/time/types"; import type { V1ColumnTimeRangeResponse, V1MetricsView, @@ -29,6 +32,43 @@ export interface LeaderboardValues { dimensionName: string; } +export enum SortDirection { + ASC = "ASCENDING", + DESC = "DESCENDING", +} + +/** + * SortType is used to determine how to sort the leaderboard + * and dimension detail table, as well as where to place the + * sort arrow. + * + * By default, the leaderboards+table will be sorted by VALUE, + * using the value of the currently selected dashboard measure. + * + * If DELTA_CHANGE or DELTA_PCT is selected, the + * leaderboards+table will be sorted by the absolute or percentage + * delta change of the currently selected dashboard measure. + * + * If PERCENT is selected, the table will be sorted by the value + * of the currently selected dashboard measure, which will return + * the same ordering as the percent-of-total sort for measures + * with valid percent-of-total. However, the sort arrow will be + * placed next to the percent-of-total icon. + * + * As of 2023-08, DIMENSION is not implemented, but at that time + * the plan was to only apply DIMENSTION sort to the dimension + * detail table, and not the leaderboards. + */ +export enum SortType { + VALUE = "VALUE", + DIMENSION = "DIMENSION", + DELTA_CHANGE = "DELTA_CHANGE", + DELTA_PCT = "DELTA_PCT", + // Note that PERCENT will return the same ordering as VALUE, + // but the sort arrow will be placed differently + PERCENT = "PERCENT", +} + export type ActiveValues = Record>; export interface MetricsExplorerEntity { @@ -57,8 +97,21 @@ export interface MetricsExplorerEntity { // TODO: clean this up when we refactor how url state is synced allDimensionsVisible: boolean; - // this is used to show leaderboard values + // This is the name of the primary active measure in the dashboard. + // This is the measure that will be shown in leaderboards, and + // will be used for sorting the leaderboard and dimension + // detail table. + // This "name" is the internal name of the measure from the YAML, + // not the human readable name. leaderboardMeasureName: string; + + // This is the sort type that will be used for the leaderboard + // and dimension detail table. See SortType for more details. + dashboardSortType: SortType; + // This is the sort direction that will be used for the leaderboard + // and dimension detail table. + sortDirection: SortDirection; + filters: V1MetricsViewFilter; // stores whether a dimension is in include/exclude filter mode // false/absence = include, true = exclude @@ -272,6 +325,8 @@ const metricViewReducers = { }, dimensionFilterExcludeMode: new Map(), leaderboardContextColumn: LeaderboardContextColumn.HIDDEN, + dashboardSortType: SortType.VALUE, + sortDirection: SortDirection.DESC, ...timeSelections, }; @@ -315,9 +370,30 @@ const metricViewReducers = { }); }, - clearLeaderboardMeasureName(name: string) { + setSortDescending(name: string) { + updateMetricsExplorerByName(name, (metricsExplorer) => { + metricsExplorer.sortDirection = SortDirection.DESC; + }); + }, + + setSortAscending(name: string) { + updateMetricsExplorerByName(name, (metricsExplorer) => { + metricsExplorer.sortDirection = SortDirection.ASC; + }); + }, + + toggleSortDirection(name: string) { + updateMetricsExplorerByName(name, (metricsExplorer) => { + metricsExplorer.sortDirection = + metricsExplorer.sortDirection === SortDirection.ASC + ? SortDirection.DESC + : SortDirection.ASC; + }); + }, + + setSortDirection(name: string, direction: SortDirection) { updateMetricsExplorerByName(name, (metricsExplorer) => { - metricsExplorer.leaderboardMeasureName = undefined; + metricsExplorer.sortDirection = direction; }); }, diff --git a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte index a0825de9e51..1bccb6ef784 100644 --- a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte +++ b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte @@ -26,6 +26,7 @@ import { useQueryClient } from "@tanstack/svelte-query"; import { runtime } from "../../../runtime-client/runtime-store"; import { + SortDirection, metricsExplorerStore, useDashboardStore, useFetchTimeRange, @@ -96,8 +97,7 @@ $dashboardStore?.visibleMeasureKeys.has(m.name) ); - $: sortByColumn = $leaderboardMeasureQuery.data?.name; - $: sortDirection = sortDirection || "desc"; + $: sortAscending = $dashboardStore.sortDirection === SortDirection.ASC; $: metricTimeSeries = useModelHasTimeSeries(instanceId, metricViewName); $: hasTimeSeries = $metricTimeSeries.data; @@ -120,8 +120,8 @@ offset: "0", sort: [ { - name: sortByColumn, - ascending: sortDirection === "asc", + name: leaderboardMeasureName, + ascending: sortAscending, }, ], }, @@ -130,8 +130,7 @@ enabled: (hasTimeSeries ? !!timeStart && !!timeEnd : true) && !!filterSet && - !!sortByColumn && - !!sortDirection, + !!leaderboardMeasureName, }, } ); @@ -174,7 +173,7 @@ metricViewName, { dimensionName: dimensionName, - measureNames: [sortByColumn], + measureNames: [leaderboardMeasureName], timeStart: comparisonTimeStart, timeEnd: comparisonTimeEnd, filter: comparisonFilterSet, @@ -182,8 +181,8 @@ offset: "0", sort: [ { - name: sortByColumn, - ascending: sortDirection === "asc", + name: leaderboardMeasureName, + ascending: sortAscending, }, ], }, @@ -244,13 +243,19 @@ $dashboardStore.visibleMeasureKeys.has(name) ); - const selectedMeasure = allMeasures.find((m) => m.name === sortByColumn); - const sortByColumnIndex = columnNames.indexOf(sortByColumn); + const selectedMeasure = allMeasures.find( + (m) => m.name === leaderboardMeasureName + ); + const sortByColumnIndex = columnNames.indexOf(leaderboardMeasureName); // Add comparison columns if available let percentOfTotalSpliceIndex = 1; if (displayComparison) { percentOfTotalSpliceIndex = 2; - columnNames.splice(sortByColumnIndex + 1, 0, `${sortByColumn}_delta`); + columnNames.splice( + sortByColumnIndex + 1, + 0, + `${leaderboardMeasureName}_delta` + ); // Only push percentage delta column if selected measure is not a percentage if (selectedMeasure?.format != FormatPreset.PERCENTAGE) { @@ -258,7 +263,7 @@ columnNames.splice( sortByColumnIndex + 2, 0, - `${sortByColumn}_delta_perc` + `${leaderboardMeasureName}_delta_perc` ); } } @@ -266,7 +271,7 @@ columnNames.splice( sortByColumnIndex + percentOfTotalSpliceIndex, 0, - `${sortByColumn}_percent_of_total` + `${leaderboardMeasureName}_percent_of_total` ); } @@ -326,14 +331,14 @@ const columnName = event.detail; if (!measureNames.includes(columnName)) return; - if (columnName === sortByColumn) { - sortDirection = sortDirection === "desc" ? "asc" : "desc"; + if (columnName === leaderboardMeasureName) { + metricsExplorerStore.toggleSortDirection(metricViewName); } else { metricsExplorerStore.setLeaderboardMeasureName( metricViewName, columnName ); - sortDirection = "desc"; + metricsExplorerStore.setSortDescending(metricViewName); } } @@ -351,8 +356,8 @@ $leaderboardMeasureQuery?.data as MetricsViewMeasure )?.validPercentOfTotal; - $: if (validPercentOfTotal && values.length && sortByColumn) { - const referenceValue = $totalsQuery.data?.data?.[sortByColumn]; + $: if (validPercentOfTotal && values.length && leaderboardMeasureName) { + const referenceValue = $totalsQuery.data?.data?.[leaderboardMeasureName]; values = computePercentOfTotal( values, referenceValue, @@ -396,7 +401,7 @@ {columns} {selectedValues} rows={values} - {sortByColumn} + sortByColumn={leaderboardMeasureName} {excludeMode} /> From f0b9553e1489c6f45ead07de009e8270c84a9c96 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Wed, 16 Aug 2023 12:31:43 -0700 Subject: [PATCH 04/58] add type --- .../dashboards/dimension-table/DimensionDisplay.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte index 1bccb6ef784..82018782b52 100644 --- a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte +++ b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte @@ -22,6 +22,7 @@ MetricsViewDimension, MetricsViewFilterCond, MetricsViewMeasure, + V1MetricsViewToplistResponseDataItem, } from "@rilldata/web-common/runtime-client"; import { useQueryClient } from "@tanstack/svelte-query"; import { runtime } from "../../../runtime-client/runtime-store"; @@ -227,7 +228,7 @@ }); } - let values = []; + let values: V1MetricsViewToplistResponseDataItem[] = []; let columns = []; let measureNames = []; From 3b02e5e25e297806a3f1c4f8769ff3ebf5500468 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Wed, 16 Aug 2023 12:38:08 -0700 Subject: [PATCH 05/58] move business logic out of component to typed fns --- .../dimension-table/DimensionTable.svelte | 93 +++++------------- .../dimension-table/dimension-table-utils.ts | 97 +++++++++++++++++++ 2 files changed, 119 insertions(+), 71 deletions(-) diff --git a/web-common/src/features/dashboards/dimension-table/DimensionTable.svelte b/web-common/src/features/dashboards/dimension-table/DimensionTable.svelte index 22cd48bf890..9f25e3969be 100644 --- a/web-common/src/features/dashboards/dimension-table/DimensionTable.svelte +++ b/web-common/src/features/dashboards/dimension-table/DimensionTable.svelte @@ -10,8 +10,12 @@ TableCells – the cell contents. import { createVirtualizer } from "@tanstack/svelte-virtual"; import { createEventDispatcher, setContext } from "svelte"; import DimensionFilterGutter from "./DimensionFilterGutter.svelte"; - import { DimensionTableConfig } from "./DimensionTableConfig"; + import { DimensionTableConfig as config } from "./DimensionTableConfig"; import DimensionValueHeader from "./DimensionValueHeader.svelte"; + import { + estimateColumnCharacterWidths, + estimateColumnSizes, + } from "./dimension-table-utils"; const dispatch = createEventDispatcher(); @@ -40,13 +44,8 @@ TableCells – the cell contents. /** this is a perceived character width value, in pixels, when our monospace * font is 12px high. */ - const CHARACTER_WIDTH = 7; - const CHARACTER_X_PAD = 16 * 2; - const HEADER_ICON_WIDTHS = 16; - const HEADER_X_PAD = CHARACTER_X_PAD; - const HEADER_FLEX_SPACING = 14; const CHARACTER_LIMIT_FOR_WRAPPING = 9; - const FILTER_COLUMN_WIDTH = DimensionTableConfig.indexWidth; + const FILTER_COLUMN_WIDTH = config.indexWidth; $: selectedIndex = selectedValues .map((label) => { @@ -61,29 +60,12 @@ TableCells – the cell contents. * find the largest strings in the column and use that to bootstrap the * column widths. */ - let columnWidths: { [key: string]: number } = {}; - let largestColumnLength = 0; - columns.forEach((column, i) => { - // get values - const values = rows - .filter((row) => row[column.name] !== null) - .map( - (row) => - `${row["__formatted_" + column.name] || row[column.name]}`.length - ); - values.sort(); - let largest = Math.max(...values); - columnWidths[column.name] = largest; - if (i != 0) { - largestColumnLength = Math.max( - largestColumnLength, - column.label?.length || column.name.length - ); - } - }); + const { columnWidths, largestColumnLength } = estimateColumnCharacterWidths( + columns, + rows + ); /* check if column header requires extra space for larger column names */ - const config = DimensionTableConfig; if (largestColumnLength > CHARACTER_LIMIT_FOR_WRAPPING) { config.columnHeaderHeight = 46; } @@ -91,9 +73,13 @@ TableCells – the cell contents. /* set context for child components */ setContext("config", config); - let estimateColumnSize; + let estimateColumnSize: number[] = []; let measureColumns = []; - let dimensionColumn; + + /* Separate out dimension column */ + $: dimensionColumn = columns?.find((c) => c.name == dimensionName); + $: measureColumns = columns?.filter((c) => c.name !== dimensionName); + let horizontalScrolling = false; $: if (rows && columns) { @@ -106,43 +92,12 @@ TableCells – the cell contents. initialOffset: rowScrollOffset, }); - estimateColumnSize = columns.map((column, i) => { - if (column.name.includes("delta")) return config.comparisonColumnWidth; - if (i != 0) return config.defaultColumnWidth; - - const largestStringLength = - columnWidths[column.name] * CHARACTER_WIDTH + CHARACTER_X_PAD; - - /** The header width is largely a function of the total number of characters in the column.*/ - const headerWidth = - (column.label?.length || column.name.length) * CHARACTER_WIDTH + - HEADER_ICON_WIDTHS + - HEADER_X_PAD + - HEADER_FLEX_SPACING; - - /** If the header is bigger than the largestStringLength and that's not at threshold, default to threshold. - * This will prevent the case where we have very long column names for very short column values. - */ - let effectiveHeaderWidth = - headerWidth > 160 && largestStringLength < 160 - ? config.minHeaderWidthWhenColumsAreSmall - : headerWidth; - - return largestStringLength - ? Math.min( - config.maxColumnWidth, - Math.max( - largestStringLength, - effectiveHeaderWidth, - /** All columns must be minColumnWidth regardless of user settings. */ - config.minColumnWidth - ) - ) - : /** if there isn't a longet string length for some reason, let's go with a - * default column width. We should not be in this state. - */ - config.defaultColumnWidth; - }); + estimateColumnSize = estimateColumnSizes( + columns, + columnWidths, + containerWidth, + config + ); const measureColumnSizeSum = estimateColumnSize .slice(1) @@ -154,10 +109,6 @@ TableCells – the cell contents. estimateColumnSize[0] ); - /* Separate out dimension column */ - dimensionColumn = columns.find((c) => c.name == dimensionName); - measureColumns = columns.filter((c) => c.name !== dimensionName); - columnVirtualizer = createVirtualizer({ getScrollElement: () => container, horizontal: true, diff --git a/web-common/src/features/dashboards/dimension-table/dimension-table-utils.ts b/web-common/src/features/dashboards/dimension-table/dimension-table-utils.ts index 31fef275b1d..ca0a201393b 100644 --- a/web-common/src/features/dashboards/dimension-table/dimension-table-utils.ts +++ b/web-common/src/features/dashboards/dimension-table/dimension-table-utils.ts @@ -11,6 +11,8 @@ import { formatMeasurePercentageDifference, } from "../humanize-numbers"; import PercentOfTotal from "./PercentOfTotal.svelte"; +import type { VirtualizedTableColumns } from "@rilldata/web-local/lib/types"; +import type { VirtualizedTableConfig } from "@rilldata/web-common/components/virtualized-table/types"; /** Returns an updated filter set for a given dimension on search */ export function updateFilterOnSearch( @@ -175,3 +177,98 @@ export function getComparisonProperties( }; } } + +export function estimateColumnCharacterWidths( + columns: VirtualizedTableColumns[], + rows: V1MetricsViewToplistResponseDataItem[] +) { + const columnWidths: { [key: string]: number } = {}; + let largestColumnLength = 0; + columns.forEach((column, i) => { + // get values + const values = rows + .filter((row) => row[column.name] !== null) + .map( + (row) => + `${row["__formatted_" + column.name] || row[column.name]}`.length + ); + values.sort(); + const largest = Math.max(...values); + columnWidths[column.name] = largest; + if (i != 0) { + largestColumnLength = Math.max( + largestColumnLength, + column.label?.length || column.name.length + ); + } + }); + return { columnWidths, largestColumnLength }; +} + +/** this is a perceived character width value, in pixels, when our monospace + * font is 12px high. */ +const CHARACTER_WIDTH = 7; +const CHARACTER_X_PAD = 16 * 2; +const HEADER_ICON_WIDTHS = 16; +const HEADER_X_PAD = CHARACTER_X_PAD; +const HEADER_FLEX_SPACING = 14; +// const CHARACTER_LIMIT_FOR_WRAPPING = 9; + +export function estimateColumnSizes( + columns: VirtualizedTableColumns[], + columnWidths: { + [key: string]: number; + }, + containerWidth: number, + config: VirtualizedTableConfig +) { + const estimateColumnSize = columns.map((column, i) => { + if (column.name.includes("delta")) return config.comparisonColumnWidth; + if (i != 0) return config.defaultColumnWidth; + + const largestStringLength = + columnWidths[column.name] * CHARACTER_WIDTH + CHARACTER_X_PAD; + + /** The header width is largely a function of the total number of characters in the column.*/ + const headerWidth = + (column.label?.length || column.name.length) * CHARACTER_WIDTH + + HEADER_ICON_WIDTHS + + HEADER_X_PAD + + HEADER_FLEX_SPACING; + + /** If the header is bigger than the largestStringLength and that's not at threshold, default to threshold. + * This will prevent the case where we have very long column names for very short column values. + */ + const effectiveHeaderWidth = + headerWidth > 160 && largestStringLength < 160 + ? config.minHeaderWidthWhenColumsAreSmall + : headerWidth; + + return largestStringLength + ? Math.min( + config.maxColumnWidth, + Math.max( + largestStringLength, + effectiveHeaderWidth, + /** All columns must be minColumnWidth regardless of user settings. */ + config.minColumnWidth + ) + ) + : /** if there isn't a longet string length for some reason, let's go with a + * default column width. We should not be in this state. + */ + config.defaultColumnWidth; + }); + + const measureColumnSizeSum = estimateColumnSize + .slice(1) + .reduce((a, b) => a + b, 0); + + /* Dimension column should expand to cover whole container */ + estimateColumnSize[0] = Math.max( + containerWidth - measureColumnSizeSum - config.indexWidth, + estimateColumnSize[0] + ); + + return estimateColumnSize; +} From 62a883f334d6967e989e2ac6a9337c2666d3998b Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Wed, 16 Aug 2023 13:34:37 -0700 Subject: [PATCH 06/58] Showing sort arrow in leaderboards --- .../dashboards/leaderboard/Leaderboard.svelte | 4 ++- .../leaderboard/LeaderboardHeader.svelte | 25 +++++++++++++++---- .../leaderboard/LeaderboardListItem.svelte | 3 ++- .../LeaderboardHeader.stories.svelte | 6 +++++ .../leaderboard/leaderboard-utils.ts | 2 ++ 5 files changed, 33 insertions(+), 7 deletions(-) diff --git a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte index da6d8c134b3..f2c6305ea1c 100644 --- a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte +++ b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte @@ -23,6 +23,7 @@ import { useQueryClient } from "@tanstack/svelte-query"; import { runtime } from "../../../runtime-client/runtime-store"; import { + SortDirection, metricsExplorerStore, useComparisonRange, useDashboardStore, @@ -106,6 +107,7 @@ $: timeStart = $fetchTimeStore?.start?.toISOString(); $: timeEnd = $fetchTimeStore?.end?.toISOString(); + $: sortAscending = $dashboardStore.sortDirection === SortDirection.ASC; $: topListQuery = createQueryServiceMetricsViewToplist( $runtime.instanceId, metricViewName, @@ -120,7 +122,7 @@ sort: [ { name: measure?.name, - ascending: false, + ascending: sortAscending, }, ], }, diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte index a05532411d0..e0c32ab28c1 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte @@ -9,6 +9,8 @@ import LeaderboardOptionsMenu from "../leaderboard/LeaderboardOptionsMenu.svelte"; import Delta from "@rilldata/web-common/components/icons/Delta.svelte"; import PieChart from "@rilldata/web-common/components/icons/PieChart.svelte"; + import ArrowDown from "@rilldata/web-common/components/icons/ArrowDown.svelte"; + import { CONTEXT_COLUMN_WIDTH } from "./leaderboard-utils"; export let displayName: string; export let isFetching: boolean; @@ -16,11 +18,13 @@ export let hovered: boolean; export let showTimeComparison: boolean; export let showPercentOfTotal: boolean; + export let sortDescending: boolean; export let filterExcludeMode: boolean; let optionsMenuActive = false; + $: arrowTransform = sortDescending ? "scale(1 1)" : "scale(1 -1)"; $: iconShown = showTimeComparison ? "delta" : showPercentOfTotal @@ -92,11 +96,22 @@ -
- {#if iconShown === "delta"} - % - {:else if iconShown === "pie"} - % +
+
+ # +
+ + {#if iconShown} +
+ {#if iconShown === "delta"} + % + {:else if iconShown === "pie"} + % + {/if} +
{/if}
diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardListItem.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardListItem.svelte index b746fa090cb..805b74d67ab 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardListItem.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardListItem.svelte @@ -22,6 +22,7 @@ import { humanizeDataType } from "../humanize-numbers"; import LongBarZigZag from "./LongBarZigZag.svelte"; import { + CONTEXT_COLUMN_WIDTH, LeaderboardItemData, getFormatterValueForPercDiff, } from "./leaderboard-utils"; @@ -179,7 +180,7 @@ {#if percentChangeFormatted !== undefined}
diff --git a/web-common/src/features/dashboards/leaderboard/__stories__/LeaderboardHeader.stories.svelte b/web-common/src/features/dashboards/leaderboard/__stories__/LeaderboardHeader.stories.svelte index f09d4feb972..2776987bd76 100644 --- a/web-common/src/features/dashboards/leaderboard/__stories__/LeaderboardHeader.stories.svelte +++ b/web-common/src/features/dashboards/leaderboard/__stories__/LeaderboardHeader.stories.svelte @@ -19,6 +19,7 @@ showTimeComparison: false, showPercentOfTotal: false, filterExcludeMode: false, + sortDescending: true, // referenceValue: 400, // unfilteredTotal: 1000, // formatPreset: "humanize", @@ -46,6 +47,11 @@ type: "boolean", }, }, + sortDescending: { + control: { + type: "boolean", + }, + }, }} /> diff --git a/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts b/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts index 249c175a9af..f7c89d5e012 100644 --- a/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts +++ b/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts @@ -38,3 +38,5 @@ export function prepareLeaderboardItemData( }; }); } + +export const CONTEXT_COLUMN_WIDTH = 44; From 548e5119777554071ffa3001d0a4cbdfc00d21c6 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Wed, 16 Aug 2023 14:06:24 -0700 Subject: [PATCH 07/58] add sorting to leaderboard --- .../dashboards/leaderboard/Leaderboard.svelte | 10 +++++-- .../leaderboard/LeaderboardHeader.svelte | 29 ++++++++++++------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte index f2c6305ea1c..0c8b0813451 100644 --- a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte +++ b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte @@ -105,6 +105,10 @@ metricsExplorerStore.setMetricDimensionName(metricViewName, dimensionName); } + function toggleSortDirection() { + metricsExplorerStore.toggleSortDirection(metricViewName); + } + $: timeStart = $fetchTimeStore?.start?.toISOString(); $: timeEnd = $fetchTimeStore?.end?.toISOString(); $: sortAscending = $dashboardStore.sortDirection === SortDirection.ASC; @@ -260,8 +264,10 @@ on:toggle-filter-mode={toggleFilterMode} {filterExcludeMode} {hovered} + {sortAscending} dimensionDescription={dimension?.description} - on:click={() => selectDimension(dimensionName)} + on:open-dimension-details={() => selectDimension(dimensionName)} + on:toggle-sort-direction={toggleSortDirection} /> {#if values}
@@ -313,7 +319,7 @@ {#if values.length > slice}
-
+ @@ -97,9 +102,13 @@
-
+
+ {#if iconShown}
{/if}
- +
From 0e91f6064d78c2b183683784f63bbf7fbee6e43e Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Wed, 16 Aug 2023 14:29:52 -0700 Subject: [PATCH 08/58] remove comments --- .../__stories__/LeaderboardHeader.stories.svelte | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/web-common/src/features/dashboards/leaderboard/__stories__/LeaderboardHeader.stories.svelte b/web-common/src/features/dashboards/leaderboard/__stories__/LeaderboardHeader.stories.svelte index 2776987bd76..5ea9e2fcda3 100644 --- a/web-common/src/features/dashboards/leaderboard/__stories__/LeaderboardHeader.stories.svelte +++ b/web-common/src/features/dashboards/leaderboard/__stories__/LeaderboardHeader.stories.svelte @@ -3,13 +3,6 @@ import { action } from "@storybook/addon-actions"; import LeaderboardHeader from "../LeaderboardHeader.svelte"; - // import { FormatPreset } from "../../humanize-numbers"; - - // const atLeastOneActive = true; - // const filterExcludeMode = true; - // const isSummableMeasure = true; - // const referenceValue = 400; - // const formatPreset = "humanize"; const defaultArgs = { displayName: "Leaderboard Name", @@ -20,9 +13,6 @@ showPercentOfTotal: false, filterExcludeMode: false, sortDescending: true, - // referenceValue: 400, - // unfilteredTotal: 1000, - // formatPreset: "humanize", }; From 9650b8f03dbb921aa4aaa66305e69f1ed483c1bc Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Wed, 16 Aug 2023 16:08:52 -0700 Subject: [PATCH 09/58] update copy --- .../features/dashboards/leaderboard/LeaderboardControls.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardControls.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardControls.svelte index 29274e6b0a3..0ed3e24ed95 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardControls.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardControls.svelte @@ -118,7 +118,7 @@ tooltipText="Choose dimensions to display" /> -
showing top values by
+
showing
Date: Wed, 16 Aug 2023 17:46:20 -0700 Subject: [PATCH 10/58] add enum variant for DELTA_ABSOLUTE --- .../src/features/dashboards/leaderboard-context-column.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web-common/src/features/dashboards/leaderboard-context-column.ts b/web-common/src/features/dashboards/leaderboard-context-column.ts index b518b7326db..7143d27b09f 100644 --- a/web-common/src/features/dashboards/leaderboard-context-column.ts +++ b/web-common/src/features/dashboards/leaderboard-context-column.ts @@ -6,6 +6,8 @@ export enum LeaderboardContextColumn { PERCENT = "percent", // show percent change of the value compared to the previous time range DELTA_CHANGE = "delta_change", + // show absolute change of the value compared to the previous time range + DELTA_ABSOLUTE = "delta_absolute", // Do not show the context column HIDDEN = "hidden", } From f0011449f06c465533fbc6b40f600de22c7ae3e6 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Wed, 16 Aug 2023 18:32:40 -0700 Subject: [PATCH 11/58] fix arrow flipping on SearchableFilterButton --- .../searchable-filter-menu/SeachableFilterButton.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web-common/src/components/searchable-filter-menu/SeachableFilterButton.svelte b/web-common/src/components/searchable-filter-menu/SeachableFilterButton.svelte index 985cdd7935d..5553c997bed 100644 --- a/web-common/src/components/searchable-filter-menu/SeachableFilterButton.svelte +++ b/web-common/src/components/searchable-filter-menu/SeachableFilterButton.svelte @@ -68,7 +68,7 @@ props as needed. location="bottom" suppress={active} > - {numShownString} {label}
From 5d39aec742d2971b09099cd5d2a63a2e2028c574 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Wed, 16 Aug 2023 22:32:06 -0700 Subject: [PATCH 12/58] change leaderboard context column controls --- .../menu/compositions/SelectMenu.svelte | 46 ++--- .../menu/triggers/SelectButton.svelte | 1 + web-common/src/components/menu/types.ts | 14 ++ .../LeaderboardContextColumnMenu.svelte | 74 +++++++ .../leaderboard/LeaderboardControls.svelte | 10 +- web-local/test/ui/dashboards.spec.ts | 180 +++++++++++++----- 6 files changed, 241 insertions(+), 84 deletions(-) create mode 100644 web-common/src/components/menu/types.ts create mode 100644 web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte diff --git a/web-common/src/components/menu/compositions/SelectMenu.svelte b/web-common/src/components/menu/compositions/SelectMenu.svelte index 648f079be92..e25c709607c 100644 --- a/web-common/src/components/menu/compositions/SelectMenu.svelte +++ b/web-common/src/components/menu/compositions/SelectMenu.svelte @@ -7,22 +7,15 @@ A slot is provided to change the text within the button. --> @@ -47,7 +36,6 @@ A slot is provided to change the text within the button. { - if (!disabled) { - toggleMenu(); - } - }} - {tailwindClasses} - {activeTailwindClasses} + on:click={toggleMenu} + tailwindClasses="overflow-hidden" {active} - {block} - {disabled} - {level} - {label} + label={ariaLabel} > - -
- {selection?.main || ""} -
-
+ {fixedText} {selection?.main}
diff --git a/web-common/src/components/menu/triggers/SelectButton.svelte b/web-common/src/components/menu/triggers/SelectButton.svelte index 34b143d00eb..316257c3f4c 100644 --- a/web-common/src/components/menu/triggers/SelectButton.svelte +++ b/web-common/src/components/menu/triggers/SelectButton.svelte @@ -9,6 +9,7 @@ export let activeTailwindClasses: string = undefined; export let active = false; export let level: undefined | "error" = undefined; + // used as aria-label export let label: undefined | string = undefined; let childRequestedTooltipSuppression; diff --git a/web-common/src/components/menu/types.ts b/web-common/src/components/menu/types.ts new file mode 100644 index 00000000000..9ea200414e4 --- /dev/null +++ b/web-common/src/components/menu/types.ts @@ -0,0 +1,14 @@ +export type SelectMenuItem = { + // the key of that identifies the item in call + key: string | number; + // the main text + main: string; + // the secondary text below the main text + description?: string; + // text to display to the right of "main" + right?: string; + // will this item be used as a divider? + divider?: boolean; + // will this item be disabled? + disabled?: boolean; +}; diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte new file mode 100644 index 00000000000..fd798a44d95 --- /dev/null +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte @@ -0,0 +1,74 @@ + + + diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardControls.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardControls.svelte index 29274e6b0a3..6a3021cf288 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardControls.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardControls.svelte @@ -14,7 +14,7 @@ metricsExplorerStore, } from "../dashboard-stores"; import { useMetaQuery } from "../selectors"; - import LeaderboardContextColumnToggle from "./LeaderboardContextColumnToggle.svelte"; + import LeaderboardContextColumnMenu from "./LeaderboardContextColumnMenu.svelte"; export let metricViewName; @@ -125,13 +125,11 @@ paddingBottom={2} {options} {selection} - tailwindClasses="overflow-hidden" alignment="end" on:select={handleMeasureUpdate} - > - {selection?.main} - - + /> + +
{:else}
{ await page.getByRole("menuitem", { name: "Last 6 Hours" }).click(); }); + // check that the "select a context column" button now reads "with Percent change" + await expect( + page.getByLabel("Select a context column") + // getByRole("button", { name: "with Percent change" }) + ).toContainText("Percent change"); + // This regex matches a line that: // - starts with "Facebook" // - has two white space separated sets of characters (the number and the percent change) @@ -587,23 +612,44 @@ async function runThroughLeaderboardContextColumnFlows(page: Page) { // Check that time comparison context column is visible with correct value now that there is a time comparison await expect(page.getByText(comparisonColumnRegex)).toBeVisible(); - // Check that the "toggle percent change" button is enabled + + // click to open the context column menu + await page.getByLabel("Select a context column").click(); + // Check that the "percent change" menuitem is enabled await expect( - page.getByRole("button", { name: "Toggle percent change" }) + page.getByRole("menuitem", { name: "Percent change" }) ).toBeEnabled(); - // Check that the "toggle percent change" button is pressed + // check that the "percent of total" menuitem is still disabled await expect( - page.getByRole("button", { name: "Toggle percent change" }) - ).toHaveAttribute("aria-pressed", "true"); - - // click the "toggle percent change" button, and check that the percent change is hidden - await page.getByRole("button", { name: "Toggle percent change" }).click(); + page.getByRole("menuitem", { name: "Percent of total" }) + ).toBeDisabled(); + // click to close the context column menu + await page.getByLabel("Select a context column").click(); + + /** + * SUBFLOW: check correct behavior when + * - a time comparison is activated, + * - there is no valid_percent_of_total, + * - and then the context column is turned off + */ + + // turn off the context column + await page.getByLabel("Select a context column").click(); + await page.getByRole("menuitem", { name: "No context column" }).click(); // Check that time comparison context column is hidden await expect(page.getByText(comparisonColumnRegex)).not.toBeVisible(); await expect(page.getByText("Facebook 68")).toBeVisible(); - // click the "toggle percent change" button, and check that the percent change is visible again - await page.getByRole("button", { name: "Toggle percent change" }).click(); + /** + * SUBFLOW: check correct behavior when + * - the context column is turned back on, + * - there is no valid_percent_of_total, + * - and then time comparison is turned off + */ + + // turn on the context column + await page.getByLabel("Select a context column").click(); + await page.getByRole("menuitem", { name: "Percent change" }).click(); await expect(page.getByText(comparisonColumnRegex)).toBeVisible(); // click back to "All time" to clear the time comparison @@ -614,64 +660,112 @@ async function runThroughLeaderboardContextColumnFlows(page: Page) { // Check that time comparison context column is hidden await expect(page.getByText(comparisonColumnRegex)).not.toBeVisible(); await expect(page.getByText("Facebook 19.3k")).toBeVisible(); - // Check that the "toggle percent change" button is disabled + // Check that the "percent change" menuitem is disabled + await page.getByLabel("Select a context column").click(); await expect( - page.getByRole("button", { name: "Toggle percent change" }) + page.getByRole("menuitem", { name: "Percent change" }) ).toBeDisabled(); - // Switch to metric "total bid price" + /** + * SUBFLOW: check correct behavior when + * - switching to a measure with valid_percent_of_total + * - but no time comparison enabled + */ + + // Switch to measure "total bid price" await page.getByRole("button", { name: "Total rows" }).click(); await page.getByRole("menuitem", { name: "Total Bid Price" }).click(); - // Check that the "toggle percent of total" button is enabled for this measure + // open the context column menu + await page.getByLabel("Select a context column").click(); + // Check that the "0ercent of total" menuitem is enabled await expect( - page.getByRole("button", { name: "Toggle percent of total" }) - ).toBeEnabled(); - // Check that the "toggle percent change" button is disabled since there is no time comparison + page.getByRole("menuitem", { name: "Percent of total" }) + ).toBeDisabled(); + // Check that the "percent change" menuitem is disabled await expect( - page.getByRole("button", { name: "Toggle percent change" }) + page.getByRole("menuitem", { name: "Percent change" }) ).toBeDisabled(); + // close the context column menu + await page.getByLabel("Select a context column").click(); // Check that the percent of total is hidden await expect(page.getByText(comparisonColumnRegex)).not.toBeVisible(); - // Click on the "toggle percent of total" button - await page.getByRole("button", { name: "Toggle percent of total" }).click(); + /** + * SUBFLOW: check correct behavior when + * - measure with valid_percent_of_total + * - no time comparison enabled + * - percent of total context column is turned on + */ + + // open the context column menu + await page.getByLabel("Select a context column").click(); + // click on "percent of total" menuitem + await page.getByRole("menuitem", { name: "Percent of total" }).click(); + //close the context column menu + await page.getByLabel("Select a context column").click(); // check that the percent of total is visible await expect(page.getByText("Facebook $57.8k 19%")).toBeVisible(); + /** + * SUBFLOW: check correct behavior when + * - measure with valid_percent_of_total + * - no time comparison enabled + * - percent of total context column is turned on + * - and then time comparison is enabled + */ + // Add a time comparison await interactWithTimeRangeMenu(page, async () => { await page.getByRole("menuitem", { name: "Last 6 Hours" }).click(); }); + // check that the percent of total remains visible, + // with updated value for the time comparison + await expect(page.getByText("Facebook $229.26 28%")).toBeVisible(); - // check that the percent of total button remains pressed after adding a time comparison - await expect( - page.getByRole("button", { name: "Toggle percent of total" }) - ).toHaveAttribute("aria-pressed", "true"); - - // Click on "toggle percent change" button - await page.getByRole("button", { name: "Toggle percent change" }).click(); + /** + * SUBFLOW: check correct behavior when + * - switch context column to percent change + * - and then switch back to percent of total + */ + + // open the context column menu + await page.getByLabel("Select a context column").click(); + // click on "percent change" menuitem + await page.getByRole("menuitem", { name: "Percent change" }).click(); + //close the context column menu + await page.getByLabel("Select a context column").click(); // check that the percent change is visible+correct await expect(page.getByText("Facebook $229.26 3%")).toBeVisible(); - // click on "toggle percent of total" button - await page.getByRole("button", { name: "Toggle percent of total" }).click(); + + // open the context column menu + await page.getByLabel("Select a context column").click(); + // click on "percent of total" menuitem + await page.getByRole("menuitem", { name: "Percent of total" }).click(); + //close the context column menu + await page.getByLabel("Select a context column").click(); // check that the percent of total is visible+correct await expect(page.getByText("Facebook $229.26 28%")).toBeVisible(); - // Go back to measure without valid_percent_of_total - // while percent of total is still pressed, and make - // sure that it is unpressed and disabled. + /** + * Go back to measure without valid_percent_of_total + * while percent of total context column is enabled. + * Make sure the context column is hidden, + * and the menuitems have the correct enabled/disabled state. + */ + + // Switch to measure "total rows" (no valid_percent_of_total) await page.getByRole("button", { name: "Total Bid Price" }).click(); await page.getByRole("menuitem", { name: "Total rows" }).click(); - await expect( - page.getByRole("button", { name: "Toggle percent of total" }) - ).toHaveAttribute("aria-pressed", "false"); - await expect( - page.getByRole("button", { name: "Toggle percent of total" }) - ).toBeDisabled(); // check that the context column is hidden await expect(page.getByText(comparisonColumnRegex)).not.toBeVisible(); + // open the context column menu + await page.getByLabel("Select a context column").click(); + // check that the "percent of total" menuitem is disabled + await expect( + page.getByRole("menuitem", { name: "Percent of total" }) + ).toBeDisabled(); } async function runThroughEmptyMetricsFlows(page) { From 5c32f1f18ab330eec678b76cdef26708a176b2d0 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Wed, 16 Aug 2023 23:08:30 -0700 Subject: [PATCH 13/58] slightly tighten spacing on SelectButton --- web-common/src/components/menu/triggers/SelectButton.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web-common/src/components/menu/triggers/SelectButton.svelte b/web-common/src/components/menu/triggers/SelectButton.svelte index 316257c3f4c..42f94c2f4ba 100644 --- a/web-common/src/components/menu/triggers/SelectButton.svelte +++ b/web-common/src/components/menu/triggers/SelectButton.svelte @@ -45,7 +45,7 @@ aria-label={label} class=" {block ? 'flex w-full h-full px-2' : 'inline-flex w-max rounded px-1'} - items-center gap-x-2 justify-between + items-center gap-x-1 justify-between {classes[level]} {tailwindClasses} {active && !disabled ? activeTailwindClasses : ''} From 1cecc0e7833e28cc62344aff39263ec8c2160389 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Wed, 16 Aug 2023 23:44:39 -0700 Subject: [PATCH 14/58] remove deprecated component --- .../LeaderboardContextColumnToggle.svelte | 102 ------------------ 1 file changed, 102 deletions(-) delete mode 100644 web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnToggle.svelte diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnToggle.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnToggle.svelte deleted file mode 100644 index beeff67089e..00000000000 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnToggle.svelte +++ /dev/null @@ -1,102 +0,0 @@ - - - - - % - - - % - - From dd432728d3587ad7f43640ffd830138aba0593d8 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 17 Aug 2023 00:51:57 -0700 Subject: [PATCH 15/58] add absolute change to menu and leaderboard header --- .../src/features/dashboards/dashboard-stores.ts | 10 ++++++++++ .../dashboards/leaderboard/Leaderboard.svelte | 10 ++++++---- .../LeaderboardContextColumnMenu.svelte | 10 ++++++++++ .../leaderboard/LeaderboardHeader.svelte | 16 ++++++---------- .../dashboards/leaderboard/leaderboard-utils.ts | 4 ++++ 5 files changed, 36 insertions(+), 14 deletions(-) diff --git a/web-common/src/features/dashboards/dashboard-stores.ts b/web-common/src/features/dashboards/dashboard-stores.ts index 0f0206e5a1a..412396384d8 100644 --- a/web-common/src/features/dashboards/dashboard-stores.ts +++ b/web-common/src/features/dashboards/dashboard-stores.ts @@ -405,6 +405,16 @@ const metricViewReducers = { }); }, + displayDeltaAbsolute(name: string) { + updateMetricsExplorerByName(name, (metricsExplorer) => { + // NOTE: only show delta absolute if comparison is enabled + if (metricsExplorer.showComparison === false) return; + + metricsExplorer.leaderboardContextColumn = + LeaderboardContextColumn.DELTA_ABSOLUTE; + }); + }, + displayPercentOfTotal(name: string) { updateMetricsExplorerByName(name, (metricsExplorer) => { metricsExplorer.leaderboardContextColumn = diff --git a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte index da6d8c134b3..cf8946dc7c3 100644 --- a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte +++ b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte @@ -166,10 +166,13 @@ return b.value - a.value; }); + $: contextColumn = $dashboardStore?.leaderboardContextColumn; // Compose the comparison /toplist query $: showTimeComparison = - $dashboardStore?.leaderboardContextColumn === - LeaderboardContextColumn.DELTA_CHANGE && $dashboardStore?.showComparison; + (contextColumn === LeaderboardContextColumn.DELTA_CHANGE || + contextColumn === LeaderboardContextColumn.DELTA_ABSOLUTE) && + $dashboardStore?.showComparison; + $: showPercentOfTotal = $dashboardStore?.leaderboardContextColumn === LeaderboardContextColumn.PERCENT; @@ -251,8 +254,7 @@ on:mouseleave={() => (hovered = false)} >
@@ -93,9 +87,11 @@
- {#if iconShown === "delta"} + {#if contextColumn === LeaderboardContextColumn.DELTA_CHANGE} % - {:else if iconShown === "pie"} + {:else if contextColumn === LeaderboardContextColumn.DELTA_ABSOLUTE} + + {:else if contextColumn === LeaderboardContextColumn.PERCENT} % {/if}
diff --git a/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts b/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts index 249c175a9af..b585ffa3048 100644 --- a/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts +++ b/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts @@ -13,7 +13,11 @@ export function getFormatterValueForPercDiff(numerator, denominator) { export type LeaderboardItemData = { label: string | number; + // main value to be shown in the leaderboard value: number; + // the comparison value, which may be either the previous value + // (used to calculate the absolute or percentage change) or + // the measure total (used to calculate the percentage of total) comparisonValue: number; // selection is not enough to determine if the item is included // or excluded; for that we need to know the leaderboard's From 88bffbfe769a2ab7c368f106aebaf89469d8e7b2 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 17 Aug 2023 04:12:33 -0700 Subject: [PATCH 16/58] move context column value to own component, render with color and "no data" --- .../leaderboard/ContextColumnValue.svelte | 30 ++++++++++++++ .../leaderboard/LeaderboardListItem.svelte | 33 ++++++---------- .../leaderboard/leaderboard-utils.ts | 39 ++++++++++++++++++- 3 files changed, 80 insertions(+), 22 deletions(-) create mode 100644 web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte diff --git a/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte b/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte new file mode 100644 index 00000000000..7eeab2dffb4 --- /dev/null +++ b/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte @@ -0,0 +1,30 @@ + + +{#if showContext === LeaderboardContextColumn.DELTA_CHANGE || showContext === LeaderboardContextColumn.PERCENT} +
+ +
+{:else if showContext === LeaderboardContextColumn.DELTA_ABSOLUTE} +
+ {#if noData} + no data + {:else} + + {/if} +
+{/if} diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardListItem.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardListItem.svelte index b746fa090cb..5f7f699cd6c 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardListItem.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardListItem.svelte @@ -17,14 +17,14 @@ import LeaderboardTooltipContent from "./LeaderboardTooltipContent.svelte"; - import PercentageChange from "../../../components/data-types/PercentageChange.svelte"; import LeaderboardItemFilterIcon from "./LeaderboardItemFilterIcon.svelte"; import { humanizeDataType } from "../humanize-numbers"; import LongBarZigZag from "./LongBarZigZag.svelte"; import { LeaderboardItemData, - getFormatterValueForPercDiff, + formatContextColumnValue, } from "./leaderboard-utils"; + import ContextColumnValue from "./ContextColumnValue.svelte"; export let itemData: LeaderboardItemData; $: label = itemData.label; @@ -51,17 +51,12 @@ $: formattedValue = humanizeDataType(measureValue, formatPreset); - $: percentChangeFormatted = - showContext === LeaderboardContextColumn.DELTA_CHANGE - ? getFormatterValueForPercDiff( - measureValue && comparisonValue - ? measureValue - comparisonValue - : null, - comparisonValue - ) - : showContext === LeaderboardContextColumn.PERCENT - ? getFormatterValueForPercDiff(measureValue, unfilteredTotal) - : undefined; + $: contextColumnFormattedValue = formatContextColumnValue( + itemData, + unfilteredTotal, + showContext, + formatPreset + ); $: previousValueString = comparisonValue !== undefined && comparisonValue !== null @@ -176,14 +171,10 @@ value={formattedValue || measureValue} />
- {#if percentChangeFormatted !== undefined} -
- -
- {/if} + diff --git a/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts b/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts index b585ffa3048..2f908f5986a 100644 --- a/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts +++ b/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts @@ -1,5 +1,10 @@ import { PERC_DIFF } from "../../../components/data-types/type-utils"; -import { formatMeasurePercentageDifference } from "../humanize-numbers"; +import { + FormatPreset, + formatMeasurePercentageDifference, + humanizeDataType, +} from "../humanize-numbers"; +import { LeaderboardContextColumn } from "../leaderboard-context-column"; export function getFormatterValueForPercDiff(numerator, denominator) { if (denominator === 0) return PERC_DIFF.PREV_VALUE_ZERO; @@ -42,3 +47,35 @@ export function prepareLeaderboardItemData( }; }); } + +/** + * Returns the formatted value for the context column + * given the + * accounting for the context column type. + */ +export function formatContextColumnValue( + itemData: LeaderboardItemData, + unfilteredTotal: number, + contextType: LeaderboardContextColumn, + formatPreset: FormatPreset +): string { + const { value, comparisonValue } = itemData; + let formattedValue = ""; + + if (contextType === LeaderboardContextColumn.DELTA_CHANGE) { + formattedValue = getFormatterValueForPercDiff( + value && comparisonValue ? value - comparisonValue : null, + comparisonValue + ); + } else if (contextType === LeaderboardContextColumn.PERCENT) { + formattedValue = getFormatterValueForPercDiff(value, unfilteredTotal); + } else if (contextType === LeaderboardContextColumn.DELTA_ABSOLUTE) { + formattedValue = humanizeDataType( + value && comparisonValue ? value - comparisonValue : null, + formatPreset + ); + } else { + formattedValue = ""; + } + return formattedValue; +} From db76abf61d38c3fa3e54a5ba178eb478b38b69e0 Mon Sep 17 00:00:00 2001 From: Aditya Hegde Date: Tue, 22 Aug 2023 11:58:12 +0530 Subject: [PATCH 17/58] Adding to url state --- proto/rill/ui/v1/dashboard.proto | 52 ++++++- .../features/dashboards/dashboard-stores.ts | 69 +++------ .../dimension-table/DimensionDisplay.svelte | 6 +- .../dashboards/leaderboard/Leaderboard.svelte | 6 +- .../dashboards/proto-state/fromProto.ts | 19 ++- .../dashboards/proto-state/toProto.ts | 17 ++- .../src/proto/gen/rill/ui/v1/dashboard_pb.ts | 134 ++++++++++++++++-- 7 files changed, 219 insertions(+), 84 deletions(-) diff --git a/proto/rill/ui/v1/dashboard.proto b/proto/rill/ui/v1/dashboard.proto index 89e8e011a44..47f0088b862 100644 --- a/proto/rill/ui/v1/dashboard.proto +++ b/proto/rill/ui/v1/dashboard.proto @@ -7,11 +7,48 @@ import "rill/runtime/v1/queries.proto"; // DashboardState represents the dashboard as seen by the user message DashboardState { - enum DashboardLeaderboardContextColumn { - DASHBOARD_LEADERBOARD_CONTEXT_COLUMN_UNSPECIFIED=0; - DASHBOARD_LEADERBOARD_CONTEXT_COLUMN_PERCENT = 1; - DASHBOARD_LEADERBOARD_CONTEXT_COLUMN_DELTA_CHANGE = 2; - DASHBOARD_LEADERBOARD_CONTEXT_COLUMN_HIDDEN = 3; + enum LeaderboardContextColumn { + LEADERBOARD_CONTEXT_COLUMN_UNSPECIFIED=0; + LEADERBOARD_CONTEXT_COLUMN_PERCENT = 1; + LEADERBOARD_CONTEXT_COLUMN_DELTA_CHANGE = 2; + LEADERBOARD_CONTEXT_COLUMN_HIDDEN = 3; + } + + enum LeaderboardSortDirection { + LEADERBOARD_SORT_DIRECTION_UNSPECIFIED = 0; + LEADERBOARD_SORT_DIRECTION_ASCENDING = 1; + LEADERBOARD_SORT_DIRECTION_DESCENDING = 2; + } + + /** + * SortType is used to determine how to sort the leaderboard + * and dimension detail table, as well as where to place the + * sort arrow. + * + * By default, the leaderboards+table will be sorted by VALUE, + * using the value of the currently selected dashboard measure. + * + * If DELTA_CHANGE or DELTA_PCT is selected, the + * leaderboards+table will be sorted by the absolute or percentage + * delta change of the currently selected dashboard measure. + * + * If PERCENT is selected, the table will be sorted by the value + * of the currently selected dashboard measure, which will return + * the same ordering as the percent-of-total sort for measures + * with valid percent-of-total. However, the sort arrow will be + * placed next to the percent-of-total icon. + * + * As of 2023-08, DIMENSION is not implemented, but at that time + * the plan was to only apply DIMENSTION sort to the dimension + * detail table, and not the leaderboards. + */ + enum LeaderboardSortType { + LEADERBOARD_SORT_TYPE_UNSPECIFIED = 0; + LEADERBOARD_SORT_TYPE_VALUE = 1; + LEADERBOARD_SORT_TYPE_DIMENSION = 2; + LEADERBOARD_SORT_TYPE_DELTA_CHANGE = 3; + LEADERBOARD_SORT_TYPE_DELTA_PCT = 4; + LEADERBOARD_SORT_TYPE_PERCENT = 5; } // Selected time range @@ -36,13 +73,16 @@ message DashboardState { repeated string visible_dimensions = 10; optional bool all_dimensions_visible = 11; - optional DashboardLeaderboardContextColumn leaderboard_context_column = 12; + optional LeaderboardContextColumn leaderboard_context_column = 12; // Selected timezone for the dashboard optional string selected_timezone = 13; // Scrub time range optional DashboardTimeRange scrub_range = 14; + + optional LeaderboardSortDirection leaderboard_sort_direction = 15; + optional LeaderboardSortType leaderboard_sort_type = 16; } message DashboardTimeRange { diff --git a/web-common/src/features/dashboards/dashboard-stores.ts b/web-common/src/features/dashboards/dashboard-stores.ts index 57bd9f5e34e..d812e06e6f8 100644 --- a/web-common/src/features/dashboards/dashboard-stores.ts +++ b/web-common/src/features/dashboards/dashboard-stores.ts @@ -10,11 +10,15 @@ import { import { getComparionRangeForScrub } from "@rilldata/web-common/lib/time/comparisons"; import { getDefaultTimeGrain } from "@rilldata/web-common/lib/time/grains"; import { convertTimeRangePreset } from "@rilldata/web-common/lib/time/ranges"; +import type { DashboardTimeControls } from "@rilldata/web-common/lib/time/types"; import { ScrubRange, TimeRangePreset, } from "@rilldata/web-common/lib/time/types"; -import type { DashboardTimeControls } from "@rilldata/web-common/lib/time/types"; +import { + DashboardState_LeaderboardSortDirection, + DashboardState_LeaderboardSortType, +} from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; import type { V1ColumnTimeRangeResponse, V1MetricsView, @@ -32,43 +36,6 @@ export interface LeaderboardValues { dimensionName: string; } -export enum SortDirection { - ASC = "ASCENDING", - DESC = "DESCENDING", -} - -/** - * SortType is used to determine how to sort the leaderboard - * and dimension detail table, as well as where to place the - * sort arrow. - * - * By default, the leaderboards+table will be sorted by VALUE, - * using the value of the currently selected dashboard measure. - * - * If DELTA_CHANGE or DELTA_PCT is selected, the - * leaderboards+table will be sorted by the absolute or percentage - * delta change of the currently selected dashboard measure. - * - * If PERCENT is selected, the table will be sorted by the value - * of the currently selected dashboard measure, which will return - * the same ordering as the percent-of-total sort for measures - * with valid percent-of-total. However, the sort arrow will be - * placed next to the percent-of-total icon. - * - * As of 2023-08, DIMENSION is not implemented, but at that time - * the plan was to only apply DIMENSTION sort to the dimension - * detail table, and not the leaderboards. - */ -export enum SortType { - VALUE = "VALUE", - DIMENSION = "DIMENSION", - DELTA_CHANGE = "DELTA_CHANGE", - DELTA_PCT = "DELTA_PCT", - // Note that PERCENT will return the same ordering as VALUE, - // but the sort arrow will be placed differently - PERCENT = "PERCENT", -} - export type ActiveValues = Record>; export interface MetricsExplorerEntity { @@ -107,10 +74,10 @@ export interface MetricsExplorerEntity { // This is the sort type that will be used for the leaderboard // and dimension detail table. See SortType for more details. - dashboardSortType: SortType; + dashboardSortType: DashboardState_LeaderboardSortType; // This is the sort direction that will be used for the leaderboard // and dimension detail table. - sortDirection: SortDirection; + sortDirection: DashboardState_LeaderboardSortDirection; filters: V1MetricsViewFilter; // stores whether a dimension is in include/exclude filter mode @@ -325,8 +292,8 @@ const metricViewReducers = { }, dimensionFilterExcludeMode: new Map(), leaderboardContextColumn: LeaderboardContextColumn.HIDDEN, - dashboardSortType: SortType.VALUE, - sortDirection: SortDirection.DESC, + dashboardSortType: DashboardState_LeaderboardSortType.VALUE, + sortDirection: DashboardState_LeaderboardSortDirection.DESCENDING, ...timeSelections, showComparison: false, @@ -373,26 +340,32 @@ const metricViewReducers = { setSortDescending(name: string) { updateMetricsExplorerByName(name, (metricsExplorer) => { - metricsExplorer.sortDirection = SortDirection.DESC; + metricsExplorer.sortDirection = + DashboardState_LeaderboardSortDirection.DESCENDING; }); }, setSortAscending(name: string) { updateMetricsExplorerByName(name, (metricsExplorer) => { - metricsExplorer.sortDirection = SortDirection.ASC; + metricsExplorer.sortDirection = + DashboardState_LeaderboardSortDirection.ASCENDING; }); }, toggleSortDirection(name: string) { updateMetricsExplorerByName(name, (metricsExplorer) => { metricsExplorer.sortDirection = - metricsExplorer.sortDirection === SortDirection.ASC - ? SortDirection.DESC - : SortDirection.ASC; + metricsExplorer.sortDirection === + DashboardState_LeaderboardSortDirection.ASCENDING + ? DashboardState_LeaderboardSortDirection.DESCENDING + : DashboardState_LeaderboardSortDirection.ASCENDING; }); }, - setSortDirection(name: string, direction: SortDirection) { + setSortDirection( + name: string, + direction: DashboardState_LeaderboardSortDirection + ) { updateMetricsExplorerByName(name, (metricsExplorer) => { metricsExplorer.sortDirection = direction; }); diff --git a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte index 82018782b52..1da348c4406 100644 --- a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte +++ b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte @@ -16,6 +16,7 @@ import { getComparisonRange } from "@rilldata/web-common/lib/time/comparisons"; import { DEFAULT_TIME_RANGES } from "@rilldata/web-common/lib/time/config"; import type { TimeComparisonOption } from "@rilldata/web-common/lib/time/types"; + import { DashboardState_LeaderboardSortDirection } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; import { createQueryServiceMetricsViewToplist, createQueryServiceMetricsViewTotals, @@ -27,7 +28,6 @@ import { useQueryClient } from "@tanstack/svelte-query"; import { runtime } from "../../../runtime-client/runtime-store"; import { - SortDirection, metricsExplorerStore, useDashboardStore, useFetchTimeRange, @@ -98,7 +98,9 @@ $dashboardStore?.visibleMeasureKeys.has(m.name) ); - $: sortAscending = $dashboardStore.sortDirection === SortDirection.ASC; + $: sortAscending = + $dashboardStore.sortDirection === + DashboardState_LeaderboardSortDirection.ASCENDING; $: metricTimeSeries = useModelHasTimeSeries(instanceId, metricViewName); $: hasTimeSeries = $metricTimeSeries.data; diff --git a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte index 0c8b0813451..2163d8ebbbd 100644 --- a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte +++ b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte @@ -15,6 +15,7 @@ useMetaMeasure, useModelHasTimeSeries, } from "@rilldata/web-common/features/dashboards/selectors"; + import { DashboardState_LeaderboardSortDirection } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; import { createQueryServiceMetricsViewToplist, MetricsViewDimension, @@ -23,7 +24,6 @@ import { useQueryClient } from "@tanstack/svelte-query"; import { runtime } from "../../../runtime-client/runtime-store"; import { - SortDirection, metricsExplorerStore, useComparisonRange, useDashboardStore, @@ -111,7 +111,9 @@ $: timeStart = $fetchTimeStore?.start?.toISOString(); $: timeEnd = $fetchTimeStore?.end?.toISOString(); - $: sortAscending = $dashboardStore.sortDirection === SortDirection.ASC; + $: sortAscending = + $dashboardStore.sortDirection === + DashboardState_LeaderboardSortDirection.ASCENDING; $: topListQuery = createQueryServiceMetricsViewToplist( $runtime.instanceId, metricViewName, diff --git a/web-common/src/features/dashboards/proto-state/fromProto.ts b/web-common/src/features/dashboards/proto-state/fromProto.ts index 60f3d245ffd..36d35bbb532 100644 --- a/web-common/src/features/dashboards/proto-state/fromProto.ts +++ b/web-common/src/features/dashboards/proto-state/fromProto.ts @@ -9,7 +9,7 @@ import { TimeGrain } from "@rilldata/web-common/proto/gen/rill/runtime/v1/time_g import type { MetricsViewFilter_Cond } from "@rilldata/web-common/proto/gen/rill/runtime/v1/queries_pb"; import { DashboardState, - DashboardState_DashboardLeaderboardContextColumn, + DashboardState_LeaderboardContextColumn, DashboardTimeRange, } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; import { @@ -19,16 +19,16 @@ import { // TODO: make a follow up PR to use the one from the proto directly const LeaderboardContextColumnReverseMap: Record< - DashboardState_DashboardLeaderboardContextColumn, + DashboardState_LeaderboardContextColumn, LeaderboardContextColumn > = { - [DashboardState_DashboardLeaderboardContextColumn.UNSPECIFIED]: + [DashboardState_LeaderboardContextColumn.UNSPECIFIED]: LeaderboardContextColumn.HIDDEN, - [DashboardState_DashboardLeaderboardContextColumn.PERCENT]: + [DashboardState_LeaderboardContextColumn.PERCENT]: LeaderboardContextColumn.PERCENT, - [DashboardState_DashboardLeaderboardContextColumn.DELTA_CHANGE]: + [DashboardState_LeaderboardContextColumn.DELTA_CHANGE]: LeaderboardContextColumn.DELTA_CHANGE, - [DashboardState_DashboardLeaderboardContextColumn.HIDDEN]: + [DashboardState_LeaderboardContextColumn.HIDDEN]: LeaderboardContextColumn.HIDDEN, }; @@ -117,6 +117,13 @@ export function getDashboardStateFromProto( LeaderboardContextColumnReverseMap[dashboard.leaderboardContextColumn]; } + if (dashboard.leaderboardSortDirection) { + entity.sortDirection = dashboard.leaderboardSortDirection; + } + if (dashboard.leaderboardSortType) { + entity.dashboardSortType = dashboard.leaderboardSortType; + } + return entity; } diff --git a/web-common/src/features/dashboards/proto-state/toProto.ts b/web-common/src/features/dashboards/proto-state/toProto.ts index bafddbc83ff..9ecb7e4f703 100644 --- a/web-common/src/features/dashboards/proto-state/toProto.ts +++ b/web-common/src/features/dashboards/proto-state/toProto.ts @@ -21,7 +21,7 @@ import { } from "@rilldata/web-common/proto/gen/rill/runtime/v1/time_grain_pb"; import { DashboardState, - DashboardState_DashboardLeaderboardContextColumn, + DashboardState_LeaderboardContextColumn, DashboardTimeRange, } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; import type { @@ -33,14 +33,14 @@ import { V1TimeGrain } from "@rilldata/web-common/runtime-client"; // TODO: make a follow up PR to use the one from the proto directly const LeaderboardContextColumnMap: Record< LeaderboardContextColumn, - DashboardState_DashboardLeaderboardContextColumn + DashboardState_LeaderboardContextColumn > = { [LeaderboardContextColumn.PERCENT]: - DashboardState_DashboardLeaderboardContextColumn.PERCENT, + DashboardState_LeaderboardContextColumn.PERCENT, [LeaderboardContextColumn.DELTA_CHANGE]: - DashboardState_DashboardLeaderboardContextColumn.DELTA_CHANGE, + DashboardState_LeaderboardContextColumn.DELTA_CHANGE, [LeaderboardContextColumn.HIDDEN]: - DashboardState_DashboardLeaderboardContextColumn.HIDDEN, + DashboardState_LeaderboardContextColumn.HIDDEN, }; export function getProtoFromDashboardState( @@ -94,6 +94,13 @@ export function getProtoFromDashboardState( LeaderboardContextColumnMap[metrics.leaderboardContextColumn]; } + if (metrics.sortDirection) { + state.leaderboardSortDirection = metrics.sortDirection; + } + if (metrics.dashboardSortType) { + state.leaderboardSortType = metrics.dashboardSortType; + } + const message = new DashboardState(state); return protoToBase64(message.toBinary()); } diff --git a/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts b/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts index 6e09f4545ba..963ec2cb7b0 100644 --- a/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts +++ b/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts @@ -82,9 +82,9 @@ export class DashboardState extends Message { allDimensionsVisible?: boolean; /** - * @generated from field: optional rill.ui.v1.DashboardState.DashboardLeaderboardContextColumn leaderboard_context_column = 12; + * @generated from field: optional rill.ui.v1.DashboardState.LeaderboardContextColumn leaderboard_context_column = 12; */ - leaderboardContextColumn?: DashboardState_DashboardLeaderboardContextColumn; + leaderboardContextColumn?: DashboardState_LeaderboardContextColumn; /** * Selected timezone for the dashboard @@ -100,6 +100,16 @@ export class DashboardState extends Message { */ scrubRange?: DashboardTimeRange; + /** + * @generated from field: optional rill.ui.v1.DashboardState.LeaderboardSortDirection leaderboard_sort_direction = 15; + */ + leaderboardSortDirection?: DashboardState_LeaderboardSortDirection; + + /** + * @generated from field: optional rill.ui.v1.DashboardState.LeaderboardSortType leaderboard_sort_type = 16; + */ + leaderboardSortType?: DashboardState_LeaderboardSortType; + constructor(data?: PartialMessage) { super(); proto3.util.initPartial(data, this); @@ -119,9 +129,11 @@ export class DashboardState extends Message { { no: 9, name: "all_measures_visible", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, { no: 10, name: "visible_dimensions", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, { no: 11, name: "all_dimensions_visible", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, - { no: 12, name: "leaderboard_context_column", kind: "enum", T: proto3.getEnumType(DashboardState_DashboardLeaderboardContextColumn), opt: true }, + { no: 12, name: "leaderboard_context_column", kind: "enum", T: proto3.getEnumType(DashboardState_LeaderboardContextColumn), opt: true }, { no: 13, name: "selected_timezone", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, { no: 14, name: "scrub_range", kind: "message", T: DashboardTimeRange, opt: true }, + { no: 15, name: "leaderboard_sort_direction", kind: "enum", T: proto3.getEnumType(DashboardState_LeaderboardSortDirection), opt: true }, + { no: 16, name: "leaderboard_sort_type", kind: "enum", T: proto3.getEnumType(DashboardState_LeaderboardSortType), opt: true }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): DashboardState { @@ -142,35 +154,127 @@ export class DashboardState extends Message { } /** - * @generated from enum rill.ui.v1.DashboardState.DashboardLeaderboardContextColumn + * @generated from enum rill.ui.v1.DashboardState.LeaderboardContextColumn */ -export enum DashboardState_DashboardLeaderboardContextColumn { +export enum DashboardState_LeaderboardContextColumn { /** - * @generated from enum value: DASHBOARD_LEADERBOARD_CONTEXT_COLUMN_UNSPECIFIED = 0; + * @generated from enum value: LEADERBOARD_CONTEXT_COLUMN_UNSPECIFIED = 0; */ UNSPECIFIED = 0, /** - * @generated from enum value: DASHBOARD_LEADERBOARD_CONTEXT_COLUMN_PERCENT = 1; + * @generated from enum value: LEADERBOARD_CONTEXT_COLUMN_PERCENT = 1; */ PERCENT = 1, /** - * @generated from enum value: DASHBOARD_LEADERBOARD_CONTEXT_COLUMN_DELTA_CHANGE = 2; + * @generated from enum value: LEADERBOARD_CONTEXT_COLUMN_DELTA_CHANGE = 2; */ DELTA_CHANGE = 2, /** - * @generated from enum value: DASHBOARD_LEADERBOARD_CONTEXT_COLUMN_HIDDEN = 3; + * @generated from enum value: LEADERBOARD_CONTEXT_COLUMN_HIDDEN = 3; */ HIDDEN = 3, } -// Retrieve enum metadata with: proto3.getEnumType(DashboardState_DashboardLeaderboardContextColumn) -proto3.util.setEnumType(DashboardState_DashboardLeaderboardContextColumn, "rill.ui.v1.DashboardState.DashboardLeaderboardContextColumn", [ - { no: 0, name: "DASHBOARD_LEADERBOARD_CONTEXT_COLUMN_UNSPECIFIED" }, - { no: 1, name: "DASHBOARD_LEADERBOARD_CONTEXT_COLUMN_PERCENT" }, - { no: 2, name: "DASHBOARD_LEADERBOARD_CONTEXT_COLUMN_DELTA_CHANGE" }, - { no: 3, name: "DASHBOARD_LEADERBOARD_CONTEXT_COLUMN_HIDDEN" }, +// Retrieve enum metadata with: proto3.getEnumType(DashboardState_LeaderboardContextColumn) +proto3.util.setEnumType(DashboardState_LeaderboardContextColumn, "rill.ui.v1.DashboardState.LeaderboardContextColumn", [ + { no: 0, name: "LEADERBOARD_CONTEXT_COLUMN_UNSPECIFIED" }, + { no: 1, name: "LEADERBOARD_CONTEXT_COLUMN_PERCENT" }, + { no: 2, name: "LEADERBOARD_CONTEXT_COLUMN_DELTA_CHANGE" }, + { no: 3, name: "LEADERBOARD_CONTEXT_COLUMN_HIDDEN" }, +]); + +/** + * @generated from enum rill.ui.v1.DashboardState.LeaderboardSortDirection + */ +export enum DashboardState_LeaderboardSortDirection { + /** + * @generated from enum value: LEADERBOARD_SORT_DIRECTION_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: LEADERBOARD_SORT_DIRECTION_ASCENDING = 1; + */ + ASCENDING = 1, + + /** + * @generated from enum value: LEADERBOARD_SORT_DIRECTION_DESCENDING = 2; + */ + DESCENDING = 2, +} +// Retrieve enum metadata with: proto3.getEnumType(DashboardState_LeaderboardSortDirection) +proto3.util.setEnumType(DashboardState_LeaderboardSortDirection, "rill.ui.v1.DashboardState.LeaderboardSortDirection", [ + { no: 0, name: "LEADERBOARD_SORT_DIRECTION_UNSPECIFIED" }, + { no: 1, name: "LEADERBOARD_SORT_DIRECTION_ASCENDING" }, + { no: 2, name: "LEADERBOARD_SORT_DIRECTION_DESCENDING" }, +]); + +/** + * + * SortType is used to determine how to sort the leaderboard + * and dimension detail table, as well as where to place the + * sort arrow. + * + * By default, the leaderboards+table will be sorted by VALUE, + * using the value of the currently selected dashboard measure. + * + * If DELTA_CHANGE or DELTA_PCT is selected, the + * leaderboards+table will be sorted by the absolute or percentage + * delta change of the currently selected dashboard measure. + * + * If PERCENT is selected, the table will be sorted by the value + * of the currently selected dashboard measure, which will return + * the same ordering as the percent-of-total sort for measures + * with valid percent-of-total. However, the sort arrow will be + * placed next to the percent-of-total icon. + * + * As of 2023-08, DIMENSION is not implemented, but at that time + * the plan was to only apply DIMENSTION sort to the dimension + * detail table, and not the leaderboards. + * + * @generated from enum rill.ui.v1.DashboardState.LeaderboardSortType + */ +export enum DashboardState_LeaderboardSortType { + /** + * @generated from enum value: LEADERBOARD_SORT_TYPE_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: LEADERBOARD_SORT_TYPE_VALUE = 1; + */ + VALUE = 1, + + /** + * @generated from enum value: LEADERBOARD_SORT_TYPE_DIMENSION = 2; + */ + DIMENSION = 2, + + /** + * @generated from enum value: LEADERBOARD_SORT_TYPE_DELTA_CHANGE = 3; + */ + DELTA_CHANGE = 3, + + /** + * @generated from enum value: LEADERBOARD_SORT_TYPE_DELTA_PCT = 4; + */ + DELTA_PCT = 4, + + /** + * @generated from enum value: LEADERBOARD_SORT_TYPE_PERCENT = 5; + */ + PERCENT = 5, +} +// Retrieve enum metadata with: proto3.getEnumType(DashboardState_LeaderboardSortType) +proto3.util.setEnumType(DashboardState_LeaderboardSortType, "rill.ui.v1.DashboardState.LeaderboardSortType", [ + { no: 0, name: "LEADERBOARD_SORT_TYPE_UNSPECIFIED" }, + { no: 1, name: "LEADERBOARD_SORT_TYPE_VALUE" }, + { no: 2, name: "LEADERBOARD_SORT_TYPE_DIMENSION" }, + { no: 3, name: "LEADERBOARD_SORT_TYPE_DELTA_CHANGE" }, + { no: 4, name: "LEADERBOARD_SORT_TYPE_DELTA_PCT" }, + { no: 5, name: "LEADERBOARD_SORT_TYPE_PERCENT" }, ]); /** From a420c865d4424f29e2554f85dfffe26455223c73 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 17 Aug 2023 05:39:53 -0700 Subject: [PATCH 18/58] change enums back to original names --- .../features/dashboards/dashboard-stores.ts | 33 +++++++++---------- .../dimension-table/DimensionDisplay.svelte | 6 ++-- .../dashboards/leaderboard/Leaderboard.svelte | 6 ++-- 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/web-common/src/features/dashboards/dashboard-stores.ts b/web-common/src/features/dashboards/dashboard-stores.ts index d812e06e6f8..05811c25e50 100644 --- a/web-common/src/features/dashboards/dashboard-stores.ts +++ b/web-common/src/features/dashboards/dashboard-stores.ts @@ -16,8 +16,8 @@ import { TimeRangePreset, } from "@rilldata/web-common/lib/time/types"; import { - DashboardState_LeaderboardSortDirection, - DashboardState_LeaderboardSortType, + DashboardState_LeaderboardSortDirection as SortDirection, + DashboardState_LeaderboardSortType as SortType, } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; import type { V1ColumnTimeRangeResponse, @@ -26,6 +26,9 @@ import type { } from "@rilldata/web-common/runtime-client"; import { derived, get, Readable, Writable, writable } from "svelte/store"; +export { SortType as SortType }; +export { SortDirection as SortDirection }; + export interface LeaderboardValue { value: number; label: string; @@ -74,10 +77,10 @@ export interface MetricsExplorerEntity { // This is the sort type that will be used for the leaderboard // and dimension detail table. See SortType for more details. - dashboardSortType: DashboardState_LeaderboardSortType; + dashboardSortType: SortType; // This is the sort direction that will be used for the leaderboard // and dimension detail table. - sortDirection: DashboardState_LeaderboardSortDirection; + sortDirection: SortDirection; filters: V1MetricsViewFilter; // stores whether a dimension is in include/exclude filter mode @@ -292,8 +295,8 @@ const metricViewReducers = { }, dimensionFilterExcludeMode: new Map(), leaderboardContextColumn: LeaderboardContextColumn.HIDDEN, - dashboardSortType: DashboardState_LeaderboardSortType.VALUE, - sortDirection: DashboardState_LeaderboardSortDirection.DESCENDING, + dashboardSortType: SortType.VALUE, + sortDirection: SortDirection.DESCENDING, ...timeSelections, showComparison: false, @@ -340,32 +343,26 @@ const metricViewReducers = { setSortDescending(name: string) { updateMetricsExplorerByName(name, (metricsExplorer) => { - metricsExplorer.sortDirection = - DashboardState_LeaderboardSortDirection.DESCENDING; + metricsExplorer.sortDirection = SortDirection.DESCENDING; }); }, setSortAscending(name: string) { updateMetricsExplorerByName(name, (metricsExplorer) => { - metricsExplorer.sortDirection = - DashboardState_LeaderboardSortDirection.ASCENDING; + metricsExplorer.sortDirection = SortDirection.ASCENDING; }); }, toggleSortDirection(name: string) { updateMetricsExplorerByName(name, (metricsExplorer) => { metricsExplorer.sortDirection = - metricsExplorer.sortDirection === - DashboardState_LeaderboardSortDirection.ASCENDING - ? DashboardState_LeaderboardSortDirection.DESCENDING - : DashboardState_LeaderboardSortDirection.ASCENDING; + metricsExplorer.sortDirection === SortDirection.ASCENDING + ? SortDirection.DESCENDING + : SortDirection.ASCENDING; }); }, - setSortDirection( - name: string, - direction: DashboardState_LeaderboardSortDirection - ) { + setSortDirection(name: string, direction: SortDirection) { updateMetricsExplorerByName(name, (metricsExplorer) => { metricsExplorer.sortDirection = direction; }); diff --git a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte index 1da348c4406..f751323ad5d 100644 --- a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte +++ b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte @@ -16,7 +16,6 @@ import { getComparisonRange } from "@rilldata/web-common/lib/time/comparisons"; import { DEFAULT_TIME_RANGES } from "@rilldata/web-common/lib/time/config"; import type { TimeComparisonOption } from "@rilldata/web-common/lib/time/types"; - import { DashboardState_LeaderboardSortDirection } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; import { createQueryServiceMetricsViewToplist, createQueryServiceMetricsViewTotals, @@ -28,6 +27,7 @@ import { useQueryClient } from "@tanstack/svelte-query"; import { runtime } from "../../../runtime-client/runtime-store"; import { + SortDirection, metricsExplorerStore, useDashboardStore, useFetchTimeRange, @@ -98,9 +98,7 @@ $dashboardStore?.visibleMeasureKeys.has(m.name) ); - $: sortAscending = - $dashboardStore.sortDirection === - DashboardState_LeaderboardSortDirection.ASCENDING; + $: sortAscending = $dashboardStore.sortDirection === SortDirection.ASCENDING; $: metricTimeSeries = useModelHasTimeSeries(instanceId, metricViewName); $: hasTimeSeries = $metricTimeSeries.data; diff --git a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte index 2163d8ebbbd..f82a6466e22 100644 --- a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte +++ b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte @@ -15,7 +15,6 @@ useMetaMeasure, useModelHasTimeSeries, } from "@rilldata/web-common/features/dashboards/selectors"; - import { DashboardState_LeaderboardSortDirection } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; import { createQueryServiceMetricsViewToplist, MetricsViewDimension, @@ -24,6 +23,7 @@ import { useQueryClient } from "@tanstack/svelte-query"; import { runtime } from "../../../runtime-client/runtime-store"; import { + SortDirection, metricsExplorerStore, useComparisonRange, useDashboardStore, @@ -111,9 +111,7 @@ $: timeStart = $fetchTimeStore?.start?.toISOString(); $: timeEnd = $fetchTimeStore?.end?.toISOString(); - $: sortAscending = - $dashboardStore.sortDirection === - DashboardState_LeaderboardSortDirection.ASCENDING; + $: sortAscending = $dashboardStore.sortDirection === SortDirection.ASCENDING; $: topListQuery = createQueryServiceMetricsViewToplist( $runtime.instanceId, metricViewName, From dc9cef6777ab401e87b5a65e0ff2fc82f4155e8a Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 17 Aug 2023 07:17:26 -0700 Subject: [PATCH 19/58] merge fixes --- .../leaderboard/ContextColumnValue.svelte | 6 ++-- .../leaderboard/LeaderboardHeader.svelte | 30 ++++++++++--------- .../leaderboard/leaderboard-utils.ts | 14 ++++++++- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte b/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte index 7eeab2dffb4..e402c13e47d 100644 --- a/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte +++ b/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte @@ -2,6 +2,7 @@ import { LeaderboardContextColumn } from "@rilldata/web-common/features/dashboards/leaderboard-context-column"; import PercentageChange from "../../../components/data-types/PercentageChange.svelte"; import { FormattedDataType } from "@rilldata/web-common/components/data-types"; + import { contextColumnWidth } from "./leaderboard-utils"; export let formattedValue: string; export let showContext: LeaderboardContextColumn; @@ -13,14 +14,15 @@ console.log({ formattedValue, neg, noData }); } } + $: width = contextColumnWidth(showContext); {#if showContext === LeaderboardContextColumn.DELTA_CHANGE || showContext === LeaderboardContextColumn.PERCENT} -
+
{:else if showContext === LeaderboardContextColumn.DELTA_ABSOLUTE} -
+
{#if noData} no data {:else} diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte index e7969847a4b..f6f3b6e95b7 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte @@ -11,7 +11,7 @@ import PieChart from "@rilldata/web-common/components/icons/PieChart.svelte"; import { LeaderboardContextColumn } from "../leaderboard-context-column"; import ArrowDown from "@rilldata/web-common/components/icons/ArrowDown.svelte"; - import { CONTEXT_COLUMN_WIDTH } from "./leaderboard-utils"; + import { contextColumnWidth } from "./leaderboard-utils"; import { createEventDispatcher } from "svelte"; export let displayName: string; @@ -100,24 +100,26 @@
-
- {#if contextColumn === LeaderboardContextColumn.DELTA_CHANGE} - % - {:else if contextColumn === LeaderboardContextColumn.DELTA_ABSOLUTE} - - {:else if contextColumn === LeaderboardContextColumn.PERCENT} - % - {/if} -
+ {#if contextColumn !== LeaderboardContextColumn.HIDDEN} +
+ {#if contextColumn === LeaderboardContextColumn.DELTA_CHANGE} + % + {:else if contextColumn === LeaderboardContextColumn.DELTA_ABSOLUTE} + + {:else if contextColumn === LeaderboardContextColumn.PERCENT} + % + {/if} +
+ {/if}
diff --git a/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts b/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts index 1792f2990c6..184db815976 100644 --- a/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts +++ b/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts @@ -79,4 +79,16 @@ export function formatContextColumnValue( } return formattedValue; } -export const CONTEXT_COLUMN_WIDTH = 44; +export const contextColumnWidth = ( + contextType: LeaderboardContextColumn +): string => { + if (contextType === LeaderboardContextColumn.DELTA_CHANGE) { + return "44px"; + } else if (contextType === LeaderboardContextColumn.PERCENT) { + return "44px"; + } else if (contextType === LeaderboardContextColumn.DELTA_ABSOLUTE) { + return "56px"; + } else { + return "0px"; + } +}; From 23721d4544f4c58a0807edefbaf1d9be09199d37 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 17 Aug 2023 12:55:02 -0700 Subject: [PATCH 20/58] UI for context column sorting is in place --- proto/rill/ui/v1/dashboard.proto | 6 +-- .../features/dashboards/dashboard-stores.ts | 40 ++++++++++++++----- .../dimension-table/DimensionDisplay.svelte | 2 +- .../dashboards/leaderboard-context-column.ts | 2 +- .../leaderboard/ContextColumnValue.svelte | 7 +--- .../dashboards/leaderboard/Leaderboard.svelte | 12 ++++-- .../LeaderboardContextColumnMenu.svelte | 4 +- .../leaderboard/LeaderboardHeader.svelte | 30 ++++++++++---- .../leaderboard/leaderboard-utils.ts | 4 +- .../dashboards/proto-state/fromProto.ts | 4 +- .../dashboards/proto-state/toProto.ts | 4 +- .../src/proto/gen/rill/ui/v1/dashboard_pb.ts | 14 +++---- 12 files changed, 82 insertions(+), 47 deletions(-) diff --git a/proto/rill/ui/v1/dashboard.proto b/proto/rill/ui/v1/dashboard.proto index 47f0088b862..7d25d6caead 100644 --- a/proto/rill/ui/v1/dashboard.proto +++ b/proto/rill/ui/v1/dashboard.proto @@ -10,7 +10,7 @@ message DashboardState { enum LeaderboardContextColumn { LEADERBOARD_CONTEXT_COLUMN_UNSPECIFIED=0; LEADERBOARD_CONTEXT_COLUMN_PERCENT = 1; - LEADERBOARD_CONTEXT_COLUMN_DELTA_CHANGE = 2; + LEADERBOARD_CONTEXT_COLUMN_DELTA_ABSOLUTE = 2; LEADERBOARD_CONTEXT_COLUMN_HIDDEN = 3; } @@ -28,7 +28,7 @@ message DashboardState { * By default, the leaderboards+table will be sorted by VALUE, * using the value of the currently selected dashboard measure. * - * If DELTA_CHANGE or DELTA_PCT is selected, the + * If DELTA_ABSOLUTE or DELTA_PCT is selected, the * leaderboards+table will be sorted by the absolute or percentage * delta change of the currently selected dashboard measure. * @@ -46,7 +46,7 @@ message DashboardState { LEADERBOARD_SORT_TYPE_UNSPECIFIED = 0; LEADERBOARD_SORT_TYPE_VALUE = 1; LEADERBOARD_SORT_TYPE_DIMENSION = 2; - LEADERBOARD_SORT_TYPE_DELTA_CHANGE = 3; + LEADERBOARD_SORT_TYPE_DELTA_ABSOLUTE = 3; LEADERBOARD_SORT_TYPE_DELTA_PCT = 4; LEADERBOARD_SORT_TYPE_PERCENT = 5; } diff --git a/web-common/src/features/dashboards/dashboard-stores.ts b/web-common/src/features/dashboards/dashboard-stores.ts index 199723dfbc6..85957d8e94d 100644 --- a/web-common/src/features/dashboards/dashboard-stores.ts +++ b/web-common/src/features/dashboards/dashboard-stores.ts @@ -354,12 +354,32 @@ const metricViewReducers = { }); }, - toggleSortDirection(name: string) { + toggleSort(name: string, sortType?: SortType) { updateMetricsExplorerByName(name, (metricsExplorer) => { - metricsExplorer.sortDirection = - metricsExplorer.sortDirection === SortDirection.ASCENDING - ? SortDirection.DESCENDING - : SortDirection.ASCENDING; + // if sortType is not provided, or if it is provided + // and is the same as the current sort type, + // then just toggle the current sort direction + console.log( + "sortType", + sortType, + "dashbord", + metricsExplorer.dashboardSortType + ); + if ( + sortType === undefined || + metricsExplorer.dashboardSortType === sortType + ) { + metricsExplorer.sortDirection = + metricsExplorer.sortDirection === SortDirection.ASCENDING + ? SortDirection.DESCENDING + : SortDirection.ASCENDING; + } else { + // if the sortType is different from the current sort type, + // then update the sort type and set the sort direction + // to descending + metricsExplorer.dashboardSortType = sortType; + metricsExplorer.sortDirection = SortDirection.DESCENDING; + } }); }, @@ -416,22 +436,22 @@ const metricViewReducers = { updateMetricsExplorerByName(name, (metricsExplorer) => { metricsExplorer.showComparison = showComparison; // if setting showComparison===true and not currently - // showing any context column, then show DELTA_CHANGE + // showing any context column, then show DELTA_ABSOLUTE if ( showComparison && metricsExplorer.leaderboardContextColumn === LeaderboardContextColumn.HIDDEN ) { metricsExplorer.leaderboardContextColumn = - LeaderboardContextColumn.DELTA_CHANGE; + LeaderboardContextColumn.DELTA_PCT; } // if setting showComparison===false and currently - // showing DELTA_CHANGE, then hide context column + // showing DELTA_ABSOLUTE, then hide context column if ( !showComparison && metricsExplorer.leaderboardContextColumn === - LeaderboardContextColumn.DELTA_CHANGE + LeaderboardContextColumn.DELTA_PCT ) { metricsExplorer.leaderboardContextColumn = LeaderboardContextColumn.HIDDEN; @@ -445,7 +465,7 @@ const metricViewReducers = { if (metricsExplorer.showComparison === false) return; metricsExplorer.leaderboardContextColumn = - LeaderboardContextColumn.DELTA_CHANGE; + LeaderboardContextColumn.DELTA_PCT; }); }, diff --git a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte index f751323ad5d..c5cfec03616 100644 --- a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte +++ b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte @@ -333,7 +333,7 @@ if (!measureNames.includes(columnName)) return; if (columnName === leaderboardMeasureName) { - metricsExplorerStore.toggleSortDirection(metricViewName); + metricsExplorerStore.toggleSort(metricViewName); } else { metricsExplorerStore.setLeaderboardMeasureName( metricViewName, diff --git a/web-common/src/features/dashboards/leaderboard-context-column.ts b/web-common/src/features/dashboards/leaderboard-context-column.ts index 7143d27b09f..b5360205459 100644 --- a/web-common/src/features/dashboards/leaderboard-context-column.ts +++ b/web-common/src/features/dashboards/leaderboard-context-column.ts @@ -5,7 +5,7 @@ export enum LeaderboardContextColumn { // show percent-of-total PERCENT = "percent", // show percent change of the value compared to the previous time range - DELTA_CHANGE = "delta_change", + DELTA_PCT = "delta_change", // show absolute change of the value compared to the previous time range DELTA_ABSOLUTE = "delta_absolute", // Do not show the context column diff --git a/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte b/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte index e402c13e47d..0ec0082c499 100644 --- a/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte +++ b/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte @@ -9,15 +9,10 @@ $: neg = formattedValue[0] === "-"; $: noData = formattedValue === "" || !formattedValue; $: customStyle = neg ? "text-red-500" : noData ? "opacity-50 italic" : ""; - $: { - if (showContext === LeaderboardContextColumn.DELTA_ABSOLUTE) { - console.log({ formattedValue, neg, noData }); - } - } $: width = contextColumnWidth(showContext); -{#if showContext === LeaderboardContextColumn.DELTA_CHANGE || showContext === LeaderboardContextColumn.PERCENT} +{#if showContext === LeaderboardContextColumn.DELTA_PCT || showContext === LeaderboardContextColumn.PERCENT}
diff --git a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte index 317c72d7a07..c1ec54b6b07 100644 --- a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte +++ b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte @@ -105,13 +105,16 @@ metricsExplorerStore.setMetricDimensionName(metricViewName, dimensionName); } - function toggleSortDirection() { - metricsExplorerStore.toggleSortDirection(metricViewName); + function toggleSort(evt) { + console.log("toggleSort", evt.detail); + metricsExplorerStore.toggleSort(metricViewName, evt.detail); } $: timeStart = $fetchTimeStore?.start?.toISOString(); $: timeEnd = $fetchTimeStore?.end?.toISOString(); $: sortAscending = $dashboardStore.sortDirection === SortDirection.ASCENDING; + $: sortType = $dashboardStore.dashboardSortType; + $: topListQuery = createQueryServiceMetricsViewToplist( $runtime.instanceId, metricViewName, @@ -175,7 +178,7 @@ $: contextColumn = $dashboardStore?.leaderboardContextColumn; // Compose the comparison /toplist query $: showTimeComparison = - (contextColumn === LeaderboardContextColumn.DELTA_CHANGE || + (contextColumn === LeaderboardContextColumn.DELTA_PCT || contextColumn === LeaderboardContextColumn.DELTA_ABSOLUTE) && $dashboardStore?.showComparison; @@ -267,9 +270,10 @@ {filterExcludeMode} {hovered} {sortAscending} + {sortType} dimensionDescription={dimension?.description} on:open-dimension-details={() => selectDimension(dimensionName)} - on:toggle-sort-direction={toggleSortDirection} + on:toggle-sort={toggleSort} /> {#if values}
diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte index ae647db05dd..cd55e8d609a 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte @@ -27,7 +27,7 @@ if (key === LeaderboardContextColumn.HIDDEN) { metricsExplorerStore.hideContextColumn(metricViewName); - } else if (key === LeaderboardContextColumn.DELTA_CHANGE) { + } else if (key === LeaderboardContextColumn.DELTA_PCT) { metricsExplorerStore.displayDeltaChange(metricViewName); } else if (key === LeaderboardContextColumn.PERCENT) { metricsExplorerStore.displayPercentOfTotal(metricViewName); @@ -45,7 +45,7 @@ }, { main: "Percent change", - key: LeaderboardContextColumn.DELTA_CHANGE, + key: LeaderboardContextColumn.DELTA_PCT, disabled: !hasTimeSeries || !metricsExplorer.showComparison || diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte index f6f3b6e95b7..8b5422c2116 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte @@ -13,6 +13,7 @@ import ArrowDown from "@rilldata/web-common/components/icons/ArrowDown.svelte"; import { contextColumnWidth } from "./leaderboard-utils"; import { createEventDispatcher } from "svelte"; + import { SortType } from "../dashboard-stores"; export let displayName: string; export let isFetching: boolean; @@ -20,10 +21,19 @@ export let hovered: boolean; export let contextColumn: LeaderboardContextColumn; export let sortAscending: boolean; - + export let sortType: SortType; export let filterExcludeMode: boolean; const dispatch = createEventDispatcher(); + $: contextColumnSortType = { + [LeaderboardContextColumn.DELTA_PCT]: SortType.DELTA_PCT, + [LeaderboardContextColumn.DELTA_ABSOLUTE]: SortType.DELTA_ABSOLUTE, + [LeaderboardContextColumn.PERCENT]: SortType.PERCENT, + }[contextColumn]; + + $: console.log("sortType", sortType); + $: console.log("contextColumnSortType", contextColumnSortType); + $: arrowTransform = sortAscending ? "scale(1 -1)" : "scale(1 1)"; let optionsMenuActive = false; @@ -99,26 +109,32 @@
{#if contextColumn !== LeaderboardContextColumn.HIDDEN} -
dispatch("toggle-sort", contextColumnSortType)} class="shrink flex flex-row items-center justify-end" + aria-label="Toggle sort leaderboards by context column" style:width={contextColumnWidth(contextColumn)} > - {#if contextColumn === LeaderboardContextColumn.DELTA_CHANGE} + {#if contextColumn === LeaderboardContextColumn.DELTA_PCT} % {:else if contextColumn === LeaderboardContextColumn.DELTA_ABSOLUTE} {:else if contextColumn === LeaderboardContextColumn.PERCENT} % + {/if}{#if sortType !== SortType.VALUE} + {/if} -
+ {/if}
diff --git a/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts b/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts index 184db815976..531b54e8090 100644 --- a/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts +++ b/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts @@ -62,7 +62,7 @@ export function formatContextColumnValue( const { value, comparisonValue } = itemData; let formattedValue = ""; - if (contextType === LeaderboardContextColumn.DELTA_CHANGE) { + if (contextType === LeaderboardContextColumn.DELTA_PCT) { formattedValue = getFormatterValueForPercDiff( value && comparisonValue ? value - comparisonValue : null, comparisonValue @@ -82,7 +82,7 @@ export function formatContextColumnValue( export const contextColumnWidth = ( contextType: LeaderboardContextColumn ): string => { - if (contextType === LeaderboardContextColumn.DELTA_CHANGE) { + if (contextType === LeaderboardContextColumn.DELTA_PCT) { return "44px"; } else if (contextType === LeaderboardContextColumn.PERCENT) { return "44px"; diff --git a/web-common/src/features/dashboards/proto-state/fromProto.ts b/web-common/src/features/dashboards/proto-state/fromProto.ts index 36d35bbb532..f75991d2681 100644 --- a/web-common/src/features/dashboards/proto-state/fromProto.ts +++ b/web-common/src/features/dashboards/proto-state/fromProto.ts @@ -26,8 +26,8 @@ const LeaderboardContextColumnReverseMap: Record< LeaderboardContextColumn.HIDDEN, [DashboardState_LeaderboardContextColumn.PERCENT]: LeaderboardContextColumn.PERCENT, - [DashboardState_LeaderboardContextColumn.DELTA_CHANGE]: - LeaderboardContextColumn.DELTA_CHANGE, + [DashboardState_LeaderboardContextColumn.DELTA_ABSOLUTE]: + LeaderboardContextColumn.DELTA_PCT, [DashboardState_LeaderboardContextColumn.HIDDEN]: LeaderboardContextColumn.HIDDEN, }; diff --git a/web-common/src/features/dashboards/proto-state/toProto.ts b/web-common/src/features/dashboards/proto-state/toProto.ts index d9ad9301977..31458d8a660 100644 --- a/web-common/src/features/dashboards/proto-state/toProto.ts +++ b/web-common/src/features/dashboards/proto-state/toProto.ts @@ -38,8 +38,8 @@ const LeaderboardContextColumnMap: Record< > = { [LeaderboardContextColumn.PERCENT]: DashboardState_LeaderboardContextColumn.PERCENT, - [LeaderboardContextColumn.DELTA_CHANGE]: - DashboardState_LeaderboardContextColumn.DELTA_CHANGE, + [LeaderboardContextColumn.DELTA_PCT]: + DashboardState_LeaderboardContextColumn.DELTA_ABSOLUTE, [LeaderboardContextColumn.HIDDEN]: DashboardState_LeaderboardContextColumn.HIDDEN, }; diff --git a/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts b/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts index 963ec2cb7b0..6bdcade5aa9 100644 --- a/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts +++ b/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts @@ -168,9 +168,9 @@ export enum DashboardState_LeaderboardContextColumn { PERCENT = 1, /** - * @generated from enum value: LEADERBOARD_CONTEXT_COLUMN_DELTA_CHANGE = 2; + * @generated from enum value: LEADERBOARD_CONTEXT_COLUMN_DELTA_ABSOLUTE = 2; */ - DELTA_CHANGE = 2, + DELTA_ABSOLUTE = 2, /** * @generated from enum value: LEADERBOARD_CONTEXT_COLUMN_HIDDEN = 3; @@ -181,7 +181,7 @@ export enum DashboardState_LeaderboardContextColumn { proto3.util.setEnumType(DashboardState_LeaderboardContextColumn, "rill.ui.v1.DashboardState.LeaderboardContextColumn", [ { no: 0, name: "LEADERBOARD_CONTEXT_COLUMN_UNSPECIFIED" }, { no: 1, name: "LEADERBOARD_CONTEXT_COLUMN_PERCENT" }, - { no: 2, name: "LEADERBOARD_CONTEXT_COLUMN_DELTA_CHANGE" }, + { no: 2, name: "LEADERBOARD_CONTEXT_COLUMN_DELTA_ABSOLUTE" }, { no: 3, name: "LEADERBOARD_CONTEXT_COLUMN_HIDDEN" }, ]); @@ -220,7 +220,7 @@ proto3.util.setEnumType(DashboardState_LeaderboardSortDirection, "rill.ui.v1.Das * By default, the leaderboards+table will be sorted by VALUE, * using the value of the currently selected dashboard measure. * - * If DELTA_CHANGE or DELTA_PCT is selected, the + * If DELTA_ABSOLUTE or DELTA_PCT is selected, the * leaderboards+table will be sorted by the absolute or percentage * delta change of the currently selected dashboard measure. * @@ -253,9 +253,9 @@ export enum DashboardState_LeaderboardSortType { DIMENSION = 2, /** - * @generated from enum value: LEADERBOARD_SORT_TYPE_DELTA_CHANGE = 3; + * @generated from enum value: LEADERBOARD_SORT_TYPE_DELTA_ABSOLUTE = 3; */ - DELTA_CHANGE = 3, + DELTA_ABSOLUTE = 3, /** * @generated from enum value: LEADERBOARD_SORT_TYPE_DELTA_PCT = 4; @@ -272,7 +272,7 @@ proto3.util.setEnumType(DashboardState_LeaderboardSortType, "rill.ui.v1.Dashboar { no: 0, name: "LEADERBOARD_SORT_TYPE_UNSPECIFIED" }, { no: 1, name: "LEADERBOARD_SORT_TYPE_VALUE" }, { no: 2, name: "LEADERBOARD_SORT_TYPE_DIMENSION" }, - { no: 3, name: "LEADERBOARD_SORT_TYPE_DELTA_CHANGE" }, + { no: 3, name: "LEADERBOARD_SORT_TYPE_DELTA_ABSOLUTE" }, { no: 4, name: "LEADERBOARD_SORT_TYPE_DELTA_PCT" }, { no: 5, name: "LEADERBOARD_SORT_TYPE_PERCENT" }, ]); From 8ff22400a26f7df4030f0bb59042516f2e23a28e Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 17 Aug 2023 13:08:51 -0700 Subject: [PATCH 21/58] change name from DELTA_CHANGE to DELTA_PERCENT --- proto/rill/ui/v1/dashboard.proto | 11 ++++++----- .../src/features/dashboards/dashboard-stores.ts | 10 +++++----- .../dashboards/leaderboard-context-column.ts | 2 +- .../dashboards/leaderboard/Leaderboard.svelte | 2 +- .../LeaderboardContextColumnToggle.svelte | 8 ++++---- .../leaderboard/LeaderboardListItem.svelte | 2 +- .../features/dashboards/proto-state/fromProto.ts | 4 ++-- .../src/features/dashboards/proto-state/toProto.ts | 4 ++-- .../src/proto/gen/rill/ui/v1/dashboard_pb.ts | 14 +++++++------- 9 files changed, 29 insertions(+), 28 deletions(-) diff --git a/proto/rill/ui/v1/dashboard.proto b/proto/rill/ui/v1/dashboard.proto index 47f0088b862..509c660a0f0 100644 --- a/proto/rill/ui/v1/dashboard.proto +++ b/proto/rill/ui/v1/dashboard.proto @@ -10,7 +10,8 @@ message DashboardState { enum LeaderboardContextColumn { LEADERBOARD_CONTEXT_COLUMN_UNSPECIFIED=0; LEADERBOARD_CONTEXT_COLUMN_PERCENT = 1; - LEADERBOARD_CONTEXT_COLUMN_DELTA_CHANGE = 2; + LEADERBOARD_CONTEXT_COLUMN_DELTA_PERCENT = 2; + LEADERBOARD_CONTEXT_COLUMN_DELTA_ABSOLUTE = 2; LEADERBOARD_CONTEXT_COLUMN_HIDDEN = 3; } @@ -28,7 +29,7 @@ message DashboardState { * By default, the leaderboards+table will be sorted by VALUE, * using the value of the currently selected dashboard measure. * - * If DELTA_CHANGE or DELTA_PCT is selected, the + * If DELTA_ABSOLUTE or DELTA_PERCENT is selected, the * leaderboards+table will be sorted by the absolute or percentage * delta change of the currently selected dashboard measure. * @@ -46,9 +47,9 @@ message DashboardState { LEADERBOARD_SORT_TYPE_UNSPECIFIED = 0; LEADERBOARD_SORT_TYPE_VALUE = 1; LEADERBOARD_SORT_TYPE_DIMENSION = 2; - LEADERBOARD_SORT_TYPE_DELTA_CHANGE = 3; - LEADERBOARD_SORT_TYPE_DELTA_PCT = 4; - LEADERBOARD_SORT_TYPE_PERCENT = 5; + LEADERBOARD_SORT_TYPE_PERCENT = 3; + LEADERBOARD_SORT_TYPE_DELTA_PERCENT = 4; + LEADERBOARD_SORT_TYPE_DELTA_ABSOLUTE = 5; } // Selected time range diff --git a/web-common/src/features/dashboards/dashboard-stores.ts b/web-common/src/features/dashboards/dashboard-stores.ts index 05811c25e50..e3589b97587 100644 --- a/web-common/src/features/dashboards/dashboard-stores.ts +++ b/web-common/src/features/dashboards/dashboard-stores.ts @@ -415,22 +415,22 @@ const metricViewReducers = { updateMetricsExplorerByName(name, (metricsExplorer) => { metricsExplorer.showComparison = showComparison; // if setting showComparison===true and not currently - // showing any context column, then show DELTA_CHANGE + // showing any context column, then show DELTA_PERCENT if ( showComparison && metricsExplorer.leaderboardContextColumn === LeaderboardContextColumn.HIDDEN ) { metricsExplorer.leaderboardContextColumn = - LeaderboardContextColumn.DELTA_CHANGE; + LeaderboardContextColumn.DELTA_PERCENT; } // if setting showComparison===false and currently - // showing DELTA_CHANGE, then hide context column + // showing DELTA_PERCENT, then hide context column if ( !showComparison && metricsExplorer.leaderboardContextColumn === - LeaderboardContextColumn.DELTA_CHANGE + LeaderboardContextColumn.DELTA_PERCENT ) { metricsExplorer.leaderboardContextColumn = LeaderboardContextColumn.HIDDEN; @@ -444,7 +444,7 @@ const metricViewReducers = { if (metricsExplorer.showComparison === false) return; metricsExplorer.leaderboardContextColumn = - LeaderboardContextColumn.DELTA_CHANGE; + LeaderboardContextColumn.DELTA_PERCENT; }); }, diff --git a/web-common/src/features/dashboards/leaderboard-context-column.ts b/web-common/src/features/dashboards/leaderboard-context-column.ts index b518b7326db..d689208c450 100644 --- a/web-common/src/features/dashboards/leaderboard-context-column.ts +++ b/web-common/src/features/dashboards/leaderboard-context-column.ts @@ -5,7 +5,7 @@ export enum LeaderboardContextColumn { // show percent-of-total PERCENT = "percent", // show percent change of the value compared to the previous time range - DELTA_CHANGE = "delta_change", + DELTA_PERCENT = "delta_change", // Do not show the context column HIDDEN = "hidden", } diff --git a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte index f82a6466e22..97fa59b7359 100644 --- a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte +++ b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte @@ -175,7 +175,7 @@ // Compose the comparison /toplist query $: showTimeComparison = $dashboardStore?.leaderboardContextColumn === - LeaderboardContextColumn.DELTA_CHANGE && $dashboardStore?.showComparison; + LeaderboardContextColumn.DELTA_PERCENT && $dashboardStore?.showComparison; $: showPercentOfTotal = $dashboardStore?.leaderboardContextColumn === LeaderboardContextColumn.PERCENT; diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnToggle.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnToggle.svelte index beeff67089e..ebd5324a62c 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnToggle.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnToggle.svelte @@ -26,7 +26,7 @@ $: metricsExplorer = $metricsExplorerStore.entities[metricViewName]; let disabledButtons: ( - | LeaderboardContextColumn.DELTA_CHANGE + | LeaderboardContextColumn.DELTA_PERCENT | LeaderboardContextColumn.PERCENT )[] = []; $: { @@ -36,7 +36,7 @@ !metricsExplorer.showComparison || metricsExplorer.selectedComparisonTimeRange === undefined ) - disabledButtons.push(LeaderboardContextColumn.DELTA_CHANGE); + disabledButtons.push(LeaderboardContextColumn.DELTA_PERCENT); if (validPercentOfTotal !== true) disabledButtons.push(LeaderboardContextColumn.PERCENT); } @@ -57,7 +57,7 @@ // If a non-selected button is clicked, show the corresponding // context column - if (value === LeaderboardContextColumn.DELTA_CHANGE) { + if (value === LeaderboardContextColumn.DELTA_PERCENT) { metricsExplorerStore.displayDeltaChange(metricViewName); } else if (value === LeaderboardContextColumn.PERCENT) { metricsExplorerStore.displayPercentOfTotal(metricViewName); @@ -86,7 +86,7 @@ selected={selectedButtons} > diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardListItem.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardListItem.svelte index 805b74d67ab..f79bed8610f 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardListItem.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardListItem.svelte @@ -53,7 +53,7 @@ $: formattedValue = humanizeDataType(measureValue, formatPreset); $: percentChangeFormatted = - showContext === LeaderboardContextColumn.DELTA_CHANGE + showContext === LeaderboardContextColumn.DELTA_PERCENT ? getFormatterValueForPercDiff( measureValue && comparisonValue ? measureValue - comparisonValue diff --git a/web-common/src/features/dashboards/proto-state/fromProto.ts b/web-common/src/features/dashboards/proto-state/fromProto.ts index 36d35bbb532..010df400ed6 100644 --- a/web-common/src/features/dashboards/proto-state/fromProto.ts +++ b/web-common/src/features/dashboards/proto-state/fromProto.ts @@ -26,8 +26,8 @@ const LeaderboardContextColumnReverseMap: Record< LeaderboardContextColumn.HIDDEN, [DashboardState_LeaderboardContextColumn.PERCENT]: LeaderboardContextColumn.PERCENT, - [DashboardState_LeaderboardContextColumn.DELTA_CHANGE]: - LeaderboardContextColumn.DELTA_CHANGE, + [DashboardState_LeaderboardContextColumn.DELTA_PERCENT]: + LeaderboardContextColumn.DELTA_PERCENT, [DashboardState_LeaderboardContextColumn.HIDDEN]: LeaderboardContextColumn.HIDDEN, }; diff --git a/web-common/src/features/dashboards/proto-state/toProto.ts b/web-common/src/features/dashboards/proto-state/toProto.ts index 9ecb7e4f703..6584ac1009b 100644 --- a/web-common/src/features/dashboards/proto-state/toProto.ts +++ b/web-common/src/features/dashboards/proto-state/toProto.ts @@ -37,8 +37,8 @@ const LeaderboardContextColumnMap: Record< > = { [LeaderboardContextColumn.PERCENT]: DashboardState_LeaderboardContextColumn.PERCENT, - [LeaderboardContextColumn.DELTA_CHANGE]: - DashboardState_LeaderboardContextColumn.DELTA_CHANGE, + [LeaderboardContextColumn.DELTA_PERCENT]: + DashboardState_LeaderboardContextColumn.DELTA_PERCENT, [LeaderboardContextColumn.HIDDEN]: DashboardState_LeaderboardContextColumn.HIDDEN, }; diff --git a/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts b/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts index 963ec2cb7b0..2853730d379 100644 --- a/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts +++ b/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts @@ -168,9 +168,9 @@ export enum DashboardState_LeaderboardContextColumn { PERCENT = 1, /** - * @generated from enum value: LEADERBOARD_CONTEXT_COLUMN_DELTA_CHANGE = 2; + * @generated from enum value: LEADERBOARD_CONTEXT_COLUMN_DELTA_PERCENT = 2; */ - DELTA_CHANGE = 2, + DELTA_PERCENT = 2, /** * @generated from enum value: LEADERBOARD_CONTEXT_COLUMN_HIDDEN = 3; @@ -181,7 +181,7 @@ export enum DashboardState_LeaderboardContextColumn { proto3.util.setEnumType(DashboardState_LeaderboardContextColumn, "rill.ui.v1.DashboardState.LeaderboardContextColumn", [ { no: 0, name: "LEADERBOARD_CONTEXT_COLUMN_UNSPECIFIED" }, { no: 1, name: "LEADERBOARD_CONTEXT_COLUMN_PERCENT" }, - { no: 2, name: "LEADERBOARD_CONTEXT_COLUMN_DELTA_CHANGE" }, + { no: 2, name: "LEADERBOARD_CONTEXT_COLUMN_DELTA_PERCENT" }, { no: 3, name: "LEADERBOARD_CONTEXT_COLUMN_HIDDEN" }, ]); @@ -220,7 +220,7 @@ proto3.util.setEnumType(DashboardState_LeaderboardSortDirection, "rill.ui.v1.Das * By default, the leaderboards+table will be sorted by VALUE, * using the value of the currently selected dashboard measure. * - * If DELTA_CHANGE or DELTA_PCT is selected, the + * If DELTA_PERCENT or DELTA_PCT is selected, the * leaderboards+table will be sorted by the absolute or percentage * delta change of the currently selected dashboard measure. * @@ -253,9 +253,9 @@ export enum DashboardState_LeaderboardSortType { DIMENSION = 2, /** - * @generated from enum value: LEADERBOARD_SORT_TYPE_DELTA_CHANGE = 3; + * @generated from enum value: LEADERBOARD_SORT_TYPE_DELTA_PERCENT = 3; */ - DELTA_CHANGE = 3, + DELTA_PERCENT = 3, /** * @generated from enum value: LEADERBOARD_SORT_TYPE_DELTA_PCT = 4; @@ -272,7 +272,7 @@ proto3.util.setEnumType(DashboardState_LeaderboardSortType, "rill.ui.v1.Dashboar { no: 0, name: "LEADERBOARD_SORT_TYPE_UNSPECIFIED" }, { no: 1, name: "LEADERBOARD_SORT_TYPE_VALUE" }, { no: 2, name: "LEADERBOARD_SORT_TYPE_DIMENSION" }, - { no: 3, name: "LEADERBOARD_SORT_TYPE_DELTA_CHANGE" }, + { no: 3, name: "LEADERBOARD_SORT_TYPE_DELTA_PERCENT" }, { no: 4, name: "LEADERBOARD_SORT_TYPE_DELTA_PCT" }, { no: 5, name: "LEADERBOARD_SORT_TYPE_PERCENT" }, ]); From 017af7e573bc24c66241d97f491e029d8ba33199 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 17 Aug 2023 13:11:15 -0700 Subject: [PATCH 22/58] update and regenerate protobuf --- proto/rill/ui/v1/dashboard.proto | 4 +-- .../src/proto/gen/rill/ui/v1/dashboard_pb.ts | 34 +++++++++++-------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/proto/rill/ui/v1/dashboard.proto b/proto/rill/ui/v1/dashboard.proto index 509c660a0f0..71634687e06 100644 --- a/proto/rill/ui/v1/dashboard.proto +++ b/proto/rill/ui/v1/dashboard.proto @@ -11,8 +11,8 @@ message DashboardState { LEADERBOARD_CONTEXT_COLUMN_UNSPECIFIED=0; LEADERBOARD_CONTEXT_COLUMN_PERCENT = 1; LEADERBOARD_CONTEXT_COLUMN_DELTA_PERCENT = 2; - LEADERBOARD_CONTEXT_COLUMN_DELTA_ABSOLUTE = 2; - LEADERBOARD_CONTEXT_COLUMN_HIDDEN = 3; + LEADERBOARD_CONTEXT_COLUMN_DELTA_ABSOLUTE = 3; + LEADERBOARD_CONTEXT_COLUMN_HIDDEN = 4; } enum LeaderboardSortDirection { diff --git a/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts b/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts index 2853730d379..8b14dd75ea0 100644 --- a/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts +++ b/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts @@ -173,16 +173,22 @@ export enum DashboardState_LeaderboardContextColumn { DELTA_PERCENT = 2, /** - * @generated from enum value: LEADERBOARD_CONTEXT_COLUMN_HIDDEN = 3; + * @generated from enum value: LEADERBOARD_CONTEXT_COLUMN_DELTA_ABSOLUTE = 3; */ - HIDDEN = 3, + DELTA_ABSOLUTE = 3, + + /** + * @generated from enum value: LEADERBOARD_CONTEXT_COLUMN_HIDDEN = 4; + */ + HIDDEN = 4, } // Retrieve enum metadata with: proto3.getEnumType(DashboardState_LeaderboardContextColumn) proto3.util.setEnumType(DashboardState_LeaderboardContextColumn, "rill.ui.v1.DashboardState.LeaderboardContextColumn", [ { no: 0, name: "LEADERBOARD_CONTEXT_COLUMN_UNSPECIFIED" }, { no: 1, name: "LEADERBOARD_CONTEXT_COLUMN_PERCENT" }, { no: 2, name: "LEADERBOARD_CONTEXT_COLUMN_DELTA_PERCENT" }, - { no: 3, name: "LEADERBOARD_CONTEXT_COLUMN_HIDDEN" }, + { no: 3, name: "LEADERBOARD_CONTEXT_COLUMN_DELTA_ABSOLUTE" }, + { no: 4, name: "LEADERBOARD_CONTEXT_COLUMN_HIDDEN" }, ]); /** @@ -212,7 +218,7 @@ proto3.util.setEnumType(DashboardState_LeaderboardSortDirection, "rill.ui.v1.Das ]); /** - * + * * * SortType is used to determine how to sort the leaderboard * and dimension detail table, as well as where to place the * sort arrow. @@ -220,7 +226,7 @@ proto3.util.setEnumType(DashboardState_LeaderboardSortDirection, "rill.ui.v1.Das * By default, the leaderboards+table will be sorted by VALUE, * using the value of the currently selected dashboard measure. * - * If DELTA_PERCENT or DELTA_PCT is selected, the + * If DELTA_ABSOLUTE or DELTA_PERCENT is selected, the * leaderboards+table will be sorted by the absolute or percentage * delta change of the currently selected dashboard measure. * @@ -253,28 +259,28 @@ export enum DashboardState_LeaderboardSortType { DIMENSION = 2, /** - * @generated from enum value: LEADERBOARD_SORT_TYPE_DELTA_PERCENT = 3; + * @generated from enum value: LEADERBOARD_SORT_TYPE_PERCENT = 3; */ - DELTA_PERCENT = 3, + PERCENT = 3, /** - * @generated from enum value: LEADERBOARD_SORT_TYPE_DELTA_PCT = 4; + * @generated from enum value: LEADERBOARD_SORT_TYPE_DELTA_PERCENT = 4; */ - DELTA_PCT = 4, + DELTA_PERCENT = 4, /** - * @generated from enum value: LEADERBOARD_SORT_TYPE_PERCENT = 5; + * @generated from enum value: LEADERBOARD_SORT_TYPE_DELTA_ABSOLUTE = 5; */ - PERCENT = 5, + DELTA_ABSOLUTE = 5, } // Retrieve enum metadata with: proto3.getEnumType(DashboardState_LeaderboardSortType) proto3.util.setEnumType(DashboardState_LeaderboardSortType, "rill.ui.v1.DashboardState.LeaderboardSortType", [ { no: 0, name: "LEADERBOARD_SORT_TYPE_UNSPECIFIED" }, { no: 1, name: "LEADERBOARD_SORT_TYPE_VALUE" }, { no: 2, name: "LEADERBOARD_SORT_TYPE_DIMENSION" }, - { no: 3, name: "LEADERBOARD_SORT_TYPE_DELTA_PERCENT" }, - { no: 4, name: "LEADERBOARD_SORT_TYPE_DELTA_PCT" }, - { no: 5, name: "LEADERBOARD_SORT_TYPE_PERCENT" }, + { no: 3, name: "LEADERBOARD_SORT_TYPE_PERCENT" }, + { no: 4, name: "LEADERBOARD_SORT_TYPE_DELTA_PERCENT" }, + { no: 5, name: "LEADERBOARD_SORT_TYPE_DELTA_ABSOLUTE" }, ]); /** From b82256ffb6ea5b1903246ccb87a95a30c31b96f0 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 17 Aug 2023 13:35:39 -0700 Subject: [PATCH 23/58] clean up reexport --- .../src/features/dashboards/dashboard-stores.ts | 8 +------- .../dimension-table/DimensionDisplay.svelte | 3 ++- .../dashboards/leaderboard/Leaderboard.svelte | 2 +- .../features/dashboards/proto-state/derived-types.ts | 12 ++++++++++++ 4 files changed, 16 insertions(+), 9 deletions(-) create mode 100644 web-common/src/features/dashboards/proto-state/derived-types.ts diff --git a/web-common/src/features/dashboards/dashboard-stores.ts b/web-common/src/features/dashboards/dashboard-stores.ts index e3589b97587..87d84925d9c 100644 --- a/web-common/src/features/dashboards/dashboard-stores.ts +++ b/web-common/src/features/dashboards/dashboard-stores.ts @@ -15,19 +15,13 @@ import { ScrubRange, TimeRangePreset, } from "@rilldata/web-common/lib/time/types"; -import { - DashboardState_LeaderboardSortDirection as SortDirection, - DashboardState_LeaderboardSortType as SortType, -} from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; import type { V1ColumnTimeRangeResponse, V1MetricsView, V1MetricsViewFilter, } from "@rilldata/web-common/runtime-client"; import { derived, get, Readable, Writable, writable } from "svelte/store"; - -export { SortType as SortType }; -export { SortDirection as SortDirection }; +import { SortDirection, SortType } from "./proto-state/derived-types"; export interface LeaderboardValue { value: number; diff --git a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte index f751323ad5d..434155c3524 100644 --- a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte +++ b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte @@ -26,8 +26,9 @@ } from "@rilldata/web-common/runtime-client"; import { useQueryClient } from "@tanstack/svelte-query"; import { runtime } from "../../../runtime-client/runtime-store"; + import { SortDirection } from "../proto-state/derived-types"; + import { - SortDirection, metricsExplorerStore, useDashboardStore, useFetchTimeRange, diff --git a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte index 97fa59b7359..73846b2a147 100644 --- a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte +++ b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte @@ -22,8 +22,8 @@ } from "@rilldata/web-common/runtime-client"; import { useQueryClient } from "@tanstack/svelte-query"; import { runtime } from "../../../runtime-client/runtime-store"; + import { SortDirection } from "../proto-state/derived-types"; import { - SortDirection, metricsExplorerStore, useComparisonRange, useDashboardStore, diff --git a/web-common/src/features/dashboards/proto-state/derived-types.ts b/web-common/src/features/dashboards/proto-state/derived-types.ts new file mode 100644 index 00000000000..b1dd8ac4625 --- /dev/null +++ b/web-common/src/features/dashboards/proto-state/derived-types.ts @@ -0,0 +1,12 @@ +/** + * This file is used to rexport types generated by the proto compiler + * that end up with mangled names that we don't want in product code. + * + */ +import { + DashboardState_LeaderboardSortDirection, + DashboardState_LeaderboardSortType, +} from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; + +export { DashboardState_LeaderboardSortDirection as SortType }; +export { DashboardState_LeaderboardSortType as SortDirection }; From ab409a91aa6cf8951ce66f327dac4687c3a43604 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 17 Aug 2023 13:56:57 -0700 Subject: [PATCH 24/58] fix swapped export --- .../src/features/dashboards/proto-state/derived-types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web-common/src/features/dashboards/proto-state/derived-types.ts b/web-common/src/features/dashboards/proto-state/derived-types.ts index b1dd8ac4625..2522445cd46 100644 --- a/web-common/src/features/dashboards/proto-state/derived-types.ts +++ b/web-common/src/features/dashboards/proto-state/derived-types.ts @@ -8,5 +8,5 @@ import { DashboardState_LeaderboardSortType, } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; -export { DashboardState_LeaderboardSortDirection as SortType }; -export { DashboardState_LeaderboardSortType as SortDirection }; +export { DashboardState_LeaderboardSortDirection as SortDirection }; +export { DashboardState_LeaderboardSortType as SortType }; From 88de0a0c7986a98b37809bf47fac954c62cd13be Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 17 Aug 2023 14:18:49 -0700 Subject: [PATCH 25/58] update generated protobuf --- web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts b/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts index e4ba14d4ea7..8b14dd75ea0 100644 --- a/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts +++ b/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts @@ -226,11 +226,7 @@ proto3.util.setEnumType(DashboardState_LeaderboardSortDirection, "rill.ui.v1.Das * By default, the leaderboards+table will be sorted by VALUE, * using the value of the currently selected dashboard measure. * -<<<<<<< HEAD - * If DELTA_ABSOLUTE or DELTA_PCT is selected, the -======= * If DELTA_ABSOLUTE or DELTA_PERCENT is selected, the ->>>>>>> leaderboard-sort-toggle * leaderboards+table will be sorted by the absolute or percentage * delta change of the currently selected dashboard measure. * From 056b0c1811c5010a6c5dcd1802111b0575674a4f Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 17 Aug 2023 17:32:42 -0700 Subject: [PATCH 26/58] update DELTA_CHANGE to DELTA_PERCENT --- .../leaderboard/LeaderboardContextColumnMenu.svelte | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte index fd798a44d95..45941af5177 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte @@ -21,13 +21,18 @@ let metricsExplorer: MetricsExplorerEntity; $: metricsExplorer = $metricsExplorerStore.entities[metricViewName]; + $: console.log( + "leaderboardContextColumn", + metricsExplorer?.leaderboardContextColumn + ); + const handleContextValueButtonGroupClick = (evt) => { const value: SelectMenuItem = evt.detail; const key = value.key; if (key === LeaderboardContextColumn.HIDDEN) { metricsExplorerStore.hideContextColumn(metricViewName); - } else if (key === LeaderboardContextColumn.DELTA_CHANGE) { + } else if (key === LeaderboardContextColumn.DELTA_PERCENT) { metricsExplorerStore.displayDeltaChange(metricViewName); } else if (key === LeaderboardContextColumn.PERCENT) { metricsExplorerStore.displayPercentOfTotal(metricViewName); @@ -43,7 +48,7 @@ }, { main: "Percent change", - key: LeaderboardContextColumn.DELTA_CHANGE, + key: LeaderboardContextColumn.DELTA_PERCENT, disabled: !hasTimeSeries || !metricsExplorer.showComparison || @@ -60,6 +65,9 @@ $: selection = options.find( (option) => option.key === metricsExplorer?.leaderboardContextColumn ); + $: console.log("options", options); + + $: console.log("selection", selection); Date: Thu, 17 Aug 2023 18:21:49 -0700 Subject: [PATCH 27/58] merge cleanups --- .../features/dashboards/leaderboard/ContextColumnValue.svelte | 2 +- .../features/dashboards/leaderboard/LeaderboardHeader.svelte | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte b/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte index 7eeab2dffb4..64859b7e71f 100644 --- a/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte +++ b/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte @@ -15,7 +15,7 @@ } -{#if showContext === LeaderboardContextColumn.DELTA_CHANGE || showContext === LeaderboardContextColumn.PERCENT} +{#if showContext === LeaderboardContextColumn.DELTA_PERCENT || showContext === LeaderboardContextColumn.PERCENT}
diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte index 218ddab20eb..5736776c503 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte @@ -105,7 +105,7 @@ # - {#if contextColumn === LeaderboardContextColumn.DELTA_CHANGE} + {#if contextColumn === LeaderboardContextColumn.DELTA_PERCENT} % {:else if contextColumn === LeaderboardContextColumn.DELTA_ABSOLUTE} From e5370a12062a31e34814b816cd6c5984c90d53ed Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 17 Aug 2023 19:11:32 -0700 Subject: [PATCH 28/58] remove logging --- .../dashboards/leaderboard/ContextColumnValue.svelte | 5 ----- .../leaderboard/LeaderboardContextColumnMenu.svelte | 5 ----- 2 files changed, 10 deletions(-) diff --git a/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte b/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte index 64859b7e71f..5407188707d 100644 --- a/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte +++ b/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte @@ -8,11 +8,6 @@ $: neg = formattedValue[0] === "-"; $: noData = formattedValue === "" || !formattedValue; $: customStyle = neg ? "text-red-500" : noData ? "opacity-50 italic" : ""; - $: { - if (showContext === LeaderboardContextColumn.DELTA_ABSOLUTE) { - console.log({ formattedValue, neg, noData }); - } - } {#if showContext === LeaderboardContextColumn.DELTA_PERCENT || showContext === LeaderboardContextColumn.PERCENT} diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte index d3d7936f1a7..19d2bcfba9d 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte @@ -21,11 +21,6 @@ let metricsExplorer: MetricsExplorerEntity; $: metricsExplorer = $metricsExplorerStore.entities[metricViewName]; - $: console.log( - "leaderboardContextColumn", - metricsExplorer?.leaderboardContextColumn - ); - const handleContextValueButtonGroupClick = (evt) => { const value: SelectMenuItem = evt.detail; const key = value.key; From b37adde7f6eb05b4b466c7cd190ca3c95743b817 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 17 Aug 2023 19:34:15 -0700 Subject: [PATCH 29/58] adjustment to leaderboard column widths --- .../leaderboard/LeaderboardHeader.svelte | 33 +++++++++++++++---- .../leaderboard/LeaderboardListItem.svelte | 1 - 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte index 5736776c503..71581ea9b77 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte @@ -10,7 +10,6 @@ import Delta from "@rilldata/web-common/components/icons/Delta.svelte"; import PieChart from "@rilldata/web-common/components/icons/PieChart.svelte"; import ArrowDown from "@rilldata/web-common/components/icons/ArrowDown.svelte"; - import { CONTEXT_COLUMN_WIDTH } from "./leaderboard-utils"; import { createEventDispatcher } from "svelte"; import { LeaderboardContextColumn } from "../leaderboard-context-column"; @@ -26,6 +25,19 @@ let optionsMenuActive = false; const dispatch = createEventDispatcher(); + $: contextColumnWidth = (contextColumn: LeaderboardContextColumn) => { + if (contextColumn === LeaderboardContextColumn.HIDDEN) { + return "0px"; + } else if (contextColumn === LeaderboardContextColumn.DELTA_ABSOLUTE) { + return "54px"; + } else if ( + contextColumn === LeaderboardContextColumn.DELTA_PERCENT || + contextColumn === LeaderboardContextColumn.PERCENT + ) { + return "44px"; + } + }; + $: arrowTransform = sortAscending ? "scale(1 -1)" : "scale(1 1)"; @@ -105,12 +117,19 @@ # - {#if contextColumn === LeaderboardContextColumn.DELTA_PERCENT} - % - {:else if contextColumn === LeaderboardContextColumn.DELTA_ABSOLUTE} - - {:else if contextColumn === LeaderboardContextColumn.PERCENT} - % + {#if contextColumn !== LeaderboardContextColumn.HIDDEN} +
+ {#if contextColumn === LeaderboardContextColumn.DELTA_PERCENT} + % + {:else if contextColumn === LeaderboardContextColumn.DELTA_ABSOLUTE} + + {:else if contextColumn === LeaderboardContextColumn.PERCENT} + % + {/if} +
{/if}
diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardListItem.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardListItem.svelte index 911613c8caf..5f7f699cd6c 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardListItem.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardListItem.svelte @@ -21,7 +21,6 @@ import { humanizeDataType } from "../humanize-numbers"; import LongBarZigZag from "./LongBarZigZag.svelte"; import { - CONTEXT_COLUMN_WIDTH, LeaderboardItemData, formatContextColumnValue, } from "./leaderboard-utils"; From 0c848303f7482ab10bf8caa328baa2de8acdf75f Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 24 Aug 2023 14:44:56 -0700 Subject: [PATCH 30/58] fix dimension table sort arrow --- .../core/ColumnHeader.svelte | 36 ++++++++++++------- .../sections/ColumnHeaders.svelte | 2 ++ .../dimension-table/DimensionDisplay.svelte | 3 ++ .../dimension-table/DimensionTable.svelte | 4 +++ 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/web-common/src/components/virtualized-table/core/ColumnHeader.svelte b/web-common/src/components/virtualized-table/core/ColumnHeader.svelte index b4c0943d8cc..404c1368146 100644 --- a/web-common/src/components/virtualized-table/core/ColumnHeader.svelte +++ b/web-common/src/components/virtualized-table/core/ColumnHeader.svelte @@ -27,6 +27,8 @@ export let enableResize = true; export let isSelected = false; export let bgClass = ""; + // + export let sortAscending: boolean | undefined = undefined; const config: VirtualizedTableConfig = getContext("config"); const dispatch = createEventDispatcher(); @@ -34,7 +36,11 @@ const { shiftClickAction } = createShiftClickAction(); let showMore = false; - $: isSortingDesc = true; + + // if sorting is controlled externally, use that prop value + // otherwise, default to true + $: isSortingDesc = sortAscending !== undefined ? !sortAscending : true; + $: console.log(isSortingDesc, sortAscending); $: isDimensionTable = config.table === "DimensionTable"; $: isDimensionColumn = isDimensionTable && type === "VARCHAR"; @@ -68,8 +74,12 @@ showMore = false; }} on:click={() => { - if (isSelected) isSortingDesc = !isSortingDesc; - else isSortingDesc = true; + // only toggle `isSortingDesc` within the component if + // sorting is not controlled externally + if (sortAscending === undefined) { + if (isSelected) isSortingDesc = !isSortingDesc; + else isSortingDesc = true; + } dispatch("click-column"); }} > @@ -154,21 +164,23 @@ {#if isDimensionTable}
- {#if isSortingDesc} + +
+ +
{/if} diff --git a/web-common/src/components/virtualized-table/sections/ColumnHeaders.svelte b/web-common/src/components/virtualized-table/sections/ColumnHeaders.svelte index 8cabf1876f1..1e29134fb4c 100644 --- a/web-common/src/components/virtualized-table/sections/ColumnHeaders.svelte +++ b/web-common/src/components/virtualized-table/sections/ColumnHeaders.svelte @@ -12,6 +12,7 @@ export let showDataIcon = false; export let selectedColumn: string = null; export let fallbackBGClass = ""; + export let sortAscending: boolean; const getColumnHeaderProps = (header) => { const name = columns[header.index]?.label || columns[header.index]?.name; @@ -61,6 +62,7 @@ {header} {noPin} {showDataIcon} + {sortAscending} on:pin={() => { dispatch("pin", columns[header.index]); }} diff --git a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte index 434155c3524..4abc46732bd 100644 --- a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte +++ b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte @@ -378,6 +378,8 @@ values = humanizeGroupByColumns(values, measureFormatSpec); } } + $: sortAscending = $dashboardStore.sortDirection === SortDirection.ASCENDING; + $: console.log({ sortAscending }); {#if topListQuery} @@ -399,6 +401,7 @@ onSelectItem(event)} on:sort={(event) => onSortByColumn(event)} + {sortAscending} dimensionName={dimensionColumn} {columns} {selectedValues} diff --git a/web-common/src/features/dashboards/dimension-table/DimensionTable.svelte b/web-common/src/features/dashboards/dimension-table/DimensionTable.svelte index 9f25e3969be..09f83ae05a7 100644 --- a/web-common/src/features/dashboards/dimension-table/DimensionTable.svelte +++ b/web-common/src/features/dashboards/dimension-table/DimensionTable.svelte @@ -23,9 +23,12 @@ TableCells – the cell contents. export let columns: VirtualizedTableColumns[]; export let selectedValues: Array = []; export let sortByColumn: string; + export let sortAscending: boolean; export let dimensionName: string; export let excludeMode = false; + $: console.log("dim table", { sortAscending }); + /** the overscan values tell us how much to render off-screen. These may be set by the consumer * in certain circumstances. The tradeoff: the higher the overscan amount, the more DOM elements we have * to render on initial load. @@ -203,6 +206,7 @@ TableCells – the cell contents. selectedColumn={sortByColumn} columns={measureColumns} fallbackBGClass="bg-white" + {sortAscending} on:click-column={handleColumnHeaderClick} /> From 2fa593c563534b6812daec6821006f974d1be0c8 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 24 Aug 2023 14:51:26 -0700 Subject: [PATCH 31/58] cleanup --- .../core/ColumnHeader.svelte | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/web-common/src/components/virtualized-table/core/ColumnHeader.svelte b/web-common/src/components/virtualized-table/core/ColumnHeader.svelte index 404c1368146..87c8f0eb359 100644 --- a/web-common/src/components/virtualized-table/core/ColumnHeader.svelte +++ b/web-common/src/components/virtualized-table/core/ColumnHeader.svelte @@ -164,23 +164,21 @@ {#if isDimensionTable}
- -
- -
+ {:else} +
+ +
+ {/if}
{/if} From 8355785796551f389d3396b6b5e8ed426833d1e9 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 24 Aug 2023 14:53:13 -0700 Subject: [PATCH 32/58] cleanups --- .../src/components/virtualized-table/core/ColumnHeader.svelte | 1 - .../features/dashboards/dimension-table/DimensionDisplay.svelte | 1 - .../features/dashboards/dimension-table/DimensionTable.svelte | 2 -- .../dashboards/dimension-table/dimension-table-utils.ts | 1 - 4 files changed, 5 deletions(-) diff --git a/web-common/src/components/virtualized-table/core/ColumnHeader.svelte b/web-common/src/components/virtualized-table/core/ColumnHeader.svelte index 87c8f0eb359..e857e2ead82 100644 --- a/web-common/src/components/virtualized-table/core/ColumnHeader.svelte +++ b/web-common/src/components/virtualized-table/core/ColumnHeader.svelte @@ -40,7 +40,6 @@ // if sorting is controlled externally, use that prop value // otherwise, default to true $: isSortingDesc = sortAscending !== undefined ? !sortAscending : true; - $: console.log(isSortingDesc, sortAscending); $: isDimensionTable = config.table === "DimensionTable"; $: isDimensionColumn = isDimensionTable && type === "VARCHAR"; diff --git a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte index 4abc46732bd..a88164421ae 100644 --- a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte +++ b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte @@ -379,7 +379,6 @@ } } $: sortAscending = $dashboardStore.sortDirection === SortDirection.ASCENDING; - $: console.log({ sortAscending }); {#if topListQuery} diff --git a/web-common/src/features/dashboards/dimension-table/DimensionTable.svelte b/web-common/src/features/dashboards/dimension-table/DimensionTable.svelte index 09f83ae05a7..08b97483693 100644 --- a/web-common/src/features/dashboards/dimension-table/DimensionTable.svelte +++ b/web-common/src/features/dashboards/dimension-table/DimensionTable.svelte @@ -27,8 +27,6 @@ TableCells – the cell contents. export let dimensionName: string; export let excludeMode = false; - $: console.log("dim table", { sortAscending }); - /** the overscan values tell us how much to render off-screen. These may be set by the consumer * in certain circumstances. The tradeoff: the higher the overscan amount, the more DOM elements we have * to render on initial load. diff --git a/web-common/src/features/dashboards/dimension-table/dimension-table-utils.ts b/web-common/src/features/dashboards/dimension-table/dimension-table-utils.ts index ca0a201393b..77b2f4596c9 100644 --- a/web-common/src/features/dashboards/dimension-table/dimension-table-utils.ts +++ b/web-common/src/features/dashboards/dimension-table/dimension-table-utils.ts @@ -26,7 +26,6 @@ export function updateFilterOnSearch( let foundDimension = false; filterSet["include"].forEach((filter) => { - // console.log(filter.name, dimensionName); if (filter.name === dimensionName) { filter.like = [`%${searchText}%`]; foundDimension = true; From cd3b7fdd023a8dec6b3044420fcf62aa7d9e3115 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 24 Aug 2023 14:58:21 -0700 Subject: [PATCH 33/58] cleanups --- .../src/components/virtualized-table/core/ColumnHeader.svelte | 3 ++- web-common/src/features/dashboards/dashboard-stores.ts | 4 ++-- .../src/features/dashboards/leaderboard/leaderboard-utils.ts | 2 -- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/web-common/src/components/virtualized-table/core/ColumnHeader.svelte b/web-common/src/components/virtualized-table/core/ColumnHeader.svelte index e857e2ead82..b54b3194ac7 100644 --- a/web-common/src/components/virtualized-table/core/ColumnHeader.svelte +++ b/web-common/src/components/virtualized-table/core/ColumnHeader.svelte @@ -27,7 +27,8 @@ export let enableResize = true; export let isSelected = false; export let bgClass = ""; - // + // set this prop to control sorting arrow externally. + // if undefined, sorting arrow is toggled within the component. export let sortAscending: boolean | undefined = undefined; const config: VirtualizedTableConfig = getContext("config"); diff --git a/web-common/src/features/dashboards/dashboard-stores.ts b/web-common/src/features/dashboards/dashboard-stores.ts index 4d0277183c0..3084b1bec56 100644 --- a/web-common/src/features/dashboards/dashboard-stores.ts +++ b/web-common/src/features/dashboards/dashboard-stores.ts @@ -435,7 +435,7 @@ const metricViewReducers = { displayDeltaChange(name: string) { updateMetricsExplorerByName(name, (metricsExplorer) => { - // NOTE: only show delta change if comparison is enabled + // NOTE: only show delta change if time comparison is enabled if (metricsExplorer.showComparison === false) return; metricsExplorer.leaderboardContextColumn = @@ -445,7 +445,7 @@ const metricViewReducers = { displayDeltaAbsolute(name: string) { updateMetricsExplorerByName(name, (metricsExplorer) => { - // NOTE: only show delta absolute if comparison is enabled + // NOTE: only show delta absolute if time comparison is enabled if (metricsExplorer.showComparison === false) return; metricsExplorer.leaderboardContextColumn = diff --git a/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts b/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts index 308c85f2ebd..0173ca9e7d5 100644 --- a/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts +++ b/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts @@ -48,8 +48,6 @@ export function prepareLeaderboardItemData( }); } -export const CONTEXT_COLUMN_WIDTH = 44; - /** * Returns the formatted value for the context column * given the From 015adba395b8bfde2ccc0611e3cc8e9fdd05b34c Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 24 Aug 2023 15:00:58 -0700 Subject: [PATCH 34/58] remove redeclaration --- .../features/dashboards/dimension-table/DimensionDisplay.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte index a88164421ae..308e01f870d 100644 --- a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte +++ b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte @@ -378,7 +378,6 @@ values = humanizeGroupByColumns(values, measureFormatSpec); } } - $: sortAscending = $dashboardStore.sortDirection === SortDirection.ASCENDING; {#if topListQuery} From d1ae608ec2f5af409725803789173523c7035632 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 24 Aug 2023 15:04:02 -0700 Subject: [PATCH 35/58] add default prop --- .../virtualized-table/sections/ColumnHeaders.svelte | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/web-common/src/components/virtualized-table/sections/ColumnHeaders.svelte b/web-common/src/components/virtualized-table/sections/ColumnHeaders.svelte index 1e29134fb4c..18ea03688a8 100644 --- a/web-common/src/components/virtualized-table/sections/ColumnHeaders.svelte +++ b/web-common/src/components/virtualized-table/sections/ColumnHeaders.svelte @@ -12,7 +12,11 @@ export let showDataIcon = false; export let selectedColumn: string = null; export let fallbackBGClass = ""; - export let sortAscending: boolean; + + // set this prop to control sorting arrow externally. + // if undefined, sorting arrow is toggled within the + // cell header component. + export let sortAscending: boolean = undefined; const getColumnHeaderProps = (header) => { const name = columns[header.index]?.label || columns[header.index]?.name; From c224f2c068286143a3019551bb55c4e5149120dd Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 24 Aug 2023 15:28:42 -0700 Subject: [PATCH 36/58] final merg conflict updates --- .../LeaderboardContextColumnMenu.svelte | 2 +- .../leaderboard/LeaderboardHeader.svelte | 22 +++---------------- .../leaderboard/leaderboard-utils.ts | 2 +- 3 files changed, 5 insertions(+), 21 deletions(-) diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte index e9ec5795501..dfc87bdc6b5 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte @@ -27,7 +27,7 @@ if (key === LeaderboardContextColumn.HIDDEN) { metricsExplorerStore.hideContextColumn(metricViewName); - } else if (key === LeaderboardContextColumn.DELTA_PCT) { + } else if (key === LeaderboardContextColumn.DELTA_PERCENT) { } else if (key === LeaderboardContextColumn.DELTA_PERCENT) { metricsExplorerStore.displayDeltaChange(metricViewName); } else if (key === LeaderboardContextColumn.PERCENT) { diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte index a3f78458820..2858fed4899 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte @@ -12,10 +12,8 @@ import ArrowDown from "@rilldata/web-common/components/icons/ArrowDown.svelte"; import { createEventDispatcher } from "svelte"; import { LeaderboardContextColumn } from "../leaderboard-context-column"; - import ArrowDown from "@rilldata/web-common/components/icons/ArrowDown.svelte"; import { contextColumnWidth } from "./leaderboard-utils"; - import { createEventDispatcher } from "svelte"; - import { SortType } from "../dashboard-stores"; + import { SortType } from "../proto-state/derived-types"; export let displayName: string; export let isFetching: boolean; @@ -28,7 +26,7 @@ const dispatch = createEventDispatcher(); $: contextColumnSortType = { - [LeaderboardContextColumn.DELTA_PCT]: SortType.DELTA_PCT, + [LeaderboardContextColumn.DELTA_PERCENT]: SortType.DELTA_PERCENT, [LeaderboardContextColumn.DELTA_ABSOLUTE]: SortType.DELTA_ABSOLUTE, [LeaderboardContextColumn.PERCENT]: SortType.PERCENT, }[contextColumn]; @@ -39,20 +37,6 @@ $: arrowTransform = sortAscending ? "scale(1 -1)" : "scale(1 1)"; let optionsMenuActive = false; - const dispatch = createEventDispatcher(); - - $: contextColumnWidth = (contextColumn: LeaderboardContextColumn) => { - if (contextColumn === LeaderboardContextColumn.HIDDEN) { - return "0px"; - } else if (contextColumn === LeaderboardContextColumn.DELTA_ABSOLUTE) { - return "54px"; - } else if ( - contextColumn === LeaderboardContextColumn.DELTA_PERCENT || - contextColumn === LeaderboardContextColumn.PERCENT - ) { - return "44px"; - } - }; $: arrowTransform = sortAscending ? "scale(1 -1)" : "scale(1 1)"; @@ -143,7 +127,7 @@ aria-label="Toggle sort leaderboards by context column" style:width={contextColumnWidth(contextColumn)} > - {#if contextColumn === LeaderboardContextColumn.DELTA_PCT} + {#if contextColumn === LeaderboardContextColumn.DELTA_PERCENT} % {:else if contextColumn === LeaderboardContextColumn.DELTA_ABSOLUTE} diff --git a/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts b/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts index ebdfcfe45c8..62d9833ea0f 100644 --- a/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts +++ b/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts @@ -82,7 +82,7 @@ export function formatContextColumnValue( export const contextColumnWidth = ( contextType: LeaderboardContextColumn ): string => { - if (contextType === LeaderboardContextColumn.DELTA_PCT) { + if (contextType === LeaderboardContextColumn.DELTA_PERCENT) { return "44px"; } else if (contextType === LeaderboardContextColumn.PERCENT) { return "44px"; From b0725230618717391f8bea5e7db59608617e04ba Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Fri, 25 Aug 2023 11:43:23 -0700 Subject: [PATCH 37/58] fix weid dupe line --- .../leaderboard/LeaderboardContextColumnMenu.svelte | 9 --------- 1 file changed, 9 deletions(-) diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte index dfc87bdc6b5..09558efb919 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte @@ -27,7 +27,6 @@ if (key === LeaderboardContextColumn.HIDDEN) { metricsExplorerStore.hideContextColumn(metricViewName); - } else if (key === LeaderboardContextColumn.DELTA_PERCENT) { } else if (key === LeaderboardContextColumn.DELTA_PERCENT) { metricsExplorerStore.displayDeltaChange(metricViewName); } else if (key === LeaderboardContextColumn.PERCENT) { @@ -52,14 +51,6 @@ !metricsExplorer.showComparison || metricsExplorer.selectedComparisonTimeRange === undefined, }, - { - main: "Absolute change", - key: LeaderboardContextColumn.DELTA_ABSOLUTE, - disabled: - !hasTimeSeries || - !metricsExplorer.showComparison || - metricsExplorer.selectedComparisonTimeRange === undefined, - }, { main: "No context column", key: LeaderboardContextColumn.HIDDEN, From 4e273fd0641a1ed56ebd5314e7521c029629d3bc Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Wed, 6 Sep 2023 13:45:46 -0700 Subject: [PATCH 38/58] cleanup --- .../features/dashboards/leaderboard/LeaderboardHeader.svelte | 3 --- 1 file changed, 3 deletions(-) diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte index 2858fed4899..cd5302c2708 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte @@ -31,9 +31,6 @@ [LeaderboardContextColumn.PERCENT]: SortType.PERCENT, }[contextColumn]; - $: console.log("sortType", sortType); - $: console.log("contextColumnSortType", contextColumnSortType); - $: arrowTransform = sortAscending ? "scale(1 -1)" : "scale(1 1)"; let optionsMenuActive = false; From b59ad62bff8fa088afca3a0d64c52d8875c79504 Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Wed, 6 Sep 2023 13:46:56 -0700 Subject: [PATCH 39/58] sortedQuery returning correct results --- .../dashboards/leaderboard/Leaderboard.svelte | 126 ++++++++++++++- .../leaderboard/leaderboard-utils.ts | 151 ++++++++++++++++++ 2 files changed, 275 insertions(+), 2 deletions(-) diff --git a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte index bdebb52f5dc..95153e6f1dc 100644 --- a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte +++ b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte @@ -16,9 +16,11 @@ useModelHasTimeSeries, } from "@rilldata/web-common/features/dashboards/selectors"; import { + createQueryServiceMetricsViewComparisonToplist, createQueryServiceMetricsViewToplist, MetricsViewDimension, MetricsViewMeasure, + V1MetricsViewComparisonSortType, } from "@rilldata/web-common/runtime-client"; import { useQueryClient } from "@tanstack/svelte-query"; import { runtime } from "../../../runtime-client/runtime-store"; @@ -32,7 +34,12 @@ import { getFilterForComparsion } from "../dimension-table/dimension-table-utils"; import type { FormatPreset } from "../humanize-numbers"; import LeaderboardHeader from "./LeaderboardHeader.svelte"; - import { prepareLeaderboardItemData } from "./leaderboard-utils"; + import { + LeaderboardItemData2, + getLabeledComparisonFromComparisonRow, + prepareLeaderboardItemData, + prepareLeaderboardItemData2, + } from "./leaderboard-utils"; import LeaderboardListItem from "./LeaderboardListItem.svelte"; export let metricViewName: string; @@ -215,7 +222,7 @@ sort: [ { name: measure?.name, - ascending: false, + ascending: sortAscending, }, ], }, @@ -239,6 +246,121 @@ })) ?? []; } + $: sortedQueryBody = { + dimensionName: dimensionName, + measureNames: [measure?.name], + baseTimeRange: { start: timeStart, end: timeEnd }, + comparisonTimeRange: { + start: comparisonTimeStart, + end: comparisonTimeEnd, + }, + sort: [ + { + ascending: sortAscending, + measureName: measure?.name, + type: V1MetricsViewComparisonSortType.METRICS_VIEW_COMPARISON_SORT_TYPE_BASE_VALUE, + }, + ], + filter: filterForDimension, + // this limit was only appropriate for the comparison query. + // limit: currentVisibleValues.length.toString(), + limit: "250", + offset: "0", + }; + // $: console.log("sortedQueryBody", sortedQueryBody); + + // NOTE: this is the version of "enabled" that applied to + // the comparison query. + // $: sortedQueryEnabled = Boolean( + // showTimeComparison && + // !!comparisonTimeStart && + // !!comparisonTimeEnd && + // !!updatedFilters + // ); + + $: sortedQueryEnabled = + (hasTimeSeries ? !!timeStart && !!timeEnd : true) && !!filterForDimension; + + // $: console.log("sortedQueryEnabled", sortedQueryEnabled); + + $: sortedQuery = createQueryServiceMetricsViewComparisonToplist( + $runtime.instanceId, + metricViewName, + sortedQueryBody, + { + query: { + enabled: sortedQueryEnabled, + }, + } + ); + + // $: console.log("$topListQuery.status", $topListQuery.status); + // $: console.log("$sortedQuery.status", $sortedQuery.status); + + // $: console.log( + // "topListQuery", + // $topListQuery?.data?.data?.map((v) => [v.domain, v.total_records]) + // ); + // $: console.log( + // "comparisonTopListQuery", + // $comparisonTopListQuery?.data?.data?.map((v) => [v.domain, v.total_records]) + // ); + // $: if ($sortedQuery.isError) { + // console.log("sortedQuery isError", $sortedQuery); + // } + + // $: console.log("$sortedQuery.status", $sortedQuery.status); + + let aboveTheFold: LeaderboardItemData2[] = []; + let selectedBelowTheFold: LeaderboardItemData2[] = []; + + /** replace data after fetched. */ + $: if (!$sortedQuery?.isFetching) { + const leaderboardData = prepareLeaderboardItemData2( + $sortedQuery?.data?.rows?.map((r) => + getLabeledComparisonFromComparisonRow(r, measure.name) + ) ?? [], + slice, + activeValues, + null + ); + + aboveTheFold = leaderboardData.aboveTheFold; + selectedBelowTheFold = leaderboardData.selectedBelowTheFold; + console.log("sortedQuery data", dimensionName, leaderboardData); + } + + $: if (!$sortedQuery.isFetching) { + console.log( + "sortedQuery", + dimensionName, + $sortedQuery?.data?.rows.map((v) => [ + v.dimensionValue, + { + name: v.measureValues[0].measureName, + base: v.measureValues[0].baseValue, + comparison: v.measureValues[0].comparisonValue, + deltaRel: v.measureValues[0].deltaRel, + deltaAbs: v.measureValues[0].deltaAbs, + }, + ]) + ); + } + + // console.log( + // "sortedQuery RAW ROWS", + // dimensionName, + // $sortedQuery?.data?.rows + // ); + // console.log( + // "sortedQuery getLabeledComparisonFromComparisonRow", + // dimensionName, + // $sortedQuery?.data?.rows.map((r) => + // getLabeledComparisonFromComparisonRow(r, measure.name) + // ) + // ); + // } + let hovered: boolean; $: comparisonMap = new Map(comparisonValues?.map((v) => [v.label, v.value])); diff --git a/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts b/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts index 62d9833ea0f..da1fb7429f5 100644 --- a/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts +++ b/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts @@ -1,3 +1,7 @@ +import type { + V1MetricsViewComparisonRow, + V1MetricsViewComparisonValue, +} from "@rilldata/web-common/runtime-client"; import { PERC_DIFF } from "../../../components/data-types/type-utils"; import { FormatPreset, @@ -48,6 +52,153 @@ export function prepareLeaderboardItemData( }); } +/** + * A `V1MetricsViewComparisonRow` basically represents a row of data + * in the *dimension detail table*, NOT in the leaderboard. Therefore, + * to convert to rows of leaderboard data, we need to extract a single + * measure from the dimension table shaped data (namely, the active + * measure in the leaderboard). + * @param params + */ +export function getLabeledComparisonFromComparisonRow( + row: V1MetricsViewComparisonRow, + measureName: string | number +): ComparisonValueWithLabel { + const measure = row.measureValues?.find((v) => v.measureName === measureName); + if (!measure) { + throw new Error( + `Could not find measure ${measureName} in row ${JSON.stringify(row)}` + ); + } + return { + dimensionValue: row.dimensionValue as string | number, + ...measure, + }; +} + +export type LeaderboardItemData2 = { + // The dimension value label to be shown in the leaderboard + dimensionValue: string | number; + + // main value to be shown in the leaderboard + value: number | null; + + // percent of total for summable measures; null if not summable + pctOfTotal: number | null; + + // The value from the comparison period. + // Techinally this might not be a "previous value" but + // we use that name as a shorthand, since it's the most + // common use case. + prevValue: number | null; + + // the % change from the previous value + deltaPct: number | null; + + // the absolute change from the previous value + deltaAbs: number | null; + + // selection is not enough to determine if the item is included + // or excluded; for that we need to know the leaderboard's + // include/exclude state + selected: boolean; +}; + +function cleanUpComparisonValue( + v: ComparisonValueWithLabel, + total: number | null, + selected: boolean +): LeaderboardItemData2 { + if (!(Number.isFinite(v.baseValue) || v.baseValue === null)) { + throw new Error( + `Leaderboards only implemented for numeric baseValues or missing data (null). Got: ${JSON.stringify( + v + )}` + ); + } + const value = v.baseValue as number; + + return { + dimensionValue: v.dimensionValue, + value, + pctOfTotal: total && value ? (value / total) * 100 : null, + prevValue: Number.isFinite(v.comparisonValue) + ? (v.comparisonValue as number) + : null, + deltaPct: Number.isFinite(v.deltaRel) ? (v.deltaRel as number) * 100 : null, + deltaAbs: Number.isFinite(v.deltaAbs) ? (v.deltaAbs as number) : null, + + selected, + }; +} + +/** + * A `V1MetricsViewComparisonValue` augmented with the dimension + * value that it corresponds to. + */ +type ComparisonValueWithLabel = V1MetricsViewComparisonValue & { + dimensionValue: string | number; +}; + +/** + * + * @param values + * @param selectedValues + * @param total: the total of the measure for the current period, + * or null if the measure is not valid_percent_of_total + * @returns + */ +export function prepareLeaderboardItemData2( + values: ComparisonValueWithLabel[], + numberAboveTheFold: number, + selectedValues: (string | number)[], + total: number | null +): { + aboveTheFold: LeaderboardItemData2[]; + selectedBelowTheFold: LeaderboardItemData2[]; +} { + const aboveTheFold: LeaderboardItemData2[] = []; + const selectedBelowTheFold: LeaderboardItemData2[] = []; + // console.log({ values, len: values.length, selectedValues }); + values.forEach((v, i) => { + // console.log({ dimval: v.dimensionValue, selectedValues }); + const selected = + selectedValues.findIndex((value) => value === v.dimensionValue) >= 0; + // drop the value from the selectedValues array so that we'll + // have any left over values that were selected but not included + // in the results returned by the API + if (selected) + selectedValues = selectedValues.filter( + (value) => value !== v.dimensionValue + ); + if (i < numberAboveTheFold) { + aboveTheFold.push(cleanUpComparisonValue(v, total, selected)); + } else if (selected) { + selectedBelowTheFold.push(cleanUpComparisonValue(v, total, selected)); + } + }); + + // FIXME: note that it is possible for some values to be selected + // but not included in the results returned by the API, for example + // if a dimension value is selected and then a filter is applied + // that pushes it out of the top N. In that case, we will follow + // the previous strategy, and just push a dummy value with only + // the dimension value and nulls for all measure values. + selectedValues.forEach((v) => { + selectedBelowTheFold.push({ + dimensionValue: v, + selected: true, + value: null, + pctOfTotal: null, + prevValue: null, + deltaPct: null, + deltaAbs: null, + }); + }); + + return { aboveTheFold, selectedBelowTheFold }; +} + /** * Returns the formatted value for the context column * given the From e7b64b846d795f15cd18d59a3125c722d34554dd Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Wed, 6 Sep 2023 16:33:50 -0700 Subject: [PATCH 40/58] seems to be working, but sort API returns multiple rows with `null` dimensionValue --- .../dashboards/leaderboard/Leaderboard.svelte | 91 ++++++------------- .../leaderboard/LeaderboardListItem.svelte | 10 +- .../leaderboard/leaderboard-utils.ts | 34 +++---- 3 files changed, 41 insertions(+), 94 deletions(-) diff --git a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte index 95153e6f1dc..5688be93223 100644 --- a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte +++ b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte @@ -262,27 +262,13 @@ }, ], filter: filterForDimension, - // this limit was only appropriate for the comparison query. - // limit: currentVisibleValues.length.toString(), limit: "250", offset: "0", }; - // $: console.log("sortedQueryBody", sortedQueryBody); - - // NOTE: this is the version of "enabled" that applied to - // the comparison query. - // $: sortedQueryEnabled = Boolean( - // showTimeComparison && - // !!comparisonTimeStart && - // !!comparisonTimeEnd && - // !!updatedFilters - // ); $: sortedQueryEnabled = (hasTimeSeries ? !!timeStart && !!timeEnd : true) && !!filterForDimension; - // $: console.log("sortedQueryEnabled", sortedQueryEnabled); - $: sortedQuery = createQueryServiceMetricsViewComparisonToplist( $runtime.instanceId, metricViewName, @@ -294,27 +280,18 @@ } ); - // $: console.log("$topListQuery.status", $topListQuery.status); - // $: console.log("$sortedQuery.status", $sortedQuery.status); - - // $: console.log( - // "topListQuery", - // $topListQuery?.data?.data?.map((v) => [v.domain, v.total_records]) - // ); - // $: console.log( - // "comparisonTopListQuery", - // $comparisonTopListQuery?.data?.data?.map((v) => [v.domain, v.total_records]) - // ); - // $: if ($sortedQuery.isError) { - // console.log("sortedQuery isError", $sortedQuery); - // } - - // $: console.log("$sortedQuery.status", $sortedQuery.status); + $: console.log("sortedQuery BODY --", dimensionName, sortedQueryBody); + $: if (!$sortedQuery.isFetching) { + console.log( + "sortedQuery RAW DATA --", + dimensionName, + $sortedQuery?.data?.rows + ); + } + /** replace data after fetched. */ let aboveTheFold: LeaderboardItemData2[] = []; let selectedBelowTheFold: LeaderboardItemData2[] = []; - - /** replace data after fetched. */ $: if (!$sortedQuery?.isFetching) { const leaderboardData = prepareLeaderboardItemData2( $sortedQuery?.data?.rows?.map((r) => @@ -322,42 +299,28 @@ ) ?? [], slice, activeValues, - null + unfilteredTotal ); aboveTheFold = leaderboardData.aboveTheFold; selectedBelowTheFold = leaderboardData.selectedBelowTheFold; - console.log("sortedQuery data", dimensionName, leaderboardData); + // console.log("sortedQuery data", dimensionName, leaderboardData); } - $: if (!$sortedQuery.isFetching) { - console.log( - "sortedQuery", - dimensionName, - $sortedQuery?.data?.rows.map((v) => [ - v.dimensionValue, - { - name: v.measureValues[0].measureName, - base: v.measureValues[0].baseValue, - comparison: v.measureValues[0].comparisonValue, - deltaRel: v.measureValues[0].deltaRel, - deltaAbs: v.measureValues[0].deltaAbs, - }, - ]) - ); - } - - // console.log( - // "sortedQuery RAW ROWS", - // dimensionName, - // $sortedQuery?.data?.rows - // ); + // $: if (!$sortedQuery.isFetching) { // console.log( - // "sortedQuery getLabeledComparisonFromComparisonRow", + // "sortedQuery", // dimensionName, - // $sortedQuery?.data?.rows.map((r) => - // getLabeledComparisonFromComparisonRow(r, measure.name) - // ) + // $sortedQuery?.data?.rows.map((v) => [ + // v.dimensionValue, + // { + // name: v.measureValues[0].measureName, + // base: v.measureValues[0].baseValue, + // comparison: v.measureValues[0].comparisonValue, + // deltaRel: v.measureValues[0].deltaRel, + // deltaAbs: v.measureValues[0].deltaAbs, + // }, + // ]) // ); // } @@ -370,6 +333,7 @@ activeValues, comparisonMap ); + // $: console.log("aboveTheFoldItems", dimensionName, aboveTheFoldItems); $: belowTheFoldItems = prepareLeaderboardItemData( selectedValuesThatAreBelowTheFold, @@ -400,13 +364,12 @@ {#if values}
- {#each aboveTheFoldItems as itemData (itemData.label)} + {#each aboveTheFold as itemData (itemData.dimensionValue)} {/each} - {#if selectedValuesThatAreBelowTheFold?.length} + {#if selectedBelowTheFold?.length}
- {#each belowTheFoldItems as itemData (itemData.label)} + {#each selectedBelowTheFold as itemData (itemData.dimensionValue)} { // console.log({ dimval: v.dimensionValue, selectedValues }); const selected = - selectedValues.findIndex((value) => value === v.dimensionValue) >= 0; + selectedValuesCopy.findIndex((value) => value === v.dimensionValue) >= 0; // drop the value from the selectedValues array so that we'll // have any left over values that were selected but not included // in the results returned by the API if (selected) - selectedValues = selectedValues.filter( + selectedValuesCopy = selectedValuesCopy.filter( (value) => value !== v.dimensionValue ); if (i < numberAboveTheFold) { @@ -205,26 +201,18 @@ export function prepareLeaderboardItemData2( * accounting for the context column type. */ export function formatContextColumnValue( - itemData: LeaderboardItemData, - unfilteredTotal: number, + itemData: LeaderboardItemData2, contextType: LeaderboardContextColumn, formatPreset: FormatPreset ): string { - const { value, comparisonValue } = itemData; let formattedValue = ""; if (contextType === LeaderboardContextColumn.DELTA_PERCENT) { - formattedValue = getFormatterValueForPercDiff( - value && comparisonValue ? value - comparisonValue : null, - comparisonValue - ); + formattedValue = getFormatterValueForPercDiff(itemData.deltaPct); } else if (contextType === LeaderboardContextColumn.PERCENT) { - formattedValue = getFormatterValueForPercDiff(value, unfilteredTotal); + formattedValue = getFormatterValueForPercDiff(itemData.pctOfTotal); } else if (contextType === LeaderboardContextColumn.DELTA_ABSOLUTE) { - formattedValue = humanizeDataType( - value && comparisonValue ? value - comparisonValue : null, - formatPreset - ); + formattedValue = humanizeDataType(itemData.deltaAbs, formatPreset); } else { formattedValue = ""; } From 844e9f4c550f7567c293bc7af3d7786aad18718a Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 7 Sep 2023 11:28:40 -0700 Subject: [PATCH 41/58] comment out or remove old query code --- .../dashboards/leaderboard/Leaderboard.svelte | 260 +++++++++--------- .../leaderboard/leaderboard-utils.ts | 16 +- 2 files changed, 136 insertions(+), 140 deletions(-) diff --git a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte index b060bb2df3e..aaa4ab2a584 100644 --- a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte +++ b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte @@ -110,63 +110,63 @@ $: sortAscending = $dashboardStore.sortDirection === SortDirection.ASCENDING; $: sortType = $dashboardStore.dashboardSortType; - $: topListQuery = createQueryServiceMetricsViewToplist( - $runtime.instanceId, - metricViewName, - { - dimensionName: dimensionName, - measureNames: [measure?.name], - timeStart: $timeControlsStore.timeStart, - timeEnd: $timeControlsStore.timeEnd, - filter: filterForDimension, - limit: "250", - offset: "0", - sort: [ - { - name: measure?.name, - ascending: sortAscending, - }, - ], - }, - { - query: { - enabled: $timeControlsStore.ready && !!filterForDimension, - }, - } - ); - - let values: { value: number; label: string | number }[] = []; - let comparisonValues = []; - - /** replace data after fetched. */ - $: if (!$topListQuery?.isFetching) { - values = - $topListQuery?.data?.data.map((val) => ({ - value: val[measure?.name], - label: val[dimensionColumn], - })) ?? []; - } + // $: topListQuery = createQueryServiceMetricsViewToplist( + // $runtime.instanceId, + // metricViewName, + // { + // dimensionName: dimensionName, + // measureNames: [measure?.name], + // timeStart: $timeControlsStore.timeStart, + // timeEnd: $timeControlsStore.timeEnd, + // filter: filterForDimension, + // limit: "250", + // offset: "0", + // sort: [ + // { + // name: measure?.name, + // ascending: sortAscending, + // }, + // ], + // }, + // { + // query: { + // enabled: $timeControlsStore.ready && !!filterForDimension, + // }, + // } + // ); + + // let values: { value: number; label: string | number }[] = []; + // // let comparisonValues = []; + + // /** replace data after fetched. */ + // $: if (!$topListQuery?.isFetching) { + // values = + // $topListQuery?.data?.data.map((val) => ({ + // value: val[measure?.name], + // label: val[dimensionColumn], + // })) ?? []; + // } - // get all values that are selected but not visible. - // we'll put these at the bottom w/ a divider. - $: selectedValuesThatAreBelowTheFold = activeValues - ?.filter((label) => { - return ( - // the value is visible within the fold. - !values.slice(0, slice).some((value) => { - return value.label === label; - }) - ); - }) - .map((label) => { - const existingValue = values.find((value) => value.label === label); - // return the existing value, or if it does not exist, just return the label. - // FIX ME return values for label which are not in the query - return existingValue ? { ...existingValue } : { label }; - }) - .sort((a, b) => { - return b.value - a.value; - }); + // // get all values that are selected but not visible. + // // we'll put these at the bottom w/ a divider. + // $: selectedValuesThatAreBelowTheFold = activeValues + // ?.filter((label) => { + // return ( + // // the value is visible within the fold. + // !values.slice(0, slice).some((value) => { + // return value.label === label; + // }) + // ); + // }) + // .map((label) => { + // const existingValue = values.find((value) => value.label === label); + // // return the existing value, or if it does not exist, just return the label. + // // FIX ME return values for label which are not in the query + // return existingValue ? { ...existingValue } : { label }; + // }) + // .sort((a, b) => { + // return b.value - a.value; + // }); $: contextColumn = $dashboardStore?.leaderboardContextColumn; // Compose the comparison /toplist query @@ -182,56 +182,51 @@ $: showContext = $dashboardStore?.leaderboardContextColumn; // add all sliced and active values to the include filter. - $: currentVisibleValues = - $topListQuery?.data?.data - ?.slice(0, slice) - ?.concat(selectedValuesThatAreBelowTheFold) - ?.map((v) => v[dimensionColumn]) ?? []; - $: updatedFilters = getFilterForComparsion( - filterForDimension, - dimensionName, - currentVisibleValues - ); - $: comparisonTopListQuery = createQueryServiceMetricsViewToplist( - $runtime.instanceId, - metricViewName, - { - dimensionName: dimensionName, - measureNames: [measure?.name], - timeStart: $timeControlsStore.comparisonTimeStart, - timeEnd: $timeControlsStore.comparisonTimeEnd, - filter: updatedFilters, - limit: currentVisibleValues.length.toString(), - offset: "0", - sort: [ - { - name: measure?.name, - ascending: sortAscending, - }, - ], - }, - { - query: { - enabled: Boolean(showTimeComparison && !!updatedFilters), - }, - } - ); - - $: if (!$comparisonTopListQuery?.isFetching) { - comparisonValues = - $comparisonTopListQuery?.data?.data?.map((val) => ({ - value: val[measure?.name], - label: val[dimensionColumn], - })) ?? []; - } + // $: currentVisibleValues = + // $topListQuery?.data?.data + // ?.slice(0, slice) + // ?.concat(selectedValuesThatAreBelowTheFold) + // ?.map((v) => v[dimensionColumn]) ?? []; + // $: updatedFilters = getFilterForComparsion( + // filterForDimension, + // dimensionName, + // currentVisibleValues + // ); + // $: comparisonTopListQuery = createQueryServiceMetricsViewToplist( + // $runtime.instanceId, + // metricViewName, + // { + // dimensionName: dimensionName, + // measureNames: [measure?.name], + // timeStart: $timeControlsStore.comparisonTimeStart, + // timeEnd: $timeControlsStore.comparisonTimeEnd, + // filter: updatedFilters, + // limit: currentVisibleValues.length.toString(), + // offset: "0", + // sort: [ + // { + // name: measure?.name, + // ascending: sortAscending, + // }, + // ], + // }, + // { + // query: { + // enabled: Boolean(showTimeComparison && !!updatedFilters), + // }, + // } + // ); $: sortedQueryBody = { dimensionName: dimensionName, measureNames: [measure?.name], - baseTimeRange: { start: timeStart, end: timeEnd }, + baseTimeRange: { + start: $timeControlsStore.timeStart, + end: $timeControlsStore.timeEnd, + }, comparisonTimeRange: { - start: comparisonTimeStart, - end: comparisonTimeEnd, + start: $timeControlsStore.comparisonTimeStart, + end: $timeControlsStore.comparisonTimeEnd, }, sort: [ { @@ -245,32 +240,36 @@ offset: "0", }; - $: sortedQueryEnabled = - (hasTimeSeries ? !!timeStart && !!timeEnd : true) && !!filterForDimension; + $: sortedQueryEnabled = $timeControlsStore.ready && !!filterForDimension; + + $: sortedQueryOptions = { + query: { + enabled: sortedQueryEnabled, + }, + }; $: sortedQuery = createQueryServiceMetricsViewComparisonToplist( $runtime.instanceId, metricViewName, sortedQueryBody, - { - query: { - enabled: sortedQueryEnabled, - }, - } + sortedQueryOptions ); $: console.log("sortedQuery BODY --", dimensionName, sortedQueryBody); - $: if (!$sortedQuery.isFetching) { - console.log( - "sortedQuery RAW DATA --", - dimensionName, - $sortedQuery?.data?.rows - ); - } + $: console.log("sortedQuery OPTIONS --", dimensionName, sortedQueryOptions); + // $: if (!$sortedQuery.isFetching) { + // console.log( + // "sortedQuery RAW DATA --", + // dimensionName, + // $sortedQuery?.data?.rows + // ); + // } /** replace data after fetched. */ let aboveTheFold: LeaderboardItemData2[] = []; let selectedBelowTheFold: LeaderboardItemData2[] = []; + let noAvailableValues = true; + let showExpandTable = false; $: if (!$sortedQuery?.isFetching) { const leaderboardData = prepareLeaderboardItemData2( $sortedQuery?.data?.rows?.map((r) => @@ -283,6 +282,8 @@ aboveTheFold = leaderboardData.aboveTheFold; selectedBelowTheFold = leaderboardData.selectedBelowTheFold; + noAvailableValues = leaderboardData.noAvailableValues; + showExpandTable = leaderboardData.showExpandTable; // console.log("sortedQuery data", dimensionName, leaderboardData); } @@ -304,24 +305,9 @@ // } let hovered: boolean; - - $: comparisonMap = new Map(comparisonValues?.map((v) => [v.label, v.value])); - - $: aboveTheFoldItems = prepareLeaderboardItemData( - values.slice(0, slice), - activeValues, - comparisonMap - ); - // $: console.log("aboveTheFoldItems", dimensionName, aboveTheFoldItems); - - $: belowTheFoldItems = prepareLeaderboardItemData( - selectedValuesThatAreBelowTheFold, - activeValues, - comparisonMap - ); -{#if topListQuery} +{#if sortedQuery}
(hovered = true)} @@ -329,7 +315,7 @@ > selectDimension(dimensionName)} on:toggle-sort={toggleSort} /> - {#if values} + {#if aboveTheFold || selectedBelowTheFold}
{#each aboveTheFold as itemData (itemData.dimensionValue)} @@ -377,16 +363,16 @@
{/if} - {#if $topListQuery?.isError} + {#if $sortedQuery?.isError}
- {$topListQuery?.error} + {$sortedQuery?.error}
- {:else if values.length === 0} + {:else if noAvailableValues}
no available values
{/if} - {#if values.length > slice} + {#if showExpandTable}
-
+ diff --git a/web-common/src/features/dashboards/config.ts b/web-common/src/features/dashboards/config.ts index d27402a2342..0b2063eea92 100644 --- a/web-common/src/features/dashboards/config.ts +++ b/web-common/src/features/dashboards/config.ts @@ -15,3 +15,26 @@ export const MEASURE_CONFIG = { }, }, }; + +// Colors for Tailwind +// fill-pink-400 fill-cyan-400 fill-green-500 fill-orange-400 fill-purple-500 fill-red-600 fill-blue-500 fill-gray-600 +// stroke-pink-400 stroke-cyan-400 stroke-green-500 stroke-orange-400 stroke-purple-500 stroke-red-600 stroke-blue-500 stroke-gray-500 +export const CHECKMARK_COLORS = [ + "blue-500", + "orange-400", + "green-500", + "red-600", + "purple-500", + "pink-400", + "cyan-400", +]; + +export const LINE_COLORS = [ + "blue-500", + "orange-400", + "green-500", + "red-600", + "purple-500", + "pink-400", + "cyan-400", +]; diff --git a/web-common/src/features/dashboards/dashboard-stores.spec.ts b/web-common/src/features/dashboards/dashboard-stores.spec.ts index 9ae38e8fd9c..3ec038cf4b3 100644 --- a/web-common/src/features/dashboards/dashboard-stores.spec.ts +++ b/web-common/src/features/dashboards/dashboard-stores.spec.ts @@ -234,7 +234,7 @@ describe("dashboard-stores", () => { end: TestTimeOffsetConstants.NOW, }); const metrics = get(metricsExplorerStore).entities[AD_BIDS_NAME]; - expect(metrics.showComparison).toBeTruthy(); + expect(metrics.showTimeComparison).toBeTruthy(); expect(metrics.selectedComparisonTimeRange.name).toBe("CONTIGUOUS"); expect(metrics.selectedComparisonTimeRange.start).toEqual( TestTimeOffsetConstants.LAST_12_HOURS diff --git a/web-common/src/features/dashboards/dashboard-stores.ts b/web-common/src/features/dashboards/dashboard-stores.ts index ee309a6c751..a6bfe451ced 100644 --- a/web-common/src/features/dashboards/dashboard-stores.ts +++ b/web-common/src/features/dashboards/dashboard-stores.ts @@ -93,6 +93,7 @@ export interface MetricsExplorerEntity { lastDefinedScrubRange?: ScrubRange; selectedComparisonTimeRange?: DashboardTimeControls; + selectedComparisonDimension?: string; // user selected timezone selectedTimezone?: string; @@ -101,7 +102,7 @@ export interface MetricsExplorerEntity { // This controls whether a time comparison is shown in e.g. // the line charts and bignums. // It does NOT affect the leaderboard context column. - showComparison?: boolean; + showTimeComparison?: boolean; // state of context column in the leaderboard leaderboardContextColumn: LeaderboardContextColumn; @@ -296,7 +297,7 @@ const metricViewReducers = { start: comparisonRange.start, end: comparisonRange.end, }; - timeSelections.showComparison = true; + timeSelections.showTimeComparison = true; timeSelections.leaderboardContextColumn = LeaderboardContextColumn.DELTA_PERCENT; } @@ -327,7 +328,7 @@ const metricViewReducers = { dashboardSortType: SortType.VALUE, sortDirection: SortDirection.DESCENDING, - showComparison: false, + showTimeComparison: false, ...timeSelections, }; @@ -430,6 +431,24 @@ const metricViewReducers = { }); }, + setComparisonDimension(name: string, dimensionName: string) { + updateMetricsExplorerByName(name, (metricsExplorer) => { + if (dimensionName === undefined) { + setDisplayComparison(metricsExplorer, true); + } else { + setDisplayComparison(metricsExplorer, false); + } + metricsExplorer.selectedComparisonDimension = dimensionName; + }); + }, + + disableAllComparisons(name: string) { + updateMetricsExplorerByName(name, (metricsExplorer) => { + metricsExplorer.selectedComparisonDimension = undefined; + setDisplayComparison(metricsExplorer, false); + }); + }, + setSelectedComparisonRange( name: string, comparisonTimeRange: DashboardTimeControls @@ -449,9 +468,9 @@ const metricViewReducers = { }); }, - displayComparison(name: string, showComparison: boolean) { + displayTimeComparison(name: string, showTimeComparison: boolean) { updateMetricsExplorerByName(name, (metricsExplorer) => { - setDisplayComparison(metricsExplorer, showComparison); + setDisplayComparison(metricsExplorer, showTimeComparison); }); }, @@ -499,7 +518,8 @@ const metricViewReducers = { setDisplayComparison( metricsExplorer, - metricsExplorer.selectedComparisonTimeRange !== undefined + metricsExplorer.selectedComparisonTimeRange !== undefined && + metricsExplorer.selectedComparisonDimension === undefined ); }); }, @@ -514,7 +534,7 @@ const metricViewReducers = { case LeaderboardContextColumn.DELTA_PERCENT: { // if there is no time comparison, then we can't show // these context columns, so return with no change - if (metricsExplorer.showComparison === false) return; + if (metricsExplorer.showTimeComparison === false) return; metricsExplorer.leaderboardContextColumn = contextColumn; break; @@ -659,23 +679,28 @@ export function useDashboardStore( function setDisplayComparison( metricsExplorer: MetricsExplorerEntity, - showComparison: boolean + showTimeComparison: boolean ) { - metricsExplorer.showComparison = showComparison; - // if setting showComparison===true and not currently + metricsExplorer.showTimeComparison = showTimeComparison; + + if (showTimeComparison) { + metricsExplorer.selectedComparisonDimension = undefined; + } + + // if setting showTimeComparison===true and not currently // showing any context column, then show DELTA_PERCENT if ( - showComparison && + showTimeComparison && metricsExplorer.leaderboardContextColumn === LeaderboardContextColumn.HIDDEN ) { metricsExplorer.leaderboardContextColumn = LeaderboardContextColumn.DELTA_PERCENT; } - // if setting showComparison===false and currently + // if setting showTimeComparison===false and currently // showing DELTA_PERCENT, then hide context column if ( - !showComparison && + !showTimeComparison && metricsExplorer.leaderboardContextColumn === LeaderboardContextColumn.DELTA_PERCENT ) { diff --git a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte index c5674d879e4..f5a4321d88b 100644 --- a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte +++ b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte @@ -62,6 +62,8 @@ const timeControlsStore = useTimeControlStore(getStateManagers()); $: leaderboardMeasureName = $dashboardStore?.leaderboardMeasureName; + $: isBeingCompared = + $dashboardStore?.selectedComparisonDimension === dimensionName; $: leaderboardMeasureQuery = useMetaMeasure( instanceId, metricViewName, @@ -303,6 +305,13 @@ } } + function toggleComparisonDimension(dimensionName, isBeingCompared) { + metricsExplorerStore.setComparisonDimension( + metricViewName, + isBeingCompared ? undefined : dimensionName + ); + } + $: if ($comparisonTopListQuery?.data && values.length && displayComparison) { values = computeComparisonValues( $comparisonTopListQuery?.data, @@ -358,8 +367,11 @@ onSelectItem(event)} on:sort={(event) => onSortByColumn(event)} + on:toggle-dimension-comparison={() => + toggleComparisonDimension(dimensionName, isBeingCompared)} {sortAscending} dimensionName={dimensionColumn} + {isBeingCompared} {columns} {selectedValues} rows={values} diff --git a/web-common/src/features/dashboards/dimension-table/DimensionFilterGutter.svelte b/web-common/src/features/dashboards/dimension-table/DimensionFilterGutter.svelte index 58d8e653396..d8a6220cbce 100644 --- a/web-common/src/features/dashboards/dimension-table/DimensionFilterGutter.svelte +++ b/web-common/src/features/dashboards/dimension-table/DimensionFilterGutter.svelte @@ -2,14 +2,54 @@ import Cancel from "@rilldata/web-common/components/icons/Cancel.svelte"; import Check from "@rilldata/web-common/components/icons/Check.svelte"; import Spacer from "@rilldata/web-common/components/icons/Spacer.svelte"; + import Circle from "@rilldata/web-common/components/icons/Circle.svelte"; + import CheckCircle from "@rilldata/web-common/components/icons/CheckCircle.svelte"; + import { CHECKMARK_COLORS } from "@rilldata/web-common/features/dashboards/config"; + import StickyHeader from "@rilldata/web-common/components/virtualized-table/core/StickyHeader.svelte"; import { getContext } from "svelte"; import type { VirtualizedTableConfig } from "../../../components/virtualized-table/types"; + import DimensionCompareMenu from "@rilldata/web-common/features/dashboards/leaderboard/DimensionCompareMenu.svelte"; export let totalHeight: number; export let virtualRowItems; export let selectedIndex = []; export let excludeMode = false; + export let isBeingCompared = false; + export let atLeastOneActive = false; + + function getInsertIndex(arr, num) { + return arr + .concat(num) + .sort((a, b) => a - b) + .indexOf(num); + } + + function getColor(i) { + const posInSelection = selectedIndex.indexOf(i); + if (posInSelection >= 7) return "fill-gray-300"; + + let colorIndex = i; + if (posInSelection >= 0) { + colorIndex = posInSelection; + } else if (excludeMode && selectedIndex.length) { + colorIndex = (showCircleIcon(i) as number) - 1; + } + return "fill-" + CHECKMARK_COLORS[colorIndex]; + } + + function showCircleIcon(index) { + if (excludeMode && selectedIndex.length) { + if (selectedIndex.includes(index)) { + return false; + } else { + const posExcludingSelection = getInsertIndex(selectedIndex, index); + const colorPos = index - posExcludingSelection; + return colorPos < 3 ? colorPos + 1 : false; + } + } + return isBeingCompared && !atLeastOneActive && index < 3; + } const config: VirtualizedTableConfig = getContext("config"); @@ -19,11 +59,12 @@ style:height="{totalHeight}px" style:width="{config.indexWidth}px" > -
+ class="sticky left-0 top-0 surface z-40 flex items-center" + > + +
{#each virtualRowItems as row (`row-${row.key}`)} {@const isSelected = selectedIndex.includes(row.index)}
- {#if isSelected && !excludeMode} + {#if isSelected && !excludeMode && isBeingCompared} + + {:else if showCircleIcon(row.index)} + + {:else if isSelected && !excludeMode} {:else if isSelected && excludeMode} diff --git a/web-common/src/features/dashboards/dimension-table/DimensionTable.svelte b/web-common/src/features/dashboards/dimension-table/DimensionTable.svelte index 08b97483693..115161814cc 100644 --- a/web-common/src/features/dashboards/dimension-table/DimensionTable.svelte +++ b/web-common/src/features/dashboards/dimension-table/DimensionTable.svelte @@ -26,6 +26,7 @@ TableCells – the cell contents. export let sortAscending: boolean; export let dimensionName: string; export let excludeMode = false; + export let isBeingCompared = false; /** the overscan values tell us how much to render off-screen. These may be set by the consumer * in certain circumstances. The tradeoff: the higher the overscan amount, the more DOM elements we have @@ -214,7 +215,10 @@ TableCells – the cell contents. virtualRowItems={virtualRows} totalHeight={virtualHeight} {selectedIndex} + {isBeingCompared} {excludeMode} + atLeastOneActive={selectedValues?.length > 0} + on:toggle-dimension-comparison on:select-item={(event) => onSelectItem(event)} /> + import IconButton from "@rilldata/web-common/components/button/IconButton.svelte"; + import Compare from "@rilldata/web-common/components/icons/Compare.svelte"; + import Tooltip from "@rilldata/web-common/components/tooltip/Tooltip.svelte"; + import TooltipContent from "@rilldata/web-common/components/tooltip/TooltipContent.svelte"; + import { createEventDispatcher } from "svelte"; + + export let isBeingCompared = false; + + const dispatch = createEventDispatcher(); + + + { + dispatch("toggle-dimension-comparison"); + e.stopPropagation(); + }} +> + + + + {isBeingCompared ? "Remove comparison" : "Compare"} + + + diff --git a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte index 57fede4a2de..c2ffd51c808 100644 --- a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte +++ b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte @@ -7,7 +7,7 @@ */ import Tooltip from "@rilldata/web-common/components/tooltip/Tooltip.svelte"; import TooltipContent from "@rilldata/web-common/components/tooltip/TooltipContent.svelte"; - import { cancelDashboardQueries } from "@rilldata/web-common/features/dashboards/dashboard-queries"; + import { LeaderboardContextColumn } from "@rilldata/web-common/features/dashboards/leaderboard-context-column"; import { getFilterForDimension, useMetaDimension, @@ -20,7 +20,6 @@ MetricsViewDimension, MetricsViewMeasure, } from "@rilldata/web-common/runtime-client"; - import { useQueryClient } from "@tanstack/svelte-query"; import { runtime } from "../../../runtime-client/runtime-store"; import { SortDirection } from "../proto-state/derived-types"; import { metricsExplorerStore, useDashboardStore } from "../dashboard-stores"; @@ -48,8 +47,6 @@ let slice = 7; - const queryClient = useQueryClient(); - $: dashboardStore = useDashboardStore(metricViewName); let filterExcludeMode: boolean; @@ -89,19 +86,24 @@ const timeControlsStore = useTimeControlStore(getStateManagers()); - function toggleFilterMode() { - cancelDashboardQueries(queryClient, metricViewName); - metricsExplorerStore.toggleFilterMode(metricViewName, dimensionName); - } - function selectDimension(dimensionName) { metricsExplorerStore.setMetricDimensionName(metricViewName, dimensionName); } + function toggleComparisonDimension(dimensionName, isBeingCompared) { + metricsExplorerStore.setComparisonDimension( + metricViewName, + isBeingCompared ? undefined : dimensionName + ); + } + function toggleSort(evt) { metricsExplorerStore.toggleSort(metricViewName, evt.detail); } + $: isBeingCompared = + $dashboardStore?.selectedComparisonDimension === dimensionName; + $: sortAscending = $dashboardStore.sortDirection === SortDirection.ASCENDING; $: sortType = $dashboardStore.dashboardSortType; @@ -109,6 +111,51 @@ $: querySortType = getQuerySortType(sortType); + // + // + // ======= dhiraj section + let valuesComparedInExcludeMode = []; + $: if (isBeingCompared && filterExcludeMode) { + let count = 0; + valuesComparedInExcludeMode = values + .filter((value) => { + if (!activeValues.includes(value.label) && count < 3) { + count++; + return true; + } + return false; + }) + .map((value) => value.label); + } else { + valuesComparedInExcludeMode = []; + } + + // get all values that are selected but not visible. + // we'll put these at the bottom w/ a divider. + $: selectedValuesThatAreBelowTheFold = activeValues + ?.concat(valuesComparedInExcludeMode) + ?.filter((label) => { + return ( + // the value is visible within the fold. + !values.slice(0, slice).some((value) => { + return value.label === label; + }) + ); + }) + .map((label) => { + const existingValue = values.find((value) => value.label === label); + // return the existing value, or if it does not exist, just return the label. + // FIX ME return values for label which are not in the query + return existingValue ? { ...existingValue } : { label }; + }) + .sort((a, b) => { + return b.value - a.value; + }); + // + // >>>>>>> main --- dhiraj section END + // + // + $: sortedQueryBody = { dimensionName: dimensionName, measureNames: [measure?.name], @@ -168,6 +215,31 @@ } let hovered: boolean; + // <<<<<<< HEAD + // ======= + // from dhirajs branch + + $: comparisonMap = new Map(comparisonValues?.map((v) => [v.label, v.value])); + + $: aboveTheFoldItems = prepareLeaderboardItemData_dhiraj( + values.slice(0, slice), + activeValues, + comparisonMap, + filterExcludeMode + ); + + $: defaultComparisonsPresentInAboveFold = + aboveTheFoldItems?.filter((item) => item.defaultComparedIndex >= 0) + ?.length || 0; + + $: belowTheFoldItems = prepareLeaderboardItemData_dhiraj( + selectedValuesThatAreBelowTheFold, + activeValues, + comparisonMap, + filterExcludeMode, + defaultComparisonsPresentInAboveFold + ); + // >>>>>>> main {#if sortedQuery} @@ -180,8 +252,9 @@ {contextColumn} isFetching={$sortedQuery.isFetching} {displayName} - on:toggle-filter-mode={toggleFilterMode} - {filterExcludeMode} + on:toggle-dimension-comparison={() => + toggleComparisonDimension(dimensionName, isBeingCompared)} + {isBeingCompared} {hovered} {sortAscending} {sortType} @@ -197,6 +270,7 @@ {itemData} {contextColumn} {atLeastOneActive} + {isBeingCompared} {filterExcludeMode} {isSummableMeasure} {referenceValue} @@ -214,6 +288,7 @@ {itemData} {contextColumn} {atLeastOneActive} + {isBeingCompared} {filterExcludeMode} {isSummableMeasure} {referenceValue} diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte index 3ab624b01f4..f38ee711a62 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardHeader.svelte @@ -6,7 +6,7 @@ import TooltipTitle from "@rilldata/web-common/components/tooltip/TooltipTitle.svelte"; import { EntityStatus } from "@rilldata/web-common/features/entity-management/types"; import Spinner from "../../entity-management/Spinner.svelte"; - import LeaderboardOptionsMenu from "../leaderboard/LeaderboardOptionsMenu.svelte"; + import DimensionCompareMenu from "./DimensionCompareMenu.svelte"; import Delta from "@rilldata/web-common/components/icons/Delta.svelte"; import PieChart from "@rilldata/web-common/components/icons/PieChart.svelte"; import ArrowDown from "@rilldata/web-common/components/icons/ArrowDown.svelte"; @@ -22,7 +22,7 @@ export let contextColumn: LeaderboardContextColumn; export let sortAscending: boolean; export let sortType: SortType; - export let filterExcludeMode: boolean; + export let isBeingCompared: boolean; const dispatch = createEventDispatcher(); $: contextColumnSortType = { @@ -32,21 +32,18 @@ }[contextColumn]; $: arrowTransform = sortAscending ? "scale(1 -1)" : "scale(1 1)"; - - let optionsMenuActive = false;
{#if isFetching} - {:else if hovered || optionsMenuActive} + {:else if hovered || isBeingCompared}
-
diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardItemFilterIcon.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardItemFilterIcon.svelte index dad82bcd53c..3871071db73 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardItemFilterIcon.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardItemFilterIcon.svelte @@ -1,13 +1,29 @@
- {#if selected && !excluded} + {#if selected && !excluded && isBeingCompared} + + {:else if isBeingCompared && defaultComparedIndex >= 0} + + {:else if selected && !excluded} {:else if selected && excluded} diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardListItem.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardListItem.svelte index f9106e5fb36..1b5ff41931c 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardListItem.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardListItem.svelte @@ -29,13 +29,13 @@ export let itemData: LeaderboardItemData; $: label = itemData.dimensionValue; $: measureValue = itemData.value; - $: selected = itemData.selected; + $: selected = itemData.selectedIndex >= 0; $: comparisonValue = itemData.prevValue; export let contextColumn: LeaderboardContextColumn; export let atLeastOneActive = false; - + export let isBeingCompared = false; export let formattedValue: string; export let filterExcludeMode; @@ -108,7 +108,7 @@
- -{#if contextMenuOpen} - - - - zoomScrub()}> - Zoom to subrange - Z - - resetScrub()}> - Remove scrub - esc - - - -{/if} diff --git a/web-common/src/features/dashboards/time-series/MeasureZoom.svelte b/web-common/src/features/dashboards/time-series/MeasureZoom.svelte new file mode 100644 index 00000000000..e1ebd9caaa5 --- /dev/null +++ b/web-common/src/features/dashboards/time-series/MeasureZoom.svelte @@ -0,0 +1,71 @@ + + +
+ {#if $dashboardStore?.selectedScrubRange?.end && !$dashboardStore?.selectedScrubRange?.isScrubbing} + + +
+ +
+
+
+ {/if} +
+ + + diff --git a/web-common/src/features/dashboards/time-series/MetricsTimeSeriesCharts.svelte b/web-common/src/features/dashboards/time-series/MetricsTimeSeriesCharts.svelte index 95323092eea..a36cb3a1cd9 100644 --- a/web-common/src/features/dashboards/time-series/MetricsTimeSeriesCharts.svelte +++ b/web-common/src/features/dashboards/time-series/MetricsTimeSeriesCharts.svelte @@ -3,36 +3,39 @@ import { Axis } from "@rilldata/web-common/components/data-graphic/guides"; import CrossIcon from "@rilldata/web-common/components/icons/CrossIcon.svelte"; import SeachableFilterButton from "@rilldata/web-common/components/searchable-filter-menu/SeachableFilterButton.svelte"; - import { - useDashboardStore, - metricsExplorerStore, - } from "@rilldata/web-common/features/dashboards/dashboard-stores"; + import { useDashboardStore } from "@rilldata/web-common/features/dashboards/dashboard-stores"; + import { getFilterForComparedDimension, prepareTimeSeries } from "./utils"; import { humanizeDataType, FormatPreset, nicelyFormattedTypesToNumberKind, } from "@rilldata/web-common/features/dashboards/humanize-numbers"; - import { useMetaQuery } from "@rilldata/web-common/features/dashboards/selectors"; + import { + getFilterForDimension, + useMetaQuery, + } from "@rilldata/web-common/features/dashboards/selectors"; import { createShowHideMeasuresStore } from "@rilldata/web-common/features/dashboards/show-hide-selectors"; import { getStateManagers } from "@rilldata/web-common/features/dashboards/state-managers/state-managers"; import { useTimeControlStore } from "@rilldata/web-common/features/dashboards/time-controls/time-control-store"; + import { adjustOffsetForZone } from "@rilldata/web-common/lib/convertTimestampPreview"; import { EntityStatus } from "@rilldata/web-common/features/entity-management/types"; import { TIME_GRAIN } from "@rilldata/web-common/lib/time/config"; + import { SortDirection } from "@rilldata/web-common/features/dashboards/proto-state/derived-types"; import { getAdjustedChartTime } from "@rilldata/web-common/lib/time/ranges"; import { createQueryServiceMetricsViewTimeSeries, + createQueryServiceMetricsViewToplist, createQueryServiceMetricsViewTotals, V1MetricsViewTimeSeriesResponse, } from "@rilldata/web-common/runtime-client"; import type { CreateQueryResult } from "@tanstack/svelte-query"; + import { getDimensionValueTimeSeries } from "./multiple-dimension-queries"; import { runtime } from "../../../runtime-client/runtime-store"; import Spinner from "../../entity-management/Spinner.svelte"; import MeasureBigNumber from "../big-number/MeasureBigNumber.svelte"; import MeasureChart from "./MeasureChart.svelte"; + import MeasureZoom from "./MeasureZoom.svelte"; import TimeSeriesChartContainer from "./TimeSeriesChartContainer.svelte"; - import { getOrderedStartEnd, prepareTimeSeries } from "./utils"; - import { adjustOffsetForZone } from "@rilldata/web-common/lib/convertTimestampPreview"; - import { TimeRangePreset } from "@rilldata/web-common/lib/time/types"; export let metricViewName; export let workspaceWidth: number; @@ -47,7 +50,8 @@ const timeControlsStore = useTimeControlStore(getStateManagers()); $: selectedMeasureNames = $dashboardStore?.selectedMeasureNames; - $: showComparison = $timeControlsStore.showComparison; + $: comparisonDimension = $dashboardStore?.selectedComparisonDimension; + $: showComparison = !comparisonDimension && $timeControlsStore.showComparison; $: interval = $timeControlsStore.selectedTimeRange?.interval ?? $timeControlsStore.minTimeGrain; @@ -100,6 +104,9 @@ Error >; + let includedValues; + let allDimQuery; + $: if ( $dashboardStore && metaQuery && @@ -190,6 +197,86 @@ endValue = adjustedChartValue?.end; } + let topListQuery; + $: if (comparisonDimension && $timeControlsStore.ready) { + const dimensionFilters = $dashboardStore.filters.include.filter( + (filter) => filter.name === comparisonDimension + ); + if (dimensionFilters) { + includedValues = dimensionFilters[0]?.in.slice(0, 7) || []; + } + + if (includedValues.length === 0) { + // TODO: Create a central store for topList + // Fetch top values for the dimension + const filterForDimension = getFilterForDimension( + $dashboardStore?.filters, + comparisonDimension + ); + topListQuery = createQueryServiceMetricsViewToplist( + $runtime.instanceId, + metricViewName, + { + dimensionName: comparisonDimension, + measureNames: [$dashboardStore?.leaderboardMeasureName], + timeStart: $timeControlsStore.timeStart, + timeEnd: $timeControlsStore.timeEnd, + filter: filterForDimension, + limit: "250", + offset: "0", + sort: [ + { + name: $dashboardStore?.leaderboardMeasureName, + ascending: + $dashboardStore.sortDirection === SortDirection.ASCENDING, + }, + ], + }, + { + query: { + enabled: $timeControlsStore.ready && !!filterForDimension, + }, + } + ); + } + } + + $: if ( + includedValues?.length || + (topListQuery && !$topListQuery?.isFetching) + ) { + let filters = $dashboardStore.filters; + + // Handle case when there are no included filters for the dimension + if (!includedValues?.length) { + const columnName = $topListQuery?.data?.meta[0]?.name; + const topListValues = $topListQuery?.data?.data.map((d) => d[columnName]); + + const computedFilter = getFilterForComparedDimension( + comparisonDimension, + $dashboardStore?.filters, + topListValues + ); + filters = computedFilter?.updatedFilter; + includedValues = computedFilter?.includedValues; + } + + allDimQuery = getDimensionValueTimeSeries( + includedValues, + instanceId, + metricViewName, + comparisonDimension, + selectedMeasureNames, + filters, + $timeControlsStore.adjustedStart, + $timeControlsStore.adjustedEnd, + interval, + $dashboardStore?.selectedTimezone + ); + } + + $: dimensionData = comparisonDimension ? $allDimQuery : []; + $: showHideMeasures = createShowHideMeasuresStore(metricViewName, metaQuery); const toggleMeasureVisibility = (e) => { @@ -201,32 +288,10 @@ const setAllMeasuresVisible = () => { showHideMeasures.setAllToVisible(); }; - - function onKeyDown(e) { - if (scrubStart && scrubEnd) { - // if key Z is pressed, zoom the scrub - if (e.key === "z") { - const { start, end } = getOrderedStartEnd( - $dashboardStore?.selectedScrubRange?.start, - $dashboardStore?.selectedScrubRange?.end - ); - metricsExplorerStore.setSelectedTimeRange(metricViewName, { - name: TimeRangePreset.CUSTOM, - start, - end, - }); - } else if ( - !$dashboardStore.selectedScrubRange?.isScrubbing && - e.key === "Escape" - ) { - metricsExplorerStore.setSelectedScrubRange(metricViewName, undefined); - } - } - } -
+
-
+ {#if $dashboardStore?.selectedTimeRange} {#if $metaQuery.data?.measures} - {#each $metaQuery.data?.measures.filter((_, i) => $showHideMeasures.selectedItems[i]) as measure, index (measure.name)} + {#each $metaQuery.data?.measures.filter((_, i) => $showHideMeasures.selectedItems[i]) as measure (measure.name)} {@const bigNum = $totalsQuery?.data?.data?.[measure.name]} {@const comparisonValue = totalsComparisons?.[measure.name]} @@ -294,12 +359,13 @@
{:else if formattedData} - - - diff --git a/web-common/src/features/dashboards/time-series/TimeSeriesChartContainer.svelte b/web-common/src/features/dashboards/time-series/TimeSeriesChartContainer.svelte index 6077ce7ed99..27a1b24c68f 100644 --- a/web-common/src/features/dashboards/time-series/TimeSeriesChartContainer.svelte +++ b/web-common/src/features/dashboards/time-series/TimeSeriesChartContainer.svelte @@ -7,6 +7,7 @@ A container GraphicContext for the time series in a metrics dashboard. export let start: Date; export let end: Date; export let workspaceWidth: number; + export let enableFullWidth = false;
= MEASURE_CONFIG.breakpoint + width={(enableFullWidth + ? workspaceWidth + : workspaceWidth >= MEASURE_CONFIG.breakpoint ? MEASURE_CONFIG.container.width.full : MEASURE_CONFIG.container.width.breakpoint) - MEASURE_CONFIG.bigNumber.widthWithChart} diff --git a/web-common/src/features/dashboards/time-series/multiple-dimension-queries.ts b/web-common/src/features/dashboards/time-series/multiple-dimension-queries.ts new file mode 100644 index 00000000000..1d9b4edcff1 --- /dev/null +++ b/web-common/src/features/dashboards/time-series/multiple-dimension-queries.ts @@ -0,0 +1,89 @@ +// Query for fetching timeseries data for individual dimension values +// TODO: Replace this with MetricsViewAggregationRequest API call +import { derived, writable } from "svelte/store"; + +import { + createQueryServiceMetricsViewTimeSeries, + V1MetricsViewFilter, + V1TimeGrain, +} from "@rilldata/web-common/runtime-client"; +import { prepareTimeSeries } from "./utils"; +import { + CHECKMARK_COLORS, + LINE_COLORS, +} from "@rilldata/web-common/features/dashboards/config"; +import { TIME_GRAIN } from "@rilldata/web-common/lib/time/config"; + +/*** + * Create a dervied svelte store that fetches the + * timeseries data for a given dimension value + * individually for a given set of dimension values + */ + +export function getDimensionValueTimeSeries( + values: string[], + instanceId: string, + metricViewName: string, + dimensionName: string, + selectedMeasureNames: string[], + filters: V1MetricsViewFilter, + start: string, + end: string, + interval: V1TimeGrain, + zone: string +) { + if (!values && values.length == 0) return; + return derived( + values.map((value, i) => { + const updatedIncludeFilter = filters.include.map((filter) => { + if (filter.name === dimensionName) + return { name: dimensionName, in: [value] }; + else return filter; + }); + // remove excluded values + const updatedExcludeFilter = filters.exclude.filter( + (filter) => filter.name !== dimensionName + ); + const updatedFilter = { + exclude: updatedExcludeFilter, + include: updatedIncludeFilter, + }; + + return derived( + [ + writable(value), + createQueryServiceMetricsViewTimeSeries(instanceId, metricViewName, { + measureNames: selectedMeasureNames, + filter: updatedFilter, + timeStart: start, + timeEnd: end, + timeGranularity: interval, + timeZone: zone, + }), + ], + ([value, timeseries]) => { + let prepData = timeseries?.data?.data; + if (!timeseries?.isFetching) { + prepData = prepareTimeSeries( + timeseries?.data?.data, + undefined, + TIME_GRAIN[interval].duration, + zone + ); + } + return { + value, + strokeClass: "stroke-" + LINE_COLORS[i], + fillClass: "fill-" + CHECKMARK_COLORS[i], + data: prepData, + isFetching: timeseries.isFetching, + }; + } + ); + }), + + (combos) => { + return combos; + } + ); +} diff --git a/web-common/src/features/dashboards/time-series/utils.spec.ts b/web-common/src/features/dashboards/time-series/utils.spec.ts index a39b7d0d61a..f658eeaad54 100644 --- a/web-common/src/features/dashboards/time-series/utils.spec.ts +++ b/web-common/src/features/dashboards/time-series/utils.spec.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from "vitest"; -import { niceMeasureExtents } from "./utils"; +import { niceMeasureExtents, getFilterForComparedDimension } from "./utils"; describe("niceMeasureExtents", () => { it("should return [0, 1] if both values are 0", () => { @@ -18,3 +18,80 @@ describe("niceMeasureExtents", () => { expect(niceMeasureExtents([0, 5], 2)).toEqual([0, 10]); }); }); + +describe("getFilterForComparedDimension", () => { + it("should return filter with dimension added if no existing filter", () => { + const dimensionName = "country"; + const filters = { include: [], exclude: [] }; + const topListValues = ["US", "IN", "CN"]; + + const result = getFilterForComparedDimension( + dimensionName, + filters, + topListValues + ); + + expect(result.updatedFilter).toEqual({ + include: [{ name: "country", in: ["US", "IN", "CN"] }], + exclude: [], + }); + }); + + it("should exclude values from top list based on existing exclude filter", () => { + const dimensionName = "country"; + const filters = { + include: [], + exclude: [{ name: "country", in: ["CN"] }], + }; + const topListValues = ["US", "IN", "CN"]; + + const result = getFilterForComparedDimension( + dimensionName, + filters, + topListValues + ); + + expect(result.updatedFilter).toEqual({ + include: [{ name: "country", in: ["US", "IN"] }], + exclude: [{ name: "country", in: ["CN"] }], + }); + }); + + it("should slice top list values to max of 3", () => { + const dimensionName = "country"; + const filters = { include: [], exclude: [] }; + const topListValues = ["US", "IN", "CN", "UK", "FR"]; + + const result = getFilterForComparedDimension( + dimensionName, + filters, + topListValues + ); + + expect(result.updatedFilter.include[0].in).toHaveLength(3); + }); + it("should not modify filters for unrelated dimensions", () => { + const dimensionName = "country"; + + const filters = { + include: [{ name: "company", in: ["zoom"] }], + exclude: [{ name: "device", in: ["mobile"] }], + }; + + const topListValues = ["US", "IN", "CN"]; + + const result = getFilterForComparedDimension( + dimensionName, + filters, + topListValues + ); + + expect(result.updatedFilter).toEqual({ + include: [ + { name: "company", in: ["zoom"] }, + { name: "country", in: ["US", "IN", "CN"] }, + ], + exclude: [{ name: "device", in: ["mobile"] }], + }); + }); +}); diff --git a/web-common/src/features/dashboards/time-series/utils.ts b/web-common/src/features/dashboards/time-series/utils.ts index 917d75735b4..855c5779ae5 100644 --- a/web-common/src/features/dashboards/time-series/utils.ts +++ b/web-common/src/features/dashboards/time-series/utils.ts @@ -95,3 +95,36 @@ export function getOrderedStartEnd(start: Date, stop: Date) { return { start, end: stop }; } } + +export function getFilterForComparedDimension( + dimensionName, + filters, + topListValues +) { + // Check if we have an excluded filter for the dimension + const excludedFilter = filters.exclude.find((d) => d.name === dimensionName); + + let excludedValues = []; + if (excludedFilter) { + excludedValues = excludedFilter.in; + } + + // Remove excluded values from top list + const includedValues = topListValues + ?.filter((d) => !excludedValues.includes(d)) + ?.slice(0, 3); + + // Add dimension to filter + const updatedFilter = { + ...filters, + include: [ + ...filters.include, + { + name: dimensionName, + in: includedValues, + }, + ], + }; + + return { includedValues, updatedFilter }; +} diff --git a/web-common/src/lib/time/config.ts b/web-common/src/lib/time/config.ts index 3ebd0fadfa7..b57a3b3d893 100644 --- a/web-common/src/lib/time/config.ts +++ b/web-common/src/lib/time/config.ts @@ -455,7 +455,7 @@ export const TIME_COMPARISON = { }, }; -export const NO_COMPARISON_LABEL = "no comparison"; +export const NO_COMPARISON_LABEL = "No comparison"; export const DEFAULT_TIMEZONES = [ "America/Los_Angeles", diff --git a/web-common/src/lib/time/ranges/index.ts b/web-common/src/lib/time/ranges/index.ts index ac17d96f75a..e6946db1179 100644 --- a/web-common/src/lib/time/ranges/index.ts +++ b/web-common/src/lib/time/ranges/index.ts @@ -109,10 +109,14 @@ export function ISODurationToTimePreset( switch (isoDuration) { case "PT6H": return TimeRangePreset.LAST_SIX_HOURS; + case "PT24H": + return TimeRangePreset.LAST_24_HOURS; case "P1D": return TimeRangePreset.LAST_24_HOURS; case "P7D": return TimeRangePreset.LAST_7_DAYS; + case "P14D": + return TimeRangePreset.LAST_14_DAYS; case "P4W": return TimeRangePreset.LAST_4_WEEKS; case "inf": diff --git a/web-common/src/lib/time/types.ts b/web-common/src/lib/time/types.ts index b42fc9fcac5..a4bf94dfad5 100644 --- a/web-common/src/lib/time/types.ts +++ b/web-common/src/lib/time/types.ts @@ -123,6 +123,7 @@ export const TimeRangePreset: { [K in TimeRangeType]: K } = { LAST_SIX_HOURS: "LAST_SIX_HOURS", LAST_24_HOURS: "LAST_24_HOURS", LAST_7_DAYS: "LAST_7_DAYS", + LAST_14_DAYS: "LAST_14_DAYS", LAST_4_WEEKS: "LAST_4_WEEKS", LAST_12_MONTHS: "LAST_12_MONTHS", TODAY: "TODAY", diff --git a/web-common/src/proto/gen/rill/runtime/v1/resources_pb.ts b/web-common/src/proto/gen/rill/runtime/v1/resources_pb.ts index e398ba2de37..014c272d634 100644 --- a/web-common/src/proto/gen/rill/runtime/v1/resources_pb.ts +++ b/web-common/src/proto/gen/rill/runtime/v1/resources_pb.ts @@ -392,13 +392,6 @@ export class ProjectParserSpec extends Message { */ modelMaterializeDelaySeconds = 0; - /** - * duckdb_connectors is a list of connectors that use DuckDB - * - * @generated from field: repeated string duckdb_connectors = 7; - */ - duckdbConnectors: string[] = []; - constructor(data?: PartialMessage) { super(); proto3.util.initPartial(data, this); @@ -413,7 +406,6 @@ export class ProjectParserSpec extends Message { { no: 4, name: "source_stream_ingestion", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, { no: 5, name: "model_default_materialize", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, { no: 6, name: "model_materialize_delay_seconds", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 7, name: "duckdb_connectors", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): ProjectParserSpec { diff --git a/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts b/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts index 308231a41cf..7ca981b40b1 100644 --- a/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts +++ b/web-common/src/proto/gen/rill/ui/v1/dashboard_pb.ts @@ -55,9 +55,9 @@ export class DashboardState extends Message { selectedDimension?: string; /** - * @generated from field: optional bool show_comparison = 7; + * @generated from field: optional bool show_time_comparison = 7; */ - showComparison?: boolean; + showTimeComparison?: boolean; /** * Selected measures and dimensions to be shown @@ -110,6 +110,11 @@ export class DashboardState extends Message { */ leaderboardSortType?: DashboardState_LeaderboardSortType; + /** + * @generated from field: optional string comparison_dimension = 17; + */ + comparisonDimension?: string; + constructor(data?: PartialMessage) { super(); proto3.util.initPartial(data, this); @@ -124,7 +129,7 @@ export class DashboardState extends Message { { no: 4, name: "compare_time_range", kind: "message", T: DashboardTimeRange }, { no: 5, name: "leaderboard_measure", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, { no: 6, name: "selected_dimension", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 7, name: "show_comparison", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, + { no: 7, name: "show_time_comparison", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, { no: 8, name: "visible_measures", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, { no: 9, name: "all_measures_visible", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, { no: 10, name: "visible_dimensions", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, @@ -134,6 +139,7 @@ export class DashboardState extends Message { { no: 14, name: "scrub_range", kind: "message", T: DashboardTimeRange, opt: true }, { no: 15, name: "leaderboard_sort_direction", kind: "enum", T: proto3.getEnumType(DashboardState_LeaderboardSortDirection), opt: true }, { no: 16, name: "leaderboard_sort_type", kind: "enum", T: proto3.getEnumType(DashboardState_LeaderboardSortType), opt: true }, + { no: 17, name: "comparison_dimension", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): DashboardState { diff --git a/web-common/src/runtime-client/error.ts b/web-common/src/runtime-client/error.ts new file mode 100644 index 00000000000..d40388f2ba2 --- /dev/null +++ b/web-common/src/runtime-client/error.ts @@ -0,0 +1,10 @@ +// The Orval-generated type for query errors is `RpcStatus`, but the following is the observed type that is actually returned. +export interface QueryError { + response: { + status: number; + data: { + message: string; + }; + }; + message: string; +} diff --git a/web-common/src/runtime-client/gen/index.schemas.ts b/web-common/src/runtime-client/gen/index.schemas.ts index f4ff9168e75..bb855b6218b 100644 --- a/web-common/src/runtime-client/gen/index.schemas.ts +++ b/web-common/src/runtime-client/gen/index.schemas.ts @@ -937,7 +937,6 @@ export interface V1ProjectParserSpec { sourceStreamIngestion?: boolean; modelDefaultMaterialize?: boolean; modelMaterializeDelaySeconds?: number; - duckdbConnectors?: string[]; } export interface V1ProjectParser { diff --git a/web-common/src/runtime-client/invalidation.ts b/web-common/src/runtime-client/invalidation.ts index 9d5afd17606..5060ba4056d 100644 --- a/web-common/src/runtime-client/invalidation.ts +++ b/web-common/src/runtime-client/invalidation.ts @@ -19,7 +19,7 @@ import { get } from "svelte/store"; // invalidation helpers export function invalidateRuntimeQueries(queryClient: QueryClient) { - return queryClient.invalidateQueries({ + return queryClient.resetQueries({ predicate: (query) => typeof query.queryKey[0] === "string" && query.queryKey[0].startsWith("/v1/instances"), diff --git a/web-local/test/ui/dashboards.spec.ts b/web-local/test/ui/dashboards.spec.ts index 943bdf37358..467a756acb5 100644 --- a/web-local/test/ui/dashboards.spec.ts +++ b/web-local/test/ui/dashboards.spec.ts @@ -173,22 +173,20 @@ test.describe("dashboard", () => { expect(parquetRegex.test(downloadParquet.suggestedFilename())).toBe(true); // Turn off comparison + await page.getByRole("button", { name: "Comparing by Time" }).click(); await page - .getByRole("button", { name: "Comparing to last period" }) - .click(); - await page - .getByLabel("Time comparison selector") - .getByRole("menuitem", { name: "no comparison" }) + .getByLabel("Comparison selector") + .getByRole("menuitem", { name: "No comparison" }) .click(); // Check number await expect(page.getByText("272", { exact: true })).toBeVisible(); // Add comparison back - await page.getByRole("button", { name: "no comparison" }).click(); + await page.getByRole("button", { name: "No comparison" }).click(); await page - .getByLabel("Time comparison selector") - .getByRole("menuitem", { name: "last period" }) + .getByLabel("Comparison selector") + .getByRole("menuitem", { name: "Time" }) .click(); /* @@ -202,7 +200,7 @@ test.describe("dashboard", () => { For now, we will wait for the menu to disappear before clicking the next menu */ - await expect(page.getByLabel("Time comparison selector")).not.toBeVisible(); + await expect(page.getByLabel("Comparison selector")).not.toBeVisible(); // Switch to a custom time range await interactWithTimeRangeMenu(page, async () => { From 207c1378451c5bb07866e31a0a6dbfea2bde53fa Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Thu, 14 Sep 2023 15:23:56 -0700 Subject: [PATCH 57/58] fix merge conflicts --- .../dashboards/leaderboard/Leaderboard.svelte | 77 +-------- .../leaderboard/leaderboard-utils.ts | 162 +++++++++++------- 2 files changed, 104 insertions(+), 135 deletions(-) diff --git a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte index c2ffd51c808..db02b5df8bd 100644 --- a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte +++ b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte @@ -7,7 +7,7 @@ */ import Tooltip from "@rilldata/web-common/components/tooltip/Tooltip.svelte"; import TooltipContent from "@rilldata/web-common/components/tooltip/TooltipContent.svelte"; - import { LeaderboardContextColumn } from "@rilldata/web-common/features/dashboards/leaderboard-context-column"; + // import { LeaderboardContextColumn } from "@rilldata/web-common/features/dashboards/leaderboard-context-column"; import { getFilterForDimension, useMetaDimension, @@ -84,6 +84,8 @@ ?.in ?? []; $: atLeastOneActive = !!activeValues?.length; + $: console.log("activeValues", activeValues); + const timeControlsStore = useTimeControlStore(getStateManagers()); function selectDimension(dimensionName) { @@ -111,51 +113,6 @@ $: querySortType = getQuerySortType(sortType); - // - // - // ======= dhiraj section - let valuesComparedInExcludeMode = []; - $: if (isBeingCompared && filterExcludeMode) { - let count = 0; - valuesComparedInExcludeMode = values - .filter((value) => { - if (!activeValues.includes(value.label) && count < 3) { - count++; - return true; - } - return false; - }) - .map((value) => value.label); - } else { - valuesComparedInExcludeMode = []; - } - - // get all values that are selected but not visible. - // we'll put these at the bottom w/ a divider. - $: selectedValuesThatAreBelowTheFold = activeValues - ?.concat(valuesComparedInExcludeMode) - ?.filter((label) => { - return ( - // the value is visible within the fold. - !values.slice(0, slice).some((value) => { - return value.label === label; - }) - ); - }) - .map((label) => { - const existingValue = values.find((value) => value.label === label); - // return the existing value, or if it does not exist, just return the label. - // FIX ME return values for label which are not in the query - return existingValue ? { ...existingValue } : { label }; - }) - .sort((a, b) => { - return b.value - a.value; - }); - // - // >>>>>>> main --- dhiraj section END - // - // - $: sortedQueryBody = { dimensionName: dimensionName, measureNames: [measure?.name], @@ -205,7 +162,8 @@ ) ?? [], slice, activeValues, - unfilteredTotal + unfilteredTotal, + filterExcludeMode ); aboveTheFold = leaderboardData.aboveTheFold; @@ -215,31 +173,6 @@ } let hovered: boolean; - // <<<<<<< HEAD - // ======= - // from dhirajs branch - - $: comparisonMap = new Map(comparisonValues?.map((v) => [v.label, v.value])); - - $: aboveTheFoldItems = prepareLeaderboardItemData_dhiraj( - values.slice(0, slice), - activeValues, - comparisonMap, - filterExcludeMode - ); - - $: defaultComparisonsPresentInAboveFold = - aboveTheFoldItems?.filter((item) => item.defaultComparedIndex >= 0) - ?.length || 0; - - $: belowTheFoldItems = prepareLeaderboardItemData_dhiraj( - selectedValuesThatAreBelowTheFold, - activeValues, - comparisonMap, - filterExcludeMode, - defaultComparisonsPresentInAboveFold - ); - // >>>>>>> main {#if sortedQuery} diff --git a/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts b/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts index eb10f4c3482..83dc4731a26 100644 --- a/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts +++ b/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts @@ -62,17 +62,23 @@ export type LeaderboardItemData = { // the absolute change from the previous value deltaAbs: number | null; - // selection is not enough to determine if the item is included - // or excluded; for that we need to know the leaderboard's - // include/exclude state + // This is the index of the item from within the list + // selected filters in the dashboard store. + // This index is retained to keep track of selection color? + // Will be -1 if the item is not selected. + // IMPORTANT: either this or defaultComparedIndex must be -1 !!! selectedIndex: number; + + // This is the list index of a default comparison item. + // IMPORTANT: either this or selectedIndex must be -1 !!! defaultComparedIndex: number; }; function cleanUpComparisonValue( v: ComparisonValueWithLabel, total: number | null, - selected: boolean + selectedIndex: number, + defaultComparedIndex: number ): LeaderboardItemData { if (!(Number.isFinite(v.baseValue) || v.baseValue === null)) { throw new Error( @@ -92,8 +98,8 @@ function cleanUpComparisonValue( : null, deltaRel: Number.isFinite(v.deltaRel) ? (v.deltaRel as number) : null, deltaAbs: Number.isFinite(v.deltaAbs) ? (v.deltaAbs as number) : null, - - selected, + selectedIndex, + defaultComparedIndex, }; } @@ -117,7 +123,8 @@ export function prepareLeaderboardItemData( values: ComparisonValueWithLabel[], numberAboveTheFold: number, selectedValues: (string | number)[], - total: number | null + total: number | null, + excludeMode: boolean ): { aboveTheFold: LeaderboardItemData[]; selectedBelowTheFold: LeaderboardItemData[]; @@ -126,32 +133,57 @@ export function prepareLeaderboardItemData( } { const aboveTheFold: LeaderboardItemData[] = []; const selectedBelowTheFold: LeaderboardItemData[] = []; - let selectedValuesCopy = [...selectedValues]; + const comparisonDefaultSelection = getComparisonDefaultSelection( + values, + selectedValues, + excludeMode + ); + + console.log("excludeMode", excludeMode); + console.log("comparisonDefaultSelection", comparisonDefaultSelection); + + // we keep a copy of the selected values array to keep + // track of values that the user has selected but that + // are not included in the latest filtered results returned + // by the API. We'll filter this list as we encounter + // selected values that _are_ in the API results. + // + // We also need to retain the original selection indices + let selectedButNotInAPIResults: [string | number, number][] = + selectedValues.map((v, i) => [v, i]); + values.forEach((v, i) => { - const selected = - selectedValuesCopy.findIndex((value) => value === v.dimensionValue) >= 0; - // drop the value from the selectedValues array so that we'll - // have any left over values that were selected but not included - // in the results returned by the API - if (selected) - selectedValuesCopy = selectedValuesCopy.filter( - (value) => value !== v.dimensionValue + const selectedIndex = selectedValues.findIndex( + (value) => value === v.dimensionValue + ); + // if we have found this selected value in the API results, + // remove it from the selectedButNotInAPIResults array + if (selectedIndex > -1) + selectedButNotInAPIResults = selectedButNotInAPIResults.filter( + (value) => value[0] !== v.dimensionValue ); - if (!excludeMode && count < 3 && !selectedValues.length) { - defaultComparedIndex = count; - count = count + 1; - } else if (excludeMode && count < 3) { - if (selectedIndex === -1) { - defaultComparedIndex = count; - count += 1; - } - } + const defaultComparedIndex = comparisonDefaultSelection.findIndex( + (value) => value === v.dimensionValue + ); + // if we have found this selected value in the API results, + // remove it from the selectedButNotInAPIResults array + + const cleanValue = cleanUpComparisonValue( + v, + total, + selectedIndex, + defaultComparedIndex + ); if (i < numberAboveTheFold) { - aboveTheFold.push(cleanUpComparisonValue(v, total, selected)); - } else if (selected) { - selectedBelowTheFold.push(cleanUpComparisonValue(v, total, selected)); + aboveTheFold.push(cleanValue); + } else if (selectedIndex > -1 || defaultComparedIndex > -1) { + // Note: only one of selectedIndex or defaultComparedIndex + // can be > -1 at one time, so if one is > -1, + // it represents either a selected value or a default selection, + // and must be included in the below-the-fold list. + selectedBelowTheFold.push(cleanValue); } }); @@ -161,10 +193,15 @@ export function prepareLeaderboardItemData( // that pushes it out of the top N. In that case, we will follow // the previous strategy, and just push a dummy value with only // the dimension value and nulls for all measure values. - selectedValuesCopy.forEach((v) => { + selectedButNotInAPIResults.forEach(([dimensionValue, selectedIndex]) => { + const defaultComparedIndex = comparisonDefaultSelection.findIndex( + (value) => value === dimensionValue + ); + selectedBelowTheFold.push({ - dimensionValue: v, - selected: true, + dimensionValue, + selectedIndex, + defaultComparedIndex, value: null, pctOfTotal: null, prevValue: null, @@ -184,40 +221,39 @@ export function prepareLeaderboardItemData( }; } -export function prepareLeaderboardItemData_dhiraj( - values: { value: number; label: string | number }[], +/** + * This returns the "default selection" item labels that + * will be used when a leaderboard has a comparison active + * but no items have been directly selected *and included* + * by the user. + * + * Thus, there are three cases: + * - the leaderboard is in include mode, and there is + * a selection, we DO NOT return a _default selection_, + * because the user has made an _explicit selection_. + * + * - the leaderboard is in include mode, and there is + * _no selection_, we return the first three items. + * + * - the leaderboard is in exclude mode, we return the + * first three items that are not selected. + */ +export function getComparisonDefaultSelection( + values: ComparisonValueWithLabel[], selectedValues: (string | number)[], - comparisonMap: Map, - excludeMode: boolean, - initalCount = 0 -): LeaderboardItemData[] { - let count = initalCount; - - return values.map((v) => { - const selectedIndex = selectedValues.findIndex( - (value) => value === v.label - ); - const comparisonValue = comparisonMap.get(v.label); - - // Tag values which will be compared by default - let defaultComparedIndex = -1; - - if (!excludeMode && count < 3 && !selectedValues.length) { - defaultComparedIndex = count; - count = count + 1; - } else if (excludeMode && count < 3) { - if (selectedIndex === -1) { - defaultComparedIndex = count; - count += 1; - } + excludeMode: boolean +): (string | number)[] { + if (!excludeMode) { + if (selectedValues.length > 0) { + return []; } - return { - ...v, - selectedIndex, - comparisonValue, - defaultComparedIndex, - }; - }); + return values.slice(0, 3).map((value) => value.dimensionValue); + } + + return values + .filter((value) => !selectedValues.includes(value.dimensionValue)) + .map((value) => value.dimensionValue) + .slice(0, 3); } /** From 291ec20d3d276dd7e30067ab2e0c9f4108132a6f Mon Sep 17 00:00:00 2001 From: brendan colloran Date: Tue, 19 Sep 2023 09:13:30 -0700 Subject: [PATCH 58/58] cleanups --- .../src/features/dashboards/leaderboard/Leaderboard.svelte | 3 --- .../src/features/dashboards/leaderboard/leaderboard-utils.ts | 3 --- 2 files changed, 6 deletions(-) diff --git a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte index 3123c8c249c..591ba9f0cc9 100644 --- a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte +++ b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte @@ -7,7 +7,6 @@ */ import Tooltip from "@rilldata/web-common/components/tooltip/Tooltip.svelte"; import TooltipContent from "@rilldata/web-common/components/tooltip/TooltipContent.svelte"; - // import { LeaderboardContextColumn } from "@rilldata/web-common/features/dashboards/leaderboard-context-column"; import { getFilterForDimension, useMetaDimension, @@ -84,8 +83,6 @@ ?.in ?? []; $: atLeastOneActive = !!activeValues?.length; - $: console.log("activeValues", activeValues); - const timeControlsStore = useTimeControlStore(getStateManagers()); function selectDimension(dimensionName) { diff --git a/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts b/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts index 83dc4731a26..ebe2e27d4b1 100644 --- a/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts +++ b/web-common/src/features/dashboards/leaderboard/leaderboard-utils.ts @@ -139,9 +139,6 @@ export function prepareLeaderboardItemData( excludeMode ); - console.log("excludeMode", excludeMode); - console.log("comparisonDefaultSelection", comparisonDefaultSelection); - // we keep a copy of the selected values array to keep // track of values that the user has selected but that // are not included in the latest filtered results returned