From c2c8684e48035e3184e79e1a1172478dcb6bfa14 Mon Sep 17 00:00:00 2001 From: logonoff Date: Thu, 22 Jan 2026 16:14:38 -0500 Subject: [PATCH] CONSOLE-5044: TypeScript port of events.jsx Convert events.jsx to TypeScript with the following improvements: - Replace PropTypes with TypeScript interfaces - Replace defaultProps with default parameters - Replace connectToFlags HOC with useFlag hook - Add proper type annotations for all components and functions Co-Authored-By: Claude Opus 4.5 --- .../components/__tests__/events.spec.tsx | 3 + .../components/{events.jsx => events.tsx} | 510 ++++++++++-------- frontend/public/locales/en/public.json | 14 +- frontend/public/propTypes.ts | 18 - 4 files changed, 296 insertions(+), 249 deletions(-) rename frontend/public/components/{events.jsx => events.tsx} (68%) delete mode 100644 frontend/public/propTypes.ts diff --git a/frontend/public/components/__tests__/events.spec.tsx b/frontend/public/components/__tests__/events.spec.tsx index e4e5b0fef1..b0e2c19abe 100644 --- a/frontend/public/components/__tests__/events.spec.tsx +++ b/frontend/public/components/__tests__/events.spec.tsx @@ -12,6 +12,9 @@ const createMockEvent = (namespace: string, name: string, uid: string, lastTimes name: `${name}-pod`, namespace, }, + source: { + component: 'test-component', + }, type: 'Normal', reason: 'Created', message: `Created pod ${name}-pod`, diff --git a/frontend/public/components/events.jsx b/frontend/public/components/events.tsx similarity index 68% rename from frontend/public/components/events.jsx rename to frontend/public/components/events.tsx index ecb533ba02..ef23c11c8b 100644 --- a/frontend/public/components/events.jsx +++ b/frontend/public/components/events.tsx @@ -1,8 +1,7 @@ -/* eslint-disable @typescript-eslint/no-use-before-define, tsdoc/syntax */ import * as _ from 'lodash'; +import type { ComponentType, FC, ReactNode } from 'react'; import { useEffect, useState, useRef, useMemo } from 'react'; import { css } from '@patternfly/react-styles'; -import * as PropTypes from 'prop-types'; import { Link, useParams } from 'react-router-dom-v5-compat'; import { DocumentTitle } from '@console/shared/src/components/document-title/DocumentTitle'; import { @@ -18,8 +17,8 @@ import { } from '@patternfly/react-core'; import { Trans, useTranslation } from 'react-i18next'; +import { Action, MenuOption } from '@console/dynamic-plugin-sdk'; -import { namespaceProptype } from '../propTypes'; import { ResourceListDropdown } from './resource-dropdown'; import { TextFilter } from './factory/text-filter'; import { @@ -32,7 +31,7 @@ import { import { withStartGuide } from './start-guide'; import { WSFactory } from '../module/ws-factory'; import { EventModel, NodeModel } from '../models'; -import { connectToFlags } from '../reducers/connectToFlags'; +import { useFlag } from '@console/shared/src/hooks/flag'; import { FLAGS } from '@console/shared/src/constants/common'; import { PageHeading } from '@console/shared/src/components/heading/PageHeading'; import { ConsoleSelect } from '@console/internal/components/utils/console-select'; @@ -41,29 +40,94 @@ import { ResourceIcon } from './utils/resource-icon'; import { ResourceLink, resourcePathFromModel } from './utils/resource-link'; import { TogglePlay } from './utils/toggle-play'; import { Timestamp } from '@console/shared/src/components/datetime/Timestamp'; -import { EventStreamList } from './utils/event-stream'; +import { EventStreamList, EventComponentProps } from './utils/event-stream'; import ActionMenu from '@console/shared/src/components/actions/menu/ActionMenu'; import { ActionMenuVariant } from '@console/shared/src/components/actions/types'; import ActionServiceProvider from '@console/shared/src/components/actions/ActionServiceProvider'; import ActionMenuItem from '@console/shared/src/components/actions/menu/ActionMenuItem'; import PaneBody from '@console/shared/src/components/layout/PaneBody'; +import type { EventKind } from '../module/k8s/types'; +import type { EventInvolvedObject } from '../module/k8s/event'; +import type { CellMeasurerCache } from 'react-virtualized'; +import type { WSOptions } from '@console/dynamic-plugin-sdk/src/utils/k8s/ws-factory'; +import type { + K8sResourceCommon, + ResourceEventStreamProps, +} from '@console/dynamic-plugin-sdk/src/extensions/console-types'; const maxMessages = 500; const flushInterval = 500; +// Extended EventKind type to include reportingComponent field present in v1 events +interface ExtendedEventKind extends EventKind { + reportingComponent?: string; +} + +// Types +interface ActionsProps { + actions: Action[]; + options?: MenuOption[]; + list?: EventComponentProps['list']; + cache: CellMeasurerCache; + index: number; +} + +interface InnerProps extends Omit { + event: ExtendedEventKind; +} + +interface EventsListProps { + title?: string; + autoFocus?: boolean; + mock?: boolean; +} + +interface NoMatchingEventsProps { + allCount: number; +} + +type FilterFunction = (involvedObject: EventInvolvedObject, event: EventKind) => boolean; + +interface EventStreamProps { + namespace?: string; + fieldSelector?: string; + mock?: boolean; + resourceEventStream?: boolean; + kind?: string; + type?: string; + filter?: FilterFunction[]; + textFilter?: string; +} + +interface InternalResourceEventStreamProps { + obj: K8sResourceCommon; +} + +interface InternalResourcesEventStreamProps { + filters: FilterFunction[]; + namespace?: string; +} + // We have to check different properties depending on whether events were // created with the core/v1 events API or the new events.k8s.io API. -const getFirstTime = (event) => event.firstTimestamp || event.eventTime; -export const getLastTime = (event) => { +const getFirstTime = (event: EventKind): string | undefined => + event.firstTimestamp || event.eventTime; + +export const getLastTime = (event: EventKind): string | null | undefined => { const lastObservedTime = event.series ? event.series.lastObservedTime : null; return event.lastTimestamp || lastObservedTime || event.eventTime; }; -export const sortEvents = (events) => { - return _.orderBy(events, [getLastTime, getFirstTime, 'name'], ['desc', 'desc', 'asc']); + +export const sortEvents = (events: EventKind[] | Record): EventKind[] => { + return _.orderBy( + events, + [getLastTime, getFirstTime, 'name'], + ['desc', 'desc', 'asc'], + ) as EventKind[]; }; // Predicate function to filter by event "type" (normal, warning, or all) -export const typeFilter = (eventType, event) => { +export const typeFilter = (eventType: string, event: EventKind): boolean => { if (eventType === 'all') { return true; } @@ -71,7 +135,7 @@ export const typeFilter = (eventType, event) => { return type.toLowerCase() === eventType; }; -const kindFilter = (reference, { involvedObject }) => { +const kindFilter = (reference: string, { involvedObject }: EventKind): boolean => { if (!reference) { return true; } @@ -95,11 +159,11 @@ const kindFilter = (reference, { involvedObject }) => { }); }; -const Actions = ({ actions, options, list, cache, index }) => { +const Actions: FC = ({ actions, options, list, cache, index }) => { useEffect(() => { // Actions contents will render after the initial row height calculation, // so recompute the row height. - cache.clear(index); + cache.clear(index, 0); list?.recomputeRowHeights(index); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -109,7 +173,7 @@ const Actions = ({ actions, options, list, cache, index }) => { {actions.length === 1 ? ( ( + component={(props: any) => (