Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 14 additions & 32 deletions dynamic-demo-plugin/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -172,16 +172,16 @@ __metadata:
"@openshift/dynamic-plugin-sdk": "npm:^8.0.0"
immutable: "npm:3.x"
lodash: "npm:^4.17.21"
reselect: "npm:4.x"
typesafe-actions: "npm:^4.2.1"
reselect: "npm:^5.1.1"
typesafe-actions: "npm:^5.1.0"
peerDependencies:
"@patternfly/react-topology": ~6.4.0
react: ^18.3.1
react-i18next: ~16.5.8
react-redux: 8.1.3
react-redux: ~9.2.0
react-router: ~7.13.1
redux: ^4.0.4
redux-thunk: 2.4.0
redux: ~5.0.1
redux-thunk: ~3.1.0
peerDependenciesMeta:
"@patternfly/react-topology":
optional: true
Expand Down Expand Up @@ -1841,7 +1841,7 @@ __metadata:
languageName: node
linkType: hard

"get-intrinsic@npm:^1.2.4":
"get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.3.0":
version: 1.3.1
resolution: "get-intrinsic@npm:1.3.1"
dependencies:
Expand All @@ -1862,24 +1862,6 @@ __metadata:
languageName: node
linkType: hard

"get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.3.0":
version: 1.3.0
resolution: "get-intrinsic@npm:1.3.0"
dependencies:
call-bind-apply-helpers: "npm:^1.0.2"
es-define-property: "npm:^1.0.1"
es-errors: "npm:^1.3.0"
es-object-atoms: "npm:^1.1.1"
function-bind: "npm:^1.1.2"
get-proto: "npm:^1.0.1"
gopd: "npm:^1.2.0"
has-symbols: "npm:^1.1.0"
hasown: "npm:^2.0.2"
math-intrinsics: "npm:^1.1.0"
checksum: 10c0/52c81808af9a8130f581e6a6a83e1ba4a9f703359e7a438d1369a5267a25412322f03dcbd7c549edaef0b6214a0630a28511d7df0130c93cfd380f4fa0b5b66a
languageName: node
linkType: hard

"get-proto@npm:^1.0.1":
version: 1.0.1
resolution: "get-proto@npm:1.0.1"
Expand Down Expand Up @@ -3332,10 +3314,10 @@ __metadata:
languageName: node
linkType: hard

"reselect@npm:4.x":
version: 4.1.8
resolution: "reselect@npm:4.1.8"
checksum: 10c0/06a305a504affcbb67dd0561ddc8306b35796199c7e15b38934c80606938a021eadcf68cfd58e7bb5e17786601c37602a3362a4665c7bf0a96c1041ceee9d0b7
"reselect@npm:^5.1.1":
version: 5.1.1
resolution: "reselect@npm:5.1.1"
checksum: 10c0/219c30da122980f61853db3aebd173524a2accd4b3baec770e3d51941426c87648a125ca08d8c57daa6b8b086f2fdd2703cb035dd6231db98cdbe1176a71f489
languageName: node
linkType: hard

Expand Down Expand Up @@ -4023,10 +4005,10 @@ __metadata:
languageName: node
linkType: hard

"typesafe-actions@npm:^4.2.1":
version: 4.4.2
resolution: "typesafe-actions@npm:4.4.2"
checksum: 10c0/9d07a5902f49d94169ccdbe5ca062d54ee710f6067e2b52f59896c0a725ed0a681d4e4f3a71cf0541052d2f741a3c2003f21984e1a0694c5256f0ba6aca63418
"typesafe-actions@npm:^5.1.0":
version: 5.1.0
resolution: "typesafe-actions@npm:5.1.0"
checksum: 10c0/64e745aed10abf829b8aad3b178492c5f3602455cb02b7d565aad5560c0ded01dc5db06b12055ebb85fc9fd448d54d3444599eca65b074b48d0c9fb796bbb6a2
languageName: node
linkType: hard

Expand Down
11 changes: 5 additions & 6 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -211,19 +211,19 @@
"react-helmet-async": "^2.0.5",
"react-i18next": "~16.5.8",
"react-linkify": "^0.2.2",
"react-redux": "8.1.3",
"react-redux": "~9.2.0",
"react-router": "~7.13.1",
"react-svg": "^16.2.0",
"react-tagsinput": "3.20.x",
"react-virtualized": "9.x",
"redux": "^4.0.4",
"redux-thunk": "2.4.0",
"reselect": "4.x",
"redux": "~5.0.1",
"redux-thunk": "~3.1.0",
"reselect": "^5.1.1",
"sanitize-html": "^2.3.2",
"semver": "6.x",
"showdown": "1.8.6",
"subscriptions-transport-ws": "^0.9.16",
"typesafe-actions": "^4.2.1",
"typesafe-actions": "^5.1.0",
"victory": "^37.3.6",
"yup": "^1.7.1"
},
Expand Down Expand Up @@ -300,7 +300,6 @@
"puppeteer-core": "^23.9.0",
"react-refresh": "^0.10.0",
"read-pkg": "5.x",
"redux-mock-store": "^1.5.3",
"resolve-url-loader": "2.x",
"sass": "^1.42.1",
"sass-loader": "^10.1.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@
Additional updates to these shared modules might occur before the 4.22 release is generally available.

