context.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. import {createContext, useCallback, useContext, useMemo, useState} from 'react';
  2. import {MRI} from 'sentry/types';
  3. import {
  4. defaultMetricDisplayType,
  5. MetricDisplayType,
  6. MetricWidgetQueryParams,
  7. updateQuery,
  8. } from 'sentry/utils/metrics';
  9. import {decodeList} from 'sentry/utils/queryString';
  10. import useRouter from 'sentry/utils/useRouter';
  11. import {DEFAULT_SORT_STATE} from 'sentry/views/ddm/constants';
  12. interface DDMContextValue {
  13. addWidget: () => void;
  14. duplicateWidget: (index: number) => void;
  15. removeWidget: (index: number) => void;
  16. selectedWidgetIndex: number;
  17. setSelectedWidgetIndex: (index: number) => void;
  18. updateWidget: (index: number, data: Partial<MetricWidgetQueryParams>) => void;
  19. widgets: MetricWidgetQueryParams[];
  20. }
  21. export const DDMContext = createContext<DDMContextValue>({
  22. selectedWidgetIndex: 0,
  23. setSelectedWidgetIndex: () => {},
  24. addWidget: () => {},
  25. updateWidget: () => {},
  26. removeWidget: () => {},
  27. duplicateWidget: () => {},
  28. widgets: [],
  29. });
  30. export function useDDMContext() {
  31. return useContext(DDMContext);
  32. }
  33. const emptyWidget: MetricWidgetQueryParams = {
  34. mri: '' as MRI,
  35. op: undefined,
  36. query: '',
  37. groupBy: [],
  38. sort: DEFAULT_SORT_STATE,
  39. displayType: MetricDisplayType.LINE,
  40. };
  41. export function useMetricWidgets() {
  42. const router = useRouter();
  43. const widgets = useMemo<MetricWidgetQueryParams[]>(() => {
  44. const currentWidgets = JSON.parse(
  45. router.location.query.widgets ?? JSON.stringify([emptyWidget])
  46. );
  47. return currentWidgets.map((widget: MetricWidgetQueryParams) => {
  48. return {
  49. mri: widget.mri,
  50. op: widget.op,
  51. query: widget.query,
  52. groupBy: decodeList(widget.groupBy),
  53. displayType: widget.displayType ?? defaultMetricDisplayType,
  54. focusedSeries: widget.focusedSeries,
  55. showSummaryTable: widget.showSummaryTable ?? true, // temporary default
  56. powerUserMode: widget.powerUserMode,
  57. sort: widget.sort ?? DEFAULT_SORT_STATE,
  58. };
  59. });
  60. }, [router.location.query.widgets]);
  61. const setWidgets = useCallback(
  62. (newWidgets: MetricWidgetQueryParams[]) => {
  63. updateQuery(router, {
  64. widgets: JSON.stringify(newWidgets),
  65. });
  66. },
  67. [router]
  68. );
  69. const updateWidget = useCallback(
  70. (index: number, data: Partial<MetricWidgetQueryParams>) => {
  71. const widgetsCopy = [...widgets];
  72. widgetsCopy[index] = {...widgets[index], ...data};
  73. setWidgets(widgetsCopy);
  74. },
  75. [widgets, setWidgets]
  76. );
  77. const addWidget = useCallback(() => {
  78. const widgetsCopy = [...widgets];
  79. widgetsCopy.push(emptyWidget);
  80. setWidgets(widgetsCopy);
  81. }, [widgets, setWidgets]);
  82. const removeWidget = useCallback(
  83. (index: number) => {
  84. const widgetsCopy = [...widgets];
  85. widgetsCopy.splice(index, 1);
  86. setWidgets(widgetsCopy);
  87. },
  88. [setWidgets, widgets]
  89. );
  90. const duplicateWidget = useCallback(
  91. (index: number) => {
  92. const widgetsCopy = [...widgets];
  93. widgetsCopy.splice(index, 0, widgets[index]);
  94. setWidgets(widgetsCopy);
  95. },
  96. [setWidgets, widgets]
  97. );
  98. return {
  99. widgets,
  100. updateWidget,
  101. addWidget,
  102. removeWidget,
  103. duplicateWidget,
  104. };
  105. }
  106. export function DDMContextProvider({children}: {children: React.ReactNode}) {
  107. const [selectedWidgetIndex, setSelectedWidgetIndex] = useState(0);
  108. const {widgets, updateWidget, addWidget, removeWidget, duplicateWidget} =
  109. useMetricWidgets();
  110. const handleAddWidget = useCallback(() => {
  111. addWidget();
  112. setSelectedWidgetIndex(widgets.length);
  113. }, [addWidget, widgets.length]);
  114. const handleUpdateWidget = useCallback(
  115. (index: number, data: Partial<MetricWidgetQueryParams>) => {
  116. updateWidget(index, data);
  117. setSelectedWidgetIndex(index);
  118. },
  119. [updateWidget]
  120. );
  121. const handleDuplicate = useCallback(
  122. (index: number) => {
  123. duplicateWidget(index);
  124. setSelectedWidgetIndex(index + 1);
  125. },
  126. [duplicateWidget]
  127. );
  128. const contextValue = useMemo<DDMContextValue>(
  129. () => ({
  130. addWidget: handleAddWidget,
  131. selectedWidgetIndex:
  132. selectedWidgetIndex > widgets.length - 1 ? 0 : selectedWidgetIndex,
  133. setSelectedWidgetIndex,
  134. updateWidget: handleUpdateWidget,
  135. removeWidget,
  136. duplicateWidget: handleDuplicate,
  137. widgets,
  138. }),
  139. [
  140. handleAddWidget,
  141. handleDuplicate,
  142. handleUpdateWidget,
  143. removeWidget,
  144. selectedWidgetIndex,
  145. widgets,
  146. ]
  147. );
  148. return <DDMContext.Provider value={contextValue}>{children}</DDMContext.Provider>;
  149. }