@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.
Files changed (289) hide show
  1. package/.editorconfig +9 -0
  2. package/.eslintrc.js +1 -0
  3. package/.huskyrc +5 -0
  4. package/.lintstagedrc.json +4 -0
  5. package/.prettierignore +4 -0
  6. package/.prettierrc.js +1 -0
  7. package/LICENSE.txt +203 -0
  8. package/README.md +81 -0
  9. package/__mocks__/styleMock.js +1 -0
  10. package/__mocks__/version.json +4 -0
  11. package/babel.config.js +3 -0
  12. package/build.gradle +67 -0
  13. package/build_scripts/checkLicenses.js +79 -0
  14. package/gradle.properties +0 -0
  15. package/jest.config.js +204 -0
  16. package/jest.setup.js +5 -0
  17. package/package.json +166 -0
  18. package/rollup-plugin-angularjs-template-loader.js +82 -0
  19. package/rollup.config.js +30 -0
  20. package/src/index.ts +2 -0
  21. package/src/kayenta/actions/creators.ts +163 -0
  22. package/src/kayenta/actions/index.ts +98 -0
  23. package/src/kayenta/canary.dataSource.bridge.ts +53 -0
  24. package/src/kayenta/canary.dataSource.stub.ts +64 -0
  25. package/src/kayenta/canary.help.ts +136 -0
  26. package/src/kayenta/canary.less +168 -0
  27. package/src/kayenta/canary.settings.ts +26 -0
  28. package/src/kayenta/canary.tsx +67 -0
  29. package/src/kayenta/components/canaryScore.component.less +77 -0
  30. package/src/kayenta/components/canaryScore.component.ts +12 -0
  31. package/src/kayenta/components/canaryScore.tsx +63 -0
  32. package/src/kayenta/components/canaryScores.component.ts +20 -0
  33. package/src/kayenta/components/canaryScores.less +22 -0
  34. package/src/kayenta/components/canaryScores.tsx +163 -0
  35. package/src/kayenta/components/loadStates.tsx +52 -0
  36. package/src/kayenta/domain/ICanaryConfig.ts +57 -0
  37. package/src/kayenta/domain/ICanaryConfigSummary.ts +7 -0
  38. package/src/kayenta/domain/ICanaryConfigUpdateResponse.ts +3 -0
  39. package/src/kayenta/domain/ICanaryExecutionStatusResult.ts +72 -0
  40. package/src/kayenta/domain/ICanaryJudgeResult.ts +51 -0
  41. package/src/kayenta/domain/ICanaryJudgeResultSummary.ts +5 -0
  42. package/src/kayenta/domain/ICanaryScoreThresholds.ts +4 -0
  43. package/src/kayenta/domain/IJudge.ts +5 -0
  44. package/src/kayenta/domain/IKayentaAccount.ts +14 -0
  45. package/src/kayenta/domain/IKayentaStageConfig.ts +58 -0
  46. package/src/kayenta/domain/IMetricSetPair.ts +17 -0
  47. package/src/kayenta/domain/IMetricsServiceMetadata.ts +2 -0
  48. package/src/kayenta/domain/ISetupCanaryStage.ts +11 -0
  49. package/src/kayenta/domain/MetricClassificationLabel.ts +8 -0
  50. package/src/kayenta/domain/ScoreClassificationLabel.ts +7 -0
  51. package/src/kayenta/domain/index.ts +15 -0
  52. package/src/kayenta/edit/changeMetricGroupModal.tsx +107 -0
  53. package/src/kayenta/edit/configDetail.tsx +31 -0
  54. package/src/kayenta/edit/configDetailActionButtons.tsx +24 -0
  55. package/src/kayenta/edit/configDetailHeader.tsx +104 -0
  56. package/src/kayenta/edit/configDetailLoadStates.tsx +36 -0
  57. package/src/kayenta/edit/configDetailLoader.tsx +60 -0
  58. package/src/kayenta/edit/configJson.less +42 -0
  59. package/src/kayenta/edit/configJsonModal.tsx +158 -0
  60. package/src/kayenta/edit/configList.less +7 -0
  61. package/src/kayenta/edit/configList.tsx +57 -0
  62. package/src/kayenta/edit/copyConfigButton.tsx +34 -0
  63. package/src/kayenta/edit/createConfigButton.tsx +34 -0
  64. package/src/kayenta/edit/deleteModal.tsx +87 -0
  65. package/src/kayenta/edit/edit.tsx +24 -0
  66. package/src/kayenta/edit/editMetricEffectSizes.tsx +186 -0
  67. package/src/kayenta/edit/editMetricModal.less +9 -0
  68. package/src/kayenta/edit/editMetricModal.spec.tsx +129 -0
  69. package/src/kayenta/edit/editMetricModal.tsx +294 -0
  70. package/src/kayenta/edit/editMetricValidation.spec.ts +63 -0
  71. package/src/kayenta/edit/editMetricValidation.ts +50 -0
  72. package/src/kayenta/edit/filterTemplateSelector.less +15 -0
  73. package/src/kayenta/edit/filterTemplateSelector.spec.tsx +106 -0
  74. package/src/kayenta/edit/filterTemplateSelector.tsx +194 -0
  75. package/src/kayenta/edit/filterTemplatesValidation.spec.ts +108 -0
  76. package/src/kayenta/edit/filterTemplatesValidation.ts +95 -0
  77. package/src/kayenta/edit/footer.less +30 -0
  78. package/src/kayenta/edit/footer.tsx +12 -0
  79. package/src/kayenta/edit/groupName.tsx +80 -0
  80. package/src/kayenta/edit/groupTabs.tsx +106 -0
  81. package/src/kayenta/edit/groupWeight.tsx +80 -0
  82. package/src/kayenta/edit/groupWeights.tsx +38 -0
  83. package/src/kayenta/edit/inlineTemplateEditor.spec.tsx +42 -0
  84. package/src/kayenta/edit/inlineTemplateEditor.tsx +61 -0
  85. package/src/kayenta/edit/judgeSelect.tsx +82 -0
  86. package/src/kayenta/edit/metricConfigurerDelegator.tsx +30 -0
  87. package/src/kayenta/edit/metricList.less +21 -0
  88. package/src/kayenta/edit/metricList.tsx +215 -0
  89. package/src/kayenta/edit/metricStoreSelector.tsx +66 -0
  90. package/src/kayenta/edit/nameAndDescription.tsx +90 -0
  91. package/src/kayenta/edit/openConfigJsonModalButton.tsx +33 -0
  92. package/src/kayenta/edit/openDeleteModalButton.tsx +50 -0
  93. package/src/kayenta/edit/ownedBy.tsx +34 -0
  94. package/src/kayenta/edit/save.tsx +19 -0
  95. package/src/kayenta/edit/saveConfigButton.tsx +65 -0
  96. package/src/kayenta/edit/saveConfigError.tsx +59 -0
  97. package/src/kayenta/edit/scoring.tsx +35 -0
  98. package/src/kayenta/edit/selectConfig.tsx +10 -0
  99. package/src/kayenta/edit/validationErrors.tsx +39 -0
  100. package/src/kayenta/index.ts +6 -0
  101. package/src/kayenta/layout/addNewButton.tsx +20 -0
  102. package/src/kayenta/layout/centeredDetail.tsx +13 -0
  103. package/src/kayenta/layout/deleteButton.tsx +11 -0
  104. package/src/kayenta/layout/disableable.tsx +87 -0
  105. package/src/kayenta/layout/formList.tsx +26 -0
  106. package/src/kayenta/layout/formRow.tsx +36 -0
  107. package/src/kayenta/layout/formattedDate.tsx +14 -0
  108. package/src/kayenta/layout/index.ts +2 -0
  109. package/src/kayenta/layout/keyValueList.less +20 -0
  110. package/src/kayenta/layout/keyValueList.tsx +114 -0
  111. package/src/kayenta/layout/list.less +9 -0
  112. package/src/kayenta/layout/list.spec.tsx +83 -0
  113. package/src/kayenta/layout/list.tsx +73 -0
  114. package/src/kayenta/layout/listDetail.tsx +33 -0
  115. package/src/kayenta/layout/radioChoice.tsx +29 -0
  116. package/src/kayenta/layout/styleguide.tsx +16 -0
  117. package/src/kayenta/layout/table/index.ts +5 -0
  118. package/src/kayenta/layout/table/nativeTable.tsx +51 -0
  119. package/src/kayenta/layout/table/nativeTableHeader.tsx +26 -0
  120. package/src/kayenta/layout/table/table.tsx +56 -0
  121. package/src/kayenta/layout/table/tableColumn.ts +7 -0
  122. package/src/kayenta/layout/table/tableHeader.tsx +23 -0
  123. package/src/kayenta/layout/tabs.tsx +26 -0
  124. package/src/kayenta/layout/titledSection.less +16 -0
  125. package/src/kayenta/layout/titledSection.tsx +20 -0
  126. package/src/kayenta/layout/titledSubsection.less +11 -0
  127. package/src/kayenta/layout/titledSubsection.tsx +22 -0
  128. package/src/kayenta/manualAnalysis/ManualAnalysisModal.tsx +716 -0
  129. package/src/kayenta/metricStore/atlas/atlasMetricConfigurer.tsx +130 -0
  130. package/src/kayenta/metricStore/atlas/index.ts +8 -0
  131. package/src/kayenta/metricStore/datadog/domain/IDatadogMetricDescriptor.ts +5 -0
  132. package/src/kayenta/metricStore/datadog/index.ts +9 -0
  133. package/src/kayenta/metricStore/datadog/metricConfigurer.tsx +90 -0
  134. package/src/kayenta/metricStore/datadog/metricTypeSelector.spec.tsx +59 -0
  135. package/src/kayenta/metricStore/datadog/metricTypeSelector.tsx +73 -0
  136. package/src/kayenta/metricStore/graphite/domain/IGraphiteMetricDescriptor.ts +5 -0
  137. package/src/kayenta/metricStore/graphite/index.ts +8 -0
  138. package/src/kayenta/metricStore/graphite/metricConfigurer.tsx +54 -0
  139. package/src/kayenta/metricStore/graphite/metricTypeSelector.tsx +80 -0
  140. package/src/kayenta/metricStore/graphite/typeahead.less +3 -0
  141. package/src/kayenta/metricStore/index.ts +8 -0
  142. package/src/kayenta/metricStore/metricStoreConfig.service.ts +12 -0
  143. package/src/kayenta/metricStore/newrelic/domain/INewRelicMetricDescriptor.ts +5 -0
  144. package/src/kayenta/metricStore/newrelic/index.ts +8 -0
  145. package/src/kayenta/metricStore/newrelic/metricConfigurer.tsx +58 -0
  146. package/src/kayenta/metricStore/prometheus/domain/IPrometheusCanaryMetricSetQueryConfig.ts +14 -0
  147. package/src/kayenta/metricStore/prometheus/domain/IPrometheusMetricDescriptor.ts +5 -0
  148. package/src/kayenta/metricStore/prometheus/index.ts +12 -0
  149. package/src/kayenta/metricStore/prometheus/metricConfigurer.tsx +157 -0
  150. package/src/kayenta/metricStore/prometheus/metricTypeSelector.less +5 -0
  151. package/src/kayenta/metricStore/prometheus/metricTypeSelector.spec.tsx +62 -0
  152. package/src/kayenta/metricStore/prometheus/metricTypeSelector.tsx +144 -0
  153. package/src/kayenta/metricStore/prometheus/queryTypeSelectors.spec.ts +61 -0
  154. package/src/kayenta/metricStore/prometheus/queryTypeSelectors.ts +38 -0
  155. package/src/kayenta/metricStore/signalfx/domain/ISignalFxCanaryMetricSetQueryConfig.ts +7 -0
  156. package/src/kayenta/metricStore/signalfx/index.ts +8 -0
  157. package/src/kayenta/metricStore/signalfx/metricConfigurer.less +10 -0
  158. package/src/kayenta/metricStore/signalfx/metricConfigurer.tsx +187 -0
  159. package/src/kayenta/metricStore/stackdriver/domain/IStackdriverCanaryMetricSetQueryConfig.ts +9 -0
  160. package/src/kayenta/metricStore/stackdriver/domain/IStackdriverMetricDescriptor.ts +17 -0
  161. package/src/kayenta/metricStore/stackdriver/index.ts +12 -0
  162. package/src/kayenta/metricStore/stackdriver/metricConfigurer.tsx +144 -0
  163. package/src/kayenta/metricStore/stackdriver/metricTypeSelector.spec.tsx +92 -0
  164. package/src/kayenta/metricStore/stackdriver/metricTypeSelector.tsx +113 -0
  165. package/src/kayenta/middleware/actionInterceptor.ts +29 -0
  166. package/src/kayenta/middleware/asyncDispatch.ts +37 -0
  167. package/src/kayenta/middleware/epics.ts +211 -0
  168. package/src/kayenta/middleware/index.ts +3 -0
  169. package/src/kayenta/navigation/canary.states.stub.ts +28 -0
  170. package/src/kayenta/navigation/canary.states.ts +182 -0
  171. package/src/kayenta/reducers/app.ts +56 -0
  172. package/src/kayenta/reducers/asyncRequest.ts +5 -0
  173. package/src/kayenta/reducers/data.ts +169 -0
  174. package/src/kayenta/reducers/editingTemplate.ts +54 -0
  175. package/src/kayenta/reducers/group.ts +82 -0
  176. package/src/kayenta/reducers/index.ts +245 -0
  177. package/src/kayenta/reducers/prometheusMetricConfig.spec.ts +33 -0
  178. package/src/kayenta/reducers/prometheusMetricConfig.ts +56 -0
  179. package/src/kayenta/reducers/selectedConfig.spec.ts +190 -0
  180. package/src/kayenta/reducers/selectedConfig.ts +566 -0
  181. package/src/kayenta/reducers/selectedRun.ts +101 -0
  182. package/src/kayenta/reducers/signalFxMetricConfig.ts +36 -0
  183. package/src/kayenta/reducers/stackdriverMetricConfig.spec.ts +33 -0
  184. package/src/kayenta/reducers/stackdriverMetricConfig.ts +41 -0
  185. package/src/kayenta/reducers/templates.spec.ts +192 -0
  186. package/src/kayenta/reducers/validators.ts +118 -0
  187. package/src/kayenta/report/detail/allMetricResultsHeader.tsx +32 -0
  188. package/src/kayenta/report/detail/clickableHeader.tsx +21 -0
  189. package/src/kayenta/report/detail/colors.ts +47 -0
  190. package/src/kayenta/report/detail/detail.less +16 -0
  191. package/src/kayenta/report/detail/detail.tsx +48 -0
  192. package/src/kayenta/report/detail/detailLoader.tsx +55 -0
  193. package/src/kayenta/report/detail/graph/graph.tsx +37 -0
  194. package/src/kayenta/report/detail/graph/metricSetPairGraph.service.ts +35 -0
  195. package/src/kayenta/report/detail/graph/semiotic/boxplot.less +45 -0
  196. package/src/kayenta/report/detail/graph/semiotic/boxplot.tsx +283 -0
  197. package/src/kayenta/report/detail/graph/semiotic/chartHeader.tsx +19 -0
  198. package/src/kayenta/report/detail/graph/semiotic/chartLegend.less +26 -0
  199. package/src/kayenta/report/detail/graph/semiotic/chartLegend.tsx +42 -0
  200. package/src/kayenta/report/detail/graph/semiotic/circleIcon.tsx +16 -0
  201. package/src/kayenta/report/detail/graph/semiotic/config.less +5 -0
  202. package/src/kayenta/report/detail/graph/semiotic/config.ts +38 -0
  203. package/src/kayenta/report/detail/graph/semiotic/customAxisTickLabel.tsx +17 -0
  204. package/src/kayenta/report/detail/graph/semiotic/declarations/labella.d.ts +16 -0
  205. package/src/kayenta/report/detail/graph/semiotic/declarations/react-container-dimensions.d.ts +3 -0
  206. package/src/kayenta/report/detail/graph/semiotic/declarations/semiotic.d.ts +160 -0
  207. package/src/kayenta/report/detail/graph/semiotic/differenceArea.less +17 -0
  208. package/src/kayenta/report/detail/graph/semiotic/differenceArea.tsx +186 -0
  209. package/src/kayenta/report/detail/graph/semiotic/histogram.less +22 -0
  210. package/src/kayenta/report/detail/graph/semiotic/histogram.tsx +251 -0
  211. package/src/kayenta/report/detail/graph/semiotic/index.tsx +19 -0
  212. package/src/kayenta/report/detail/graph/semiotic/noValidDataSign.less +5 -0
  213. package/src/kayenta/report/detail/graph/semiotic/noValidDataSign.tsx +10 -0
  214. package/src/kayenta/report/detail/graph/semiotic/secondaryTSXAxis.less +6 -0
  215. package/src/kayenta/report/detail/graph/semiotic/secondaryTSXAxis.tsx +58 -0
  216. package/src/kayenta/report/detail/graph/semiotic/semiotic.service.ts +32 -0
  217. package/src/kayenta/report/detail/graph/semiotic/semioticGraph.less +53 -0
  218. package/src/kayenta/report/detail/graph/semiotic/semioticGraph.tsx +49 -0
  219. package/src/kayenta/report/detail/graph/semiotic/timeSeries.less +42 -0
  220. package/src/kayenta/report/detail/graph/semiotic/timeSeries.tsx +473 -0
  221. package/src/kayenta/report/detail/graph/semiotic/tooltip.tsx +55 -0
  222. package/src/kayenta/report/detail/graph/semiotic/utils.ts +90 -0
  223. package/src/kayenta/report/detail/graphTypeSelector.less +4 -0
  224. package/src/kayenta/report/detail/graphTypeSelector.tsx +50 -0
  225. package/src/kayenta/report/detail/groupScores.tsx +68 -0
  226. package/src/kayenta/report/detail/header.less +70 -0
  227. package/src/kayenta/report/detail/header.tsx +39 -0
  228. package/src/kayenta/report/detail/headerArrow.tsx +13 -0
  229. package/src/kayenta/report/detail/loadStates.tsx +31 -0
  230. package/src/kayenta/report/detail/metricResultActions.less +29 -0
  231. package/src/kayenta/report/detail/metricResultActions.tsx +87 -0
  232. package/src/kayenta/report/detail/metricResultClassification.tsx +22 -0
  233. package/src/kayenta/report/detail/metricResultDetail.tsx +20 -0
  234. package/src/kayenta/report/detail/metricResultDetailLayout.tsx +19 -0
  235. package/src/kayenta/report/detail/metricResultDeviation.tsx +25 -0
  236. package/src/kayenta/report/detail/metricResultStats.less +9 -0
  237. package/src/kayenta/report/detail/metricResultStats.tsx +120 -0
  238. package/src/kayenta/report/detail/metricResults.less +12 -0
  239. package/src/kayenta/report/detail/metricResults.tsx +52 -0
  240. package/src/kayenta/report/detail/metricResultsClassificationFilters.tsx +65 -0
  241. package/src/kayenta/report/detail/metricResultsColumns.tsx +27 -0
  242. package/src/kayenta/report/detail/metricResultsList.less +44 -0
  243. package/src/kayenta/report/detail/metricResultsList.tsx +120 -0
  244. package/src/kayenta/report/detail/metricSetPairLoadStates.tsx +22 -0
  245. package/src/kayenta/report/detail/multipleResultsTable.tsx +81 -0
  246. package/src/kayenta/report/detail/reportException.tsx +57 -0
  247. package/src/kayenta/report/detail/reportExplanation.less +12 -0
  248. package/src/kayenta/report/detail/reportExplanation.tsx +32 -0
  249. package/src/kayenta/report/detail/reportMetadata.tsx +167 -0
  250. package/src/kayenta/report/detail/reportScores.less +47 -0
  251. package/src/kayenta/report/detail/reportScores.tsx +80 -0
  252. package/src/kayenta/report/detail/score.tsx +33 -0
  253. package/src/kayenta/report/detail/sourceLinks.tsx +69 -0
  254. package/src/kayenta/report/list/configLink.tsx +32 -0
  255. package/src/kayenta/report/list/executionList.less +7 -0
  256. package/src/kayenta/report/list/loadStates.tsx +32 -0
  257. package/src/kayenta/report/list/pipelineLink.tsx +15 -0
  258. package/src/kayenta/report/list/reportLink.tsx +33 -0
  259. package/src/kayenta/report/list/table.tsx +309 -0
  260. package/src/kayenta/report/report.tsx +11 -0
  261. package/src/kayenta/selectors/filterTemplatesSelectors.ts +87 -0
  262. package/src/kayenta/selectors/index.ts +62 -0
  263. package/src/kayenta/service/canaryConfig.service.ts +122 -0
  264. package/src/kayenta/service/canaryRun.service.ts +60 -0
  265. package/src/kayenta/service/delegateFactory.ts +24 -0
  266. package/src/kayenta/service/metricsServiceMetadata.service.ts +9 -0
  267. package/src/kayenta/stages/kayentaStage/AnalysisType.spec.tsx +47 -0
  268. package/src/kayenta/stages/kayentaStage/AnalysisType.tsx +49 -0
  269. package/src/kayenta/stages/kayentaStage/CanaryExecutionLabel.tsx +26 -0
  270. package/src/kayenta/stages/kayentaStage/analysisType.component.ts +12 -0
  271. package/src/kayenta/stages/kayentaStage/canaryRunSummaries.component.ts +12 -0
  272. package/src/kayenta/stages/kayentaStage/canaryRunSummaries.less +5 -0
  273. package/src/kayenta/stages/kayentaStage/canaryRunSummaries.tsx +136 -0
  274. package/src/kayenta/stages/kayentaStage/forAnalysisType.component.ts +45 -0
  275. package/src/kayenta/stages/kayentaStage/kayentaStage.controller.ts +789 -0
  276. package/src/kayenta/stages/kayentaStage/kayentaStage.html +528 -0
  277. package/src/kayenta/stages/kayentaStage/kayentaStage.less +5 -0
  278. package/src/kayenta/stages/kayentaStage/kayentaStage.transformer.ts +179 -0
  279. package/src/kayenta/stages/kayentaStage/kayentaStage.ts +221 -0
  280. package/src/kayenta/stages/kayentaStage/kayentaStageConfigSection.component.ts +21 -0
  281. package/src/kayenta/stages/kayentaStage/kayentaStageExecutionDetails.controller.ts +88 -0
  282. package/src/kayenta/stages/kayentaStage/kayentaStageExecutionDetails.html +114 -0
  283. package/src/kayenta/stages/kayentaStage/kayentaStageExecutionDetails.less +6 -0
  284. package/src/kayenta/stages/kayentaStage/stageTypes.ts +5 -0
  285. package/src/kayenta/utils/duration.spec.ts +69 -0
  286. package/src/kayenta/utils/duration.ts +48 -0
  287. package/src/lazy.ts +29 -0
  288. package/src/stub.ts +60 -0
  289. package/tsconfig.json +11 -0
