Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,8 @@ export const ANNOTATION_COLOR_NO_DATA = `orange`;
export const ANNOTATION_COLOR_CHECK_CREATED = `yellow`;
export const ANNOTATION_COLOR_CHECK_UPDATED = `blue`;
export const ANNOTATION_COLOR_ALERTS_FIRING = `red`;

// Selection styling constants
export const NON_SELECTED_BAR_OPACITY = 0.7;
export const SELECTED_BAR_BORDER_WIDTH = 3;
export const BAR_BORDER_WIDTH = 2;
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import {
useBuiltCheckConfigs,
useCurrentAdjustedTime,
useExecutionDurationLogs,
useIsInitialised,
useIsListResultPending,
usePersistedMaxProbeDuration,
useSceneAnnotationEvents,
Expand Down Expand Up @@ -73,6 +72,7 @@ interface TimepointExplorerContextType {
handleMiniMapPageChange: (page: number) => void;
handleMiniMapSectionChange: (sectionIndex: number) => void;
handleRefetch: () => void;
handleSetScrollToViewer: (shouldScroll: boolean) => void;
handleTimepointWidthChange: (timepointWidth: number, currentSectionRange: MiniMapSection) => void;
handleViewerStateChange: (state: ViewerState) => void;
handleViewModeChange: (viewMode: ViewMode) => void;
Expand All @@ -82,7 +82,6 @@ interface TimepointExplorerContextType {
isCheckCreationWithinTimeRange: boolean;
isError: boolean;
isFetching: boolean;
isInitialised: boolean;
isLoading: boolean;
isLogsRetentionPeriodWithinTimerange: boolean;
listLogsMap: Record<UnixTimestamp, StatefulTimepoint>;
Expand All @@ -97,6 +96,7 @@ interface TimepointExplorerContextType {
timepointWidth: number;
viewerState: ViewerState;
viewMode: ViewMode;
shouldScrollToViewer: boolean;
vizDisplay: VizDisplay;
vizOptions: Record<TimepointStatus, string>;
yAxisMax: number;
Expand Down Expand Up @@ -226,6 +226,7 @@ export const TimepointExplorerProvider = ({ children, check }: TimepointExplorer
const isError = isCheckConfigsError || isExecutionDurationLogsError || isMaxProbeDurationError;

const [viewerState, setViewerState] = useState<ViewerState>([]);
const [shouldScrollToViewer, setShouldScrollToViewer] = useState(false);

const handleMiniMapSectionChange = useCallback((sectionIndex: number) => {
setMiniMapCurrentSectionIndex(sectionIndex);
Expand All @@ -245,6 +246,10 @@ export const TimepointExplorerProvider = ({ children, check }: TimepointExplorer
setViewerState(state);
}, []);

const handleSetScrollToViewer = useCallback((shouldScroll: boolean) => {
setShouldScrollToViewer(shouldScroll);
}, []);

const handleTimepointDisplayCountChange = useCallback(
(count: number, currentSectionRange: MiniMapSection) => {
const newMiniMapPages = getMiniMapPages(timepoints.length, count, MAX_MINIMAP_SECTIONS);
Expand Down Expand Up @@ -321,14 +326,6 @@ export const TimepointExplorerProvider = ({ children, check }: TimepointExplorer
listLogsMap,
});

const isInitialised = useIsInitialised({
check,
isLoading,
handleViewerStateChange,
timepoints,
currentAdjustedTime,
});

const renderingStrategy = getRenderingStrategy({
isLogsRetentionPeriodWithinTimerange,
timepoints,
Expand Down Expand Up @@ -358,11 +355,11 @@ export const TimepointExplorerProvider = ({ children, check }: TimepointExplorer
handleViewModeChange,
handleVizDisplayChange,
handleVizOptionChange,
handleSetScrollToViewer,
hoveredState,
isCheckCreationWithinTimeRange,
isError,
isFetching,
isInitialised,
isLoading,
isLogsRetentionPeriodWithinTimerange,
listLogsMap,
Expand All @@ -377,6 +374,7 @@ export const TimepointExplorerProvider = ({ children, check }: TimepointExplorer
timepointWidth,
viewerState,
viewMode,
shouldScrollToViewer,
vizDisplay,
vizOptions,
yAxisMax,
Expand All @@ -398,11 +396,11 @@ export const TimepointExplorerProvider = ({ children, check }: TimepointExplorer
handleViewModeChange,
handleVizDisplayChange,
handleVizOptionChange,
handleSetScrollToViewer,
hoveredState,
isCheckCreationWithinTimeRange,
isError,
isFetching,
isInitialised,
isLoading,
isLogsRetentionPeriodWithinTimerange,
listLogsMap,
Expand All @@ -417,6 +415,7 @@ export const TimepointExplorerProvider = ({ children, check }: TimepointExplorer
timepointWidth,
viewerState,
viewMode,
shouldScrollToViewer,
vizDisplay,
vizOptions,
yAxisMax,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,13 @@ import {
TimepointStatus,
TimepointVizOptions,
UnixTimestamp,
ViewerState,
} from 'scenes/components/TimepointExplorer/TimepointExplorer.types';
import {
buildConfigTimeRanges,
buildlistLogsMap,
buildTimepoints,
extractFrequenciesAndConfigs,
getCouldBePending,
getIsInTheFuture,
getPendingProbeNames,
getTimeAdjustedTimepoint,
getVisibleTimepoints,
Expand Down Expand Up @@ -475,48 +473,6 @@ export function useCurrentAdjustedTime(check: Check) {
return currentAdjustedTime;
}

interface UseIsInitialisedProps {
check: Check;
currentAdjustedTime: UnixTimestamp;
handleViewerStateChange: (viewerState: ViewerState) => void;
isLoading: boolean;
timepoints: StatelessTimepoint[];
}

export function useIsInitialised({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How confident are we we don't need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll put it back. I initially had to remove it because the initialization selects a timepoint, so it would auto-scroll on page-load. I then added the shouldScrollToViewer flag that only enables on timepoint click, so that issue won't be the case anymore

check,
isLoading,
handleViewerStateChange,
timepoints,
currentAdjustedTime,
}: UseIsInitialisedProps) {
const probeVar = useSceneVarProbes(check);
const persistedIsLoading = usePersisted(isLoading, (isLoading) => !isLoading);

const handleOnInitialised = useCallback(() => {
const notInTheFuture = timepoints.filter((t) => !getIsInTheFuture(t, currentAdjustedTime));
// todo: add logic to pick a sensible entry depending on how close a timepoint is to the creation date
// e.g. if you create a time point with two seconds until the end of the timepoint, the user will essentially
// see a missing result as the first entry
// might make sense to bump the creation time artificially so it aligns with the timepoint?
const lastNotInTheFuture = notInTheFuture[notInTheFuture.length - 1] || timepoints[0];
const firstProbe = probeVar[0];

handleViewerStateChange([lastNotInTheFuture, firstProbe, 0]);
}, [currentAdjustedTime, probeVar, timepoints, handleViewerStateChange]);

useEffect(() => {
// we have to wait until we have the checkConfigs and subsequent timepoints built
// before knowing what timepoints are available and what to select
if (!persistedIsLoading) {
handleOnInitialised();
}
// eslint-disable-next-line react-hooks/exhaustive-deps -- we only want to run this once
}, [persistedIsLoading]);

return !persistedIsLoading;
}

export function useSelectedProbeNames(statefulTimepoint: StatefulTimepoint) {
const { check, checkConfigs } = useTimepointExplorerContext();
const latestConfigDate = checkConfigs[checkConfigs.length - 1].from;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const TimepointListEntry = ({ timepoint }: TimepointListEntryProps) => {
};

const Entry = (props: TimepointListEntryProps) => {
const { check, currentAdjustedTime, isInitialised, isLoading, viewMode } = useTimepointExplorerContext();
const { check, currentAdjustedTime, isLoading, viewMode } = useTimepointExplorerContext();
const statefulTimepoint = useStatefulTimepoint(props.timepoint);
const isInTheFuture = getIsInTheFuture(props.timepoint, currentAdjustedTime);
const selectedProbeNames = useSceneVarProbes(check);
Expand All @@ -46,7 +46,7 @@ const Entry = (props: TimepointListEntryProps) => {
return <div />;
}

if (isEntryLoading || !isInitialised) {
if (isEntryLoading) {
return <TimepointListEntryLoading />;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@ import { GrafanaTheme2 } from '@grafana/data';
import { Icon, styleMixins, Tooltip, useStyles2 } from '@grafana/ui';
import { css, cx } from '@emotion/css';
import { TimepointDetailsClick, trackTimepointDetailsClicked } from 'features/tracking/timepointExplorerEvents';
import { DataTestIds } from 'test/dataTestIds';

import { PlainButton } from 'components/PlainButton';
import { TIMEPOINT_GAP_PX } from 'scenes/components/TimepointExplorer/TimepointExplorer.constants';
import {
BAR_BORDER_WIDTH,
NON_SELECTED_BAR_OPACITY,
SELECTED_BAR_BORDER_WIDTH,
TIMEPOINT_GAP_PX,
} from 'scenes/components/TimepointExplorer/TimepointExplorer.constants';
import { useTimepointExplorerContext } from 'scenes/components/TimepointExplorer/TimepointExplorer.context';
import {
useSelectedProbeNames,
Expand All @@ -32,23 +38,24 @@ export const TimepointListEntryBar = ({
timepoint,
}: TimepointListEntryPendingProps) => {
const statefulTimepoint = useStatefulTimepoint(timepoint);
const { handleViewerStateChange, yAxisMax, viewerState, timepointWidth, vizDisplay } = useTimepointExplorerContext();
const { handleViewerStateChange, handleSetScrollToViewer, yAxisMax, viewerState, timepointWidth, vizDisplay } = useTimepointExplorerContext();
const selectedProbeNames = useSelectedProbeNames(statefulTimepoint);

const height = getEntryHeight(statefulTimepoint.maxProbeDuration, yAxisMax);
const styles = useStyles2(getStyles, timepointWidth, height);
const probeNameToView = selectedProbeNames.sort((a, b) => a.localeCompare(b))[0];
const [viewerTimepoint] = viewerState;
const isSelected = viewerTimepoint?.adjustedTime === timepoint.adjustedTime;
const styles = useStyles2(getStyles, timepointWidth, height, isSelected, !!viewerTimepoint);
const ref = useRef<HTMLButtonElement>(null);

const handleViewerStateClick = useCallback(() => {
trackTimepointDetailsClicked({
component: analyticsEventName,
status,
});
handleSetScrollToViewer(true);
handleViewerStateChange([timepoint, probeNameToView, 0]);
}, [analyticsEventName, status, timepoint, probeNameToView, handleViewerStateChange]);
}, [analyticsEventName, status, timepoint, probeNameToView, handleViewerStateChange, handleSetScrollToViewer]);

if (!vizDisplay.includes(status)) {
return <div />;
Expand All @@ -62,7 +69,7 @@ export const TimepointListEntryBar = ({
</div>
)}
<Tooltip content={<TimepointListEntryTooltip timepoint={timepoint} />} ref={ref} interactive placement="top">
<PlainButton className={styles.button} ref={ref} onClick={handleViewerStateClick} showFocusStyles={false}>
<PlainButton className={styles.button} ref={ref} onClick={handleViewerStateClick} showFocusStyles={false} data-testid={`${DataTestIds.TimepointListEntryBar}-${timepoint.index}`}>
<TimepointVizItem
className={cx(styles.bar, GLOBAL_CLASS, {
[styles.selected]: isSelected,
Expand All @@ -77,13 +84,20 @@ export const TimepointListEntryBar = ({
);
};

const getStyles = (theme: GrafanaTheme2, timepointWidth: number, height: number) => {
const getStyles = (
theme: GrafanaTheme2,
timepointWidth: number,
height: number,
isSelected: boolean,
hasSelection: boolean,
) => {
return {
container: css`
height: ${height}%;
display: flex;
flex-direction: column;
align-items: center;
opacity: ${hasSelection && !isSelected ? NON_SELECTED_BAR_OPACITY : 1};
`,
button: css`
width: calc(${timepointWidth}px + ${TIMEPOINT_GAP_PX}px);
Expand Down Expand Up @@ -131,7 +145,7 @@ const getStyles = (theme: GrafanaTheme2, timepointWidth: number, height: number)
}
`,
selected: css`
border-width: 2px;
border-width: ${isSelected ? SELECTED_BAR_BORDER_WIDTH : BAR_BORDER_WIDTH}px;
z-index: 1;
`,
selectedIcon: css`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { trackTimepointDetailsClicked } from 'features/tracking/timepointExplore
import { LokiFieldNames } from 'features/parseLokiLogs/parseLokiLogs.types';
import { PlainButton } from 'components/PlainButton';
import {
BAR_BORDER_WIDTH,
NON_SELECTED_BAR_OPACITY,
SELECTED_BAR_BORDER_WIDTH,
TIMEPOINT_GAP_PX,
TIMEPOINT_THEME_HEIGHT_PX,
} from 'scenes/components/TimepointExplorer/TimepointExplorer.constants';
Expand All @@ -29,6 +32,7 @@ const ICON_MAP: Record<number, IconName> = {
export const TimepointListEntryReachability = ({ timepoint }: TimepointListEntryProps) => {
const {
handleHoverStateChange,
handleSetScrollToViewer,
handleViewerStateChange,
hoveredState,
timepointWidth,
Expand All @@ -37,9 +41,12 @@ export const TimepointListEntryReachability = ({ timepoint }: TimepointListEntry
yAxisMax,
} = useTimepointExplorerContext();
const statefulTimepoint = useStatefulTimepoint(timepoint);
const styles = useStyles2(getStyles, timepointWidth);
const entryHeight = getEntryHeight(statefulTimepoint.maxProbeDuration, yAxisMax);
const [hoveredTimepoint, hoveredProbeName, hoveredExecutionIndex] = hoveredState || [];
const [viewerTimepoint] = viewerState;
const hasSelection = !!viewerTimepoint;
const isTimepointSelected = viewerTimepoint?.adjustedTime === timepoint.adjustedTime;
const styles = useStyles2(getStyles, timepointWidth, hasSelection, isTimepointSelected);

// add the timepoint size to the height so the entries are rendered in the middle of the Y Axis line
const height = `calc(${entryHeight}% + ${timepointWidth}px)`;
Expand All @@ -59,9 +66,10 @@ export const TimepointListEntryReachability = ({ timepoint }: TimepointListEntry
component: 'reachability-entry',
status: statefulTimepoint.status,
});
handleSetScrollToViewer(true);
handleViewerStateChange([statefulTimepoint, probeName, index]);
},
[statefulTimepoint, handleViewerStateChange]
[statefulTimepoint, handleViewerStateChange, handleSetScrollToViewer]
);

if (!executionsToRender.length) {
Expand Down Expand Up @@ -121,7 +129,7 @@ export const TimepointListEntryReachability = ({ timepoint }: TimepointListEntry
);
};

const getStyles = (theme: GrafanaTheme2, timepointWidth: number) => {
const getStyles = (theme: GrafanaTheme2, timepointWidth: number, hasSelection: boolean, isTimepointSelected: boolean) => {
return {
timepoint: css`
display: flex;
Expand All @@ -140,6 +148,7 @@ const getStyles = (theme: GrafanaTheme2, timepointWidth: number) => {
position: relative;
left: 50%;
transform: translateX(-50%);
opacity: ${hasSelection && !isTimepointSelected ? NON_SELECTED_BAR_OPACITY : 1};
`,
executionContainer: css`
position: absolute;
Expand Down Expand Up @@ -170,7 +179,7 @@ const getStyles = (theme: GrafanaTheme2, timepointWidth: number) => {
}
`,
viewed: css`
border-width: 2px;
border-width: ${isTimepointSelected ? SELECTED_BAR_BORDER_WIDTH : BAR_BORDER_WIDTH}px;
z-index: 1;
`,
hovered: css`
Expand Down
Loading
Loading