Dimensions.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import { isEqual } from 'lodash';
  2. import React, { useMemo, useState } from 'react';
  3. import { SelectableValue } from '@grafana/data';
  4. import { EditorList } from '@grafana/experimental';
  5. import { CloudWatchDatasource } from '../../datasource';
  6. import { Dimensions as DimensionsType, MetricStat } from '../../types';
  7. import { FilterItem } from './FilterItem';
  8. export interface Props {
  9. metricStat: MetricStat;
  10. onChange: (dimensions: DimensionsType) => void;
  11. datasource: CloudWatchDatasource;
  12. dimensionKeys: Array<SelectableValue<string>>;
  13. disableExpressions: boolean;
  14. }
  15. export interface DimensionFilterCondition {
  16. key?: string;
  17. operator?: string;
  18. value?: string;
  19. }
  20. const dimensionsToFilterConditions = (dimensions: DimensionsType | undefined) =>
  21. Object.entries(dimensions ?? {}).reduce<DimensionFilterCondition[]>((acc, [key, value]) => {
  22. if (value && typeof value === 'string') {
  23. const filter = {
  24. key,
  25. value,
  26. operator: '=',
  27. };
  28. return [...acc, filter];
  29. }
  30. return acc;
  31. }, []);
  32. const filterConditionsToDimensions = (filters: DimensionFilterCondition[]) => {
  33. return filters.reduce<DimensionsType>((acc, { key, value }) => {
  34. if (key && value) {
  35. return { ...acc, [key]: value };
  36. }
  37. return acc;
  38. }, {});
  39. };
  40. export const Dimensions: React.FC<Props> = ({
  41. metricStat,
  42. datasource,
  43. dimensionKeys,
  44. disableExpressions,
  45. onChange,
  46. }) => {
  47. const dimensionFilters = useMemo(() => dimensionsToFilterConditions(metricStat.dimensions), [metricStat.dimensions]);
  48. const [items, setItems] = useState<DimensionFilterCondition[]>(dimensionFilters);
  49. const onDimensionsChange = (newItems: Array<Partial<DimensionFilterCondition>>) => {
  50. setItems(newItems);
  51. // The onChange event should only be triggered in the case there is a complete dimension object.
  52. // So when a new key is added that does not yet have a value, it should not trigger an onChange event.
  53. const newDimensions = filterConditionsToDimensions(newItems);
  54. if (!isEqual(newDimensions, metricStat.dimensions)) {
  55. onChange(newDimensions);
  56. }
  57. };
  58. return (
  59. <EditorList
  60. items={items}
  61. onChange={onDimensionsChange}
  62. renderItem={makeRenderFilter(datasource, metricStat, dimensionKeys, disableExpressions)}
  63. />
  64. );
  65. };
  66. function makeRenderFilter(
  67. datasource: CloudWatchDatasource,
  68. metricStat: MetricStat,
  69. dimensionKeys: Array<SelectableValue<string>>,
  70. disableExpressions: boolean
  71. ) {
  72. function renderFilter(
  73. item: DimensionFilterCondition,
  74. onChange: (item: DimensionFilterCondition) => void,
  75. onDelete: () => void
  76. ) {
  77. return (
  78. <FilterItem
  79. filter={item}
  80. onChange={(item) => onChange(item)}
  81. datasource={datasource}
  82. metricStat={metricStat}
  83. disableExpressions={disableExpressions}
  84. dimensionKeys={dimensionKeys}
  85. onDelete={onDelete}
  86. />
  87. );
  88. }
  89. return renderFilter;
  90. }