@thepalaceproject/circulation-admin 1.31.0 → 1.32.0-post.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -151,5 +151,5 @@
151
151
  "*.{js,jsx,ts,tsx,css,md}": "prettier --write",
152
152
  "*.{js,css,md}": "prettier --write"
153
153
  },
154
- "version": "1.31.0"
154
+ "version": "1.32.0-post.3"
155
155
  }
@@ -1,6 +1,5 @@
1
1
  import { renderHook } from "@testing-library/react-hooks";
2
2
  import { componentWithProviders } from "../testUtils/withProviders";
3
- import { ContextProviderProps } from "../../../src/components/ContextProvider";
4
3
  import { ConfigurationSettings, FeatureFlags } from "../../../src/interfaces";
5
4
  import {
6
5
  useMayRequestInventoryReports,
@@ -12,13 +11,13 @@ const setupWrapper = ({
12
11
  roles,
13
12
  featureFlags,
14
13
  }: Partial<ConfigurationSettings>) => {
15
- const contextProviderProps: ContextProviderProps = {
14
+ const appConfigSettings: Partial<ConfigurationSettings> = {
16
15
  featureFlags,
17
16
  roles,
18
17
  email: "email",
19
18
  csrfToken: "token",
20
19
  };
21
- return componentWithProviders({ contextProviderProps });
20
+ return componentWithProviders({ appConfigSettings });
22
21
  };
23
22
 
24
23
  describe("Business rules for role-based access", () => {
@@ -3,8 +3,7 @@ import { screen, fireEvent } from "@testing-library/react";
3
3
  import "@testing-library/jest-dom";
4
4
 
5
5
  import CirculationEventsDownload from "../../../src/components/CirculationEventsDownload";
6
- import { ContextProviderProps } from "../../../src/components/ContextProvider";
7
- import { FeatureFlags } from "../../../src/interfaces";
6
+ import { ConfigurationSettings, FeatureFlags } from "../../../src/interfaces";
8
7
  import { defaultFeatureFlags } from "../../../src/utils/featureFlags";
9
8
  import { renderWithProviders } from "../testUtils/withProviders";
10
9
 
@@ -14,13 +13,13 @@ describe("CirculationEventsDownload", () => {
14
13
  ...defaultFeatureFlags,
15
14
  showCircEventsDownload: false,
16
15
  };
17
- const contextProviderProps: Partial<ContextProviderProps> = {
16
+ const appConfigSettings: Partial<ConfigurationSettings> = {
18
17
  featureFlags,
19
18
  };
20
19
  const { container } = renderWithProviders(
21
20
  <CirculationEventsDownload library="testlib" />,
22
21
  {
23
- contextProviderProps,
22
+ appConfigSettings,
24
23
  }
25
24
  );
26
25
  expect(container).toBeEmptyDOMElement();
@@ -32,13 +31,13 @@ describe("CirculationEventsDownload", () => {
32
31
  showCircEventsDownload: true,
33
32
  };
34
33
  const libraryProp = "testlib";
35
- const contextProviderProps: Partial<ContextProviderProps> = {
34
+ const appConfigSettings: Partial<ConfigurationSettings> = {
36
35
  featureFlags,
37
36
  };
38
37
 
39
38
  beforeEach(() => {
40
39
  renderWithProviders(<CirculationEventsDownload library={libraryProp} />, {
41
- contextProviderProps,
40
+ appConfigSettings,
42
41
  });
43
42
  });
44
43
 
@@ -6,6 +6,7 @@ import { http, HttpResponse } from "msw";
6
6
  import CustomLists from "../../../src/components/CustomLists";
7
7
  import renderWithContext from "../testUtils/renderWithContext";
8
8
  import buildStore from "../../../src/store";
9
+ import { ConfigurationSettings } from "../../../src/interfaces";
9
10
 
10
11
  describe("CustomLists", () => {
11
12
  // Stub scrollTo, since a component in the render tree will try to call it, and it is not
@@ -29,7 +30,7 @@ describe("CustomLists", () => {
29
30
  it("adds filters when new filter values are entered", async () => {
30
31
  const user = userEvent.setup();
31
32
 
32
- const contextProviderProps = {
33
+ const appConfigSettings: Partial<ConfigurationSettings> = {
33
34
  csrfToken: "",
34
35
  featureFlags: {},
35
36
  roles: [{ role: "system" }],
@@ -42,7 +43,7 @@ describe("CustomLists", () => {
42
43
  library="testlib"
43
44
  store={buildStore()}
44
45
  />,
45
- contextProviderProps
46
+ appConfigSettings
46
47
  );
47
48
 
48
49
  await user.click(screen.getByRole("textbox", { name: "filter value" }));
@@ -69,7 +70,7 @@ describe("CustomLists", () => {
69
70
  it("replaces the existing filters when adding a new filter when the clear filters checkbox is checked", async () => {
70
71
  const user = userEvent.setup();
71
72
 
72
- const contextProviderProps = {
73
+ const appConfigSettings: Partial<ConfigurationSettings> = {
73
74
  csrfToken: "",
74
75
  featureFlags: {},
75
76
  roles: [{ role: "system" }],
@@ -82,7 +83,7 @@ describe("CustomLists", () => {
82
83
  library="testlib"
83
84
  store={buildStore()}
84
85
  />,
85
- contextProviderProps
86
+ appConfigSettings
86
87
  );
87
88
 
88
89
  await user.click(screen.getByRole("textbox", { name: "filter value" }));
@@ -121,7 +122,7 @@ describe("CustomLists", () => {
121
122
 
122
123
  const user = userEvent.setup();
123
124
 
124
- const contextProviderProps = {
125
+ const appConfigSettings: Partial<ConfigurationSettings> = {
125
126
  csrfToken: "",
126
127
  featureFlags: {},
127
128
  roles: [{ role: "system" }],
@@ -134,7 +135,7 @@ describe("CustomLists", () => {
134
135
  library="testlib"
135
136
  store={buildStore()}
136
137
  />,
137
- contextProviderProps
138
+ appConfigSettings
138
139
  );
139
140
 
140
141
  await user.click(screen.getByRole("textbox", { name: "filter value" }));
@@ -165,7 +166,7 @@ describe("CustomLists", () => {
165
166
 
166
167
  const user = userEvent.setup();
167
168
 
168
- const contextProviderProps = {
169
+ const appConfigSettings: Partial<ConfigurationSettings> = {
169
170
  csrfToken: "",
170
171
  featureFlags: {},
171
172
  roles: [{ role: "system" }],
@@ -178,7 +179,7 @@ describe("CustomLists", () => {
178
179
  library="testlib"
179
180
  store={buildStore()}
180
181
  />,
181
- contextProviderProps
182
+ appConfigSettings
182
183
  );
183
184
 
184
185
  await userEvent.selectOptions(
@@ -3,12 +3,13 @@ import { screen } from "@testing-library/react";
3
3
  import userEvent from "@testing-library/user-event";
4
4
  import IndividualAdminEditForm from "../../../src/components/IndividualAdminEditForm";
5
5
  import renderWithContext from "../testUtils/renderWithContext";
6
+ import { ConfigurationSettings } from "../../../src/interfaces";
6
7
 
7
8
  describe("IndividualAdminEditForm", () => {
8
9
  it("clears the role checkboxes after save", async () => {
9
10
  const user = userEvent.setup();
10
11
 
11
- const contextProviderProps = {
12
+ const appConfigSettings: Partial<ConfigurationSettings> = {
12
13
  csrfToken: "",
13
14
  featureFlags: {},
14
15
  roles: [
@@ -45,7 +46,7 @@ describe("IndividualAdminEditForm", () => {
45
46
 
46
47
  const { rerender } = renderWithContext(
47
48
  <IndividualAdminEditForm {...props} />,
48
- contextProviderProps
49
+ appConfigSettings
49
50
  );
50
51
 
51
52
  const systemAdminCheckbox = screen.getByRole("checkbox", {
@@ -359,7 +359,7 @@ describe("InventoryReportRequestModal", () => {
359
359
  onHide={onHide}
360
360
  library={LIBRARY}
361
361
  />,
362
- { queryClient, contextProviderProps: EMAIL_CONTEXT_PROVIDER_PROPS }
362
+ { queryClient, appConfigSettings: EMAIL_CONTEXT_PROVIDER_PROPS }
363
363
  );
364
364
  let modalBody = getByRole("document").querySelector(".modal-body");
365
365
  expect(modalBody).toHaveTextContent(
@@ -373,7 +373,7 @@ describe("InventoryReportRequestModal", () => {
373
373
  onHide={onHide}
374
374
  library={LIBRARY}
375
375
  />,
376
- { queryClient, contextProviderProps: NO_EMAIL_CONTEXT_PROVIDER_PROPS }
376
+ { queryClient, appConfigSettings: NO_EMAIL_CONTEXT_PROVIDER_PROPS }
377
377
  ));
378
378
  modalBody = getByRole("document").querySelector(".modal-body");
379
379
  expect(modalBody).toHaveTextContent(
@@ -6,8 +6,6 @@ import {
6
6
  componentWithProviders,
7
7
  renderWithProviders,
8
8
  } from "../testUtils/withProviders";
9
- import { ContextProviderProps } from "../../../src/components/ContextProvider";
10
-
11
9
  import {
12
10
  statisticsApiResponseData,
13
11
  testLibraryKey as sampleLibraryKey,
@@ -23,6 +21,11 @@ import { renderHook } from "@testing-library/react-hooks";
23
21
  import { FetchErrorData } from "@thepalaceproject/web-opds-client/lib/interfaces";
24
22
  import { store } from "../../../src/store";
25
23
  import { api } from "../../../src/features/api/apiSlice";
24
+ import {
25
+ AdminRoleData,
26
+ ConfigurationSettings,
27
+ RolesData,
28
+ } from "../../../src/interfaces";
26
29
 
27
30
  const normalizedData = normalizeStatistics(statisticsApiResponseData);
28
31
 
@@ -292,9 +295,9 @@ describe("Dashboard Statistics", () => {
292
295
  });
293
296
 
294
297
  describe("shows the correct UI with/out sysadmin role", () => {
295
- const systemAdmin = [{ role: "system" }];
296
- const managerAll = [{ role: "manager-all" }];
297
- const librarianAll = [{ role: "librarian-all" }];
298
+ const systemAdmin: AdminRoleData[] = [{ role: "system" }];
299
+ const managerAll: AdminRoleData[] = [{ role: "manager-all" }];
300
+ const librarianAll: AdminRoleData[] = [{ role: "librarian-all" }];
298
301
 
299
302
  const collectionNames = [
300
303
  "New BiblioBoard Test",
@@ -305,13 +308,13 @@ describe("Dashboard Statistics", () => {
305
308
  ];
306
309
 
307
310
  it("tests BarChart component", () => {
308
- const contextProviderProps: Partial<ContextProviderProps> = {
311
+ const appConfigSettings: Partial<ConfigurationSettings> = {
309
312
  roles: systemAdmin,
310
313
  dashboardCollectionsBarChart: { width: 800 },
311
314
  };
312
315
  const { container, getByRole } = renderWithProviders(
313
316
  <Stats library={sampleLibraryKey} />,
314
- { contextProviderProps }
317
+ { appConfigSettings }
315
318
  );
316
319
 
317
320
  const collectionsHeading = getByRole("heading", {
@@ -335,14 +338,11 @@ describe("Dashboard Statistics", () => {
335
338
 
336
339
  it("shows collection bar chart for sysadmins, but list for others", () => {
337
340
  // We'll use this function to test multiple scenarios.
338
- const testFor = (
339
- expectBarChart: boolean,
340
- roles: { role: string; library?: string }[]
341
- ) => {
342
- const contextProviderProps: Partial<ContextProviderProps> = { roles };
341
+ const testFor = (expectBarChart: boolean, roles: AdminRoleData[]) => {
342
+ const appConfigSettings: Partial<ConfigurationSettings> = { roles };
343
343
  const { container, getByRole } = renderWithProviders(
344
344
  <Stats library={sampleLibraryKey} />,
345
- { contextProviderProps }
345
+ { appConfigSettings }
346
346
  );
347
347
 
348
348
  const collectionsHeading = getByRole("heading", {
@@ -380,11 +380,8 @@ describe("Dashboard Statistics", () => {
380
380
  const fakeQuickSightHref = "https://example.com/fakeQS";
381
381
 
382
382
  // We'll use this function to test multiple scenarios.
383
- const renderFor = (
384
- onlySysadmins: boolean,
385
- roles: { role: string; library?: string }[]
386
- ) => {
387
- const contextProviderProps: Partial<ContextProviderProps> = {
383
+ const renderFor = (onlySysadmins: boolean, roles: AdminRoleData[]) => {
384
+ const appConfigSettings: Partial<ConfigurationSettings> = {
388
385
  featureFlags: { reportsOnlyForSysadmins: onlySysadmins },
389
386
  roles,
390
387
  quicksightPagePath: fakeQuickSightHref,
@@ -395,7 +392,7 @@ describe("Dashboard Statistics", () => {
395
392
  queryByRole,
396
393
  queryByText,
397
394
  } = renderWithProviders(<Stats library={sampleLibraryKey} />, {
398
- contextProviderProps,
395
+ appConfigSettings,
399
396
  });
400
397
 
401
398
  // We should always render a Usage reports group when a library is specified.
@@ -439,11 +436,8 @@ describe("Dashboard Statistics", () => {
439
436
  const fakeQuickSightHref = "https://example.com/fakeQS";
440
437
 
441
438
  // We'll use this function to test multiple scenarios.
442
- const renderFor = (
443
- onlySysadmins: boolean,
444
- roles: { role: string; library?: string }[]
445
- ) => {
446
- const contextProviderProps: Partial<ContextProviderProps> = {
439
+ const renderFor = (onlySysadmins: boolean, roles: AdminRoleData[]) => {
440
+ const appConfigSettings: Partial<ConfigurationSettings> = {
447
441
  featureFlags: { quicksightOnlyForSysadmins: onlySysadmins },
448
442
  roles,
449
443
  quicksightPagePath: fakeQuickSightHref,
@@ -454,7 +448,7 @@ describe("Dashboard Statistics", () => {
454
448
  queryByRole,
455
449
  queryByText,
456
450
  } = renderWithProviders(<Stats library={sampleLibraryKey} />, {
457
- contextProviderProps,
451
+ appConfigSettings,
458
452
  });
459
453
 
460
454
  // We should always render a Usage reports group when a library is specified.
@@ -6,11 +6,15 @@ import {
6
6
  useAppFeatureFlags,
7
7
  useCsrfToken,
8
8
  useTermsOfService,
9
- useSupportContactUrl,
9
+ useSupportContact,
10
+ SupportContactLink,
10
11
  } from "../../../src/context/appContext";
11
12
  import { componentWithProviders } from "../testUtils/withProviders";
12
- import { ContextProviderProps } from "../../../src/components/ContextProvider";
13
- import { FeatureFlags } from "../../../src/interfaces";
13
+ import {
14
+ AdminRoleData,
15
+ ConfigurationSettings,
16
+ FeatureFlags,
17
+ } from "../../../src/interfaces";
14
18
 
15
19
  // TODO: These tests may need to be adjusted in the future.
16
20
  // Currently, an AppContext.Provider is injected into the component tree
@@ -26,23 +30,28 @@ describe("AppContext", () => {
26
30
  testTrue: true,
27
31
  testFalse: false,
28
32
  };
29
- const expectedRoles = [{ role: "system" }];
33
+ const expectedRoles: AdminRoleData[] = [{ role: "system" }];
30
34
  const expectedTermsOfService = {
31
35
  text: "Terms of Service",
32
36
  href: "/terms-of-service",
33
37
  };
34
- const expectedSupportContactUrl = "helpdesk@example.com";
38
+ const emailAddress = "helpdesk@example.com";
39
+ const expectedSupportContact: SupportContactLink = {
40
+ href: `mailto:${emailAddress}?subject=Support+request`,
41
+ text: `Email ${emailAddress}.`,
42
+ };
35
43
 
36
- const contextProviderProps: ContextProviderProps = {
44
+ const appConfigSettings: Partial<ConfigurationSettings> = {
37
45
  csrfToken: expectedCsrfToken,
38
46
  featureFlags: expectedFeatureFlags,
39
47
  roles: expectedRoles,
40
48
  email: expectedEmail,
41
49
  tos_link_text: expectedTermsOfService.text,
42
50
  tos_link_href: expectedTermsOfService.href,
43
- support_contact_url: expectedSupportContactUrl,
51
+ support_contact_url: "deprecated and should be overridden",
52
+ supportContactUrl: expectedSupportContact.href,
44
53
  };
45
- const wrapper = componentWithProviders({ contextProviderProps });
54
+ const wrapper = componentWithProviders({ appConfigSettings });
46
55
 
47
56
  it("provides useAppContext context hook", () => {
48
57
  const { result } = renderHook(() => useAppContext(), { wrapper });
@@ -51,7 +60,7 @@ describe("AppContext", () => {
51
60
  expect(value.admin.email).toEqual(expectedEmail);
52
61
  expect(value.admin.roles).toEqual(expectedRoles);
53
62
  expect(value.featureFlags).toEqual(expectedFeatureFlags);
54
- expect(value.support_contact_url).toEqual(expectedSupportContactUrl);
63
+ expect(value.supportContact).toEqual(expectedSupportContact);
55
64
  });
56
65
 
57
66
  it("provides useAppAdmin context hook", () => {
@@ -91,11 +100,14 @@ describe("AppContext", () => {
91
100
  expect(text).toEqual(expectedTermsOfService.text);
92
101
  expect(href).toEqual(expectedTermsOfService.href);
93
102
  });
94
- it("provides useSupportContactUrl context hook", () => {
95
- const { result } = renderHook(() => useSupportContactUrl(), {
103
+ it("provides useSupportContact context hook", () => {
104
+ const { result } = renderHook(() => useSupportContact(), {
96
105
  wrapper,
97
106
  });
98
- const supportContactUrl = result.current;
99
- expect(supportContactUrl).toEqual(expectedSupportContactUrl);
107
+ const supportContact = result.current;
108
+ expect(supportContact).toBeDefined();
109
+ const { href, text } = supportContact;
110
+ expect(href).toEqual(expectedSupportContact.href);
111
+ expect(text).toEqual(expectedSupportContact.text);
100
112
  });
101
113
  });
@@ -3,21 +3,23 @@ import { render, RenderOptions, RenderResult } from "@testing-library/react";
3
3
  import ContextProvider, {
4
4
  ContextProviderProps,
5
5
  } from "../../../src/components/ContextProvider";
6
+ import { ConfigurationSettings } from "../../../src/interfaces";
6
7
 
7
8
  /**
8
9
  * Renders a given React element, wrapped in a ContextProvider. The resulting rerender function is
9
10
  * also wrapped, so that rerenders will have the identical context.
10
11
  *
11
12
  * @param ui The element to render
12
- * @param contextProviderProps Props to pass to the ContextProvider wrapper
13
+ * @param config Props to pass to the ContextProvider wrapper
13
14
  * @param renderOptions Options to pass through to the RTL render function
14
15
  * @returns
15
16
  */
16
17
  export default function renderWithContext(
17
18
  ui: React.ReactElement,
18
- contextProviderProps: ContextProviderProps,
19
+ config: Partial<ConfigurationSettings>,
19
20
  renderOptions?: Omit<RenderOptions, "queries">
20
21
  ): RenderResult {
22
+ const contextProviderProps = { config } as ContextProviderProps;
21
23
  const renderResult = render(
22
24
  <ContextProvider {...contextProviderProps}>{ui}</ContextProvider>,
23
25
  renderOptions
@@ -7,10 +7,11 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
7
7
  import { render, RenderOptions, RenderResult } from "@testing-library/react";
8
8
  import { defaultFeatureFlags } from "../../../src/utils/featureFlags";
9
9
  import { store } from "../../../src/store";
10
+ import { ConfigurationSettings } from "../../../src/interfaces";
10
11
 
11
12
  export type TestProviderWrapperOptions = {
12
13
  reduxProviderProps?: ProviderProps;
13
- contextProviderProps?: Partial<ContextProviderProps>;
14
+ appConfigSettings?: Partial<ConfigurationSettings>;
14
15
  queryClient?: QueryClient;
15
16
  };
16
17
  export type TestRenderWrapperOptions = TestProviderWrapperOptions & {
@@ -21,9 +22,9 @@ export type TestRenderWrapperOptions = TestProviderWrapperOptions & {
21
22
  // be the same for both the Redux Provider and the ContextProvider.
22
23
  const defaultReduxStore = store;
23
24
 
24
- // The `csrfToken` context provider prop is required, so we provide
25
- // a default value here, so it can be easily merged with other props.
26
- const requiredContextProviderProps: ContextProviderProps = {
25
+ // Some config settings from the server are required, so we provide
26
+ // default values here, so they can be easily merged with other props.
27
+ const requiredAppConfigSettings: Partial<ConfigurationSettings> = {
27
28
  csrfToken: "",
28
29
  featureFlags: defaultFeatureFlags,
29
30
  };
@@ -34,7 +35,7 @@ const requiredContextProviderProps: ContextProviderProps = {
34
35
  *
35
36
  * @param {TestProviderWrapperOptions} options
36
37
  * @param options.reduxProviderProps Props to pass to the Redux `Provider` wrapper
37
- * @param {ContextProviderProps} options.contextProviderProps Props to pass to the ContextProvider wrapper
38
+ * @param {ConfigurationSettings} options.appConfigSettings
38
39
  * @param {QueryClient} options.queryClient A `tanstack/react-query` QueryClient
39
40
  * @returns {React.FunctionComponent} A React component that wraps children with our providers
40
41
  */
@@ -42,17 +43,14 @@ export const componentWithProviders = ({
42
43
  reduxProviderProps = {
43
44
  store: defaultReduxStore,
44
45
  },
45
- contextProviderProps = {
46
- csrfToken: "",
47
- featureFlags: defaultFeatureFlags,
48
- },
46
+ appConfigSettings = {},
49
47
  queryClient = new QueryClient(),
50
48
  }: TestProviderWrapperOptions = {}): React.FunctionComponent => {
49
+ const config = { ...requiredAppConfigSettings, ...appConfigSettings };
51
50
  const effectiveContextProviderProps = {
52
- ...requiredContextProviderProps,
53
- ...contextProviderProps,
51
+ config: config as ConfigurationSettings,
54
52
  ...reduxProviderProps.store, // Context and Redux Provider stores must match.
55
- };
53
+ } as ContextProviderProps;
56
54
  const wrapper = ({ children }) => (
57
55
  <Provider {...reduxProviderProps}>
58
56
  <ContextProvider {...effectiveContextProviderProps}>