@spinnaker/kayenta 0.0.0-2025.1-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +9 -0
- package/.eslintrc.js +1 -0
- package/.huskyrc +5 -0
- package/.lintstagedrc.json +4 -0
- package/.prettierignore +4 -0
- package/.prettierrc.js +1 -0
- package/LICENSE.txt +203 -0
- package/README.md +81 -0
- package/__mocks__/styleMock.js +1 -0
- package/__mocks__/version.json +4 -0
- package/babel.config.js +3 -0
- package/build.gradle +67 -0
- package/build_scripts/checkLicenses.js +79 -0
- package/gradle.properties +0 -0
- package/jest.config.js +204 -0
- package/jest.setup.js +5 -0
- package/package.json +166 -0
- package/rollup-plugin-angularjs-template-loader.js +82 -0
- package/rollup.config.js +30 -0
- package/src/index.ts +2 -0
- package/src/kayenta/actions/creators.ts +163 -0
- package/src/kayenta/actions/index.ts +98 -0
- package/src/kayenta/canary.dataSource.bridge.ts +53 -0
- package/src/kayenta/canary.dataSource.stub.ts +64 -0
- package/src/kayenta/canary.help.ts +136 -0
- package/src/kayenta/canary.less +168 -0
- package/src/kayenta/canary.settings.ts +26 -0
- package/src/kayenta/canary.tsx +67 -0
- package/src/kayenta/components/canaryScore.component.less +77 -0
- package/src/kayenta/components/canaryScore.component.ts +12 -0
- package/src/kayenta/components/canaryScore.tsx +63 -0
- package/src/kayenta/components/canaryScores.component.ts +20 -0
- package/src/kayenta/components/canaryScores.less +22 -0
- package/src/kayenta/components/canaryScores.tsx +163 -0
- package/src/kayenta/components/loadStates.tsx +52 -0
- package/src/kayenta/domain/ICanaryConfig.ts +57 -0
- package/src/kayenta/domain/ICanaryConfigSummary.ts +7 -0
- package/src/kayenta/domain/ICanaryConfigUpdateResponse.ts +3 -0
- package/src/kayenta/domain/ICanaryExecutionStatusResult.ts +72 -0
- package/src/kayenta/domain/ICanaryJudgeResult.ts +51 -0
- package/src/kayenta/domain/ICanaryJudgeResultSummary.ts +5 -0
- package/src/kayenta/domain/ICanaryScoreThresholds.ts +4 -0
- package/src/kayenta/domain/IJudge.ts +5 -0
- package/src/kayenta/domain/IKayentaAccount.ts +14 -0
- package/src/kayenta/domain/IKayentaStageConfig.ts +58 -0
- package/src/kayenta/domain/IMetricSetPair.ts +17 -0
- package/src/kayenta/domain/IMetricsServiceMetadata.ts +2 -0
- package/src/kayenta/domain/ISetupCanaryStage.ts +11 -0
- package/src/kayenta/domain/MetricClassificationLabel.ts +8 -0
- package/src/kayenta/domain/ScoreClassificationLabel.ts +7 -0
- package/src/kayenta/domain/index.ts +15 -0
- package/src/kayenta/edit/changeMetricGroupModal.tsx +107 -0
- package/src/kayenta/edit/configDetail.tsx +31 -0
- package/src/kayenta/edit/configDetailActionButtons.tsx +24 -0
- package/src/kayenta/edit/configDetailHeader.tsx +104 -0
- package/src/kayenta/edit/configDetailLoadStates.tsx +36 -0
- package/src/kayenta/edit/configDetailLoader.tsx +60 -0
- package/src/kayenta/edit/configJson.less +42 -0
- package/src/kayenta/edit/configJsonModal.tsx +158 -0
- package/src/kayenta/edit/configList.less +7 -0
- package/src/kayenta/edit/configList.tsx +57 -0
- package/src/kayenta/edit/copyConfigButton.tsx +34 -0
- package/src/kayenta/edit/createConfigButton.tsx +34 -0
- package/src/kayenta/edit/deleteModal.tsx +87 -0
- package/src/kayenta/edit/edit.tsx +24 -0
- package/src/kayenta/edit/editMetricEffectSizes.tsx +186 -0
- package/src/kayenta/edit/editMetricModal.less +9 -0
- package/src/kayenta/edit/editMetricModal.spec.tsx +129 -0
- package/src/kayenta/edit/editMetricModal.tsx +294 -0
- package/src/kayenta/edit/editMetricValidation.spec.ts +63 -0
- package/src/kayenta/edit/editMetricValidation.ts +50 -0
- package/src/kayenta/edit/filterTemplateSelector.less +15 -0
- package/src/kayenta/edit/filterTemplateSelector.spec.tsx +106 -0
- package/src/kayenta/edit/filterTemplateSelector.tsx +194 -0
- package/src/kayenta/edit/filterTemplatesValidation.spec.ts +108 -0
- package/src/kayenta/edit/filterTemplatesValidation.ts +95 -0
- package/src/kayenta/edit/footer.less +30 -0
- package/src/kayenta/edit/footer.tsx +12 -0
- package/src/kayenta/edit/groupName.tsx +80 -0
- package/src/kayenta/edit/groupTabs.tsx +106 -0
- package/src/kayenta/edit/groupWeight.tsx +80 -0
- package/src/kayenta/edit/groupWeights.tsx +38 -0
- package/src/kayenta/edit/inlineTemplateEditor.spec.tsx +42 -0
- package/src/kayenta/edit/inlineTemplateEditor.tsx +61 -0
- package/src/kayenta/edit/judgeSelect.tsx +82 -0
- package/src/kayenta/edit/metricConfigurerDelegator.tsx +30 -0
- package/src/kayenta/edit/metricList.less +21 -0
- package/src/kayenta/edit/metricList.tsx +215 -0
- package/src/kayenta/edit/metricStoreSelector.tsx +66 -0
- package/src/kayenta/edit/nameAndDescription.tsx +90 -0
- package/src/kayenta/edit/openConfigJsonModalButton.tsx +33 -0
- package/src/kayenta/edit/openDeleteModalButton.tsx +50 -0
- package/src/kayenta/edit/ownedBy.tsx +34 -0
- package/src/kayenta/edit/save.tsx +19 -0
- package/src/kayenta/edit/saveConfigButton.tsx +65 -0
- package/src/kayenta/edit/saveConfigError.tsx +59 -0
- package/src/kayenta/edit/scoring.tsx +35 -0
- package/src/kayenta/edit/selectConfig.tsx +10 -0
- package/src/kayenta/edit/validationErrors.tsx +39 -0
- package/src/kayenta/index.ts +6 -0
- package/src/kayenta/layout/addNewButton.tsx +20 -0
- package/src/kayenta/layout/centeredDetail.tsx +13 -0
- package/src/kayenta/layout/deleteButton.tsx +11 -0
- package/src/kayenta/layout/disableable.tsx +87 -0
- package/src/kayenta/layout/formList.tsx +26 -0
- package/src/kayenta/layout/formRow.tsx +36 -0
- package/src/kayenta/layout/formattedDate.tsx +14 -0
- package/src/kayenta/layout/index.ts +2 -0
- package/src/kayenta/layout/keyValueList.less +20 -0
- package/src/kayenta/layout/keyValueList.tsx +114 -0
- package/src/kayenta/layout/list.less +9 -0
- package/src/kayenta/layout/list.spec.tsx +83 -0
- package/src/kayenta/layout/list.tsx +73 -0
- package/src/kayenta/layout/listDetail.tsx +33 -0
- package/src/kayenta/layout/radioChoice.tsx +29 -0
- package/src/kayenta/layout/styleguide.tsx +16 -0
- package/src/kayenta/layout/table/index.ts +5 -0
- package/src/kayenta/layout/table/nativeTable.tsx +51 -0
- package/src/kayenta/layout/table/nativeTableHeader.tsx +26 -0
- package/src/kayenta/layout/table/table.tsx +56 -0
- package/src/kayenta/layout/table/tableColumn.ts +7 -0
- package/src/kayenta/layout/table/tableHeader.tsx +23 -0
- package/src/kayenta/layout/tabs.tsx +26 -0
- package/src/kayenta/layout/titledSection.less +16 -0
- package/src/kayenta/layout/titledSection.tsx +20 -0
- package/src/kayenta/layout/titledSubsection.less +11 -0
- package/src/kayenta/layout/titledSubsection.tsx +22 -0
- package/src/kayenta/manualAnalysis/ManualAnalysisModal.tsx +716 -0
- package/src/kayenta/metricStore/atlas/atlasMetricConfigurer.tsx +130 -0
- package/src/kayenta/metricStore/atlas/index.ts +8 -0
- package/src/kayenta/metricStore/datadog/domain/IDatadogMetricDescriptor.ts +5 -0
- package/src/kayenta/metricStore/datadog/index.ts +9 -0
- package/src/kayenta/metricStore/datadog/metricConfigurer.tsx +90 -0
- package/src/kayenta/metricStore/datadog/metricTypeSelector.spec.tsx +59 -0
- package/src/kayenta/metricStore/datadog/metricTypeSelector.tsx +73 -0
- package/src/kayenta/metricStore/graphite/domain/IGraphiteMetricDescriptor.ts +5 -0
- package/src/kayenta/metricStore/graphite/index.ts +8 -0
- package/src/kayenta/metricStore/graphite/metricConfigurer.tsx +54 -0
- package/src/kayenta/metricStore/graphite/metricTypeSelector.tsx +80 -0
- package/src/kayenta/metricStore/graphite/typeahead.less +3 -0
- package/src/kayenta/metricStore/index.ts +8 -0
- package/src/kayenta/metricStore/metricStoreConfig.service.ts +12 -0
- package/src/kayenta/metricStore/newrelic/domain/INewRelicMetricDescriptor.ts +5 -0
- package/src/kayenta/metricStore/newrelic/index.ts +8 -0
- package/src/kayenta/metricStore/newrelic/metricConfigurer.tsx +58 -0
- package/src/kayenta/metricStore/prometheus/domain/IPrometheusCanaryMetricSetQueryConfig.ts +14 -0
- package/src/kayenta/metricStore/prometheus/domain/IPrometheusMetricDescriptor.ts +5 -0
- package/src/kayenta/metricStore/prometheus/index.ts +12 -0
- package/src/kayenta/metricStore/prometheus/metricConfigurer.tsx +157 -0
- package/src/kayenta/metricStore/prometheus/metricTypeSelector.less +5 -0
- package/src/kayenta/metricStore/prometheus/metricTypeSelector.spec.tsx +62 -0
- package/src/kayenta/metricStore/prometheus/metricTypeSelector.tsx +144 -0
- package/src/kayenta/metricStore/prometheus/queryTypeSelectors.spec.ts +61 -0
- package/src/kayenta/metricStore/prometheus/queryTypeSelectors.ts +38 -0
- package/src/kayenta/metricStore/signalfx/domain/ISignalFxCanaryMetricSetQueryConfig.ts +7 -0
- package/src/kayenta/metricStore/signalfx/index.ts +8 -0
- package/src/kayenta/metricStore/signalfx/metricConfigurer.less +10 -0
- package/src/kayenta/metricStore/signalfx/metricConfigurer.tsx +187 -0
- package/src/kayenta/metricStore/stackdriver/domain/IStackdriverCanaryMetricSetQueryConfig.ts +9 -0
- package/src/kayenta/metricStore/stackdriver/domain/IStackdriverMetricDescriptor.ts +17 -0
- package/src/kayenta/metricStore/stackdriver/index.ts +12 -0
- package/src/kayenta/metricStore/stackdriver/metricConfigurer.tsx +144 -0
- package/src/kayenta/metricStore/stackdriver/metricTypeSelector.spec.tsx +92 -0
- package/src/kayenta/metricStore/stackdriver/metricTypeSelector.tsx +113 -0
- package/src/kayenta/middleware/actionInterceptor.ts +29 -0
- package/src/kayenta/middleware/asyncDispatch.ts +37 -0
- package/src/kayenta/middleware/epics.ts +211 -0
- package/src/kayenta/middleware/index.ts +3 -0
- package/src/kayenta/navigation/canary.states.stub.ts +28 -0
- package/src/kayenta/navigation/canary.states.ts +182 -0
- package/src/kayenta/reducers/app.ts +56 -0
- package/src/kayenta/reducers/asyncRequest.ts +5 -0
- package/src/kayenta/reducers/data.ts +169 -0
- package/src/kayenta/reducers/editingTemplate.ts +54 -0
- package/src/kayenta/reducers/group.ts +82 -0
- package/src/kayenta/reducers/index.ts +245 -0
- package/src/kayenta/reducers/prometheusMetricConfig.spec.ts +33 -0
- package/src/kayenta/reducers/prometheusMetricConfig.ts +56 -0
- package/src/kayenta/reducers/selectedConfig.spec.ts +190 -0
- package/src/kayenta/reducers/selectedConfig.ts +566 -0
- package/src/kayenta/reducers/selectedRun.ts +101 -0
- package/src/kayenta/reducers/signalFxMetricConfig.ts +36 -0
- package/src/kayenta/reducers/stackdriverMetricConfig.spec.ts +33 -0
- package/src/kayenta/reducers/stackdriverMetricConfig.ts +41 -0
- package/src/kayenta/reducers/templates.spec.ts +192 -0
- package/src/kayenta/reducers/validators.ts +118 -0
- package/src/kayenta/report/detail/allMetricResultsHeader.tsx +32 -0
- package/src/kayenta/report/detail/clickableHeader.tsx +21 -0
- package/src/kayenta/report/detail/colors.ts +47 -0
- package/src/kayenta/report/detail/detail.less +16 -0
- package/src/kayenta/report/detail/detail.tsx +48 -0
- package/src/kayenta/report/detail/detailLoader.tsx +55 -0
- package/src/kayenta/report/detail/graph/graph.tsx +37 -0
- package/src/kayenta/report/detail/graph/metricSetPairGraph.service.ts +35 -0
- package/src/kayenta/report/detail/graph/semiotic/boxplot.less +45 -0
- package/src/kayenta/report/detail/graph/semiotic/boxplot.tsx +283 -0
- package/src/kayenta/report/detail/graph/semiotic/chartHeader.tsx +19 -0
- package/src/kayenta/report/detail/graph/semiotic/chartLegend.less +26 -0
- package/src/kayenta/report/detail/graph/semiotic/chartLegend.tsx +42 -0
- package/src/kayenta/report/detail/graph/semiotic/circleIcon.tsx +16 -0
- package/src/kayenta/report/detail/graph/semiotic/config.less +5 -0
- package/src/kayenta/report/detail/graph/semiotic/config.ts +38 -0
- package/src/kayenta/report/detail/graph/semiotic/customAxisTickLabel.tsx +17 -0
- package/src/kayenta/report/detail/graph/semiotic/declarations/labella.d.ts +16 -0
- package/src/kayenta/report/detail/graph/semiotic/declarations/react-container-dimensions.d.ts +3 -0
- package/src/kayenta/report/detail/graph/semiotic/declarations/semiotic.d.ts +160 -0
- package/src/kayenta/report/detail/graph/semiotic/differenceArea.less +17 -0
- package/src/kayenta/report/detail/graph/semiotic/differenceArea.tsx +186 -0
- package/src/kayenta/report/detail/graph/semiotic/histogram.less +22 -0
- package/src/kayenta/report/detail/graph/semiotic/histogram.tsx +251 -0
- package/src/kayenta/report/detail/graph/semiotic/index.tsx +19 -0
- package/src/kayenta/report/detail/graph/semiotic/noValidDataSign.less +5 -0
- package/src/kayenta/report/detail/graph/semiotic/noValidDataSign.tsx +10 -0
- package/src/kayenta/report/detail/graph/semiotic/secondaryTSXAxis.less +6 -0
- package/src/kayenta/report/detail/graph/semiotic/secondaryTSXAxis.tsx +58 -0
- package/src/kayenta/report/detail/graph/semiotic/semiotic.service.ts +32 -0
- package/src/kayenta/report/detail/graph/semiotic/semioticGraph.less +53 -0
- package/src/kayenta/report/detail/graph/semiotic/semioticGraph.tsx +49 -0
- package/src/kayenta/report/detail/graph/semiotic/timeSeries.less +42 -0
- package/src/kayenta/report/detail/graph/semiotic/timeSeries.tsx +473 -0
- package/src/kayenta/report/detail/graph/semiotic/tooltip.tsx +55 -0
- package/src/kayenta/report/detail/graph/semiotic/utils.ts +90 -0
- package/src/kayenta/report/detail/graphTypeSelector.less +4 -0
- package/src/kayenta/report/detail/graphTypeSelector.tsx +50 -0
- package/src/kayenta/report/detail/groupScores.tsx +68 -0
- package/src/kayenta/report/detail/header.less +70 -0
- package/src/kayenta/report/detail/header.tsx +39 -0
- package/src/kayenta/report/detail/headerArrow.tsx +13 -0
- package/src/kayenta/report/detail/loadStates.tsx +31 -0
- package/src/kayenta/report/detail/metricResultActions.less +29 -0
- package/src/kayenta/report/detail/metricResultActions.tsx +87 -0
- package/src/kayenta/report/detail/metricResultClassification.tsx +22 -0
- package/src/kayenta/report/detail/metricResultDetail.tsx +20 -0
- package/src/kayenta/report/detail/metricResultDetailLayout.tsx +19 -0
- package/src/kayenta/report/detail/metricResultDeviation.tsx +25 -0
- package/src/kayenta/report/detail/metricResultStats.less +9 -0
- package/src/kayenta/report/detail/metricResultStats.tsx +120 -0
- package/src/kayenta/report/detail/metricResults.less +12 -0
- package/src/kayenta/report/detail/metricResults.tsx +52 -0
- package/src/kayenta/report/detail/metricResultsClassificationFilters.tsx +65 -0
- package/src/kayenta/report/detail/metricResultsColumns.tsx +27 -0
- package/src/kayenta/report/detail/metricResultsList.less +44 -0
- package/src/kayenta/report/detail/metricResultsList.tsx +120 -0
- package/src/kayenta/report/detail/metricSetPairLoadStates.tsx +22 -0
- package/src/kayenta/report/detail/multipleResultsTable.tsx +81 -0
- package/src/kayenta/report/detail/reportException.tsx +57 -0
- package/src/kayenta/report/detail/reportExplanation.less +12 -0
- package/src/kayenta/report/detail/reportExplanation.tsx +32 -0
- package/src/kayenta/report/detail/reportMetadata.tsx +167 -0
- package/src/kayenta/report/detail/reportScores.less +47 -0
- package/src/kayenta/report/detail/reportScores.tsx +80 -0
- package/src/kayenta/report/detail/score.tsx +33 -0
- package/src/kayenta/report/detail/sourceLinks.tsx +69 -0
- package/src/kayenta/report/list/configLink.tsx +32 -0
- package/src/kayenta/report/list/executionList.less +7 -0
- package/src/kayenta/report/list/loadStates.tsx +32 -0
- package/src/kayenta/report/list/pipelineLink.tsx +15 -0
- package/src/kayenta/report/list/reportLink.tsx +33 -0
- package/src/kayenta/report/list/table.tsx +309 -0
- package/src/kayenta/report/report.tsx +11 -0
- package/src/kayenta/selectors/filterTemplatesSelectors.ts +87 -0
- package/src/kayenta/selectors/index.ts +62 -0
- package/src/kayenta/service/canaryConfig.service.ts +122 -0
- package/src/kayenta/service/canaryRun.service.ts +60 -0
- package/src/kayenta/service/delegateFactory.ts +24 -0
- package/src/kayenta/service/metricsServiceMetadata.service.ts +9 -0
- package/src/kayenta/stages/kayentaStage/AnalysisType.spec.tsx +47 -0
- package/src/kayenta/stages/kayentaStage/AnalysisType.tsx +49 -0
- package/src/kayenta/stages/kayentaStage/CanaryExecutionLabel.tsx +26 -0
- package/src/kayenta/stages/kayentaStage/analysisType.component.ts +12 -0
- package/src/kayenta/stages/kayentaStage/canaryRunSummaries.component.ts +12 -0
- package/src/kayenta/stages/kayentaStage/canaryRunSummaries.less +5 -0
- package/src/kayenta/stages/kayentaStage/canaryRunSummaries.tsx +136 -0
- package/src/kayenta/stages/kayentaStage/forAnalysisType.component.ts +45 -0
- package/src/kayenta/stages/kayentaStage/kayentaStage.controller.ts +789 -0
- package/src/kayenta/stages/kayentaStage/kayentaStage.html +528 -0
- package/src/kayenta/stages/kayentaStage/kayentaStage.less +5 -0
- package/src/kayenta/stages/kayentaStage/kayentaStage.transformer.ts +179 -0
- package/src/kayenta/stages/kayentaStage/kayentaStage.ts +221 -0
- package/src/kayenta/stages/kayentaStage/kayentaStageConfigSection.component.ts +21 -0
- package/src/kayenta/stages/kayentaStage/kayentaStageExecutionDetails.controller.ts +88 -0
- package/src/kayenta/stages/kayentaStage/kayentaStageExecutionDetails.html +114 -0
- package/src/kayenta/stages/kayentaStage/kayentaStageExecutionDetails.less +6 -0
- package/src/kayenta/stages/kayentaStage/stageTypes.ts +5 -0
- package/src/kayenta/utils/duration.spec.ts +69 -0
- package/src/kayenta/utils/duration.ts +48 -0
- package/src/lazy.ts +29 -0
- package/src/stub.ts +60 -0
- package/tsconfig.json +11 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { scaleUtc } from 'd3-scale';
|
|
2
|
+
import { curveStepAfter } from 'd3-shape';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { XYFrame } from 'semiotic';
|
|
5
|
+
|
|
6
|
+
import { vizConfig } from './config';
|
|
7
|
+
import CustomAxisTickLabel from './customAxisTickLabel';
|
|
8
|
+
import SecondaryTSXAxis from './secondaryTSXAxis';
|
|
9
|
+
import { IMargin, ISemioticChartProps } from './semiotic.service';
|
|
10
|
+
import * as utils from './utils';
|
|
11
|
+
|
|
12
|
+
import './differenceArea.less';
|
|
13
|
+
|
|
14
|
+
interface IDataPoint {
|
|
15
|
+
timestampMillis: number;
|
|
16
|
+
value: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface IChartDataSet {
|
|
20
|
+
label: string;
|
|
21
|
+
color: string;
|
|
22
|
+
coordinates: IDataPoint[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface IInterimDataSet {
|
|
26
|
+
timestamp: number;
|
|
27
|
+
canary: number | string;
|
|
28
|
+
baseline: number | string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface IDifferenceAreaProps extends ISemioticChartProps {
|
|
32
|
+
millisSetBaseline: number[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/*
|
|
36
|
+
* Supplemental visualization in the time series view to highlight
|
|
37
|
+
* Canary difference to baseline at any given timestamp
|
|
38
|
+
*/
|
|
39
|
+
export default class DifferenceArea extends React.Component<IDifferenceAreaProps> {
|
|
40
|
+
private margin: IMargin = {
|
|
41
|
+
left: 60,
|
|
42
|
+
right: 20,
|
|
43
|
+
top: 8,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
private chartHeight = 40; // chart height not including axes height
|
|
47
|
+
private headerHeight = 17;
|
|
48
|
+
|
|
49
|
+
private getChartData = () => {
|
|
50
|
+
const { metricSetPair } = this.props;
|
|
51
|
+
const {
|
|
52
|
+
values: { experiment, control },
|
|
53
|
+
scopes,
|
|
54
|
+
} = metricSetPair;
|
|
55
|
+
|
|
56
|
+
const stepMillis = scopes.control.stepMillis;
|
|
57
|
+
const maxDataPoints = Math.max(experiment.length, control.length);
|
|
58
|
+
|
|
59
|
+
// Align data sets in case canary & baseline have different lengths and/or starting time
|
|
60
|
+
const intDataSet: IInterimDataSet[] = Array(maxDataPoints)
|
|
61
|
+
.fill(0)
|
|
62
|
+
.map((_, i) => {
|
|
63
|
+
const e = experiment[i];
|
|
64
|
+
const c = control[i];
|
|
65
|
+
return {
|
|
66
|
+
timestamp: scopes.control.startTimeMillis + i * stepMillis,
|
|
67
|
+
canary: e,
|
|
68
|
+
baseline: c,
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const baselineReferenceDataPoints: IDataPoint[] = intDataSet.map((ds: IInterimDataSet) => ({
|
|
73
|
+
timestampMillis: ds.timestamp,
|
|
74
|
+
value: 0,
|
|
75
|
+
}));
|
|
76
|
+
|
|
77
|
+
const differenceDataPoints: IDataPoint[] = intDataSet.map((ds: IInterimDataSet) => ({
|
|
78
|
+
timestampMillis: ds.timestamp,
|
|
79
|
+
value: typeof ds.canary === 'number' && typeof ds.baseline === 'number' ? ds.canary - ds.baseline : 0,
|
|
80
|
+
}));
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
chartData: [
|
|
84
|
+
{
|
|
85
|
+
label: 'difference',
|
|
86
|
+
color: vizConfig.colors.canary,
|
|
87
|
+
coordinates: differenceDataPoints,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
label: 'baselineReference',
|
|
91
|
+
color: vizConfig.colors.baseline,
|
|
92
|
+
coordinates: baselineReferenceDataPoints,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
private getSecondaryAxis = (millisOffset: number, millisSetBaseline: number[]) => {
|
|
99
|
+
const { parentWidth } = this.props;
|
|
100
|
+
const millisSetCanary = millisSetBaseline.map((ms: number) => ms + millisOffset);
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<SecondaryTSXAxis
|
|
104
|
+
margin={{ left: this.margin.left, right: this.margin.right, top: 0, bottom: 0 }}
|
|
105
|
+
width={parentWidth}
|
|
106
|
+
millisSet={millisSetCanary}
|
|
107
|
+
axisLabel={'canary'}
|
|
108
|
+
bottomOffset={0}
|
|
109
|
+
/>
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
private getXAxisTotalHeight = (shouldUseSecondaryXAxis: boolean) => {
|
|
114
|
+
const { axisTickLineHeight, axisTickLabelHeight, axisLabelHeight } = vizConfig.timeSeries;
|
|
115
|
+
|
|
116
|
+
return shouldUseSecondaryXAxis
|
|
117
|
+
? 2 * (axisLabelHeight + axisTickLabelHeight) + axisTickLineHeight
|
|
118
|
+
: axisTickLabelHeight;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
public render() {
|
|
122
|
+
const { metricSetPair, parentWidth, millisSetBaseline } = this.props;
|
|
123
|
+
|
|
124
|
+
/*
|
|
125
|
+
* Generate the data needed for the graph components
|
|
126
|
+
*/
|
|
127
|
+
const { scopes } = metricSetPair;
|
|
128
|
+
const { chartData } = this.getChartData();
|
|
129
|
+
const millisOffset = scopes.experiment.startTimeMillis - scopes.control.startTimeMillis;
|
|
130
|
+
const shouldUseSecondaryXAxis = millisOffset !== 0;
|
|
131
|
+
|
|
132
|
+
/*
|
|
133
|
+
* Build the visualization components
|
|
134
|
+
*/
|
|
135
|
+
const xAxisTotalHeight = this.getXAxisTotalHeight(shouldUseSecondaryXAxis);
|
|
136
|
+
const computedConfig = {
|
|
137
|
+
lines: chartData,
|
|
138
|
+
size: [parentWidth, this.chartHeight + xAxisTotalHeight],
|
|
139
|
+
margin: { ...this.margin, bottom: xAxisTotalHeight },
|
|
140
|
+
lineType: {
|
|
141
|
+
type: 'area',
|
|
142
|
+
interpolator: curveStepAfter,
|
|
143
|
+
},
|
|
144
|
+
lineStyle: (ds: IChartDataSet) =>
|
|
145
|
+
ds.label === 'difference'
|
|
146
|
+
? {
|
|
147
|
+
fill: ds.color,
|
|
148
|
+
fillOpacity: 0.6,
|
|
149
|
+
}
|
|
150
|
+
: {
|
|
151
|
+
stroke: ds.color,
|
|
152
|
+
strokeOpacity: 1,
|
|
153
|
+
strokeWidth: 2,
|
|
154
|
+
strokeDasharray: 5,
|
|
155
|
+
},
|
|
156
|
+
xAccessor: (d: IDataPoint) => new Date(d.timestampMillis),
|
|
157
|
+
yAccessor: 'value',
|
|
158
|
+
xScaleType: scaleUtc(),
|
|
159
|
+
axes: [
|
|
160
|
+
{
|
|
161
|
+
orient: 'left',
|
|
162
|
+
tickFormat: () => `\u0394 = 0`, // Δ = 0
|
|
163
|
+
tickValues: [0],
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
orient: 'bottom',
|
|
167
|
+
tickValues: utils.calculateDateTimeTicks(millisSetBaseline),
|
|
168
|
+
tickFormat: (d: number) => <CustomAxisTickLabel millis={d} />,
|
|
169
|
+
label: shouldUseSecondaryXAxis ? 'Baseline' : undefined,
|
|
170
|
+
className: shouldUseSecondaryXAxis ? 'baseline-dual-axis' : '',
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
xExtent: [millisSetBaseline[0], millisSetBaseline[millisSetBaseline.length - 1]],
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<div className="difference-area">
|
|
178
|
+
<div className="chart-title" style={{ height: this.headerHeight }}>
|
|
179
|
+
Canary Value Differences from Baseline
|
|
180
|
+
</div>
|
|
181
|
+
<XYFrame {...computedConfig} />
|
|
182
|
+
{shouldUseSecondaryXAxis ? this.getSecondaryAxis(millisOffset, millisSetBaseline) : null}
|
|
183
|
+
</div>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
.histogram {
|
|
2
|
+
.graph-container {
|
|
3
|
+
position: relative;
|
|
4
|
+
.bar-annotation {
|
|
5
|
+
text {
|
|
6
|
+
font-size: 12px;
|
|
7
|
+
font-weight: 600;
|
|
8
|
+
}
|
|
9
|
+
.annotation-connector {
|
|
10
|
+
display: none;
|
|
11
|
+
}
|
|
12
|
+
.note-line {
|
|
13
|
+
display: none;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
g.ordinal-labels {
|
|
17
|
+
stroke: var(--color-text-primary);
|
|
18
|
+
font-size: 12px;
|
|
19
|
+
font-weight: 200;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { extent, histogram } from 'd3-array';
|
|
2
|
+
import { scaleLinear } from 'd3-scale';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import {
|
|
5
|
+
Annotation,
|
|
6
|
+
IAnnotationType,
|
|
7
|
+
IOrFrameHoverArgs,
|
|
8
|
+
IOrGroup,
|
|
9
|
+
IOrPiece,
|
|
10
|
+
IOrXyData,
|
|
11
|
+
ISemioticAnnotationArgs,
|
|
12
|
+
OrdinalFrame,
|
|
13
|
+
} from 'semiotic';
|
|
14
|
+
|
|
15
|
+
import ChartHeader from './chartHeader';
|
|
16
|
+
import ChartLegend from './chartLegend';
|
|
17
|
+
import CircleIcon from './circleIcon';
|
|
18
|
+
import { vizConfig } from './config';
|
|
19
|
+
import { IMargin, ISemioticChartProps, ITooltip } from './semiotic.service';
|
|
20
|
+
import Tooltip from './tooltip';
|
|
21
|
+
import * as utils from './utils';
|
|
22
|
+
|
|
23
|
+
import './histogram.less';
|
|
24
|
+
|
|
25
|
+
interface IInputDataPoint {
|
|
26
|
+
value: number;
|
|
27
|
+
group: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface IChartDataPoint {
|
|
31
|
+
group: string;
|
|
32
|
+
count: number;
|
|
33
|
+
x0: number;
|
|
34
|
+
x1: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface IAnnotationData extends IChartDataPoint {
|
|
38
|
+
type: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface IHistogramState {
|
|
42
|
+
tooltip: ITooltip;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default class Histogram extends React.Component<ISemioticChartProps, IHistogramState> {
|
|
46
|
+
public state: IHistogramState = {
|
|
47
|
+
tooltip: null,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
private margin: IMargin = {
|
|
51
|
+
top: 20,
|
|
52
|
+
bottom: 20,
|
|
53
|
+
left: 60,
|
|
54
|
+
right: 10,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
private decorateData = (dataPoints: number[], group: string): IInputDataPoint[] => {
|
|
58
|
+
return dataPoints.map((dp) => ({
|
|
59
|
+
group,
|
|
60
|
+
value: dp,
|
|
61
|
+
}));
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/*
|
|
65
|
+
* Semiotic actually supports histogram as a "summary type" out of the box, but the customization is
|
|
66
|
+
* currently limited (e.g. it can't display the y-axis ticks & labels)
|
|
67
|
+
* Hence we're manually generating histogram data using D3 and display it as
|
|
68
|
+
* a grouped bar chart in semiotic
|
|
69
|
+
*/
|
|
70
|
+
private generateChartData = () => {
|
|
71
|
+
const { metricSetPair } = this.props;
|
|
72
|
+
const filterFunc = (v: IInputDataPoint) => typeof v.value === 'number';
|
|
73
|
+
const baselineInput = this.decorateData(metricSetPair.values.control, 'baseline');
|
|
74
|
+
const canaryInput = this.decorateData(metricSetPair.values.experiment, 'canary');
|
|
75
|
+
const combinedInput = baselineInput.concat(canaryInput).filter(filterFunc);
|
|
76
|
+
|
|
77
|
+
const x = scaleLinear()
|
|
78
|
+
.domain(extent(combinedInput.map((o) => o.value)))
|
|
79
|
+
.nice();
|
|
80
|
+
const domain = x.domain() as [number, number];
|
|
81
|
+
|
|
82
|
+
// create histogram bins based on the combined data points
|
|
83
|
+
const histogramData = histogram<IInputDataPoint, number>()
|
|
84
|
+
.domain(domain)
|
|
85
|
+
.value((d: IInputDataPoint) => d.value)(combinedInput);
|
|
86
|
+
|
|
87
|
+
const chartData: IChartDataPoint[] = [];
|
|
88
|
+
|
|
89
|
+
// Convert it to ordinal data format for bar chart in semiotic
|
|
90
|
+
histogramData.forEach((h) => {
|
|
91
|
+
const { x0, x1 } = h;
|
|
92
|
+
const baselineBin = { group: 'baseline', x0, x1, count: 0 };
|
|
93
|
+
const canaryBin = { group: 'canary', x0, x1, count: 0 };
|
|
94
|
+
h.forEach((d) => (d.group === 'baseline' ? baselineBin.count++ : canaryBin.count++));
|
|
95
|
+
chartData.push(baselineBin);
|
|
96
|
+
chartData.push(canaryBin);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return chartData;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Function factory to handle hover event
|
|
103
|
+
private createChartHoverHandler = (chartData: IChartDataPoint[]) => {
|
|
104
|
+
return (d: IOrFrameHoverArgs<IChartDataPoint>): void => {
|
|
105
|
+
if (d && d.type === 'column-hover') {
|
|
106
|
+
const x1Max: number = Math.max(...chartData.map((cd: IChartDataPoint) => cd.x1));
|
|
107
|
+
const xyData = d.column.xyData;
|
|
108
|
+
const x = xyData[1].xy.x + this.margin.left;
|
|
109
|
+
const halfHeight1 = xyData[0].xy.height / 2;
|
|
110
|
+
const halfHeight2 = xyData[1].xy.height / 2;
|
|
111
|
+
const y = vizConfig.height - this.margin.bottom - Math.min(halfHeight1, halfHeight2);
|
|
112
|
+
const { x0, x1 } = d.summary[0].data;
|
|
113
|
+
const tooltipRows = d.summary.map((s: IOrPiece<IChartDataPoint>) => {
|
|
114
|
+
const { group, count } = s.data;
|
|
115
|
+
const valueStyle = {
|
|
116
|
+
fontWeight: 'bold',
|
|
117
|
+
} as React.CSSProperties;
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<div key={group}>
|
|
121
|
+
<CircleIcon group={group} />
|
|
122
|
+
<span>{` ${group} count: `}</span>
|
|
123
|
+
<span style={valueStyle}>{count}</span>
|
|
124
|
+
</div>
|
|
125
|
+
);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
let label = `For metric value more than / equal to ${utils.formatMetricValue(x0)} `;
|
|
129
|
+
label +=
|
|
130
|
+
x1 === x1Max
|
|
131
|
+
? `and less than / equal to ${utils.formatMetricValue(x1)}`
|
|
132
|
+
: `and less than ${utils.formatMetricValue(x1)}`;
|
|
133
|
+
|
|
134
|
+
const tooltipContent = (
|
|
135
|
+
<div>
|
|
136
|
+
<div>{label}</div>
|
|
137
|
+
{tooltipRows}
|
|
138
|
+
</div>
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
this.setState({
|
|
142
|
+
tooltip: {
|
|
143
|
+
content: tooltipContent,
|
|
144
|
+
x,
|
|
145
|
+
y,
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
} else {
|
|
149
|
+
this.setState({ tooltip: null });
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// generate bar value annotations object
|
|
155
|
+
private defineAnnotations = (chartData: IChartDataPoint[]) => {
|
|
156
|
+
const annotations = [] as IAnnotationData[];
|
|
157
|
+
chartData.forEach((d: IChartDataPoint) => {
|
|
158
|
+
if (d.count > 0) {
|
|
159
|
+
annotations.push({
|
|
160
|
+
type: 'bar-value-custom',
|
|
161
|
+
...d,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
return annotations;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// function to actually create the JSX elements based on the annotation object
|
|
169
|
+
private customAnnotationFunction = (
|
|
170
|
+
args: ISemioticAnnotationArgs<IAnnotationData, IOrGroup<IChartDataPoint>>,
|
|
171
|
+
): JSX.Element | null => {
|
|
172
|
+
const { d, i, categories } = args;
|
|
173
|
+
if (d.type === 'bar-value-custom') {
|
|
174
|
+
const { x, y, width } = categories[d.x1].xyData.find(
|
|
175
|
+
(c: IOrXyData<IChartDataPoint>) => c.piece.data.group === d.group,
|
|
176
|
+
).xy;
|
|
177
|
+
const noteData = {
|
|
178
|
+
x: x + width / 2,
|
|
179
|
+
y,
|
|
180
|
+
nx: x + width / 2,
|
|
181
|
+
ny: y - 1,
|
|
182
|
+
note: {
|
|
183
|
+
label: `${d.count}`,
|
|
184
|
+
wrap: 100,
|
|
185
|
+
align: 'middle',
|
|
186
|
+
orientation: 'topBottom',
|
|
187
|
+
padding: 0,
|
|
188
|
+
color: vizConfig.colors[d.group],
|
|
189
|
+
lineType: 'horizontal',
|
|
190
|
+
},
|
|
191
|
+
className: `bar-annotation`,
|
|
192
|
+
};
|
|
193
|
+
return <Annotation key={i} noteData={noteData} />;
|
|
194
|
+
} else {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
private getChartProps = () => {
|
|
200
|
+
const { parentWidth } = this.props;
|
|
201
|
+
const chartData = this.generateChartData();
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
size: [parentWidth, vizConfig.height],
|
|
205
|
+
margin: this.margin,
|
|
206
|
+
projection: 'vertical',
|
|
207
|
+
type: 'clusterbar',
|
|
208
|
+
oLabel: (v: string) => <text textAnchor="middle">{utils.formatMetricValue(parseFloat(v))}</text>,
|
|
209
|
+
oPadding: 20,
|
|
210
|
+
oAccessor: (d: IChartDataPoint) => d.x1,
|
|
211
|
+
style: (d: IChartDataPoint) => {
|
|
212
|
+
return {
|
|
213
|
+
fill: vizConfig.colors[d.group],
|
|
214
|
+
strokeWidth: 1,
|
|
215
|
+
stroke: vizConfig.colors.background,
|
|
216
|
+
strokeOpacity: 1,
|
|
217
|
+
};
|
|
218
|
+
},
|
|
219
|
+
customHoverBehavior: this.createChartHoverHandler(chartData),
|
|
220
|
+
data: this.generateChartData(),
|
|
221
|
+
axis: [
|
|
222
|
+
{
|
|
223
|
+
orient: 'left',
|
|
224
|
+
label: 'measurement count',
|
|
225
|
+
tickFormat: (d: number) => (d === 0 ? null : Math.abs(d)),
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
rAccessor: (d: IChartDataPoint) => d.count,
|
|
229
|
+
annotations: this.defineAnnotations(chartData),
|
|
230
|
+
svgAnnotationRules: this.customAnnotationFunction,
|
|
231
|
+
hoverAnnotation: [] as IAnnotationType[],
|
|
232
|
+
};
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
public render() {
|
|
236
|
+
const { metricSetPair } = this.props;
|
|
237
|
+
|
|
238
|
+
return (
|
|
239
|
+
<div className="histogram">
|
|
240
|
+
<ChartHeader metric={metricSetPair.name} />
|
|
241
|
+
<ChartLegend />
|
|
242
|
+
<div className="graph-container">
|
|
243
|
+
<div className="canary-chart">
|
|
244
|
+
<OrdinalFrame {...this.getChartProps()} />
|
|
245
|
+
</div>
|
|
246
|
+
<Tooltip {...this.state.tooltip} />
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Suspense } from 'react';
|
|
3
|
+
|
|
4
|
+
import { Spinner } from '@spinnaker/core';
|
|
5
|
+
|
|
6
|
+
import { GraphType, IMetricSetPairGraphProps, metricSetPairGraphService } from '../metricSetPairGraph.service';
|
|
7
|
+
const SemioticGraphLazy = React.lazy(() => import(/* webpackChunkName: "Lazy-KayentaGraphs" */ './semioticGraph'));
|
|
8
|
+
|
|
9
|
+
const supportedGraphTypes: GraphType[] = [GraphType.TimeSeries, GraphType.Histogram, GraphType.BoxPlot];
|
|
10
|
+
// Semiotic component registration
|
|
11
|
+
metricSetPairGraphService.register({
|
|
12
|
+
name: 'semiotic',
|
|
13
|
+
handlesGraphType: (type) => supportedGraphTypes.includes(type),
|
|
14
|
+
getGraph: () => (props: IMetricSetPairGraphProps) => (
|
|
15
|
+
<Suspense fallback={<Spinner />}>
|
|
16
|
+
<SemioticGraphLazy {...props} />
|
|
17
|
+
</Suspense>
|
|
18
|
+
),
|
|
19
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { vizConfig } from './config';
|
|
4
|
+
import './noValidDataSign.less';
|
|
5
|
+
|
|
6
|
+
export default () => (
|
|
7
|
+
<div style={{ height: vizConfig.height }} className="no-data-sign">
|
|
8
|
+
No Valid Data is Available for This Metric
|
|
9
|
+
</div>
|
|
10
|
+
);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { scaleUtc } from 'd3-scale';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { Axis } from 'semiotic';
|
|
4
|
+
|
|
5
|
+
import { vizConfig } from './config';
|
|
6
|
+
import CustomAxisTickLabel from './customAxisTickLabel';
|
|
7
|
+
import { IMargin } from './semiotic.service';
|
|
8
|
+
import * as utils from './utils';
|
|
9
|
+
|
|
10
|
+
import './secondaryTSXAxis.less';
|
|
11
|
+
|
|
12
|
+
interface ISecondaryTSXAxisProps {
|
|
13
|
+
margin: IMargin;
|
|
14
|
+
width: number;
|
|
15
|
+
millisSet: number[];
|
|
16
|
+
axisLabel?: string;
|
|
17
|
+
bottomOffset: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
* Secondary X Axis for Time Series Graph
|
|
22
|
+
* Used when canary and baseline have different start time. We can overlay this axis component
|
|
23
|
+
* on the main graph component
|
|
24
|
+
*/
|
|
25
|
+
export default class SecondaryTSXAxis extends React.Component<ISecondaryTSXAxisProps> {
|
|
26
|
+
public render() {
|
|
27
|
+
const { margin, width, millisSet, axisLabel, bottomOffset } = this.props;
|
|
28
|
+
|
|
29
|
+
const { axisTickLineHeight, axisTickLabelHeight, axisLabelHeight } = vizConfig.timeSeries;
|
|
30
|
+
|
|
31
|
+
const extent = [millisSet[0], millisSet[millisSet.length - 1]].map((ms: number) => new Date(ms));
|
|
32
|
+
const totalAxisHeight = axisTickLabelHeight + axisTickLineHeight + (axisLabel ? axisLabelHeight : 0);
|
|
33
|
+
const netWidth = width - margin.left - margin.right;
|
|
34
|
+
const range = [0, netWidth];
|
|
35
|
+
const containerStyle = {
|
|
36
|
+
bottom: bottomOffset,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const svgWrapperStyle = {
|
|
40
|
+
transform: `translateX(${margin.left}px)`,
|
|
41
|
+
};
|
|
42
|
+
return (
|
|
43
|
+
<svg className="axis secondary-ts-x-axis" width={width} height={totalAxisHeight} style={containerStyle}>
|
|
44
|
+
<g className="wrapper" style={svgWrapperStyle}>
|
|
45
|
+
<Axis
|
|
46
|
+
className="x axis bottom canary-dual-axis"
|
|
47
|
+
size={[netWidth, axisTickLineHeight]}
|
|
48
|
+
scale={scaleUtc().domain(extent).range(range)}
|
|
49
|
+
orient="bottom"
|
|
50
|
+
label="Canary"
|
|
51
|
+
tickValues={utils.calculateDateTimeTicks(millisSet)}
|
|
52
|
+
tickFormat={(d: number) => <CustomAxisTickLabel millis={d} />}
|
|
53
|
+
/>
|
|
54
|
+
</g>
|
|
55
|
+
</svg>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { IMetricSetPairGraphProps } from '../metricSetPairGraph.service';
|
|
2
|
+
|
|
3
|
+
export interface ISemioticChartProps extends IMetricSetPairGraphProps {
|
|
4
|
+
parentWidth: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface IMargin {
|
|
8
|
+
top?: number;
|
|
9
|
+
bottom?: number;
|
|
10
|
+
left?: number;
|
|
11
|
+
right?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ITooltip {
|
|
15
|
+
content: JSX.Element;
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ISummaryStatistics {
|
|
21
|
+
[prop: string]: ISummaryStatisticsValue;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ISummaryStatisticsValue {
|
|
25
|
+
value: number;
|
|
26
|
+
label: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ITimeSeriesDataSets {
|
|
30
|
+
value: number;
|
|
31
|
+
label: string;
|
|
32
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
@import './config.less';
|
|
2
|
+
|
|
3
|
+
.semiotic-graph {
|
|
4
|
+
margin-bottom: 8px;
|
|
5
|
+
.visualization-layer {
|
|
6
|
+
shape-rendering: crispedges;
|
|
7
|
+
.background-graphics {
|
|
8
|
+
.axis-tick-lines {
|
|
9
|
+
stroke: @grid-color;
|
|
10
|
+
opacity: 0.1;
|
|
11
|
+
}
|
|
12
|
+
.axis-title text {
|
|
13
|
+
letter-spacing: 1.5px;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.axis {
|
|
19
|
+
font-size: 12px;
|
|
20
|
+
font-weight: 400;
|
|
21
|
+
fill: @axis-color;
|
|
22
|
+
.axis-title.left.y text {
|
|
23
|
+
letter-spacing: 1.5px;
|
|
24
|
+
}
|
|
25
|
+
.x.axis.bottom text.axis-label {
|
|
26
|
+
transform: translateY(-10px);
|
|
27
|
+
}
|
|
28
|
+
.x.axis.bottom text.axis-label:nth-child(2) {
|
|
29
|
+
transform: translateY(5px);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.chart-header {
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
justify-content: center;
|
|
37
|
+
|
|
38
|
+
.prefix {
|
|
39
|
+
font-weight: 600;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.chart-title {
|
|
44
|
+
display: flex;
|
|
45
|
+
justify-content: center;
|
|
46
|
+
flex-direction: row;
|
|
47
|
+
font-size: 12px;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.graph-container {
|
|
51
|
+
position: relative;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import ContainerDimensions from 'react-container-dimensions';
|
|
3
|
+
|
|
4
|
+
import BoxPlot from './boxplot';
|
|
5
|
+
import Histogram from './histogram';
|
|
6
|
+
import { GraphType, IMetricSetPairGraphProps } from '../metricSetPairGraph.service';
|
|
7
|
+
import NoValidDataSign from './noValidDataSign';
|
|
8
|
+
import TimeSeries from './timeSeries';
|
|
9
|
+
|
|
10
|
+
import './semioticGraph.less';
|
|
11
|
+
|
|
12
|
+
export default class SemioticGraph extends React.Component<IMetricSetPairGraphProps> {
|
|
13
|
+
private fetchChart = (parentWidth: number) => {
|
|
14
|
+
const {
|
|
15
|
+
type,
|
|
16
|
+
metricSetPair: {
|
|
17
|
+
values: { control, experiment },
|
|
18
|
+
},
|
|
19
|
+
} = this.props;
|
|
20
|
+
const chartProps = {
|
|
21
|
+
...this.props,
|
|
22
|
+
parentWidth,
|
|
23
|
+
};
|
|
24
|
+
const filterInvalidValues = (data: number[]) => data.filter((v) => typeof v === 'number');
|
|
25
|
+
|
|
26
|
+
if (filterInvalidValues(control).length === 0 && filterInvalidValues(experiment).length === 0) {
|
|
27
|
+
return <NoValidDataSign />;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
switch (type) {
|
|
31
|
+
case GraphType.TimeSeries:
|
|
32
|
+
return <TimeSeries {...chartProps} />;
|
|
33
|
+
case GraphType.Histogram:
|
|
34
|
+
return <Histogram {...chartProps} />;
|
|
35
|
+
case GraphType.BoxPlot:
|
|
36
|
+
return <BoxPlot {...chartProps} />;
|
|
37
|
+
default:
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
public render() {
|
|
43
|
+
return (
|
|
44
|
+
<div className="semiotic-graph">
|
|
45
|
+
<ContainerDimensions>{({ width }: { width: number }) => this.fetchChart(width)}</ContainerDimensions>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|