Skip to content

Commit e2728cc

Browse files
rebase and cleanup
1 parent 1faff55 commit e2728cc

File tree

8 files changed

+418
-149
lines changed

8 files changed

+418
-149
lines changed

ui/package.json

+10-12
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@
6565
},
6666
"devDependencies": {
6767
"@babel/core": "^7.26.10",
68-
"@babel/plugin-transform-runtime": "^7.26.10",
6968
"@babel/eslint-parser": "^7.26.10",
7069
"@babel/helper-plugin-utils": "^7.26.5",
70+
"@babel/plugin-transform-runtime": "^7.26.10",
7171
"@babel/preset-env": "^7.26.9",
7272
"@babel/preset-react": "^7.26.3",
7373
"@kickstartds/core": "^4.1.0",
@@ -82,10 +82,10 @@
8282
"@storybook/addon-links": "^8.6.12",
8383
"@storybook/addon-webpack5-compiler-babel": "^3.0.6",
8484
"@storybook/blocks": "^8.6.12",
85-
"@storybook/react": "^8.6.12",
86-
"@storybook/react-webpack5": "^8.6.12",
8785
"@storybook/builder-vite": "^8.6.10",
86+
"@storybook/react": "^8.6.12",
8887
"@storybook/react-vite": "^8.6.10",
88+
"@storybook/react-webpack5": "^8.6.12",
8989
"@storybook/test-runner": "^0.22.0",
9090
"@testing-library/dom": "^8.20.1",
9191
"@testing-library/jest-dom": "^6.6.3",
@@ -119,40 +119,38 @@
119119
"eslint-plugin-react-hooks": "^4.6.2",
120120
"eslint-plugin-storybook": "^0.12.0",
121121
"eslint-plugin-testing-library": "^7.1.1",
122+
"fork-ts-checker-webpack-plugin": "^9.0.2",
122123
"jest": "^29.7.0",
123124
"jest-environment-jsdom": "^29.7.0",
124125
"jest-fixed-jsdom": "^0.0.9",
125-
"fork-ts-checker-webpack-plugin": "^9.0.2",
126126
"jest-image-snapshot": "^6.4.0",
127-
"msw": "2.7.4",
128127
"jsdom": "^26.0.0",
129128
"license-webpack-plugin": "^4.0.2",
129+
"msw": "2.7.4",
130130
"msw-storybook-addon": "^2.0.4",
131131
"prettier": "^2.8.8",
132132
"querystring-es3": "^0.2.1",
133-
"storybook": "^8.6.12",
134133
"rollup-plugin-license": "^3.6.0",
134+
"storybook": "^8.6.12",
135135
"style-loader": "^4.0.0",
136136
"stylelint": "^14.16.1",
137137
"ts-node": "^10.9.2",
138138
"typescript": "^5.8.3",
139139
"undici": "^5.29.0",
140140
"url": "^0.11.4",
141-
"webpack": "^5.99.5",
142-
"vite": "^6.2.3",
143-
"vite-plugin-checker": "^0.9.0",
144141
"vite-plugin-dts": "^4.5.0",
145-
"vitest": "^3.0.7",
142+
"vitest": "^3.1.1",
143+
"webpack": "^5.99.5",
146144
"webpack-cli": "^6.0.1",
147145
"webpack-dev-server": "^5.2.1",
148146
"webpack-merge": "^6.0.1"
149147
},
150148
"peerDependencies": {
151149
"react": "^16.14.0",
152-
"typescript": "^5.8.3",
153150
"react-dom": "^16.14.0",
154151
"react-is": "^16.14.0",
155-
"styled-components": "^5.3.11"
152+
"styled-components": "^5.3.11",
153+
"typescript": "^5.8.3"
156154
},
157155
"resolutions": {
158156
"@npmcli/git": "^2.1.0",

ui/src/components/CustomMenu/CustomMenu.tsx

+15-7
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
11
import React, { Component } from 'react';
22
import { _ } from '@splunk/ui-utils/i18n';
3+
34
import { getUnifiedConfigs } from '../../util/util';
45
import { getBuildDirPath } from '../../util/script';
56
import { CustomMenuConstructor } from './CustomMenuBase';
67
import { invariant } from '../../util/invariant';
8+
import { CustomComponentContextType } from '../../context/CustomComponentContext';
79

810
type CustomMenuProps = {
911
fileName: string;
1012
type: string;
1113
handleChange: (val: { service: string; input?: string }) => void;
14+
customComponentContext?: CustomComponentContextType;
1215
};
1316

1417
type CustomMenuState = {
1518
loading: boolean;
1619
};
1720

1821
class CustomMenu extends Component<CustomMenuProps, CustomMenuState> {
22+
customComponentContext?: CustomComponentContextType;
23+
1924
shouldRender: boolean;
2025

2126
el?: HTMLElement;
@@ -70,14 +75,17 @@ class CustomMenu extends Component<CustomMenuProps, CustomMenuState> {
7075
new Promise((resolve) => {
7176
if (this.customComponentContext?.[this.props.fileName]) {
7277
const Control = this.customComponentContext?.[this.props.fileName];
73-
resolve(Control);
78+
resolve(Control as CustomMenuConstructor);
7479
} else if (this.props.type === 'external') {
75-
import(
76-
/* @vite-ignore */ `${getBuildDirPath()}/custom/${this.props.fileName}.js`
77-
).then((external) => {
78-
const Control = external.default;
79-
resolve(Control as CustomMenuConstructor);
80-
});
80+
import(/* @vite-ignore */ `${getBuildDirPath()}/custom/${this.props.fileName}.js`)
81+
.then((external) => {
82+
const Control = external.default;
83+
resolve(Control as CustomMenuConstructor);
84+
})
85+
.catch((error) => {
86+
// eslint-disable-next-line no-console
87+
console.error(`[Custom Menu] Error loading custom menu ${error.message}`);
88+
});
8189
} else {
8290
const globalConfig = getUnifiedConfigs();
8391
const appName = globalConfig.meta.name;
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,52 @@
11
import { render, screen, waitFor } from '@testing-library/react';
22
import React from 'react';
33
import userEvent from '@testing-library/user-event';
4+
import { vi } from 'vitest';
45

56
import { getBuildDirPath } from '../../../util/script';
67
import { setUnifiedConfig } from '../../../util/util';
78
import CustomMenu from '../CustomMenu';
89
import mockCustomMenu from './mocks/CustomMenuMock';
910
import { getGlobalConfigMockCustomMenu, GroupsMenuType } from './mocks/globalConfigMock';
11+
import { consoleError } from '../../../../test.setup';
1012

1113
const MODULE = 'customMenuFileName';
1214

13-
const handleChange = jest.fn();
15+
const handleChange = vi.fn();
1416

15-
const waitForCustomElementLoad = async () => {
16-
await waitFor(async () => {
17+
const waitForLoadingDisappear = async () => {
18+
return waitFor(async () => {
1719
const loading = screen.queryByText('Loading...');
18-
if (loading) {
19-
await waitFor(() => expect(loading).not.toHaveTextContent('Loading...'));
20-
}
20+
expect(loading).not.toBeInTheDocument();
2121
});
2222
};
2323

24+
const doCustomMenuMock = () => {
25+
vi.doMock(`${getBuildDirPath()}/custom/${MODULE}.js`, () => ({
26+
default: mockCustomMenu,
27+
}));
28+
};
29+
30+
const doCustomMenuUnMock = () => {
31+
vi.doUnmock(`${getBuildDirPath()}/custom/${MODULE}.js`);
32+
};
33+
2434
const setup = (groupsMenu?: GroupsMenuType) => {
2535
const mockConfig = getGlobalConfigMockCustomMenu(MODULE, groupsMenu);
2636
setUnifiedConfig(mockConfig);
2737

28-
jest.mock(`${getBuildDirPath()}/custom/${MODULE}.js`, () => mockCustomMenu, {
29-
virtual: true,
30-
});
31-
3238
render(<CustomMenu fileName={MODULE} type="external" handleChange={handleChange} />);
3339
};
3440

35-
it('should render loading text correctly (constantly)', () => {
36-
setup();
37-
const loading = screen.getByText('Loading...');
38-
expect(loading).toBeInTheDocument();
39-
});
40-
41-
it('should render component Correctly', async () => {
42-
setup();
43-
await waitForCustomElementLoad();
44-
const customMenuText = screen.getByText('Click Me! I am a button for custom menu');
45-
expect(customMenuText).toBeInTheDocument();
46-
});
47-
48-
it('should call handler correctly', async () => {
41+
it('should render component and call handler correctly', async () => {
42+
doCustomMenuMock();
4943
setup();
50-
await waitForCustomElementLoad();
51-
const customMenuText = screen.getByText('Click Me! I am a button for custom menu');
52-
44+
await waitForLoadingDisappear();
45+
const customMenuText = await waitFor(() => {
46+
const menuText = screen.getByText('Click Me! I am a button for custom menu');
47+
expect(menuText).toBeInTheDocument();
48+
return menuText;
49+
});
5350
await userEvent.click(customMenuText);
5451
expect(handleChange).toHaveBeenCalledWith({
5552
service: 'example_input_one',
@@ -73,8 +70,26 @@ it('Do not render custom if group menu provided + loading disappears', async ()
7370
groupServices: ['example_input_two', 'example_input_four'],
7471
},
7572
];
73+
doCustomMenuMock();
74+
7675
setup(groupsMenu);
77-
await waitForCustomElementLoad();
78-
const customMenuText = screen.queryByText('Click Me! I am a button for custom menu');
79-
expect(customMenuText).not.toBeInTheDocument();
76+
await waitForLoadingDisappear();
77+
await waitFor(() => {
78+
const customMenuText = screen.queryByText('Click Me! I am a button for custom menu');
79+
expect(customMenuText).not.toBeInTheDocument();
80+
});
81+
});
82+
83+
it('should render loading text correctly (constantly)', async () => {
84+
const errorHandler = vi.fn();
85+
consoleError.mockImplementation(errorHandler);
86+
doCustomMenuUnMock();
87+
setup();
88+
const loading = screen.getByText('Loading...');
89+
expect(loading).toBeInTheDocument();
90+
await waitFor(() => {
91+
expect(errorHandler).toHaveBeenCalledWith(
92+
`[Custom Menu] Error loading custom menu Failed to load url /custom/customMenuFileName.js (resolved id: /custom/customMenuFileName.js). Does the file exist?`
93+
);
94+
});
8095
});

ui/src/components/table/tests/TableExpansionRow.test.tsx

+11-14
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { expect, it, vi } from 'vitest';
1+
import { vi } from 'vitest';
22
import { render, screen, waitFor, waitForElementToBeRemoved, within } from '@testing-library/react';
33
import userEvent from '@testing-library/user-event';
44
import React from 'react';
55
import { http, HttpResponse } from 'msw';
66
import { BrowserRouter } from 'react-router-dom';
7+
78
import TableWrapper, { ITableWrapperProps } from '../TableWrapper';
89
import { server } from '../../../mocks/server';
910
import { TableContextProvider } from '../../../context/TableContext';
@@ -13,19 +14,12 @@ import { getBuildDirPath } from '../../../util/script';
1314
import mockCustomInputRowGetDLError from './mocks/CustomRowMockGetDLError';
1415
import mockCustomInputRowRenderError from './mocks/CustomRowMockRenderError';
1516
import mockCustomInputRowUnvalid from './mocks/CustomRowMockGetDLUnvalid';
16-
17-
// Import the mock outside of the setup function
18-
import mockCustomInputRow from '../../../../../tests/testdata/test_addons/package_global_config_everything/package/appserver/static/js/build/custom/custom_input_row';
17+
import mockCustomInputRow from './mocks/CustomRowMock';
1918
import { invariant } from '../../../util/invariant';
2019
import { MOCK_CONFIG } from './mocks';
2120
import { GlobalConfig } from '../../../publicApi';
2221
import { consoleError } from '../../../../test.setup';
2322

24-
// Set up the mock before any tests run, doMock is not hoisted to the top of the file
25-
vi.doMock(`${getBuildDirPath()}/custom/CustomInputRow.js`, () => ({
26-
default: mockCustomInputRow,
27-
}));
28-
2923
const inputName = 'example_input_one';
3024
const interval = 7766;
3125
const updatedInterval = 7788;
@@ -169,9 +163,14 @@ async function expectIntervalInExpandedRow(inputRow: HTMLElement, expectedInterv
169163
if (loading) {
170164
await waitForElementToBeRemoved(loading);
171165
}
172-
const allDefinitions = (await screen.findAllByRole('definition')).map((el) => el.textContent);
173166

174-
expect(allDefinitions).toContain(`${expectedInterval} sec`);
167+
await waitFor(async () => {
168+
const allDefinitions = (await screen.findAllByRole('definition')).map(
169+
(el) => el.textContent
170+
);
171+
172+
expect(allDefinitions).toContain(`${expectedInterval} sec`);
173+
});
175174
}
176175

177176
it('should update custom Expansion Row when Input has changed', async () => {
@@ -202,23 +201,21 @@ it('should update custom Expansion Row when Input has changed', async () => {
202201
}
203202
)
204203
);
205-
206204
await expectIntervalInExpandedRow(
207205
await screen.findByRole('row', { name: `row-${inputName}` }),
208206
interval
209207
);
210208

211209
await userEvent.click(within(inputRow).getByRole('button', { name: /edit/i }));
212210
const dialog = await screen.findByRole('dialog');
213-
214211
const textBoxes = within(dialog).getAllByRole('textbox');
215212
expect(textBoxes).toHaveLength(2);
216213
const intervalInput = textBoxes[1];
217214
expect(intervalInput).toHaveValue(interval.toString());
215+
218216
await userEvent.clear(intervalInput);
219217
await userEvent.type(intervalInput, updatedInterval.toString());
220218
await userEvent.click(screen.getByRole('button', { name: /update/i }));
221-
222219
await screen.findByRole('cell', { name: updatedInterval.toString() });
223220

224221
await expectIntervalInExpandedRow(

ui/src/context/CustomComponentContext.tsx

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import React, { createContext, ReactNode, useMemo } from 'react';
2-
import { CustomControlConstructor } from '../components/CustomControl/CustomControlBase';
3-
import { CustomTabConstructor } from '../components/CustomTab/CustomTabBase';
4-
import { CustomHookConstructor } from '../types/components/CustomHookClass';
2+
import { CustomElementsMap } from '../types/CustomTypes';
53

6-
export type CustomComponentContextType =
7-
| Record<string, CustomHookConstructor | CustomControlConstructor | CustomTabConstructor>
8-
| undefined;
4+
export type CustomComponentContextType = CustomElementsMap | undefined;
95

106
const CustomComponentContext = createContext<CustomComponentContextType>(undefined);
117

ui/src/publicApi.ts

+2
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,6 @@ export { CustomMenuBase } from './components/CustomMenu/CustomMenuBase';
1414

1515
export { CustomCellBase } from './components/table/CustomTableCellBase';
1616

17+
export { CustomRowBase } from './components/table/CustomRowBase';
18+
1719
export { init } from './pages/UccInit';

ui/src/types/CustomTypes.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
import { CustomControlConstructor } from '../components/CustomControl/CustomControlBase';
2+
import { CustomMenuConstructor } from '../components/CustomMenu/CustomMenuBase';
23
import { CustomTabConstructor } from '../components/CustomTab/CustomTabBase';
4+
import { CustomRowConstructor } from '../components/table/CustomRowBase';
5+
import { CustomCellConstructor } from '../components/table/CustomTableCellBase';
36
import { CustomHookConstructor } from './components/CustomHookClass';
47

5-
// Custom menu to be added as a type
68
export type CustomElementsMap = Record<
79
string,
8-
CustomHookConstructor | CustomControlConstructor | CustomTabConstructor
10+
| CustomHookConstructor
11+
| CustomControlConstructor
12+
| CustomRowConstructor
13+
| CustomCellConstructor
14+
| CustomMenuConstructor
15+
| CustomTabConstructor
916
>;

0 commit comments

Comments
 (0)