Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Major overhaul of charts #44

Merged
merged 22 commits into from
Jun 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## [Unreleased]
- Add refresh button
- clean up styling
- add date picker to single chart views
- add toggle for showing/hiding PromQL queries
- add support for the "module" label for certain graphs

## [0.4.0]
- Add date picker to the function metrics panel
- [chore] update TypeScript plugin dependency
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,15 @@
"@msgpack/msgpack": "^3.0.0-beta2",
"byte-base64": "^1.1.0",
"fiberplane-charts": "git+https://git@github.com/fiberplane/fiberplane.git#workspace=fiberplane-charts",
"framer-motion": "^10.12.16",
"node-fetch": "^2.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-is": "^18.2.0",
"react-syntax-highlighter": "^15.5.0",
"styled-components": "^5.3.10",
"typescript": "^4.9.5"
"typescript": "^5.0.0",
"valtio": "^1.10.5"
},
"packageManager": "yarn@3.5.0"
}
41 changes: 33 additions & 8 deletions src/chartPanel.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import * as vscode from "vscode";
import type { TimeRange } from "fiberplane-charts";

import { formatProviderError } from "./providerRuntime/errors";
import type { MessageFromWebview, MessageToWebview } from "./charts";
import { OPEN_PANEL_COMMAND } from "./constants";
import type { Prometheus } from "./prometheus";
import { getNonce, getTitle } from "./utils";
import { createDefaultTimeRange, getNonce, getTitle } from "./utils";

/**
* Options for the kind of chart to display.
Expand All @@ -19,6 +20,11 @@ export type PanelOptions =
| SingleChartOptions
| { type: "function_graphs"; functionName: string; moduleName?: string };

export type GlobalGraphSettings = {
timeRange: TimeRange;
showingQuery: boolean;
};

type ChartPanel = {
/**
* Reveals the panel.
Expand Down Expand Up @@ -63,7 +69,11 @@ function createChartPanel(
prometheus: Prometheus,
options: PanelOptions,
): ChartPanel {
let currentOptions = options;
let currentOptions: PanelOptions & GlobalGraphSettings = {
...options,
timeRange: createDefaultTimeRange(),
showingQuery: false,
};
const panel = vscode.window.createWebviewPanel(
"autometricsChart",
getTitle(options),
Expand All @@ -76,9 +86,11 @@ function createChartPanel(
}

function update(options: PanelOptions) {
currentOptions = options;
const timeRange = currentOptions?.timeRange || createDefaultTimeRange();
const showingQuery = currentOptions?.showingQuery || false;
currentOptions = { ...options, timeRange, showingQuery };
panel.title = getTitle(options);
postMessage({ type: "show_panel", options });
postMessage({ type: "show_panel", options: currentOptions });
}

panel.webview.onDidReceiveMessage(
Expand All @@ -92,7 +104,7 @@ function createChartPanel(
prometheus
.fetchTimeseries(query, timeRange)
.then((data) => {
postMessage({ type: "show_data", timeRange, data, id });
postMessage({ type: "show_data", data, id });
})
.catch((error: unknown) => {
const errorMessage = formatProviderError(error);
Expand All @@ -102,12 +114,25 @@ function createChartPanel(
id,
error: errorMessage,
});
vscode.window.showErrorMessage(
`Could not query Prometheus. Query: ${query} Error: ${errorMessage}`,
);

if (options.type !== "function_graphs") {
vscode.window.showErrorMessage(
`Could not query Prometheus. Query: ${query} Error: ${errorMessage}`,
);
}
});
return;
}
case "update_time_range": {
const { timeRange } = message;
currentOptions = { ...currentOptions, timeRange };
return;
}
case "update_showing_query": {
const { showingQuery } = message;
currentOptions = { ...currentOptions, showingQuery };
return;
}
}
},
undefined,
Expand Down
20 changes: 13 additions & 7 deletions src/charts/components/Button/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ButtonHTMLAttributes } from "react";
import { ButtonHTMLAttributes, forwardRef } from "react";
import styled from "styled-components";

type ButtonStyle = "primary" | "secondary";
Expand All @@ -7,14 +7,17 @@ type Props = {
buttonStyle?: ButtonStyle;
} & ButtonHTMLAttributes<HTMLButtonElement>;

export function Button(props: Props): JSX.Element {
export const Button = forwardRef(function Button(
props: Props,
ref: React.ForwardedRef<HTMLButtonElement>,
): JSX.Element {
const { children, className = "", buttonStyle = "primary", ...rest } = props;
return (
<StyledButton {...rest} className={`${className} ${buttonStyle}`}>
<StyledButton {...rest} ref={ref} className={`${className} ${buttonStyle}`}>
{children}
</StyledButton>
);
}
});

const StyledButton = styled.button`
--button-foreground: var(--vscode-button-foreground);
Expand All @@ -23,19 +26,22 @@ const StyledButton = styled.button`

border: none;
padding: var(--input-padding-vertical) var(--input-padding-horizontal);
/* width: 100%; */
text-align: center;
outline: 1px solid transparent;
outline-offset: 2px !important;
color: var(--button-foreground);
background: var(--button-background);

