Skip to content

Commit

Permalink
Prevent value wrapping and detect context col size
Browse files Browse the repository at this point in the history
  • Loading branch information
bcolloran committed Dec 18, 2023
1 parent 7f2fef8 commit 9e63a05
Show file tree
Hide file tree
Showing 11 changed files with 187 additions and 65 deletions.
2 changes: 1 addition & 1 deletion web-common/src/components/BarAndLabel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
class:pr-2={!compact}
class:pr-1={compact}
class:pl-1={compact}
class="text-right overflow-x-hidden"
class="text-right overflow-hidden"
style="position: relative;"
>
<slot />
Expand Down
5 changes: 4 additions & 1 deletion web-common/src/components/data-types/Base.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
$: color = dark ? "" : "text-gray-900";
</script>

<span class="{classes} {color}">
<span
class=" whitespace-nowrap inline-block {classes} {color}"
style="overflow-wrap: normal"
>
{#if isNull}
<span style:font-size=".925em" class="opacity-50 italic">no data</span>
{:else}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
import type { LeaderboardItemData } from "./leaderboard-utils";
import { formatProperFractionAsPercent } from "@rilldata/web-common/lib/number-formatting/proper-fraction-formatter";
import { formatMeasurePercentageDifference } from "@rilldata/web-common/lib/number-formatting/percentage-formatter";
import { onDestroy, onMount } from "svelte";

Check failure on line 8 in web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte

View workflow job for this annotation

GitHub Actions / build

'onDestroy' is defined but never used

Check failure on line 8 in web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte

View workflow job for this annotation

GitHub Actions / build

'onMount' is defined but never used
export let itemData: LeaderboardItemData;
const {
selectors: {
contextColumn: {
contextColumn,
widthPx,
isDeltaAbsolute,
isDeltaPercent,
Expand All @@ -19,36 +21,59 @@
},
numberFormat: { activeMeasureFormatter },
},
actions: {
contextCol: { observeContextColumnWidth },
},
} = getStateManagers();
$: negativeChange = itemData.deltaAbs !== null && itemData.deltaAbs < 0;
$: noChangeData = itemData.deltaRel === null;
let element: HTMLElement;
$: {
// Re-observe the width when the context column changes,
// but after a short delay to allow the DOM to update.
if (element && $contextColumn) {
setTimeout(() => {
// the element may be gone by the time we get here,
// if so, don't try to observe it
if (!element) return;
observeContextColumnWidth(
$contextColumn,
element.getBoundingClientRect().width
);
}, 17);
}
}
</script>

{#if !$isHidden}
<div style:width={$widthPx}>
{#if $isPercentOfTotal}
<PercentageChange
value={itemData.pctOfTotal
? formatProperFractionAsPercent(itemData.pctOfTotal)
: null}
/>
{:else if noChangeData}
<span class="opacity-50 italic" style:font-size=".925em">no data</span>
{:else if $isDeltaPercent}
<PercentageChange
value={itemData.deltaRel
? formatMeasurePercentageDifference(itemData.deltaRel)
: null}
/>
{:else if $isDeltaAbsolute}
<FormattedDataType
type="INTEGER"
value={itemData.deltaAbs
? $activeMeasureFormatter(itemData.deltaAbs)
: null}
customStyle={negativeChange ? "text-red-500" : ""}
/>
{/if}
<div style:width={$widthPx} class="overflow-hidden">
<div class="inline-block" bind:this={element}>
{#if $isPercentOfTotal}
<PercentageChange
value={itemData.pctOfTotal
? formatProperFractionAsPercent(itemData.pctOfTotal)
: null}
/>
{:else if noChangeData}
<span class="opacity-50 italic" style:font-size=".925em">no data</span>
{:else if $isDeltaPercent}
<PercentageChange
value={itemData.deltaRel
? formatMeasurePercentageDifference(itemData.deltaRel)
: null}
/>
{:else if $isDeltaAbsolute}
<FormattedDataType
type="INTEGER"
value={itemData.deltaAbs
? $activeMeasureFormatter(itemData.deltaAbs)
: null}
customStyle={negativeChange ? "text-red-500" : ""}
/>
{/if}
</div>
</div>
{/if}
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,26 @@
import SelectMenu from "@rilldata/web-common/components/menu/compositions/SelectMenu.svelte";
import type { SelectMenuItem } from "@rilldata/web-common/components/menu/types";
export let metricViewName: string;
// export let metricViewName: string;
export let validPercentOfTotal: boolean;
let metricsExplorer: MetricsExplorerEntity;
$: metricsExplorer = $metricsExplorerStore.entities[metricViewName];
const {
selectors: {
contextColumn: { contextColumn },
},
actions: {
contextCol: { setContextColumn },
},
} = getStateManagers();
const timeControlsStore = useTimeControlStore(getStateManagers());
const handleContextValueButtonGroupClick = (evt) => {
const value: SelectMenuItem = evt.detail;
// CAST SAFETY: the value.key passed up from the evt must
// be a LeaderboardContextColumn
const key = value.key as LeaderboardContextColumn;
metricsExplorerStore.setContextColumn(metricViewName, key);
setContextColumn(key);
};
let options: SelectMenuItem[];
Expand Down Expand Up @@ -47,7 +54,7 @@
// CAST SAFETY: the selection will always be one of the options
$: selection = options.find(
(option) => option.key === metricsExplorer.leaderboardContextColumn
(option) => option.key === $contextColumn
) as SelectMenuItem;
</script>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,17 @@
import { metricsExplorerStore } from "web-common/src/features/dashboards/stores/dashboard-stores";
import { useMetaQuery } from "../selectors";
import LeaderboardContextColumnMenu from "./LeaderboardContextColumnMenu.svelte";
import { getStateManagers } from "../state-managers/state-managers";
export let metricViewName;
const {
actions: {
contextCol: { setContextColumn },
setLeaderboardMeasureName,
},
} = getStateManagers();
$: metaQuery = useMetaQuery($runtime.instanceId, metricViewName);
$: measures = $metaQuery.data?.measures;
Expand All @@ -24,10 +32,7 @@
$: metricsExplorer = $metricsExplorerStore.entities[metricViewName];
function handleMeasureUpdate(event: CustomEvent) {
metricsExplorerStore.setLeaderboardMeasureName(
metricViewName,
event.detail.key
);
setLeaderboardMeasureName(event.detail.key);
}
function measureKeyAndMain(measure: MetricsViewSpecMeasureV2) {
Expand Down Expand Up @@ -93,10 +98,7 @@
metricsExplorer?.leaderboardContextColumn ===
LeaderboardContextColumn.PERCENT
) {
metricsExplorerStore.setContextColumn(
metricViewName,
LeaderboardContextColumn.HIDDEN
);
setContextColumn(LeaderboardContextColumn.HIDDEN);
}
$: showHideDimensions = createShowHideDimensionsStore(
Expand Down Expand Up @@ -145,7 +147,7 @@
on:select={handleMeasureUpdate}
/>

<LeaderboardContextColumnMenu {metricViewName} {validPercentOfTotal} />
<LeaderboardContextColumnMenu {validPercentOfTotal} />
</div>
{:else}
<div
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { LeaderboardContextColumn } from "../../leaderboard-context-column";
import { sortTypeForContextColumnType } from "../../stores/dashboard-stores";
import {
type MetricsExplorerEntity,

Check failure on line 4 in web-common/src/features/dashboards/state-managers/actions/context-columns.ts

View workflow job for this annotation

GitHub Actions / build

'MetricsExplorerEntity' is defined but never used
type ContextColWidths,
contextColWidthDefaults,
} from "../../stores/metrics-explorer-entity";
import type { DashboardMutables } from "./types";

const CONTEXT_COL_MAX_WIDTH = 100;

export const setContextColumn = (
{ dashboard }: DashboardMutables,

Expand All @@ -10,6 +17,10 @@ export const setContextColumn = (
const initialSort = sortTypeForContextColumnType(
dashboard.leaderboardContextColumn
);

// reset context column width to default when changing context column
resetAllContextColumnWidths(dashboard.contextColumnWidths);

switch (contextColumn) {
case LeaderboardContextColumn.DELTA_ABSOLUTE:
case LeaderboardContextColumn.DELTA_PERCENT: {
Expand All @@ -33,10 +44,47 @@ export const setContextColumn = (
}
};

export const resetAllContextColumnWidths = (
contextColumnWidths: ContextColWidths
) => {
for (const contextColumn in contextColumnWidths) {
contextColumnWidths[contextColumn as LeaderboardContextColumn] =
contextColWidthDefaults[contextColumn as LeaderboardContextColumn];
}
console.log(
"resetAllContextColumnWidths",
contextColWidthDefaults,
contextColumnWidths
);
};

/**
* Observe this width value, updating the overall width of
* the context column if the given width is larger than the
* current width.
*/
export const observeContextColumnWidth = (
{ dashboard }: DashboardMutables,
contextColumn: LeaderboardContextColumn,
width: number
) => {
dashboard.contextColumnWidths[contextColumn] = Math.min(
Math.max(width, dashboard.contextColumnWidths[contextColumn]),
CONTEXT_COL_MAX_WIDTH
);
};

export const contextColActions = {
/**
* Updates the dashboard to use the context column of the given type,
* as well as updating to sort by that context column.
*/
setContextColumn,

/**
* Observe this width value, updating the overall width of
* the context column if the given width is larger than the
* current width.
*/
observeContextColumnWidth,
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { resetAllContextColumnWidths } from "./context-columns";
import type { DashboardMutables } from "./types";

export const setLeaderboardMeasureName = (
{ dashboard }: DashboardMutables,
name: string
) => {
dashboard.leaderboardMeasureName = name;

// reset column widths when changing the leaderboard measure
resetAllContextColumnWidths(dashboard.contextColumnWidths);
};
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import { LeaderboardContextColumn } from "../../leaderboard-context-column";
import { contextColWidthDefaults } from "../../stores/metrics-explorer-entity";

Check failure on line 2 in web-common/src/features/dashboards/state-managers/selectors/context-column.ts

View workflow job for this annotation

GitHub Actions / build

'contextColWidthDefaults' is defined but never used
import type { DashboardDataSources } from "./types";

const contextColumnWidth = (contextType: LeaderboardContextColumn): string => {
switch (contextType) {
case LeaderboardContextColumn.DELTA_ABSOLUTE:
case LeaderboardContextColumn.DELTA_PERCENT:
return "56px";
case LeaderboardContextColumn.PERCENT:
return "44px";
case LeaderboardContextColumn.HIDDEN:
return "0px";
default:
throw new Error("Invalid context column, all cases must be handled");
const contextColumnWidth = ({ dashboard }: DashboardDataSources): string => {
const contextType = dashboard.leaderboardContextColumn;
const width = dashboard.contextColumnWidths[contextType];
if (typeof width === "number") {
return width + "px";
}
return "0px";
};

export const contextColSelectors = {
Expand Down Expand Up @@ -61,6 +57,5 @@ export const contextColSelectors = {
* returns a css style string specifying the width of the context
* column in the leaderboards.
*/
widthPx: ({ dashboard }: DashboardDataSources) =>
contextColumnWidth(dashboard.leaderboardContextColumn),
widthPx: contextColumnWidth,
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import {
SortDirection,
SortType,
} from "@rilldata/web-common/features/dashboards/proto-state/derived-types";
import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity";
import {
contextColWidthDefaults,
type MetricsExplorerEntity,
} from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity";
import { getLocalUserPreferences } from "@rilldata/web-common/features/dashboards/user-preferences";
import { getTimeComparisonParametersForComponent } from "@rilldata/web-common/lib/time/comparisons";
import { DEFAULT_TIME_RANGES } from "@rilldata/web-common/lib/time/config";
Expand Down Expand Up @@ -106,20 +109,27 @@ export function getDefaultMetricsExplorerEntity(
name: string,
metricsView: V1MetricsViewSpec,
fullTimeRange: V1ColumnTimeRangeResponse | undefined
) {
): MetricsExplorerEntity {
// CAST SAFETY: safe b/c (1) measure.name is a string if defined,
// and (2) we filter out undefined values
const defaultMeasureNames = (metricsView?.measures
?.map((measure) => measure?.name)
.filter((name) => name !== undefined) ?? []) as string[];

// CAST SAFETY: safe b/c (1) measure.name is a string if defined,
// and (2) we filter out undefined values
const defaultDimNames = (metricsView?.dimensions
?.map((dim) => dim.name)
.filter((name) => name !== undefined) ?? []) as string[];

const metricsExplorer: MetricsExplorerEntity = {
name,
selectedMeasureNames: metricsView.measures.map((measure) => measure.name),

visibleMeasureKeys: new Set(
metricsView.measures.map((measure) => measure.name)
),
selectedMeasureNames: [...defaultMeasureNames],
visibleMeasureKeys: new Set(...defaultMeasureNames),
allMeasuresVisible: true,
visibleDimensionKeys: new Set(
metricsView.dimensions.map((dim) => dim.name)
),
visibleDimensionKeys: new Set(...defaultDimNames),
allDimensionsVisible: true,
leaderboardMeasureName: metricsView.measures[0]?.name,
leaderboardMeasureName: defaultMeasureNames[0],
filters: {
include: [],
exclude: [],
Expand All @@ -132,6 +142,7 @@ export function getDefaultMetricsExplorerEntity(
showTimeComparison: false,
dimensionSearchText: "",
pinIndex: -1,
contextColumnWidths: { ...contextColWidthDefaults },
};
// set time range related stuff
setDefaultTimeRange(metricsView, metricsExplorer, fullTimeRange);
Expand Down
Loading

0 comments on commit 9e63a05

Please sign in to comment.