- Upgraded from `react` v17 to v18. Plugins must use `react` 18 to remain compatible with Console.
- Upgraded from `react-redux` v7 to v8. Plugins must use `react-redux` v8 to remain compatible with Console.
- Upgraded from `react-i18next` v11 to v16. Plugins must use `react-i18next` v16 to remain compatible with Console.
- Upgraded from `react-redux` v7 to v9. Plugins must use `react-redux` v9 to remain compatible with Console.
- Upgraded from `redux` v4 to v5. Plugins must use `redux` v5 to remain compatible with Console.
- Upgraded from `redux-thunk` v2 to v3. Plugins must use `redux-thunk` v3 to remain compatible with Console.
- Upgraded from `react-i18next` v11 to v15. Plugins must use `react-i18next` v15 to remain compatible with Console.
Copy link
Member Author

@logonoff logonoff Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line 16 was added in error - will remove in a follow up

- Upgraded from `react-router` v5 to v7. Plugins must use `react-router` v7 to remain compatible with Console.
- The `react-router-dom-v5-compat` and `react-router-dom` shared modules are deprecated. Plugins should replace these
imports with `react-router`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { ReactNode, FC } from 'react';
import { render } from '@testing-library/react';
import { Provider } from 'react-redux';
import { combineReducers, createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { thunk } from 'redux-thunk';
import { receivedResources } from '@console/internal/actions/k8s';
import { ConfigMapModel, SecretModel } from '@console/internal/models';
import { SDKReducers } from '../../../../app';
Expand Down Expand Up @@ -103,10 +103,8 @@ describe('useK8sModels', () => {
expect(models1).toEqual({ ConfigMap: ConfigMapModel, Secret: SecretModel });
expect(models2).toEqual({ ConfigMap: ConfigMapModel, Secret: SecretModel });

// It was saved in immutable redux store and will be cloned.
expect(models1).not.toBe(models2);
expect(models1.ConfigMap).toBe(models2.ConfigMap);
expect(models1.Secret).toBe(models2.Secret);
// Memoized via createSelector: same immutable input produces same JS output reference.
expect(models1).toBe(models2);
});

it('should return the same model JSON when rendering twice', () => {
Expand Down Expand Up @@ -136,9 +134,7 @@ describe('useK8sModels', () => {
expect(models1).toEqual({ ConfigMap: ConfigMapModel, Secret: SecretModel });
expect(models2).toEqual({ ConfigMap: ConfigMapModel, Secret: SecretModel });

// It was saved in immutable redux store and will be cloned.
expect(models1).not.toBe(models2);
expect(models1.ConfigMap).toBe(models2.ConfigMap);
expect(models1.Secret).toBe(models2.Secret);
// Memoized via createSelector: same immutable input produces same JS output reference.
expect(models1).toBe(models2);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { ReactNode, FC } from 'react';
import { act, cleanup, render } from '@testing-library/react';
import { Provider } from 'react-redux';
import { combineReducers, createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { thunk } from 'redux-thunk';
import { receivedResources } from '@console/internal/actions/k8s';
import { SDKReducers } from '../../../../app';
import type { WatchK8sResource } from '../../../../extensions/console-types';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { FC, ReactNode } from 'react';
import { act, cleanup, render } from '@testing-library/react';
import { Provider } from 'react-redux';
import { combineReducers, createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { thunk } from 'redux-thunk';
import { receivedResources } from '@console/internal/actions/k8s';
import { SDKReducers } from '../../../../app';
import type { WatchK8sResources } from '../../../../extensions/console-types';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import type { SDKStoreState } from '../../../app/redux-types';
import type { UseK8sModels } from '../../../extensions/console-types';
import type { K8sModel } from '../../../lib-core';

const modelsSelector = createSelector(
(state: SDKStoreState) => state.k8s.getIn(['RESOURCES', 'models']),
(models) => models?.toJS() ?? {},
);

/**
* Hook that retrieves all current k8s models from redux.
*
Expand All @@ -16,8 +22,6 @@ import type { K8sModel } from '../../../lib-core';
* ```
*/
export const useK8sModels: UseK8sModels = () => [
useSelector<SDKStoreState, { [key: string]: K8sModel }>(
({ k8s }) => k8s.getIn(['RESOURCES', 'models'])?.toJS() ?? {},
),
useSelector<SDKStoreState, { [key: string]: K8sModel }>(modelsSelector),
useSelector<SDKStoreState, boolean>(({ k8s }) => k8s.getIn(['RESOURCES', 'inFlight'])) ?? false,
];
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useRef, useMemo, useEffect } from 'react';
import type { Iterable as ImmutableIterable } from 'immutable';
import { Map as ImmutableMap } from 'immutable';
import { useDispatch, useSelector } from 'react-redux';
import { createSelectorCreator, defaultMemoize } from 'reselect';
import { createSelectorCreator, lruMemoize } from 'reselect';
import type { K8sModel } from '../../../api/common-types';
import * as k8sActions from '../../../app/k8s/actions/k8s';
import type { SDKDispatch, SDKStoreState } from '../../../app/redux-types';
Expand Down Expand Up @@ -119,14 +119,18 @@ export const useK8sWatchResources: UseK8sWatchResources = (initResources) => {

const resourceK8sSelectorCreator = useMemo(
() =>
createSelectorCreator(
// specifying createSelectorCreator<ImmutableMap<string, K8sKind>> throws type error
defaultMemoize as any,
(oldK8s: ImmutableMap<string, K8sModel>, newK8s: ImmutableMap<string, K8sModel>) =>
Object.keys(reduxIDs || {})
.filter((k) => !reduxIDs[k].noModel)
.every((k) => oldK8s.get(reduxIDs[k].id) === newK8s.get(reduxIDs[k].id)),
),
createSelectorCreator({
memoize: lruMemoize,
memoizeOptions: {
equalityCheck: (
oldK8s: ImmutableMap<string, K8sModel>,
newK8s: ImmutableMap<string, K8sModel>,
) =>
Object.keys(reduxIDs || {})
.filter((k) => !reduxIDs[k].noModel)
.every((k) => oldK8s.get(reduxIDs[k].id) === newK8s.get(reduxIDs[k].id)),
},
}),
[reduxIDs],
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useMemo, useState, useEffect } from 'react';
import { shallowEqual } from 'react-redux';
import type { K8sResourceKind } from '@console/dynamic-plugin-sdk/src';
import { getImpersonate, getUser } from '@console/dynamic-plugin-sdk/src';
import { useK8sWatchResource } from '@console/internal/components/utils/k8s-watch-hook';
Expand Down Expand Up @@ -30,7 +31,7 @@ export const useGetUserSettingConfigMap = () => {
const impersonateName = getImpersonate(state)?.name;
const { uid, username } = getUser(state) ?? {};
return { impersonateName, uid, username };
});
}, shallowEqual);

// Hash the username asynchronously
useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { useConsoleSelector } from '@console/shared/src/hooks/useConsoleSelector
import { SYSTEM_ALERT_RULE_LABEL } from '../constants/monitoring';
import { useUserPreference } from './useUserPreference';

const emptyNotificationAlerts: NotificationAlerts = {} as NotificationAlerts;

/** Get notification alerts from redux and filter by current user notification settings OR the
provided override labels. Alerts that match on override labels will not be fitlered even if
current user settings would normally exclude them.
Expand All @@ -24,9 +26,10 @@ export const useNotificationAlerts = (
true,
true,
);
const { data: alerts, loaded, loadError } = useConsoleSelector<NotificationAlerts>(
({ observe }) => observe.get('notificationAlerts') ?? {},
const notificationAlerts = useConsoleSelector<NotificationAlerts | undefined>(({ observe }) =>
observe.get('notificationAlerts'),
);
const { data: alerts, loaded, loadError } = notificationAlerts ?? emptyNotificationAlerts;

const [filteredAlerts, setFilteredAlerts] = useState<NotificationAlerts['data']>([]);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback } from 'react';
import { useCallback, useMemo } from 'react';
import type { UseUtilizationDuration } from '@console/dynamic-plugin-sdk/src/api/internal-types';
import * as UIActions from '@console/internal/actions/ui';
import { useConsoleDispatch } from '@console/shared/src/hooks/useConsoleDispatch';
Expand All @@ -12,10 +12,10 @@ export const useUtilizationDuration: UseUtilizationDuration = (
const duration =
useConsoleSelector<number>(({ UI }) => UI.getIn(['utilizationDuration', 'duration'])) ??
DEFAULT_DURATION;
// eslint-disable-next-line react-hooks/exhaustive-deps
const endDate = useConsoleSelector<Date>(
({ UI }) => UI.getIn(['utilizationDuration', 'endDate']) ?? new Date(),
const storeEndDate = useConsoleSelector<Date | undefined>(({ UI }) =>
UI.getIn(['utilizationDuration', 'endDate']),
);
const endDate = useMemo(() => storeEndDate ?? new Date(), [storeEndDate]);
const selectedKey =
useConsoleSelector<string>(({ UI }) => UI.getIn(['utilizationDuration', 'selectedKey'])) ??
DEFAULT_DURATION_KEY;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type WrapperProps = {
};

// Create a Redux store with reducer and initial state.
const rootReducer = combineReducers<RootState>(baseReducers);
const rootReducer = combineReducers(baseReducers);
const setupStore = (initialState?: Partial<RootState>) => {
const store = createStore(rootReducer, initialState);
// Set the store in storeHandler so that modules like rbac.tsx can access it
Expand Down
3 changes: 2 additions & 1 deletion frontend/packages/dev-console/src/utils/perspective.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { CodeIcon } from '@patternfly/react-icons/dist/esm/icons/code-icon';
import { shallowEqual } from 'react-redux';
import type { Perspective, ResolvedExtension } from '@console/dynamic-plugin-sdk';
import { getFlagsObject, flagPending } from '@console/internal/reducers/features';
import { FLAGS } from '@console/shared/src/constants/common';
import { useConsoleSelector } from '@console/shared/src/hooks/useConsoleSelector';

export const usePerspectiveDetection = () => {
const flags = useConsoleSelector((state) => getFlagsObject(state));
const flags = useConsoleSelector((state) => getFlagsObject(state), shallowEqual);
const canGetNS = flags.CAN_GET_NS;
const loadingFlag = flagPending(canGetNS);
const enablePerspective = !canGetNS;
Expand Down
12 changes: 7 additions & 5 deletions frontend/public/components/factory/table-data-hook.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useMemo } from 'react';
import * as _ from 'lodash';
import { useConsoleSelector } from '@console/shared/src/hooks/useConsoleSelector';
import { createSelectorCreator, defaultMemoize } from 'reselect';
import { createSelectorCreator, lruMemoize } from 'reselect';
import { SortByDirection } from '@patternfly/react-table';
import { useDeepCompareMemoize } from '@console/shared/src/hooks/useDeepCompareMemoize';
import { RowFilter } from '@console/dynamic-plugin-sdk/src/extensions/console-types';
Expand Down Expand Up @@ -90,10 +90,12 @@ export const useTableData = ({

const tableSelectorCreator = useMemo(
() =>
createSelectorCreator(
defaultMemoize as any,
(oldSortState, newSortState) => oldSortState === newSortState,
),
createSelectorCreator({
memoize: lruMemoize,
memoizeOptions: {
equalityCheck: (oldSortState, newSortState) => oldSortState === newSortState,
},
}),
[],
);

Expand Down
16 changes: 10 additions & 6 deletions frontend/public/components/masthead/masthead-toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { FC } from 'react';
import { Fragment, useContext, useState, useRef, useCallback, useEffect } from 'react';
import * as _ from 'lodash';
import { useTranslation } from 'react-i18next';
import { shallowEqual } from 'react-redux';
import { useConsoleSelector } from '@console/shared/src/hooks/useConsoleSelector';
import { useConsoleDispatch } from '@console/shared/src/hooks/useConsoleDispatch';
import { useNavigate } from 'react-router';
Expand Down Expand Up @@ -169,12 +170,15 @@ const MastheadToolbarContents: FC<MastheadToolbarContentsProps> = ({
t('public~Login with this command'),
externalLoginCommand,
);
const { clusterID, alertCount, canAccessNS, impersonate } = useConsoleSelector((state) => ({
clusterID: state.UI.get('clusterID'),
alertCount: state.observe.getIn(['alertCount']),
canAccessNS: !!state[featureReducerName].get(FLAGS.CAN_GET_NS),
impersonate: getImpersonate(state),
}));
const { clusterID, alertCount, canAccessNS, impersonate } = useConsoleSelector(
(state) => ({
clusterID: state.UI.get('clusterID'),
alertCount: state.observe.getIn(['alertCount']),
canAccessNS: !!state[featureReducerName].get(FLAGS.CAN_GET_NS),
impersonate: getImpersonate(state),
}),
shallowEqual,
);

// Use centralized user hook for user data
const { displayName, username } = useUser();
Expand Down
Loading