@@ -0,0 +1,45 @@
1
+ .boxplot {
2
+ .tooltip-container {
3
+ .columns {
4
+ display: flex;
5
+ flex-direction: row;
6
+ .column {
7
+ padding: 10px;
8
+ .header {
9
+ font-weight: 600;
10
+ padding-left: 0px;
11
+ }
12
+ }
13
+ }
14
+ }
15
+
16
+ .boxplot-annotation {
17
+ text {
18
+ fill: var(--color-text-primary);
19
+ font-size: 12px;
20
+ font-weight: 600;
21
+ }
22
+ .annotation-connector {
23
+ display: none;
24
+ }
25
+ .note-line {
26
+ display: none;
27
+ }
28
+ }
29
+
30
+ .boxplot-chart {
31
+ .boxplot-summary {
32
+ g {
33
+ opacity: 0.8;
34
+ }
35
+ g:nth-child(1),
36
+ g:nth-child(2),
37
+ g:nth-child(3) {
38
+ opacity: 0.3;
39
+ }
40
+ }
41
+ g.pieces {
42
+ shape-rendering: geometricprecision;
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,283 @@
1
+ import { extent } from 'd3-array';
2
+ import { Force, Node } from 'labella';
3
+ import * as React from 'react';
4
+ import {
5
+ Annotation,
6
+ IAnnotationType,
7
+ IOrFrameHoverArgs,
8
+ IOrGroup,
9
+ IOrSummaryPiece,
10
+ ISemioticAnnotationArgs,
11
+ OrdinalFrame,
12
+ } from 'semiotic';
13
+
14
+ import ChartHeader from './chartHeader';
15
+ import ChartLegend from './chartLegend';
16
+ import CircleIcon from './circleIcon';
17
+ import { vizConfig } from './config';
18
+ import { IMargin, ISemioticChartProps, ITooltip } from './semiotic.service';
19
+ import Tooltip from './tooltip';
20
+ import * as utils from './utils';
21
+
22
+ import './boxplot.less';
23
+
24
+ interface IChartDataPoint {
25
+ value: number;
26
+ group: string;
27
+ color: string;
28
+ }
29
+
30
+ interface IHoverData {
31
+ value: number;
32
+ label: string;
33
+ }
34
+
35
+ interface IHoverDataGroup {
36
+ [group: string]: IHoverData[];
37
+ }
38
+
39
+ interface IAnnotationData {
40
+ group: string;
41
+ summaryKeys: string[];
42
+ type: string;
43
+ }
44
+
45
+ interface IBoxPlotState {
46
+ tooltip: ITooltip;
47
+ }
48
+
49
+ export default class BoxPlot extends React.Component<ISemioticChartProps, IBoxPlotState> {
50
+ public state: IBoxPlotState = {
51
+ tooltip: null,
52
+ };
53
+
54
+ private margin: IMargin = {
55
+ top: 10,
56
+ bottom: 20,
57
+ left: 60,
58
+ right: 40,
59
+ };
60
+
61
+ private decorateData = (dataPoints: number[], group: string): IChartDataPoint[] => {
62
+ return dataPoints.map((dp) => ({
63
+ group,
64
+ value: dp,
65
+ color: vizConfig.colors[group],
66
+ }));
67
+ };
68
+
69
+ private generateChartData = () => {
70
+ const { metricSetPair } = this.props;
71
+ const filterFunc = (v: IChartDataPoint) => typeof v.value === 'number';
72
+ const baselineInput = this.decorateData(metricSetPair.values.control, 'baseline');
73
+ const canaryInput = this.decorateData(metricSetPair.values.experiment, 'canary');
74
+ const chartData = baselineInput.concat(canaryInput).filter(filterFunc);
75
+ return chartData;
76
+ };
77
+
78
+ // Generate tooltip content that shows the summary statistics of a boxplot
79
+ private createChartHoverHandler = () => {
80
+ return (d: IOrFrameHoverArgs<IChartDataPoint> & IOrSummaryPiece): void => {
81
+ if (d && d.type === 'frame-hover') {
82
+ const points = d.points;
83
+ const data: IHoverDataGroup = {
84
+ baseline: [],
85
+ canary: [],
86
+ };
87
+
88
+ points.forEach((p: IOrSummaryPiece) => {
89
+ data[p.key].push({ label: p.label, value: p.value });
90
+ });
91
+
92
+ const summaryLabels = data.baseline.map((b: IHoverData) => b.label);
93
+ const summaryKeysColumn = [
94
+ <div className={'header'} key={'summary'}>
95
+ Summary
96
+ </div>,
97
+ ...summaryLabels.map((label: string) => {
98
+ return <div key={label}>{label}</div>;
99
+ }),
100
+ ];
101
+
102
+ const baselineColumn = [
103
+ <div className={'header'} key={'baseline'}>
104
+ <CircleIcon group={'baseline'} />
105
+ Baseline
106
+ </div>,
107
+ data.baseline.map((hd: IHoverData, i: number) => {
108
+ return <div key={i}>{utils.formatMetricValue(hd.value)}</div>;
109
+ }),
110
+ ];
111
+
112
+ const canaryColumn = [
113
+ <div className="header" key="canary">
114
+ <CircleIcon group="canary" />
115
+ Canary
116
+ </div>,
117
+ data.canary.map((hd: IHoverData, i: number) => {
118
+ return <div key={i}>{utils.formatMetricValue(hd.value)}</div>;
119
+ }),
120
+ ];
121
+
122
+ const tooltipContent = (
123
+ <div className="tooltip-container">
124
+ <div className="columns">
125
+ <div className="column">{summaryKeysColumn}</div>
126
+ <div className="column">{baselineColumn}</div>
127
+ <div className="column">{canaryColumn}</div>
128
+ </div>
129
+ </div>
130
+ );
131
+
132
+ this.setState({
133
+ tooltip: {
134
+ content: tooltipContent,
135
+ x: d.x + this.margin.left,
136
+ y: d.y + this.margin.top,
137
+ },
138
+ });
139
+ } else {
140
+ this.setState({ tooltip: null });
141
+ }
142
+ };
143
+ };
144
+
145
+ private defineAnnotations = () => {
146
+ return ['baseline', 'canary'].map((g: string) => {
147
+ return {
148
+ type: 'summary-custom',
149
+ group: g,
150
+ summaryKeys: ['q1area', 'median', 'q3area'],
151
+ };
152
+ });
153
+ };
154
+
155
+ private customAnnotationFunction = (args: ISemioticAnnotationArgs<IAnnotationData, IOrGroup<IChartDataPoint>>) => {
156
+ const {
157
+ d,
158
+ orFrameState: { pieceDataXY },
159
+ categories,
160
+ } = args;
161
+
162
+ if (d.type === 'summary-custom') {
163
+ // If no data exists for this group, don't return any annotation elements
164
+ if (!categories[d.group]) {
165
+ return null;
166
+ }
167
+
168
+ const summaryData = pieceDataXY.filter((sd: IOrSummaryPiece) => sd.key === d.group);
169
+ const boxPlotWidth = categories[d.group].width;
170
+ const statLabelMap: { [stat: string]: string } = {
171
+ median: 'median',
172
+ q1area: '25th %-ile',
173
+ q3area: '75th %-ile',
174
+ };
175
+ const createNoteElement = (posY: number, dataPoint: IOrSummaryPiece) => {
176
+ const name = dataPoint.summaryPieceName;
177
+ const label = statLabelMap[name];
178
+ const noteData = {
179
+ x: dataPoint.x,
180
+ y: posY,
181
+ dx: boxPlotWidth / 2,
182
+ dy: 0,
183
+ note: {
184
+ label: `${label}: ${utils.formatMetricValue(dataPoint.value)}`,
185
+ wrap: 100,
186
+ lineType: 'vertical',
187
+ align: 'middle',
188
+ orientation: 'topBottom',
189
+ padding: 10,
190
+ },
191
+ className: 'boxplot-annotation',
192
+ };
193
+ return <Annotation key={name} noteData={noteData} />;
194
+ };
195
+
196
+ const nodes = d.summaryKeys
197
+ .map((summaryKey: string) => {
198
+ const pieceData = summaryData.find((sd: IOrSummaryPiece) => sd.summaryPieceName === summaryKey);
199
+ return pieceData ? new Node<IOrSummaryPiece>(pieceData.y, 20, pieceData) : null;
200
+ })
201
+ .map((v: null | Node<IOrSummaryPiece>) => v) as Array<Node<IOrSummaryPiece>>;
202
+
203
+ const forceOptions = {
204
+ minPos: this.margin.top,
205
+ maxPos: vizConfig.height - this.margin.bottom,
206
+ };
207
+ const annotations = new Force<IOrSummaryPiece>(forceOptions)
208
+ .nodes(nodes)
209
+ .compute()
210
+ .nodes()
211
+ .map((n: Node<IOrSummaryPiece>) => createNoteElement(n.currentPos, n.data));
212
+
213
+ return annotations;
214
+ } else {
215
+ return null;
216
+ }
217
+ };
218
+
219
+ private getChartProps = () => {
220
+ const { parentWidth } = this.props;
221
+ const chartData = this.generateChartData();
222
+
223
+ return {
224
+ size: [parentWidth, vizConfig.height],
225
+ margin: this.margin,
226
+ projection: 'vertical',
227
+ summaryType: 'boxplot',
228
+ oLabel: false,
229
+ oPadding: 160,
230
+ style: (d: IChartDataPoint) => {
231
+ return {
232
+ fill: d.color,
233
+ fillOpacity: 0.7,
234
+ };
235
+ },
236
+ summaryStyle: (d: IChartDataPoint) => {
237
+ return {
238
+ fill: d.color,
239
+ fillOpacity: 0.4,
240
+ stroke: '#6a6a6a',
241
+ strokeWidth: 2,
242
+ };
243
+ },
244
+ pieceClass: (d: IChartDataPoint) => `piece ${d.group}`,
245
+ type: {
246
+ type: 'swarm',
247
+ r: 3,
248
+ iterations: 50,
249
+ },
250
+ rExtent: extent(chartData.map((o) => o.value)),
251
+ customHoverBehavior: this.createChartHoverHandler(),
252
+ hoverAnnotation: false,
253
+ annotations: this.defineAnnotations(),
254
+ summaryHoverAnnotation: [] as IAnnotationType[],
255
+ data: chartData,
256
+ oAccessor: (d: IChartDataPoint) => d.group,
257
+ rAccessor: (d: IChartDataPoint) => d.value,
258
+ svgAnnotationRules: this.customAnnotationFunction,
259
+ summaryClass: 'boxplot-summary',
260
+ axis: {
261
+ orient: 'left',
262
+ label: 'metric value',
263
+ tickFormat: (d: number) => utils.formatMetricValue(d),
264
+ },
265
+ };
266
+ };
267
+
268
+ public render() {
269
+ const { metricSetPair } = this.props;
270
+ return (
271
+ <div className="boxplot">
272
+ <ChartHeader metric={metricSetPair.name} />
273
+ <ChartLegend />
274
+ <div className="graph-container">
275
+ <div className="boxplot-chart">
276
+ <OrdinalFrame {...this.getChartProps()} />
277
+ </div>
278
+ <Tooltip {...this.state.tooltip} />
279
+ </div>
280
+ </div>
281
+ );
282
+ }
283
+ }
@@ -0,0 +1,19 @@
1
+ import * as React from 'react';
2
+
3
+ export interface IChartHeaderProps {
4
+ metric: string;
5
+ }
6
+
7
+ export default ({ metric }: IChartHeaderProps) => {
8
+ return (
9
+ <div className="chart-header">
10
+ <h6 className="heading-6 color-text-primary">
11
+ <span className="uppercase prefix">metric name:</span>
12
+ <span>
13
+ &nbsp;
14
+ {metric}
15
+ </span>
16
+ </h6>
17
+ </div>
18
+ );
19
+ };
@@ -0,0 +1,26 @@
1
+ .chart-legend {
2
+ display: flex;
3
+ flex-direction: row;
4
+ align-items: center;
5
+ justify-content: center;
6
+ height: 24px;
7
+ margin-top: 4px;
8
+ .legend-item {
9
+ display: flex;
10
+ align-items: center;
11
+ margin: 0px 40px;
12
+ font-size: 12px;
13
+ }
14
+ .clickable {
15
+ cursor: pointer;
16
+ }
17
+ .deselected {
18
+ opacity: 0.5;
19
+ }
20
+ .legend-icon {
21
+ height: 12px;
22
+ width: 40px;
23
+ border-radius: 6px;
24
+ margin-right: 8px;
25
+ }
26
+ }
@@ -0,0 +1,42 @@
1
+ import classNames from 'classnames';
2
+ import * as React from 'react';
3
+
4
+ import { vizConfig } from './config';
5
+
6
+ import './chartLegend.less';
7
+
8
+ interface IChartLegendProps {
9
+ isClickable?: boolean;
10
+ onClickHandler?: (group: string) => void;
11
+ showGroup?: { [group: string]: boolean };
12
+ }
13
+
14
+ export default (props: IChartLegendProps) => {
15
+ const { onClickHandler, showGroup = { baseline: true, canary: true }, isClickable = false } = props;
16
+
17
+ const baselineIconStyle = {
18
+ backgroundColor: vizConfig.colors.baseline,
19
+ };
20
+
21
+ const canaryIconStyle = {
22
+ backgroundColor: vizConfig.colors.canary,
23
+ };
24
+
25
+ const handleClick = (group: string) => (onClickHandler ? () => onClickHandler(group) : undefined);
26
+
27
+ const legendItemClass = classNames('legend-item', { clickable: isClickable });
28
+ const legendItemClassCanary = classNames(legendItemClass, { deselected: !showGroup.canary });
29
+ const legendItemClassBaseline = classNames(legendItemClass, { deselected: !showGroup.baseline });
30
+ return (
31
+ <div className="chart-legend">
32
+ <div className={legendItemClassBaseline} onClick={handleClick('baseline')}>
33
+ <div className="legend-icon" style={baselineIconStyle} />
34
+ <div>Baseline</div>
35
+ </div>
36
+ <div className={legendItemClassCanary} onClick={handleClick('canary')}>
37
+ <div className="legend-icon" style={canaryIconStyle} />
38
+ <div>Canary</div>
39
+ </div>
40
+ </div>
41
+ );
42
+ };
@@ -0,0 +1,16 @@
1
+ import * as React from 'react';
2
+
3
+ import { vizConfig } from './config';
4
+
5
+ export interface ICircleIconProps {
6
+ group: string;
7
+ }
8
+
9
+ // simple component to show circle icons in tooltips
10
+ export default ({ group }: ICircleIconProps) => {
11
+ const iconStyle = {
12
+ color: vizConfig.colors[group],
13
+ paddingRight: 4,
14
+ };
15
+ return <i className="fas fa-circle" style={iconStyle} />;
16
+ };
@@ -0,0 +1,5 @@
1
+ @axis-color: var(--color-text-primary);
2
+ @grid-color: var(--color-dovegray);
3
+ @background-color: var(--color-alabaster);
4
+ @canary-color: #e26a6a;
5
+ @baseline-color: #52b3d9;
@@ -0,0 +1,38 @@
1
+ interface IColorConfig {
2
+ [propName: string]: string;
3
+ }
4
+
5
+ interface ITimeSeriesConfig {
6
+ [propName: string]: any;
7
+ }
8
+
9
+ interface IDataGroup {
10
+ [group: string]: string;
11
+ }
12
+
13
+ interface IVizConfig {
14
+ readonly colors: IColorConfig;
15
+ readonly height: number;
16
+ readonly timeSeries: ITimeSeriesConfig;
17
+ readonly dataGroupMap: IDataGroup;
18
+ }
19
+
20
+ export const vizConfig: IVizConfig = {
21
+ colors: {
22
+ baseline: '#52b3d9',
23
+ canary: '#e26a6a',
24
+ background: '#f8f8f8',
25
+ },
26
+ height: 400,
27
+ timeSeries: {
28
+ minimapDataPointsThreshold: 240,
29
+ minimapHeight: 40,
30
+ axisTickLineHeight: 4,
31
+ axisTickLabelHeight: 32,
32
+ axisLabelHeight: 16,
33
+ },
34
+ dataGroupMap: {
35
+ baseline: 'control',
36
+ canary: 'experiment',
37
+ },
38
+ };
@@ -0,0 +1,17 @@
1
+ import * as React from 'react';
2
+
3
+ import * as utils from './utils';
4
+
5
+ export interface ICustomAxisTickLabel {
6
+ millis: number;
7
+ }
8
+
9
+ // custom labels as we want two label rows when showing date + hour at midnight
10
+ export default ({ millis }: ICustomAxisTickLabel) => {
11
+ const text = utils.dateTimeTickFormatter(millis).map((s: string, i: number) => (
12
+ <text textAnchor={'middle'} className={'axis-label'} key={i}>
13
+ {s}
14
+ </text>
15
+ ));
16
+ return <g className={'axis-label'}>{text}</g>;
17
+ };
@@ -0,0 +1,16 @@
1
+ declare module 'labella' {
2
+ export class Node<Data> {
3
+ public readonly idealPos: number;
4
+ public readonly currentPos: number;
5
+ public readonly width: number;
6
+ public readonly data: Data;
7
+ public readonly layerIndex: number;
8
+ constructor(idealPos: number, width: number, data?: Data);
9
+ }
10
+ export class Force<Data> {
11
+ constructor(options: { [option: string]: number | string | boolean | null });
12
+ nodes(nodes: Array<Node<Data>>): this;
13
+ nodes(): Array<Node<Data>>; // eslint-disable-line no-dupe-class-members
14
+ compute(): this;
15
+ }
16
+ }
@@ -0,0 +1,3 @@
1
+ declare module 'react-container-dimensions' {
2
+ export default class ContainerDimensions extends React.Component {}
3
+ }
@@ -0,0 +1,160 @@
1
+ declare module 'semiotic' {
2
+ import { CurveFactory } from 'd3-shape';
3
+ import { ScaleTime } from 'd3-scale';
4
+
5
+ export class XYFrame extends React.Component<IXYFrameProps<DataSet, DataPoint>> {}
6
+ export class MinimapXYFrame extends React.Component<IMinimapProps<DataSet, DataPoint>> {}
7
+ export class OrdinalFrame extends React.Component {}
8
+ export class Annotation extends React.Component<IAnnotationProps> {}
9
+ export class Axis extends React.Component<IAxisProps> {}
10
+
11
+ // Props for the XYFrame component
12
+ export interface IXYFrameProps<DataSet, DataPoint> {
13
+ lines?: DataSet[];
14
+ lineType?: { type: string; interpolator: CurveFactory };
15
+ lineStyle?: (ds: DataSet) => object;
16
+ xAccessor?: string | ((d: DataPoint) => Date);
17
+ yAccessor?: string | ((d: DataPoint) => number);
18
+ xScaleType?: ScaleTime<number, number>;
19
+ baseMarkProps?: object;
20
+ hoverAnnotation?: boolean | Array<object | Function> | object | Function;
21
+ customHoverBehavior?: (d: IXYFrameHoverBaseArgs<DataPoint> & DataPoint) => void;
22
+ xExtent?: Date[] | number[];
23
+ axes?: object[];
24
+ margin?: IMargin;
25
+ matte?: boolean;
26
+ size?: number[];
27
+ yBrushable?: boolean;
28
+ brushEnd?: (d: Date[]) => void;
29
+ }
30
+
31
+ // Props for the Minimap component
32
+ export interface IMinimapProps<DataSet, DataPoint> extends IXYFrameProps<DataSet, DataPoint> {
33
+ minimap?: IXYFrameProps<DataSet, DataPoint>;
34
+ }
35
+
36
+ // Props for the Annotation component
37
+ export interface IAnnotationProps {
38
+ noteData: {
39
+ x: number;
40
+ y: number;
41
+ dx?: number;
42
+ dy?: number;
43
+ nx?: number;
44
+ ny?: number;
45
+ note: {
46
+ label: string;
47
+ wrap?: number;
48
+ align?: string;
49
+ orientation?: string;
50
+ padding?: number;
51
+ color?: string;
52
+ lineType?: string;
53
+ };
54
+ className?: string;
55
+ };
56
+ }
57
+
58
+ // Props for the Axis component
59
+ export interface IAxisProps {
60
+ className?: string;
61
+ size: number[];
62
+ scale: ScaleTime;
63
+ orient?: string;
64
+ label?: string | object;
65
+ tickValues?: number[] | Date[];
66
+ tickFormat?: (d: number) => JSX.Element | string;
67
+ ticks?: number;
68
+ }
69
+
70
+ // Args supplied by semiotic's XYFrame to client's hover event callback function
71
+ export interface IXYFrameHoverBaseArgs<DataPoint> {
72
+ data: DataPoint;
73
+ points?: DataPoint[];
74
+ voronoiX: number;
75
+ voronoiY: number;
76
+ }
77
+
78
+ /*
79
+ * Args supplied by semiotic's OrdinalFrame to client's custom hover event callback function
80
+ */
81
+ export interface IOrFrameHoverArgs<DataPoint> {
82
+ column?: IOrGroup<DataPoint>;
83
+ summary?: Array<IOrPiece<DataPoint>>;
84
+ type?: string; // type of hover event (e.g. frame-hover)
85
+ points: undefined | IOrSummaryPiece[]; // used for calculated summary datasets (e.g. boxplot)
86
+ voronoiX?: number; // x position on the SVG element
87
+ voronoiY?: number; // y position on the SVG element
88
+ }
89
+
90
+ export interface IOrSummaryPiece {
91
+ isSummaryData: boolean;
92
+ key: string; // data group (i.e. baseline or canary)
93
+ label: string;
94
+ summaryPieceName: string;
95
+ type: string; // type of hover event (e.g. frame-hover)
96
+ value: number;
97
+ x: number; // x position on the SVG element
98
+ y: number; // y position on the SVG element
99
+ }
100
+
101
+ export interface IOrPiece<DataPoint> {
102
+ base: number; // coordinate of the base along the y-axis
103
+ value: number; // original value of the data point
104
+ scaledValue: number; // value in pixels
105
+ data: DataPoint;
106
+ }
107
+
108
+ export interface IOrGroup<DataPoint> {
109
+ middle: number; // absolute position of the center of the column along the main axis
110
+ name: string; // ordinal label
111
+ padding: number;
112
+ width: number; // width of the column
113
+ x: number; // starting position of the column (along the main axis)
114
+ xyData: Array<IOrXyData<DataPoint>>;
115
+ y: number;
116
+ }
117
+
118
+ export interface IOrXyData<DataPoint> {
119
+ o: string; // ordinal label for this data
120
+ xy: {
121
+ height: number; //height of the data point svg along the y axis
122
+ width: number; // width of the data point svg (e.g. bar width)
123
+ x: number; // starting x coordinate of the drawn data svg
124
+ y: number; // starting y coordinate of the drawn data svg
125
+
126
+ // center coordinate of the data point svg along the main (x) axis
127
+ // relative to x
128
+ middle: number;
129
+ };
130
+ piece: IOrPiece<DataPoint>;
131
+ }
132
+
133
+ /*
134
+ * Args supplied by semiotic's Frame to client's custom annotation function
135
+ */
136
+ export interface ISemioticAnnotationArgs<AnnotationData, CategoryData> {
137
+ d: AnnotationData; // client-defined custom annotation data
138
+ i: number; // index
139
+ categories: {
140
+ [label: string]: CategoryData;
141
+ };
142
+ orFrameState?: ISemioticOrFrameState;
143
+ }
144
+
145
+ export interface ISemioticOrFrameState {
146
+ pieceDataXY: IOrSummaryPiece[];
147
+ }
148
+
149
+ // Frame prop that specifies any pre-defined semiotic annotation
150
+ export type IAnnotationType = {
151
+ type?: string | Function;
152
+ column?: { name: string };
153
+ x?: number;
154
+ y?: number;
155
+ yTop?: number;
156
+ yBottom?: number;
157
+ yMiddle?: number;
158
+ coordinates?: object[];
159
+ };
160
+ }
@@ -0,0 +1,17 @@
1
+ .difference-area {
2
+ margin-top: 20px;
3
+ .visualization-layer {
4
+ g.axis.x.bottom {
5
+ line.axis-baseline {
6
+ stroke: none;
7
+ }
8
+ }
9
+ }
10
+ .secondary-ts-x-axis {
11
+ g.axis.x.bottom {
12
+ line.axis-baseline {
13
+ stroke: none;
14
+ }
15
+ }
16
+ }
17
+ }