Compare commits

..

2 Commits

Author SHA1 Message Date
Ashley Harrison e9f164d9f2 undo changes to query library cause i cba dealing with enterprise rn 2026-01-14 17:20:37 +00:00
Ashley Harrison 86fc051c58 ref changes compatible with react 18 + react 19 2026-01-14 17:08:28 +00:00
40 changed files with 78 additions and 211 deletions
+1 -1
View File
@@ -89,7 +89,7 @@ require (
github.com/googleapis/gax-go/v2 v2.15.0 // @grafana/grafana-backend-group
github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // @grafana/grafana-app-platform-squad
github.com/grafana/alerting v0.0.0-20260114220314-c56cf1580dcf // @grafana/alerting-backend
github.com/grafana/alerting v0.0.0-20260112172717-98a49ed9557f // @grafana/alerting-backend
github.com/grafana/authlib v0.0.0-20250930082137-a40e2c2b094f // @grafana/identity-access-team
github.com/grafana/authlib/types v0.0.0-20251119142549-be091cf2f4d4 // @grafana/identity-access-team
github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics
-2
View File
@@ -1627,8 +1627,6 @@ github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5T
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/grafana/alerting v0.0.0-20260112172717-98a49ed9557f h1:3bXOyht68qkfvD6Y8z8XoenFbytSSOIkr/s+AqRzj0o=
github.com/grafana/alerting v0.0.0-20260112172717-98a49ed9557f/go.mod h1:Ji0SfJChcwjgq8ljy6Y5CcYfHfAYKXjKYeysOoDS/6s=
github.com/grafana/alerting v0.0.0-20260114220314-c56cf1580dcf h1:3q+bASuUgOPd3AVcmfaZCvqZjf7lhB6Oydt8OdShcy8=
github.com/grafana/alerting v0.0.0-20260114220314-c56cf1580dcf/go.mod h1:Ji0SfJChcwjgq8ljy6Y5CcYfHfAYKXjKYeysOoDS/6s=
github.com/grafana/authlib v0.0.0-20250930082137-a40e2c2b094f h1:Cbm6OKkOcJ+7CSZsGsEJzktC/SIa5bxVeYKQLuYK86o=
github.com/grafana/authlib v0.0.0-20250930082137-a40e2c2b094f/go.mod h1:axY0cdOg3q0TZHwpHnIz5x16xZ8ZBxJHShsSHHXcHQg=
github.com/grafana/authlib/types v0.0.0-20251119142549-be091cf2f4d4 h1:Muoy+FMGrHj3GdFbvsMzUT7eusgii9PKf9L1ZaXDDbY=
@@ -22,7 +22,7 @@ import { getBarColorByDiff, getBarColorByPackage, getBarColorByValue } from './c
import { CollapseConfig, CollapsedMap, FlameGraphDataContainer, LevelItem } from './dataTransform';
type RenderOptions = {
canvasRef: RefObject<HTMLCanvasElement>;
canvasRef: RefObject<HTMLCanvasElement | null>;
data: FlameGraphDataContainer;
root: LevelItem;
direction: 'children' | 'parents';
@@ -373,7 +373,7 @@ function useColorFunction(
);
}
function useSetupCanvas(canvasRef: RefObject<HTMLCanvasElement>, wrapperWidth: number, numberOfLevels: number) {
function useSetupCanvas(canvasRef: RefObject<HTMLCanvasElement | null>, wrapperWidth: number, numberOfLevels: number) {
const [ctx, setCtx] = useState<CanvasRenderingContext2D>();
useEffect(() => {
@@ -7,7 +7,7 @@ const CAUGHT_KEYS = ['ArrowUp', 'ArrowDown', 'Home', 'End', 'Enter', 'Tab'];
/** @internal */
export interface UseListFocusProps {
localRef: RefObject<HTMLUListElement>;
localRef: RefObject<HTMLUListElement | null>;
options: TimeOption[];
}
@@ -1,7 +1,7 @@
import { RefObject, useRef } from 'react';
export function useFocus(): [RefObject<HTMLInputElement>, () => void] {
const ref = useRef<HTMLInputElement>(null);
export function useFocus(): [RefObject<HTMLInputElement | null>, () => void] {
const ref = useRef<HTMLInputElement | null>(null);
const setFocus = () => {
ref.current && ref.current.focus();
};
@@ -6,7 +6,7 @@ const UNFOCUSED = -1;
/** @internal */
export interface UseMenuFocusProps {
localRef: RefObject<HTMLDivElement>;
localRef: RefObject<HTMLDivElement | null>;
isMenuOpen?: boolean;
close?: () => void;
onOpen?: (focusOnItem: (itemId: number) => void) => void;
@@ -22,7 +22,7 @@ interface Props extends Omit<BoxProps, 'display' | 'direction' | 'element' | 'fl
*
* https://developers.grafana.com/ui/latest/index.html?path=/docs/layout-scrollcontainer--docs
*/
export const ScrollContainer = forwardRef<HTMLDivElement, PropsWithChildren<Props>>(
export const ScrollContainer = forwardRef<HTMLDivElement | null, PropsWithChildren<Props>>(
(
{
children,
@@ -20,7 +20,7 @@ export interface TableCellTooltipProps {
field: Field;
getActions: (field: Field, rowIdx: number) => ActionModel[];
getTextColorForBackground: (bgColor: string) => string;
gridRef: RefObject<DataGridHandle>;
gridRef: RefObject<DataGridHandle | null>;
height: number;
placement?: TableCellTooltipPlacement;
renderer: TableCellRenderer;
@@ -463,7 +463,7 @@ export function useColumnResize(
return dataGridResizeHandler;
}
export function useScrollbarWidth(ref: RefObject<DataGridHandle>, height: number) {
export function useScrollbarWidth(ref: RefObject<DataGridHandle | null>, height: number) {
const [scrollbarWidth, setScrollbarWidth] = useState(0);
useLayoutEffect(() => {
@@ -135,7 +135,7 @@ export const Table = memo((props: Props) => {
// `useTableStateReducer`, which is needed to construct options for `useTable` (the hook that returns
// `toggleAllRowsExpanded`), and if we used a variable, that variable would be undefined at the time
// we initialize `useTableStateReducer`.
const toggleAllRowsExpandedRef = useRef<(value?: boolean) => void>();
const toggleAllRowsExpandedRef = useRef<((value?: boolean) => void) | null>(null);
// Internal react table state reducer
const stateReducer = useTableStateReducer({
@@ -14,8 +14,8 @@ import { GrafanaTableState } from './types';
Select the scrollbar element from the VariableSizeList scope
*/
export function useFixScrollbarContainer(
variableSizeListScrollbarRef: React.RefObject<HTMLDivElement>,
tableDivRef: React.RefObject<HTMLDivElement>
variableSizeListScrollbarRef: React.RefObject<HTMLDivElement | null>,
tableDivRef: React.RefObject<HTMLDivElement | null>
) {
useEffect(() => {
if (variableSizeListScrollbarRef.current && tableDivRef.current) {
@@ -43,7 +43,7 @@ export function useFixScrollbarContainer(
*/
export function useResetVariableListSizeCache(
extendedState: GrafanaTableState,
listRef: React.RefObject<VariableSizeList>,
listRef: React.RefObject<VariableSizeList | null>,
data: DataFrame,
hasUniqueId: boolean
) {
@@ -19,7 +19,7 @@ interface EventsCanvasProps {
}
export function EventsCanvas({ id, events, renderEventMarker, mapEventToXYCoords, config }: EventsCanvasProps) {
const plotInstance = useRef<uPlot>();
const plotInstance = useRef<uPlot | null>(null);
// render token required to re-render annotation markers. Rendering lines happens in uPlot and the props do not change
// so we need to force the re-render when the draw hook was performed by uPlot
const [renderToken, setRenderToken] = useState(0);
@@ -140,7 +140,7 @@ export const TooltipPlugin2 = ({
const [{ plot, isHovering, isPinned, contents, style, dismiss }, setState] = useReducer(mergeState, null, initState);
const sizeRef = useRef<TooltipContainerSize>();
const sizeRef = useRef<TooltipContainerSize | null>(null);
const styles = useStyles2(getStyles, maxWidth);
const renderRef = useRef(render);
@@ -96,7 +96,7 @@ export interface GraphNGState {
export class GraphNG extends Component<GraphNGProps, GraphNGState> {
static contextType = PanelContextRoot;
panelContext: PanelContext = {} as PanelContext;
private plotInstance: React.RefObject<uPlot>;
private plotInstance: React.RefObject<uPlot | null>;
private subscription = new Subscription();
@@ -1,7 +1,6 @@
package definitions
import (
"github.com/grafana/alerting/definition"
"github.com/prometheus/alertmanager/config"
)
@@ -118,9 +117,8 @@ type MuteTimingHeaders struct {
type MuteTimeInterval struct {
UID string `json:"-" yaml:"-"`
config.MuteTimeInterval `json:",inline" yaml:",inline"`
Version string `json:"version,omitempty"`
Provenance Provenance `json:"provenance,omitempty"`
Kind definition.TimeIntervalKind `json:"-" yaml:"-"`
Version string `json:"version,omitempty"`
Provenance Provenance `json:"provenance,omitempty"`
}
func (mt *MuteTimeInterval) ResourceType() string {
+2 -2
View File
@@ -434,8 +434,8 @@ func (ng *AlertNG) init() error {
// Provisioning
policyService := provisioning.NewNotificationPolicyService(configStore, ng.store, ng.store, ng.Cfg.UnifiedAlerting, ng.Log)
contactPointService := provisioning.NewContactPointService(configStore, ng.SecretsService, ng.store, ng.store, provisioningReceiverService, ng.Log, ng.store, ng.ResourcePermissions)
templateService := provisioning.NewTemplateService(configStore, ng.store, ng.store, ng.Log).WithIncludeImported()
muteTimingService := provisioning.NewMuteTimingService(configStore, ng.store, ng.store, ng.Log, ng.store).WithIncludeImported()
templateService := provisioning.NewTemplateService(configStore, ng.store, ng.store, ng.Log)
muteTimingService := provisioning.NewMuteTimingService(configStore, ng.store, ng.store, ng.Log, ng.store)
alertRuleService := provisioning.NewAlertRuleService(ng.store, ng.store, ng.folderService, ng.QuotaService, ng.store,
int64(ng.Cfg.UnifiedAlerting.DefaultRuleEvaluationInterval.Seconds()),
int64(ng.Cfg.UnifiedAlerting.BaseInterval.Seconds()),
+18 -150
View File
@@ -9,7 +9,6 @@ import (
"strings"
"unsafe"
"github.com/grafana/alerting/definition"
"github.com/prometheus/alertmanager/config"
"github.com/prometheus/alertmanager/timeinterval"
"golang.org/x/exp/maps"
@@ -28,7 +27,6 @@ type MuteTimingService struct {
log log.Logger
validator validation.ProvenanceStatusTransitionValidator
ruleNotificationsStore AlertRuleNotificationSettingsStore
includeImported bool
}
func NewMuteTimingService(config alertmanagerConfigStore, prov ProvisioningStore, xact TransactionManager, log log.Logger, ns AlertRuleNotificationSettingsStore) *MuteTimingService {
@@ -39,19 +37,6 @@ func NewMuteTimingService(config alertmanagerConfigStore, prov ProvisioningStore
log: log,
validator: validation.ValidateProvenanceRelaxed,
ruleNotificationsStore: ns,
includeImported: false,
}
}
func (svc *MuteTimingService) WithIncludeImported() *MuteTimingService {
return &MuteTimingService{
configStore: svc.configStore,
provenanceStore: svc.provenanceStore,
xact: svc.xact,
log: svc.log,
validator: svc.validator,
ruleNotificationsStore: svc.ruleNotificationsStore,
includeImported: true,
}
}
@@ -62,15 +47,9 @@ func (svc *MuteTimingService) GetMuteTimings(ctx context.Context, orgID int64) (
return nil, err
}
// Get Grafana intervals
grafanaIntervals := make([]config.MuteTimeInterval, 0, len(rev.Config.AlertmanagerConfig.TimeIntervals)+len(rev.Config.AlertmanagerConfig.MuteTimeIntervals))
for _, interval := range rev.Config.AlertmanagerConfig.TimeIntervals {
grafanaIntervals = append(grafanaIntervals, config.MuteTimeInterval(interval))
}
grafanaIntervals = append(grafanaIntervals, rev.Config.AlertmanagerConfig.MuteTimeIntervals...)
intervals := getTimeIntervals(rev)
// Early return if no intervals (including imported)
if len(grafanaIntervals) == 0 && (!svc.includeImported || len(rev.Config.ExtraConfigs) == 0) {
if len(intervals) == 0 {
return []definitions.MuteTimeInterval{}, nil
}
@@ -79,51 +58,22 @@ func (svc *MuteTimingService) GetMuteTimings(ctx context.Context, orgID int64) (
return nil, err
}
result := make([]definitions.MuteTimeInterval, 0, len(grafanaIntervals))
for _, interval := range grafanaIntervals {
slices.SortFunc(intervals, func(a, b config.MuteTimeInterval) int {
return strings.Compare(a.Name, b.Name)
})
result := make([]definitions.MuteTimeInterval, 0, len(intervals))
for _, interval := range intervals {
version := calculateMuteTimeIntervalFingerprint(interval)
def := definitions.MuteTimeInterval{
UID: timeIntervalUID(string(definition.GrafanaTimeIntervalKind), interval.Name),
UID: legacy_storage.NameToUid(interval.Name),
MuteTimeInterval: interval,
Version: version,
Kind: definition.GrafanaTimeIntervalKind,
}
if prov, ok := provenances[def.ResourceID()]; ok {
def.Provenance = definitions.Provenance(prov)
}
result = append(result, def)
}
// Add imported Mimir intervals if requested
if svc.includeImported && len(rev.Config.ExtraConfigs) > 0 {
mimirCfg := rev.Config.ExtraConfigs[0]
cfg, err := mimirCfg.GetAlertmanagerConfig()
if err == nil {
mimirIntervals := make([]config.MuteTimeInterval, 0, len(cfg.GetMuteTimeIntervals())+len(cfg.GetTimeIntervals()))
mimirIntervals = append(mimirIntervals, cfg.GetMuteTimeIntervals()...)
for _, ti := range cfg.GetTimeIntervals() {
mimirIntervals = append(mimirIntervals, config.MuteTimeInterval(ti))
}
for _, interval := range mimirIntervals {
version := calculateMuteTimeIntervalFingerprint(interval)
def := definitions.MuteTimeInterval{
UID: timeIntervalUID(string(definition.MimirTimeIntervalKind), interval.Name),
MuteTimeInterval: interval,
Version: version,
Kind: definition.MimirTimeIntervalKind,
Provenance: definitions.Provenance(models.ProvenanceConvertedPrometheus),
}
result = append(result, def)
}
}
}
// Sort by name
slices.SortFunc(result, func(a, b definitions.MuteTimeInterval) int {
return strings.Compare(a.Name, b.Name)
})
return result, nil
}
@@ -134,71 +84,28 @@ func (svc *MuteTimingService) GetMuteTiming(ctx context.Context, nameOrUID strin
return definitions.MuteTimeInterval{}, err
}
// Try to find in Grafana intervals first
mt, found := getMuteTimingByName(rev, nameOrUID)
kind := definition.GrafanaTimeIntervalKind
provenance := models.ProvenanceNone
// If not found by name, try UID lookup in Grafana intervals
if !found {
grafanaIntervals := getTimeIntervals(rev, false)
for _, interval := range grafanaIntervals {
if timeIntervalUID(string(definition.GrafanaTimeIntervalKind), interval.Name) == nameOrUID {
mt = interval
found = true
break
}
}
}
// If not found in Grafana and includeImported, try Mimir (by name or UID)
if !found && svc.includeImported && len(rev.Config.ExtraConfigs) > 0 {
mimirCfg := rev.Config.ExtraConfigs[0]
cfg, err := mimirCfg.GetAlertmanagerConfig()
name, err := legacy_storage.UidToName(nameOrUID)
if err == nil {
// Collect all Mimir intervals
mimirIntervals := make([]config.MuteTimeInterval, 0, len(cfg.GetMuteTimeIntervals())+len(cfg.GetTimeIntervals()))
mimirIntervals = append(mimirIntervals, cfg.GetMuteTimeIntervals()...)
for _, ti := range cfg.GetTimeIntervals() {
mimirIntervals = append(mimirIntervals, config.MuteTimeInterval(ti))
}
for _, interval := range mimirIntervals {
// Check by name or UID
if interval.Name == nameOrUID ||
timeIntervalUID(string(definition.MimirTimeIntervalKind), interval.Name) == nameOrUID {
mt = interval
found = true
kind = definition.MimirTimeIntervalKind
provenance = models.ProvenanceConvertedPrometheus
break
}
}
mt, found = getMuteTimingByName(rev, name)
}
}
if !found {
return definitions.MuteTimeInterval{}, ErrTimeIntervalNotFound.Errorf("")
}
result := definitions.MuteTimeInterval{
UID: timeIntervalUID(string(kind), mt.Name),
UID: legacy_storage.NameToUid(mt.Name),
MuteTimeInterval: mt,
Version: calculateMuteTimeIntervalFingerprint(mt),
Kind: kind,
}
// Only check provenance store for Grafana intervals
if kind == definition.GrafanaTimeIntervalKind {
prov, err := svc.provenanceStore.GetProvenance(ctx, &result, orgID)
if err != nil {
return definitions.MuteTimeInterval{}, err
}
result.Provenance = definitions.Provenance(prov)
} else {
result.Provenance = definitions.Provenance(provenance)
prov, err := svc.provenanceStore.GetProvenance(ctx, &result, orgID)
if err != nil {
return definitions.MuteTimeInterval{}, err
}
result.Provenance = definitions.Provenance(prov)
return result, nil
}
@@ -208,11 +115,6 @@ func (svc *MuteTimingService) CreateMuteTiming(ctx context.Context, mt definitio
return definitions.MuteTimeInterval{}, MakeErrTimeIntervalInvalid(err)
}
// Block creation of Mimir kind intervals
if mt.Kind == definition.MimirTimeIntervalKind {
return definitions.MuteTimeInterval{}, MakeErrTimeIntervalInvalid(fmt.Errorf("time intervals of kind 'mimir' cannot be created"))
}
revision, err := svc.configStore.Get(ctx, orgID)
if err != nil {
return definitions.MuteTimeInterval{}, err
@@ -266,16 +168,6 @@ func (svc *MuteTimingService) UpdateMuteTiming(ctx context.Context, mt definitio
return definitions.MuteTimeInterval{}, ErrTimeIntervalNotFound.Errorf("")
}
// Block updates to imported intervals
existingMT := definitions.MuteTimeInterval{MuteTimeInterval: old}
prov, err := svc.provenanceStore.GetProvenance(ctx, &existingMT, orgID)
if err != nil {
return definitions.MuteTimeInterval{}, err
}
if prov == models.ProvenanceConvertedPrometheus {
return definitions.MuteTimeInterval{}, MakeErrTimeIntervalInvalid(fmt.Errorf("cannot update imported time interval from mimir/prometheus"))
}
// check optimistic concurrency
err = svc.checkOptimisticConcurrency(old, models.Provenance(mt.Provenance), mt.Version, "update")
if err != nil {
@@ -351,12 +243,6 @@ func (svc *MuteTimingService) DeleteMuteTiming(ctx context.Context, nameOrUID st
if err != nil {
return err
}
// Block deletes of imported intervals
if storedProvenance == models.ProvenanceConvertedPrometheus {
return MakeErrTimeIntervalInvalid(fmt.Errorf("cannot delete imported time interval from mimir/prometheus"))
}
if err := svc.validator(storedProvenance, models.Provenance(provenance)); err != nil {
return err
}
@@ -410,7 +296,7 @@ func isTimeIntervalInUseInRoutes(name string, route *definitions.Route) bool {
}
func getMuteTimingByName(rev *legacy_storage.ConfigRevision, name string) (config.MuteTimeInterval, bool) {
intervals := getTimeIntervals(rev, false)
intervals := getTimeIntervals(rev)
idx := slices.IndexFunc(intervals, func(interval config.MuteTimeInterval) bool {
return interval.Name == name
})
@@ -420,26 +306,12 @@ func getMuteTimingByName(rev *legacy_storage.ConfigRevision, name string) (confi
return intervals[idx], true
}
func getTimeIntervals(rev *legacy_storage.ConfigRevision, includeImported bool) []config.MuteTimeInterval {
func getTimeIntervals(rev *legacy_storage.ConfigRevision) []config.MuteTimeInterval {
result := make([]config.MuteTimeInterval, 0, len(rev.Config.AlertmanagerConfig.TimeIntervals)+len(rev.Config.AlertmanagerConfig.MuteTimeIntervals))
for _, interval := range rev.Config.AlertmanagerConfig.TimeIntervals {
result = append(result, config.MuteTimeInterval(interval))
}
result = append(result, rev.Config.AlertmanagerConfig.MuteTimeIntervals...)
// Add imported intervals from Mimir config
if includeImported && len(rev.Config.ExtraConfigs) > 0 {
mimirCfg := rev.Config.ExtraConfigs[0]
cfg, err := mimirCfg.GetAlertmanagerConfig()
if err == nil {
result = append(result, cfg.GetMuteTimeIntervals()...)
for _, ti := range cfg.GetTimeIntervals() {
result = append(result, config.MuteTimeInterval(ti))
}
}
}
return result
return append(result, rev.Config.AlertmanagerConfig.MuteTimeIntervals...)
}
func updateTimeInterval(rev *legacy_storage.ConfigRevision, interval config.MuteTimeInterval) {
@@ -466,10 +338,6 @@ func deleteTimeInterval(rev *legacy_storage.ConfigRevision, interval config.Mute
})
}
func timeIntervalUID(kind string, name string) string {
return legacy_storage.NameToUid(fmt.Sprintf("%s|%s", kind, name))
}
func calculateMuteTimeIntervalFingerprint(interval config.MuteTimeInterval) string {
sum := fnv.New64()
@@ -66,18 +66,17 @@ func TestGetMuteTimings(t *testing.T) {
require.Equal(t, "Test1", result[0].Name)
require.EqualValues(t, provenances["Test1"], result[0].Provenance)
require.NotEmpty(t, result[0].Version)
// UID now includes kind prefix
require.Equal(t, legacy_storage.NameToUid(fmt.Sprintf("grafana|%s", result[0].Name)), result[0].UID)
require.Equal(t, legacy_storage.NameToUid(result[0].Name), result[0].UID)
require.Equal(t, "Test2", result[1].Name)
require.EqualValues(t, provenances["Test2"], result[1].Provenance)
require.NotEmpty(t, result[1].Version)
require.Equal(t, legacy_storage.NameToUid(fmt.Sprintf("grafana|%s", result[1].Name)), result[1].UID)
require.Equal(t, legacy_storage.NameToUid(result[1].Name), result[1].UID)
require.Equal(t, "Test3", result[2].Name)
require.EqualValues(t, "", result[2].Provenance)
require.NotEmpty(t, result[2].Version)
require.Equal(t, legacy_storage.NameToUid(fmt.Sprintf("grafana|%s", result[2].Name)), result[2].UID)
require.Equal(t, legacy_storage.NameToUid(result[2].Name), result[2].UID)
require.Len(t, store.Calls, 1)
require.Equal(t, "Get", store.Calls[0].Method)
@@ -162,8 +161,7 @@ func TestGetMuteTiming(t *testing.T) {
require.Equal(t, "Test1", result.Name)
require.EqualValues(t, models.ProvenanceAPI, result.Provenance)
// UID now includes kind prefix
require.Equal(t, legacy_storage.NameToUid(fmt.Sprintf("grafana|%s", result.Name)), result.UID)
require.Equal(t, legacy_storage.NameToUid(result.Name), result.UID)
require.NotEmpty(t, result.Version)
require.Len(t, store.Calls, 1)
@@ -194,8 +192,7 @@ func TestGetMuteTiming(t *testing.T) {
require.Equal(t, "Test2", result.Name)
require.EqualValues(t, models.ProvenanceFile, result.Provenance)
// UID now includes kind prefix
require.Equal(t, legacy_storage.NameToUid(fmt.Sprintf("grafana|%s", result.Name)), result.UID)
require.Equal(t, legacy_storage.NameToUid(result.Name), result.UID)
require.NotEmpty(t, result.Version)
require.Len(t, store.Calls, 1)
@@ -109,7 +109,7 @@ const defaultMatchers = {
* "Time as X" core component, expects ascending x
*/
export class GraphNG extends Component<GraphNGProps, GraphNGState> {
private plotInstance: React.RefObject<uPlot>;
private plotInstance: React.RefObject<uPlot | null>;
constructor(props: GraphNGProps) {
super(props);
@@ -33,7 +33,7 @@ global.ResizeObserver = jest.fn().mockImplementation((callback) => {
});
// Helper function to assign a mock div to a ref
function assignMockDivToRef(ref: React.RefObject<HTMLDivElement>, mockDiv: HTMLDivElement) {
function assignMockDivToRef(ref: React.RefObject<HTMLDivElement | null>, mockDiv: HTMLDivElement) {
// Use type assertion to bypass readonly restriction in tests
(ref as { current: HTMLDivElement | null }).current = mockDiv;
}
@@ -8,7 +8,7 @@ import grafanaTextLogoDarkSvg from 'img/grafana_text_logo_dark.svg';
import grafanaTextLogoLightSvg from 'img/grafana_text_logo_light.svg';
interface SoloPanelPageLogoProps {
containerRef: React.RefObject<HTMLDivElement>;
containerRef: React.RefObject<HTMLDivElement | null>;
isHovered: boolean;
hideLogo?: UrlQueryValue;
}
@@ -61,7 +61,7 @@ interface State {
class UnThemedTransformationsEditor extends React.PureComponent<TransformationsEditorProps, State> {
subscription?: Unsubscribable;
ref: RefObject<HTMLDivElement>;
ref: RefObject<HTMLDivElement | null>;
constructor(props: TransformationsEditorProps) {
super(props);
@@ -43,7 +43,7 @@ export const LibraryPanelsView = ({
}
);
const asyncDispatch = useMemo(() => asyncDispatcher(dispatch), [dispatch]);
const abortControllerRef = useRef<AbortController>();
const abortControllerRef = useRef<AbortController | null>(null);
useDebounce(
() => {
@@ -12,7 +12,7 @@ export interface Props {}
export const LiveConnectionWarning = memo(function LiveConnectionWarning() {
const [show, setShow] = useState<boolean | undefined>(undefined);
const subscriptionRef = useRef<Unsubscribable>();
const subscriptionRef = useRef<Unsubscribable | null>(null);
const styles = useStyles2(getStyle);
useEffect(() => {
@@ -38,7 +38,7 @@ export function useSearchKeyboardNavigation(
): ItemSelection {
const highlightIndexRef = useRef<ItemSelection>({ x: 0, y: -1 });
const [highlightIndex, setHighlightIndex] = useState<ItemSelection>({ x: 0, y: -1 });
const urlsRef = useRef<Field>();
const urlsRef = useRef<Field | null>(null);
// Clear selection when the search results change
useEffect(() => {
@@ -77,7 +77,7 @@ export const SuggestionsInput = ({
const theme = useTheme2();
const styles = getStyles(theme, inputHeight);
const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement>();
const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement | null>(null);
useEffect(() => {
scrollRef.current?.scrollTo(0, scrollTop);
@@ -11,7 +11,7 @@ import checkboxWhitePng from 'img/checkbox_white.png';
import { ALL_VARIABLE_VALUE } from '../../constants';
export interface Props extends React.HTMLProps<HTMLUListElement>, Themeable2 {
export interface Props extends Omit<React.HTMLProps<HTMLUListElement>, 'onToggle'>, Themeable2 {
multi: boolean;
values: VariableOption[];
selectedValues: VariableOption[];
@@ -20,8 +20,8 @@ interface CodeEditorProps {
export const LogsQLCodeEditor = (props: CodeEditorProps) => {
const { query, datasource, onChange } = props;
const monacoRef = useRef<Monaco>();
const disposalRef = useRef<monacoType.IDisposable>();
const monacoRef = useRef<Monaco | null>(null);
const disposalRef = useRef<monacoType.IDisposable | undefined>(undefined);
const onFocus = useCallback(async () => {
disposalRef.current = await reRegisterCompletionProvider(
@@ -42,8 +42,8 @@ interface LogsCodeEditorProps {
export const PPLQueryEditor = (props: LogsCodeEditorProps) => {
const { query, datasource, onChange } = props;
const monacoRef = useRef<Monaco>();
const disposalRef = useRef<monacoType.IDisposable>();
const monacoRef = useRef<Monaco | null>(null);
const disposalRef = useRef<monacoType.IDisposable | undefined>(undefined);
const onFocus = useCallback(async () => {
disposalRef.current = await reRegisterCompletionProvider(
@@ -20,8 +20,8 @@ interface SQLCodeEditorProps {
export const SQLQueryEditor = (props: SQLCodeEditorProps) => {
const { query, datasource, onChange } = props;
const monacoRef = useRef<Monaco>();
const disposalRef = useRef<monacoType.IDisposable>();
const monacoRef = useRef<Monaco | null>(null);
const disposalRef = useRef<monacoType.IDisposable | undefined>(undefined);
const onFocus = useCallback(async () => {
disposalRef.current = await reRegisterCompletionProvider(
@@ -94,7 +94,7 @@ const EDITOR_HEIGHT_OFFSET = 2;
* Hook that returns function that will set up monaco autocomplete for the label selector
*/
function useAutocomplete(getLabelValues: (label: string) => Promise<string[]>, labels?: string[]) {
const providerRef = useRef<CompletionProvider>();
const providerRef = useRef<CompletionProvider | null>(null);
if (providerRef.current === undefined) {
providerRef.current = new CompletionProvider();
}
@@ -126,7 +126,7 @@ export function LokiContextUi(props: LokiContextUiProps) {
window.localStorage.getItem(SHOULD_INCLUDE_PIPELINE_OPERATIONS) === 'true'
);
const timerHandle = useRef<number>();
const timerHandle = useRef<number | null>(null);
const previousInitialized = useRef<boolean>(false);
const previousContextFilters = useRef<ContextFilter[]>([]);
@@ -191,14 +191,18 @@ export function LokiContextUi(props: LokiContextUiProps) {
}, 1500);
return () => {
clearTimeout(timerHandle.current);
if (timerHandle.current) {
clearTimeout(timerHandle.current);
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [contextFilters, initialized]);
useEffect(() => {
return () => {
clearTimeout(timerHandle.current);
if (timerHandle.current) {
clearTimeout(timerHandle.current);
}
onClose();
};
}, [onClose]);
@@ -52,7 +52,7 @@ export function TraceQLEditor(props: Props) {
const onRunQueryRef = useRef(onRunQuery);
onRunQueryRef.current = onRunQuery;
const errorTimeoutId = useRef<number>();
const errorTimeoutId = useRef<number | null>(null);
return (
<>
@@ -103,7 +103,9 @@ export function TraceQLEditor(props: Props) {
}
// Remove previous callback if existing, to prevent squiggles from been shown while the user is still typing
window.clearTimeout(errorTimeoutId.current);
if (errorTimeoutId.current) {
window.clearTimeout(errorTimeoutId.current);
}
const errorNodes = getErrorNodes(model.getValue());
const cursorPosition = changeEvent.changes[0].rangeOffset;
@@ -1,5 +1,5 @@
export function renderHistogram(
can: React.RefObject<HTMLCanvasElement>,
can: React.RefObject<HTMLCanvasElement | null>,
histCanWidth: number,
histCanHeight: number,
xVals: number[],
+1 -1
View File
@@ -196,7 +196,7 @@ export const LogsPanel = ({
const dataSourcesMap = useDatasourcesFromTargets(panelData.request?.targets);
// Prevents the scroll position to change when new data from infinite scrolling is received
const keepScrollPositionRef = useRef<null | 'infinite-scroll' | 'user'>(null);
let closeCallback = useRef<() => void>();
const closeCallback = useRef<(() => void) | null>(null);
const { app, eventBus, onAddAdHocFilter } = usePanelContext();
useEffect(() => {
+1 -1
View File
@@ -70,7 +70,7 @@ export function useLayout(
const currentSignature = createDataSignature(rawNodes, rawEdges);
const isMounted = useMountedState();
const layoutWorkerCancelRef = useRef<(() => void) | undefined>();
const layoutWorkerCancelRef = useRef<(() => void) | null>(null);
useUnmount(() => {
if (layoutWorkerCancelRef.current) {
@@ -133,7 +133,7 @@ export const AnnotationsPlugin2 = ({
const newRangeRef = useRef(newRange);
newRangeRef.current = newRange;
const xAxisRef = useRef<HTMLDivElement>();
const xAxisRef = useRef<HTMLDivElement | null>(null);
useLayoutEffect(() => {
config.addHook('ready', (u) => {
@@ -23,7 +23,7 @@ export const ExemplarsPlugin = ({
maxHeight,
maxWidth,
}: ExemplarsPluginProps) => {
const plotInstance = useRef<uPlot>();
const plotInstance = useRef<uPlot | null>(null);
const [lockedExemplarRowIndex, setLockedExemplarRowIndex] = useState<number | undefined>();
@@ -11,7 +11,7 @@ interface ThresholdControlsPluginProps {
}
export const OutsideRangePlugin = ({ config, onChangeTimeRange }: ThresholdControlsPluginProps) => {
const plotInstance = useRef<uPlot>();
const plotInstance = useRef<uPlot | null>(null);
const [timevalues, setTimeValues] = useState<number[] | TypedArray>([]);
const [timeRange, setTimeRange] = useState<Scale | undefined>();
@@ -16,7 +16,7 @@ interface ThresholdControlsPluginProps {
}
export const ThresholdControlsPlugin = ({ config, fieldConfig, onThresholdsChange }: ThresholdControlsPluginProps) => {
const plotInstance = useRef<uPlot>();
const plotInstance = useRef<uPlot | null>(null);
const [renderToken, setRenderToken] = useState(0);
useLayoutEffect(() => {