Skip to content

Commit 678342f

Browse files
Merge pull request #1598 from ral-facilities/create-api-config-provider-#1595
Create a provider for API config #1595
2 parents 1b6c04d + ae65126 commit 678342f

22 files changed

+12205
-11980
lines changed

src/App.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
clearFailedAuthRequestsQueue,
2828
retryFailedAuthRequests,
2929
} from './api/api';
30+
import APIConfigProvider from './apiConfigProvider.component';
3031
import { MicroFrontendId } from './app.types';
3132
import CatalogueLayout, {
3233
CatalogueErrorComponent,
@@ -275,14 +276,16 @@ export function Layout() {
275276
<IMSThemeProvider>
276277
<ConfigProvider>
277278
<QueryClientProvider client={queryClient}>
278-
<React.Suspense
279-
fallback={
280-
<Preloader loading={true}>Finished loading</Preloader>
281-
}
282-
>
283-
<ViewTabs />
284-
<ReactQueryDevtools initialIsOpen={false} />
285-
</React.Suspense>
279+
<APIConfigProvider>
280+
<React.Suspense
281+
fallback={
282+
<Preloader loading={true}>Finished loading</Preloader>
283+
}
284+
>
285+
<ViewTabs />
286+
<ReactQueryDevtools initialIsOpen={false} />
287+
</React.Suspense>
288+
</APIConfigProvider>
286289
</QueryClientProvider>
287290
</ConfigProvider>
288291
</IMSThemeProvider>

src/admin/systemTypes/__snapshots__/systemsTypes.component.test.tsx.snap

Lines changed: 692 additions & 690 deletions
Large diffs are not rendered by default.

src/admin/systemTypes/systemTypes.component.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import {
88
import { MRT_Localization_EN } from 'material-react-table/locales/en';
99
import React from 'react';
1010
import type { SystemType } from '../../api/api.types';
11-
import { useGetSparesDefinition } from '../../api/settings';
1211
import { useGetSystemTypes } from '../../api/systems';
1312
import { usePreservedTableState } from '../../common/preservedTableState.component';
1413

14+
import { APISettingsContext } from '../../apiConfigProvider.component';
1515
import {
1616
COLUMN_FILTER_BOOLEAN_OPTIONS,
1717
COLUMN_FILTER_FUNCTIONS,
@@ -35,12 +35,12 @@ function SystemTypes() {
3535
const { data: systemTypesData = [], isLoading: isLoadingSystemTypes } =
3636
useGetSystemTypes();
3737

38-
const { data: sparesDefinition, isLoading: isLoadingSparesDefinition } =
39-
useGetSparesDefinition();
38+
const apiSettings = React.useContext(APISettingsContext);
39+
const sparesDefinition = apiSettings?.spares?.sparesDefinition;
4040

4141
const [tableRows, setTableRows] = React.useState<TableRowData[]>([]);
4242

43-
const isLoading = isLoadingSystemTypes || isLoadingSparesDefinition;
43+
const isLoading = isLoadingSystemTypes;
4444
//Once loading finished - use same logic as catalogueItemsTable to pair up data
4545
React.useEffect(() => {
4646
if (!isLoading && systemTypesData) {

src/admin/systemTypes/systemsTypes.component.test.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
import { screen, waitFor } from '@testing-library/react';
22
import { userEvent, type UserEvent } from '@testing-library/user-event';
3+
import APIConfigProvider from '../../apiConfigProvider.component';
34
import { renderComponentWithRouterProvider } from '../../testUtils';
45
import SystemTypes from './systemTypes.component';
56

67
describe('SystemTypes', () => {
78
let user: UserEvent;
89
const createView = () => {
9-
return renderComponentWithRouterProvider(<SystemTypes />);
10+
return renderComponentWithRouterProvider(
11+
<APIConfigProvider>
12+
<SystemTypes />
13+
</APIConfigProvider>
14+
);
1015
};
1116

1217
beforeEach(() => {
@@ -16,16 +21,16 @@ describe('SystemTypes', () => {
1621
it('renders table correctly', async () => {
1722
const view = createView();
1823

24+
await waitFor(() => {
25+
expect(screen.getByText('Storage')).toBeInTheDocument();
26+
});
27+
1928
await waitFor(
2029
() => {
2130
expect(screen.queryByRole('progressbar')).not.toBeInTheDocument();
2231
},
2332
{ timeout: 10000 }
2433
);
25-
26-
await waitFor(() => {
27-
expect(screen.getByText('Storage')).toBeInTheDocument();
28-
});
2934
expect(view.asFragment()).toMatchSnapshot();
3035
});
3136

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import type { RenderResult } from '@testing-library/react';
2+
import { screen, waitFor } from '@testing-library/react';
3+
import { http, HttpResponse } from 'msw';
4+
import * as React from 'react';
5+
import APIConfigProvider, {
6+
APISettingsContext,
7+
} from './apiConfigProvider.component';
8+
import { server } from './mocks/server';
9+
import { renderComponentWithRouterProvider } from './testUtils';
10+
11+
const APIConfigTest: React.FC = (): React.ReactElement => {
12+
const settings = React.useContext(APISettingsContext);
13+
14+
// Return the settings as a string to inspect later in tests.
15+
return <div data-testid="settings">{JSON.stringify(settings)}</div>;
16+
};
17+
18+
describe('APIConfigProvider', () => {
19+
beforeEach(() => {
20+
global.document.dispatchEvent = vi.fn();
21+
global.CustomEvent = vi.fn();
22+
});
23+
24+
afterEach(() => {
25+
vi.clearAllMocks();
26+
});
27+
28+
// Create a wrapper for our settings tests.
29+
const renderComponent = (): RenderResult =>
30+
renderComponentWithRouterProvider(
31+
<APIConfigProvider>
32+
<APIConfigTest />
33+
</APIConfigProvider>
34+
);
35+
36+
it('settings are loaded (with spares)', async () => {
37+
renderComponent();
38+
39+
// Preloader is in a loading state when ConfigProvider is
40+
// loading the configuration.
41+
expect(screen.getByText('Loading...')).toBeInTheDocument();
42+
43+
await waitFor(() => {
44+
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
45+
});
46+
expect(screen.getByTestId('settings')).toBeInTheDocument();
47+
expect(screen.getByTestId('settings')).toHaveTextContent(
48+
JSON.stringify({
49+
spares: {
50+
sparesFilterState:
51+
'?state=N4IgxgYiBcDaoEsAmMQGcCeaAuBTAtgHTYYAOuhAbgIYA2ArriADQg0NNygnmo4BOCAHYBzFmzqNUAZWwB7ftRFMAvgF11KoA',
52+
sparesColumnsFilters: {
53+
cF: [
54+
{
55+
id: 'system.type.value',
56+
value: [{ type: 'string', value: 'Storage' }],
57+
},
58+
],
59+
},
60+
isLoading: false,
61+
sparesDefinition: { system_types: [{ id: '1', value: 'Storage' }] },
62+
},
63+
})
64+
);
65+
});
66+
67+
it('settings are loaded (without spares)', async () => {
68+
server.use(
69+
http.get('/v1/settings/spares-definition', () => {
70+
return HttpResponse.json({ system_types: [] }, { status: 200 });
71+
})
72+
);
73+
renderComponent();
74+
75+
// Preloader is in a loading state when ConfigProvider is
76+
// loading the configuration.
77+
expect(screen.getByText('Loading...')).toBeInTheDocument();
78+
79+
await waitFor(() => {
80+
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
81+
});
82+
expect(screen.getByTestId('settings')).toBeInTheDocument();
83+
expect(screen.getByTestId('settings')).toHaveTextContent(
84+
JSON.stringify({})
85+
);
86+
});
87+
88+
it('settings are loaded (without spares 204 no content)', async () => {
89+
server.use(
90+
http.get('/v1/settings/spares-definition', () => {
91+
return HttpResponse.json(undefined, { status: 204 });
92+
})
93+
);
94+
renderComponent();
95+
96+
// Preloader is in a loading state when ConfigProvider is
97+
// loading the configuration.
98+
expect(screen.getByText('Loading...')).toBeInTheDocument();
99+
100+
await waitFor(() => {
101+
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
102+
});
103+
expect(screen.getByTestId('settings')).toBeInTheDocument();
104+
expect(screen.getByTestId('settings')).toHaveTextContent(
105+
JSON.stringify({})
106+
);
107+
});
108+
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import React from 'react';
2+
import { SparesFilterStateType } from './app.types';
3+
import Preloader from './preloader/preloader.component';
4+
5+
import { useSparesFilterState } from './utils';
6+
7+
interface APISettings {
8+
spares?: SparesFilterStateType;
9+
}
10+
11+
export const APISettingsContext = React.createContext<APISettings>({});
12+
13+
function APIConfigProvider({ children }: { children: React.ReactNode }) {
14+
const [loading, setLoading] = React.useState(true);
15+
const [settings, setSettings] = React.useState<APISettings>({});
16+
17+
const sparesInfo = useSparesFilterState();
18+
19+
React.useEffect(() => {
20+
const updateConfigurationState = async () => {
21+
const isSparesDefinitionDefined =
22+
sparesInfo.sparesDefinition !== '' &&
23+
sparesInfo.sparesDefinition.system_types.length !== 0;
24+
25+
if (!sparesInfo.isLoading) {
26+
setLoading(false);
27+
setSettings({
28+
spares: isSparesDefinitionDefined ? sparesInfo : undefined,
29+
});
30+
}
31+
};
32+
33+
updateConfigurationState();
34+
}, [sparesInfo]);
35+
36+
return (
37+
<Preloader loading={loading}>
38+
<APISettingsContext.Provider value={settings}>
39+
{children}
40+
</APISettingsContext.Provider>
41+
</Preloader>
42+
);
43+
}
44+
45+
export default APIConfigProvider;

src/app.types.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import type { Body, Meta } from '@uppy/core';
2+
import { MRT_ColumnFiltersState } from 'material-react-table';
23
import {
34
CatalogueCategory,
45
CatalogueItem,
56
Item,
67
ItemPost,
8+
SparesDefinition,
79
System,
810
type APIImage,
911
type ObjectFileUploadMetadata,
@@ -224,3 +226,12 @@ export interface AdvancedSerialNumberOptionsType {
224226
export interface UppyImageUploadResponse extends APIImage, Body {}
225227

226228
export interface UppyUploadMetadata extends ObjectFileUploadMetadata, Meta {}
229+
230+
// --------------------------------- SPARES -----------------------------------------------------------
231+
232+
export interface SparesFilterStateType {
233+
sparesDefinition: '' | SparesDefinition;
234+
sparesFilterState: string;
235+
sparesColumnsFilters: { cF: MRT_ColumnFiltersState };
236+
isLoading: boolean;
237+
}

0 commit comments

Comments
 (0)