&:hover {
[disabled] {
pointer-events: none;
}

&:not([disabled]):hover {
cursor: pointer;
background: var(--button-hoverBackground);
}

&:focus {
&:not([disabled]):focus {
outline-color: var(--vscode-focusBorder);
}

Expand Down
53 changes: 50 additions & 3 deletions src/charts/components/CodeBlock/CodeBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,57 @@
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { styles } from "./prismStyles";
import styled from "styled-components";
import { Button } from "../Button";
import { Copy } from "./Copy";
import { useRef } from "react";
import { pxToEm } from "../../utils";

export function CodeBlock({ query }: { query: string }) {
const ref = useRef<HTMLDivElement | null>(null);
return (
<SyntaxHighlighter language="promql" style={styles}>
{query}
</SyntaxHighlighter>
<Container>
<CodeContainer ref={ref}>
<SyntaxHighlighter language="promql" style={styles}>
{query}
</SyntaxHighlighter>
</CodeContainer>
<StyledButton
onClick={() => {
navigator.clipboard.writeText(query);
if (ref.current && window.getSelection) {
const selection = window.getSelection();
const range = document.createRange();
range.selectNodeContents(ref.current);
if (selection) {
selection.removeAllRanges();
selection.addRange(range);
}
return;
}
}}
>
<Copy width="17" height="17" />
</StyledButton>
</Container>
);
}

const Container = styled.div`
display: grid;
grid-template-columns: auto 38px;
background:var(--vscode-dropdown-background, --vscode-editor-background, #ffffff);
margin: 0;
margin-top: ${pxToEm(13)};
border-radius: ${pxToEm(4)};
border: 1px solid var(--vscode-dropdown-border, --vscode-widget-border, #ccc);
`;

const CodeContainer = styled.div`
overflow: auto;
`;

const StyledButton = styled(Button)`
border-radius: var(--vscode-corner-size, ${pxToEm(4)});
border-top-left-radius: 0;
border-bottom-left-radius: 0;
`;
27 changes: 27 additions & 0 deletions src/charts/components/CodeBlock/Copy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as React from "react";
export const Copy = (props: React.SVGAttributes<HTMLOrSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={26}
height={26}
fill="none"
viewBox="0 0 26 26"
{...props}
>
<title>Copy</title>
<path
fill="currentColor"
fillOpacity={0.4}
fillRule="evenodd"
d="M21 5H8.714v12.286H21V5ZM8.714 3h-2v16.286H23V3H8.714Z"
clipRule="evenodd"
/>
<path
fill="currentColor"
fillOpacity={0.4}
fillRule="evenodd"
d="M5 7v14h14v2H3V7h2Z"
clipRule="evenodd"
/>
</svg>
);
9 changes: 2 additions & 7 deletions src/charts/components/CodeBlock/prismStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,10 @@ export const styles: { [key: string]: React.CSSProperties } = {
wordWrap: "normal",
fontFamily: "var(--vscode-editor-font-family, monospace)",
fontSize: "var(--vscode-editor-font-size, 14px)",
color: "var(--vscode-foreground, #76d9e6)",
color: "var(--vscode-dropdown-foreground, --vscode-foreground, #76d9e6)",
textShadow: "none",
background: "var(--vscode-editor-background, #ffffff)",
padding: "12px",
borderRadius: "4px",
border: "1px solid var(--vscode-widget-border, #ccc)",
overflow: "auto",
margin: "0",
position: "relative",
},
'pre > code[class*="language-"]': {
Expand All @@ -42,8 +39,6 @@ export const styles: { [key: string]: React.CSSProperties } = {
background: "var(--vscode-editor-background, #ffffff)",
padding: "0.15em 0.2em 0.05em",
borderRadius: ".3em",
border: "0.13em solid #7a6652",
boxShadow: "1px 1px 0.3em -0.1em #000 inset",
},
'pre[class*="language-"] code': {
whiteSpace: "pre",
Expand Down
36 changes: 29 additions & 7 deletions src/charts/components/DatePicker/DatePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TimeRange } from "fiberplane-charts";
import { Button } from "../Button";
import styled from "styled-components";
import { useState } from "react";
import { useEffect, useRef, useState } from "react";
import { DatePickerContent } from "./DatePickerContent";
import { useHandler } from "../../hooks";
import { Clock } from "./Clock";
Expand All @@ -15,21 +15,47 @@ type Props = {

export function DatePicker(props: Props) {
const [opened, setOpened] = useState(false);
const contentRef = useRef<HTMLDivElement | null>(null);
const buttonRef = useRef<HTMLButtonElement | null>(null);

const handler = useHandler((timeRange: TimeRange) => {
setOpened(false);
props.onChange(timeRange);
});

useEffect(() => {
const handler = (event: MouseEvent) => {
if (!contentRef.current) {
return;
}

if (
contentRef.current.contains(event.target as Node) ||
buttonRef.current?.contains(event.target as Node)
) {
return;
}

setOpened(false);
};

document.addEventListener("click", handler);
return () => document.removeEventListener("click", handler);
}, []);

return (
<>
<StyledButton buttonStyle="secondary" onClick={() => setOpened(!opened)}>
<StyledButton
buttonStyle="secondary"
onClick={() => setOpened(!opened)}
ref={buttonRef}
>
<Clock />
{props.timeRange.from} - {props.timeRange.to}
<CaretDown />
</StyledButton>
{opened && (
<Content>
<Content ref={contentRef}>
<DatePickerContent timeRange={props.timeRange} onChange={handler} />
</Content>
)}
Expand All @@ -47,9 +73,6 @@ const StyledButton = styled(Button)`
background: var(--vscode-editorWidget-background, transparent);
color: var(--vscode-editorWidget-foreground, transparent);
border: 1px solid var(--vscode-editorWidget-border, transparent);
/* background: var(--vscode-dropdown-background, transparent);
color: var(--vscode-dropdown-foreground, transparent);
border: 1px solid var(--vscode-dropdown-border, transparent); */
display: flex;
align-items: center;
gap: ${pxToEm(10)};
Expand All @@ -60,6 +83,5 @@ const StyledButton = styled(Button)`
&:hover {
background: var(--vscode-editorWidget-foreground, transparent);
color: var(--vscode-editorWidget-background, transparent);
/* background: var(--vscode-editorWidget-border, transparent); */
}
`;
7 changes: 2 additions & 5 deletions src/charts/components/DatePicker/DatePickerContent.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import * as React from "react";
import { TimeRange, Timestamp } from "fiberplane-charts";
import { useHandler } from "../../hooks";
import { MonthTable } from "./MonthTable";
import styled, { css } from "styled-components";
import { Button } from "../Button";
import { pxToEm, validateTimeRange } from "../../utils";
import { FormEvent, useRef, useState } from "react";
import { pxToEm } from "../../utils";
import { useState } from "react";
import { DatePickerForm } from "./DatePickerForm";
import { TimeRangePresets } from "./TimeRangePresets";

Expand Down
17 changes: 17 additions & 0 deletions src/charts/components/ErrorMessage/ErrorIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as React from "react";
export const ErrorIcon = (props: React.SVGAttributes<HTMLOrSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={23}
height={20}
fill="none"
viewBox="0 0 23 20"
{...props}
>
<title>Error icon</title>
<path
fill="currentColor"
d="M22.906 19.464 11.921.178a.35.35 0 0 0-.608 0L5.816 9.821.323 19.464a.362.362 0 0 0 0 .356.35.35 0 0 0 .303.18h21.978a.35.35 0 0 0 .303-.18.363.363 0 0 0 0-.356ZM12.67 17.143H10.56V15h2.11v2.143Zm0-3.572H10.56V8.096h2.11v5.475Z"
/>
</svg>
);
Loading