Commit e5d83b95 authored by Vihang Mehta's avatar Vihang Mehta
Browse files

Move quantiles-box-whisker into pixie-components

Summary: TSIA

Test Plan: `yarn dev` and storybook

Reviewers: zasgar, michelle, nserrino, #engineering

Reviewed By: zasgar, #engineering

Differential Revision: https://phab.corp.pixielabs.ai/D6746

GitOrigin-RevId: 58c50f7e0a85022df0d60d81bac01baa62c810e4
parent 214abc99
Showing with 85 additions and 74 deletions
+85 -74
# Steps to run the UI locally and point to the staging cloud backend:
# Steps to run the UI locally and point to the prod cloud backend:
## Export environment variables for webpack
```
......@@ -16,13 +16,13 @@ mkcert -install
mkcert \
-cert-file $SELFSIGN_CERT_FILE \
-key-file $SELFSIGN_CERT_KEY \
staging.withpixie.dev "*.staging.withpixie.dev" localhost 127.0.0.1 ::1
prod.withpixie.ai "*.prod.withpixie.ai" localhost 127.0.0.1 ::1
```
## Add the following domain to /etc/hosts, or /private/etc/hosts for Mac
Replace site-name with your test site name.
```
127.0.0.1 staging.withpixie.dev <site-name>.staging.withpixie.dev id.staging.withpixie.dev
127.0.0.1 prod.withpixie.ai <site-name>.prod.withpixie.ai id.prod.withpixie.ai
```
## Run the webpack devserver
......@@ -30,4 +30,9 @@ Replace site-name with your test site name.
cd src/ui
yarn install
./node_modules/.bin/webpack-dev-server
```
\ No newline at end of file
```
## Access the frontend on the browser
Navigate to https://prod.withpixie.ai:8080/
Note the https and port. If you are not logged in, log in at work.withpixie.ai because
as of writing this, auth0 doesn't accept callbacks to prod.withpixie.ai:8080
\ No newline at end of file
......@@ -31,7 +31,10 @@
"react-dom": "^16.12.0",
"react-draggable": "^4.0.3",
"react-monaco-editor": "^0.36.0",
"react-split": "^2.0.7"
"react-split": "^2.0.7",
"react-vega": "^7.3.0",
"vega-embed": "^6.9.0",
"vega-tooltip": "^0.23.0"
},
"devDependencies": {
"@babel/core": "^7.0.0-rc.2",
......@@ -42,6 +45,7 @@
"@rollup/plugin-node-resolve": "^10.0.0",
"@rollup/plugin-url": "^5.0.1",
"@svgr/rollup": "^5.4.0",
"@types/node": "^14.0.14",
"@types/jest": "24.0.18",
"@types/react": "^16.9.11",
"@types/react-dom": "^16.9.4",
......@@ -67,12 +71,15 @@
"react-draggable": "^4.0.3",
"react-monaco-editor": "^0.36.0",
"react-split": "^2.0.7",
"react-vega": "^7.3.0",
"rollup": "^2.33.1",
"rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-postcss": "^3.1.8",
"rollup-plugin-typescript2": "^0.29.0",
"tslib": "^2.0.3",
"typescript": "^3.7.2"
"typescript": "^3.7.2",
"vega-embed": "^6.9.0",
"vega-tooltip": "^0.23.0"
},
"dependencies": {
"clsx": "^1.0.4"
......
import * as React from 'react';
import { Vega as ReactVega } from 'react-vega';
import { Vega } from 'react-vega';
import { VisualizationSpec } from 'vega-embed';
import { Handler } from 'vega-tooltip';
import { GaugeLevel } from 'utils/metric-thresholds';
import {
createStyles, Theme, useTheme, withStyles, WithStyles,
} from '@material-ui/core/styles';
import clsx from 'clsx';
function getColor(level: GaugeLevel, theme: Theme): string {
switch (level) {
case 'low':
return theme.palette.success.main;
case 'med':
return theme.palette.warning.main;
case 'high':
return theme.palette.error.main;
default:
return theme.palette.text.primary;
}
}
interface QuantilesBoxWhiskerFields {
p50: number;
......@@ -279,15 +264,6 @@ const styles = (theme: Theme) => createStyles({
marginLeft: 5,
marginRight: 10,
},
low: {
color: getColor('low', theme),
},
med: {
color: getColor('med', theme),
},
high: {
color: getColor('high', theme),
},
});
export type SelectedPercentile = 'p50' | 'p90' | 'p99';
......@@ -300,29 +276,26 @@ interface QuantilesBoxWhiskerProps extends WithStyles<typeof styles> {
p50Display: string;
p90Display: string;
p99Display: string;
p50Level: GaugeLevel;
p90Level: GaugeLevel;
p99Level: GaugeLevel;
p50HoverFill: string;
p90HoverFill: string;
p99HoverFill: string;
selectedPercentile: SelectedPercentile;
// Function to call when the selected percentile is updated.
onChangePercentile?: (percentile: SelectedPercentile) => void;
}
const QuantilesBoxWhisker = (props: QuantilesBoxWhiskerProps) => {
const QuantilesBoxWhiskerImpl = (props: QuantilesBoxWhiskerProps) => {
const {
classes, p50, p90, p99, max, p50Level, p90Level, p99Level, p50Display, p90Display, p99Display,
classes, p50, p90, p99, max, p50HoverFill, p90HoverFill, p99HoverFill, p50Display, p90Display, p99Display,
selectedPercentile, onChangePercentile,
} = props;
const theme = useTheme();
const p50HoverFill = getColor(p50Level, theme);
const p90HoverFill = getColor(p90Level, theme);
const p99HoverFill = getColor(p99Level, theme);
let p50Fill = theme.palette.text.secondary;
let p90Fill = theme.palette.text.secondary;
let p99Fill = theme.palette.text.secondary;
let percentileValue;
let selectedPercentileDisplay;
let selectedPercentileLevel;
let selectedPercentileFill;
const changePercentileIfDifferent = (percentile: SelectedPercentile) => {
if (percentile !== selectedPercentile) {
......@@ -335,14 +308,14 @@ const QuantilesBoxWhisker = (props: QuantilesBoxWhiskerProps) => {
p50Fill = p50HoverFill;
percentileValue = p50;
selectedPercentileDisplay = p50Display;
selectedPercentileLevel = p50Level;
selectedPercentileFill = p50HoverFill;
break;
}
case 'p90': {
p90Fill = p90HoverFill;
percentileValue = p90;
selectedPercentileDisplay = p90Display;
selectedPercentileLevel = p90Level;
selectedPercentileFill = p90HoverFill;
break;
}
case 'p99':
......@@ -350,7 +323,7 @@ const QuantilesBoxWhisker = (props: QuantilesBoxWhiskerProps) => {
p99Fill = p99HoverFill;
percentileValue = p99;
selectedPercentileDisplay = p99Display;
selectedPercentileLevel = p99Level;
selectedPercentileFill = p99HoverFill;
}
}
......@@ -379,7 +352,7 @@ const QuantilesBoxWhisker = (props: QuantilesBoxWhiskerProps) => {
return (
<div className={classes.root}>
<ReactVega
<Vega
className={classes.vegaWrapper}
signalListeners={{
p50Click: () => changePercentileIfDifferent('p50'),
......@@ -390,11 +363,11 @@ const QuantilesBoxWhisker = (props: QuantilesBoxWhiskerProps) => {
actions={false}
tooltip={tooltipHandler}
/>
<span className={clsx(classes.label, classes[selectedPercentileLevel])}>
<span className={classes.label} style={{color: selectedPercentileFill}}>
{selectedPercentileDisplay}
</span>
</div>
);
};
export default withStyles(styles)(QuantilesBoxWhisker);
export const QuantilesBoxWhisker = withStyles(styles)(QuantilesBoxWhiskerImpl);
......@@ -41,6 +41,8 @@ export { ModalTrigger } from './components/modal/modal';
export { Avatar, ProfileMenuWrapper } from './components/profile/profile';
export { SelectedPercentile, QuantilesBoxWhisker } from './components/quantiles-box-whisker/quantiles-box-whisker';
export { SnackbarProvider, useSnackbar } from './components/snackbar/snackbar';
export { Spinner } from './components/spinner/spinner';
......
......@@ -11,6 +11,7 @@
"es2017"
],
"allowJs": false,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"jsx": "react",
"moduleResolution": "node",
......
import { SelectedPercentile } from 'components/quantiles-box-whisker/quantiles-box-whisker';
import { SelectedPercentile } from 'pixie-components';
import { DataType, Relation, SemanticType } from 'types/generated/vizier_pb';
export interface QuantilesDisplayState {
......
......@@ -17,7 +17,7 @@ import {
STATUS_TYPES,
toStatusIndicator,
} from 'components/live-widgets/utils';
import QuantilesBoxWhisker, { SelectedPercentile } from 'components/quantiles-box-whisker/quantiles-box-whisker';
import { QuantilesBoxWhisker, SelectedPercentile } from 'pixie-components';
import { DataType, SemanticType } from 'types/generated/vizier_pb';
import { Arguments } from 'utils/args-utils';
import {
......@@ -26,7 +26,8 @@ import {
looksLikeCPUCol,
looksLikeLatencyCol,
} from 'utils/format-data';
import { GaugeLevel, getLatencyNSLevel } from 'utils/metric-thresholds';
import { getLatencyNSLevel, getColor } from 'utils/metric-thresholds';
import { Theme } from '@material-ui/core/styles';
import { ColumnDisplayInfo, QuantilesDisplayState } from './column-display-info';
// Expects a p99 field in colName.
......@@ -49,7 +50,7 @@ export function getMaxQuantile(rows: any[], colName: string): number {
}
// Expects data to contain floats in p50, p90, and p99 fields.
export function quantilesRenderer(display: ColumnDisplayInfo,
export function quantilesRenderer(display: ColumnDisplayInfo, theme: Theme,
updateDisplay: (ColumnDisplayInfo) => void, rows: any[]) {
const max = getMaxQuantile(rows, display.columnName);
......@@ -72,9 +73,9 @@ export function quantilesRenderer(display: ColumnDisplayInfo,
p50Display={p50Display}
p90Display={p90Display}
p99Display={p99Display}
p50Level='none'
p90Level='none'
p99Level='none'
p50HoverFill={getColor('none', theme)}
p90HoverFill={getColor('none', theme)}
p99HoverFill={getColor('none', theme)}
selectedPercentile={selectedPercentile}
onChangePercentile={(newPercentile: SelectedPercentile) => {
updateDisplay({
......@@ -94,7 +95,7 @@ function dataWithUnitsToString(dataWithUnits: DataWithUnits): string {
}
// Expects data to contain p50, p90, and p99 fields.
export function durationQuantilesRenderer(display: ColumnDisplayInfo,
export function durationQuantilesRenderer(display: ColumnDisplayInfo, theme: Theme,
updateDisplay: (ColumnDisplayInfo) => void, rows: any[]) {
const max = getMaxQuantile(rows, display.columnName);
......@@ -102,15 +103,15 @@ export function durationQuantilesRenderer(display: ColumnDisplayInfo,
const { p50, p90, p99 } = val;
const quantilesDisplay = display.displayState as QuantilesDisplayState;
const selectedPercentile = quantilesDisplay.selectedPercentile || 'p99';
let p50Level: GaugeLevel = 'none';
let p90Level: GaugeLevel = 'none';
let p99Level: GaugeLevel = 'none';
let p50HoverFill = getColor('none', theme);
let p90HoverFill = getColor('none', theme);
let p99HoverFill = getColor('none', theme);
// individual keys in ST_DURATION_NS_QUANTILES are FLOAT64 ST_DURATION_NS.
if (looksLikeLatencyCol(display.columnName, SemanticType.ST_DURATION_NS, DataType.FLOAT64)) {
p50Level = getLatencyNSLevel(p50);
p90Level = getLatencyNSLevel(p90);
p99Level = getLatencyNSLevel(p99);
p50HoverFill = getColor(getLatencyNSLevel(p50), theme);
p90HoverFill = getColor(getLatencyNSLevel(p90), theme);
p99HoverFill = getColor(getLatencyNSLevel(p99), theme);
}
const p50Display = dataWithUnitsToString(formatDuration(p50));
......@@ -126,9 +127,9 @@ export function durationQuantilesRenderer(display: ColumnDisplayInfo,
p50Display={p50Display}
p90Display={p90Display}
p99Display={p99Display}
p50Level={p50Level}
p90Level={p90Level}
p99Level={p99Level}
p50HoverFill={p50HoverFill}
p90HoverFill={p90HoverFill}
p99HoverFill={p99HoverFill}
selectedPercentile={selectedPercentile}
onChangePercentile={(newPercentile: SelectedPercentile) => {
updateDisplay({
......@@ -197,7 +198,7 @@ const CPUDataWrapper = (data: any) => <CPUData data={data} />;
const AlertDataWrapper = (data: any) => <AlertData data={data} />;
export const prettyCellRenderer = (display: ColumnDisplayInfo, updateDisplay: (ColumnDisplayInfo) => void,
clusterName: string, rows: any[], propagatedArgs?: Arguments) => {
clusterName: string, rows: any[], theme: Theme, propagatedArgs?: Arguments) => {
const dt = display.type;
const st = display.semanticType;
const name = display.columnName;
......@@ -209,9 +210,9 @@ export const prettyCellRenderer = (display: ColumnDisplayInfo, updateDisplay: (C
switch (st) {
case SemanticType.ST_QUANTILES:
return quantilesRenderer(display, updateDisplay, rows);
return quantilesRenderer(display, theme, updateDisplay, rows);
case SemanticType.ST_DURATION_NS_QUANTILES:
return durationQuantilesRenderer(display, updateDisplay, rows);
return durationQuantilesRenderer(display, theme, updateDisplay, rows);
case SemanticType.ST_PORT:
return renderWrapper(PortRenderer);
case SemanticType.ST_DURATION_NS:
......@@ -261,9 +262,9 @@ export const prettyCellRenderer = (display: ColumnDisplayInfo, updateDisplay: (C
};
export const vizierCellRenderer = (display: ColumnDisplayInfo, updateDisplay: (ColumnDisplayInfo) => void,
prettyRender: boolean, clusterName: string, rows: any[], propagatedArgs?: Arguments) => {
prettyRender: boolean, theme: Theme, clusterName: string, rows: any[], propagatedArgs?: Arguments) => {
if (prettyRender) {
return prettyCellRenderer(display, updateDisplay, clusterName, rows, propagatedArgs);
return prettyCellRenderer(display, updateDisplay, clusterName, rows, theme, propagatedArgs);
}
return getDataRenderer(display.type);
};
......@@ -7,7 +7,10 @@ import * as React from 'react';
import { DataType, SemanticType } from 'types/generated/vizier_pb';
import noop from 'utils/noop';
import { dataFromProto } from 'utils/result-data-utils';
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
import {
createStyles, makeStyles,
useTheme, Theme,
} from '@material-ui/core/styles';
import { IndexRange } from 'react-virtualized';
import { Arguments } from 'utils/args-utils';
import { ColumnDisplayInfo, displayInfoFromColumn, titleFromInfo } from './column-display-info';
......@@ -103,6 +106,7 @@ export const VizierDataTable = (props: VizierDataTableProps) => {
setColumnDisplayInfos(displayInfos);
}, [table, dataLength, clusterName, prettyRender]);
const theme = useTheme();
const dataTableCols = React.useMemo((): ColumnProps[] => (
[...columnDisplayInfos.values()].map((displayInfo: ColumnDisplayInfo) => {
// Some cells give the power to update the display state for the whole column.
......@@ -118,7 +122,7 @@ export const VizierDataTable = (props: VizierDataTableProps) => {
label: titleFromInfo(displayInfo),
align: DataAlignmentMap.get(displayInfo.type) || 'start',
cellRenderer: vizierCellRenderer(displayInfo, updateColumnDisplay, prettyRender,
clusterName, rows, propagatedArgs),
theme, clusterName, rows, propagatedArgs),
};
if (hasWidthOverride(displayInfo.semanticType, displayInfo.type)) {
colProps.width = getWidthOverride(displayInfo.semanticType, displayInfo.type);
......
import { Theme } from '@material-ui/core/styles';
const LATENCY_HIGH_THRESHOLD = 300;
const LATENCY_MED_THRESHOLD = 150;
......@@ -25,3 +27,16 @@ export function getLatencyNSLevel(val: number): GaugeLevel {
}
return 'high';
}
export function getColor(level: GaugeLevel, theme: Theme): string {
switch (level) {
case 'low':
return theme.palette.success.main;
case 'med':
return theme.palette.warning.main;
case 'high':
return theme.palette.error.main;
default:
return theme.palette.text.primary;
}
}
import * as React from 'react';
import QuantilesBoxWhisker, { SelectedPercentile } from 'components/quantiles-box-whisker/quantiles-box-whisker';
import { useTheme } from '@material-ui/core/styles';
import { QuantilesBoxWhisker, SelectedPercentile } from 'pixie-components';
import { getColor } from 'utils/metric-thresholds';
export default {
title: 'QuantilesBoxWhisker',
......@@ -17,9 +20,9 @@ export const Basic = () => {
p90={200.234}
p99={500.789}
max={600}
p50Level='low'
p90Level='med'
p99Level='high'
p50HoverFill={getColor('low', useTheme())}
p90HoverFill={getColor('med', useTheme())}
p99HoverFill={getColor('high', useTheme())}
p50Display='100 ms'
p90Display='200 ms'
p99Display='500 ms'
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment