richie-education 2.34.0 → 2.34.1-dev2

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.
@@ -6,7 +6,7 @@ import fetchMock from 'fetch-mock';
6
6
  import { IntlProvider } from 'react-intl';
7
7
  import countries from 'i18n-iso-countries';
8
8
  import { QueryClientProvider } from '@tanstack/react-query';
9
- import { PropsWithChildren } from 'react';
9
+ import React, { PropsWithChildren } from 'react';
10
10
  import { CunninghamProvider } from '@openfun/cunningham-react';
11
11
  import userEvent, { UserEvent } from '@testing-library/user-event';
12
12
  import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
@@ -1,4 +1,4 @@
1
- import { Children, forwardRef, useEffect, useState } from 'react';
1
+ import { Children, useEffect, useState, RefAttributes } from 'react';
2
2
  import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
3
3
  import { Button } from '@openfun/cunningham-react';
4
4
  import AddressForm, { type AddressFormValues } from 'components/AddressesManagement/AddressForm';
@@ -7,7 +7,6 @@ import { Icon, IconTypeEnum } from 'components/Icon';
7
7
  import RegisteredAddress from 'components/RegisteredAddress';
8
8
  import { useAddressesManagement } from 'hooks/useAddressesManagement';
9
9
  import type * as Joanie from 'types/Joanie';
10
- import { Address } from 'types/Joanie';
11
10
  import { Maybe } from 'types/utils';
12
11
 
13
12
  // constant used as `address.id` for local address
@@ -106,147 +105,142 @@ export const messages = defineMessages({
106
105
  },
107
106
  });
108
107
 
109
- interface AddressesManagementProps {
108
+ interface AddressesManagementProps extends RefAttributes<HTMLDivElement> {
110
109
  handleClose: () => void;
111
110
  selectAddress: (address: Joanie.Address) => void;
112
111
  }
113
112
 
114
- const AddressesManagement = forwardRef<HTMLDivElement, AddressesManagementProps>(
115
- ({ handleClose, selectAddress }, ref) => {
116
- const intl = useIntl();
117
- const [editedAddress, setEditedAddress] = useState<Maybe<Joanie.Address>>();
118
- const {
119
- methods: { setError, create, update, remove, promote },
120
- states: { error },
121
- ...addresses
122
- } = useAddressesManagement();
113
+ const AddressesManagement = ({ handleClose, selectAddress, ref }: AddressesManagementProps) => {
114
+ const intl = useIntl();
115
+ const [editedAddress, setEditedAddress] = useState<Maybe<Joanie.Address>>();
116
+ const {
117
+ methods: { setError, create, update, remove, promote },
118
+ states: { error },
119
+ ...addresses
120
+ } = useAddressesManagement();
123
121
 
124
- /**
125
- * Sort addresses ascending by title according to the locale
126
- *
127
- * @param {Joanie.Address} a
128
- * @param {Joanie.Address} b
129
- * @returns {Joanie.Address[]} Sorted addresses ascending by title
130
- */
131
- const sortAddressByTitleAsc = (a: Joanie.Address, b: Joanie.Address) => {
132
- return a.title.localeCompare(b.title, [intl.locale, intl.defaultLocale]);
133
- };
122
+ /**
123
+ * Sort addresses ascending by title according to the locale
124
+ *
125
+ * @param {Joanie.Address} a
126
+ * @param {Joanie.Address} b
127
+ * @returns {Joanie.Address[]} Sorted addresses ascending by title
128
+ */
129
+ const sortAddressByTitleAsc = (a: Joanie.Address, b: Joanie.Address) => {
130
+ return a.title.localeCompare(b.title, [intl.locale, intl.defaultLocale]);
131
+ };
134
132
 
135
- /**
136
- * update `selectedAddress` state with the address provided
137
- * then close the address management form
138
- *
139
- * @param {Joanie.Address} address
140
- */
141
- const handleSelect = (address: Joanie.Address) => {
142
- setError(undefined);
143
- selectAddress(address);
144
- handleClose();
145
- };
133
+ /**
134
+ * update `selectedAddress` state with the address provided
135
+ * then close the address management form
136
+ *
137
+ * @param {Joanie.Address} address
138
+ */
139
+ const handleSelect = (address: Joanie.Address) => {
140
+ setError(undefined);
141
+ selectAddress(address);
142
+ handleClose();
143
+ };
146
144
 
147
- /**
148
- * Create a new address according to form values
149
- * then update `selectedAddress` state with this new one.
150
- * If `save` checkbox input is checked, the address is persisted
151
- * otherwise it is only stored through the `selectedAddress` state.
152
- *
153
- * @param {AddressFormValues} formValues address fields to update
154
- */
155
- const handleCreate = async ({ save, ...address }: AddressFormValues) => {
156
- if (save) {
157
- await create(address, { onSuccess: handleSelect });
158
- } else {
159
- handleSelect({
160
- id: LOCAL_BILLING_ADDRESS_ID,
161
- is_main: false,
162
- ...address,
163
- });
164
- }
165
- };
145
+ /**
146
+ * Create a new address according to form values
147
+ * then update `selectedAddress` state with this new one.
148
+ * If `save` checkbox input is checked, the address is persisted
149
+ * otherwise it is only stored through the `selectedAddress` state.
150
+ *
151
+ * @param {AddressFormValues} formValues address fields to update
152
+ */
153
+ const handleCreate = async ({ save, ...address }: AddressFormValues) => {
154
+ if (save) {
155
+ await create(address, { onSuccess: handleSelect });
156
+ } else {
157
+ handleSelect({
158
+ id: LOCAL_BILLING_ADDRESS_ID,
159
+ is_main: false,
160
+ ...address,
161
+ });
162
+ }
163
+ };
166
164
 
167
- /**
168
- * Update the `editedAddress` with new values provided as argument
169
- * then clear `editedAddress` state if request succeeded.
170
- *
171
- * @param {AddressFormValues} formValues address fields to update
172
- */
173
- const handleUpdate = async ({ save, ...newAddress }: AddressFormValues) => {
174
- update(
175
- {
176
- ...editedAddress!,
177
- ...newAddress,
178
- },
179
- {
180
- onSuccess: () => setEditedAddress(undefined),
181
- },
182
- );
183
- };
165
+ /**
166
+ * Update the `editedAddress` with new values provided as argument
167
+ * then clear `editedAddress` state if request succeeded.
168
+ *
169
+ * @param {AddressFormValues} formValues address fields to update
170
+ */
171
+ const handleUpdate = async ({ save, ...newAddress }: AddressFormValues) => {
172
+ update(
173
+ {
174
+ ...editedAddress!,
175
+ ...newAddress,
176
+ },
177
+ {
178
+ onSuccess: () => setEditedAddress(undefined),
179
+ },
180
+ );
181
+ };
184
182
 
185
- useEffect(() => {
186
- setError(undefined);
187
- if (editedAddress) {
188
- document.querySelector<HTMLElement>('[name="address-form"] input')?.focus();
189
- }
190
- }, [editedAddress]);
183
+ useEffect(() => {
184
+ setError(undefined);
185
+ if (editedAddress) {
186
+ document.querySelector<HTMLElement>('[name="address-form"] input')?.focus();
187
+ }
188
+ }, [editedAddress]);
191
189
 
192
- return (
193
- <div className="AddressesManagement" ref={ref}>
194
- <Button
195
- className="AddressesManagement__closeButton"
196
- color="tertiary"
197
- size="small"
198
- onClick={handleClose}
199
- >
200
- <Icon name={IconTypeEnum.CHEVRON_LEFT_OUTLINE} className="button__icon" />
201
- <FormattedMessage {...messages.closeButton} />
202
- </Button>
203
- {error && <Banner message={error} type={BannerType.ERROR} rounded />}
204
- {addresses.items.length > 0 ? (
205
- <section className="address-registered">
206
- <header>
207
- <h2 className="h5">
208
- <FormattedMessage {...messages.registeredAddresses} />
209
- </h2>
210
- </header>
211
- <ul className="registered-addresses-list">
212
- {Children.toArray(
213
- addresses.items
214
- .sort(sortAddressByTitleAsc)
215
- .map((address) => (
216
- <RegisteredAddress
217
- address={address}
218
- edit={setEditedAddress}
219
- promote={promote}
220
- remove={remove}
221
- select={handleSelect}
222
- />
223
- )),
224
- )}
225
- </ul>
226
- </section>
227
- ) : null}
228
- <section className={`address-form ${editedAddress ? 'address-form--highlighted' : ''}`}>
190
+ return (
191
+ <div className="AddressesManagement" ref={ref}>
192
+ <Button
193
+ className="AddressesManagement__closeButton"
194
+ color="tertiary"
195
+ size="small"
196
+ onClick={handleClose}
197
+ >
198
+ <Icon name={IconTypeEnum.CHEVRON_LEFT_OUTLINE} className="button__icon" />
199
+ <FormattedMessage {...messages.closeButton} />
200
+ </Button>
201
+ {error && <Banner message={error} type={BannerType.ERROR} rounded />}
202
+ {addresses.items.length > 0 ? (
203
+ <section className="address-registered">
229
204
  <header>
230
205
  <h2 className="h5">
231
- {editedAddress ? (
232
- <FormattedMessage
233
- {...messages.editAddress}
234
- values={{ title: editedAddress.title }}
235
- />
236
- ) : (
237
- <FormattedMessage {...messages.addAddress} />
238
- )}
206
+ <FormattedMessage {...messages.registeredAddresses} />
239
207
  </h2>
240
208
  </header>
241
- <AddressForm
242
- address={editedAddress}
243
- handleReset={() => setEditedAddress(undefined)}
244
- onSubmit={editedAddress ? handleUpdate : handleCreate}
245
- />
209
+ <ul className="registered-addresses-list">
210
+ {Children.toArray(
211
+ addresses.items
212
+ .sort(sortAddressByTitleAsc)
213
+ .map((address) => (
214
+ <RegisteredAddress
215
+ address={address}
216
+ edit={setEditedAddress}
217
+ promote={promote}
218
+ remove={remove}
219
+ select={handleSelect}
220
+ />
221
+ )),
222
+ )}
223
+ </ul>
246
224
  </section>
247
- </div>
248
- );
249
- },
250
- );
225
+ ) : null}
226
+ <section className={`address-form ${editedAddress ? 'address-form--highlighted' : ''}`}>
227
+ <header>
228
+ <h2 className="h5">
229
+ {editedAddress ? (
230
+ <FormattedMessage {...messages.editAddress} values={{ title: editedAddress.title }} />
231
+ ) : (
232
+ <FormattedMessage {...messages.addAddress} />
233
+ )}
234
+ </h2>
235
+ </header>
236
+ <AddressForm
237
+ address={editedAddress}
238
+ handleReset={() => setEditedAddress(undefined)}
239
+ onSubmit={editedAddress ? handleUpdate : handleCreate}
240
+ />
241
+ </section>
242
+ </div>
243
+ );
244
+ };
251
245
 
252
246
  export default AddressesManagement;
@@ -134,7 +134,7 @@ const ContractFrameContent = ({
134
134
  const [signatureType, setSignatureType] = useState<SignatureType>();
135
135
  const [invitationLink, setInvitationLink] = useState<Maybe<string>>();
136
136
  const [error, setError] = useState<Maybe<string>>();
137
- const timeoutRef = useRef<NodeJS.Timeout>();
137
+ const timeoutRef = useRef<NodeJS.Timeout>(undefined);
138
138
 
139
139
  const setErrored = (e: string) => {
140
140
  setStep(ContractSteps.ERROR);
@@ -29,7 +29,7 @@ type IconContainerProps = {
29
29
  };
30
30
  const IconContainer = ({ name, enumKey }: IconContainerProps) => {
31
31
  const [showTooltip, setShowTooltip] = useState(false);
32
- const timeoutRef = useRef<NodeJS.Timeout>();
32
+ const timeoutRef = useRef<NodeJS.Timeout>(undefined);
33
33
  const ENUM_NAME = 'IconTypeEnum';
34
34
 
35
35
  const styleContainer: CSSProperties = {
@@ -17,7 +17,7 @@ const PayplugLightbox = ({
17
17
  onError,
18
18
  ...props
19
19
  }: PaymentInterfaceProps<PayplugPayment>) => {
20
- const ref = useRef<ReturnType<typeof setTimeout>>();
20
+ const ref = useRef<ReturnType<typeof setTimeout>>(undefined);
21
21
 
22
22
  /** type guard to check if the payment is a payment one click */
23
23
  const isPaidPayment = (p: PayplugPayment) => p?.is_paid === true;
@@ -47,7 +47,7 @@ const messages = defineMessages({
47
47
  });
48
48
 
49
49
  const SaleTunnelSavePaymentMethod = () => {
50
- const initialCreditCards = useRef<CreditCard[]>();
50
+ const initialCreditCards = useRef<CreditCard[]>([]);
51
51
  const [shouldPoll, setShouldPoll] = useState(false);
52
52
  const [payment, setPayment] = useState<Payment>();
53
53
  const [error, setError] = useState<string>();
@@ -71,7 +71,7 @@ const SaleTunnelSavePaymentMethod = () => {
71
71
  };
72
72
 
73
73
  const waitForNewCreditCard = () => {
74
- const initialIds = initialCreditCards.current!.map((cc) => cc.id);
74
+ const initialIds = initialCreditCards.current.map((cc) => cc.id);
75
75
  const newCard = creditCardsQuery.items.find((cc) => !initialIds.includes(cc.id));
76
76
 
77
77
  if (!newCard) return;
@@ -98,7 +98,7 @@ describe.each([
98
98
  expect(mockCheckArchive).not.toHaveBeenCalled();
99
99
  });
100
100
 
101
- it('should check if archive exist when a id is stored', async () => {
101
+ it('should check if archive exist when an id is stored', async () => {
102
102
  storeContractArchiveId({
103
103
  ...localStorageArchiveFilters,
104
104
  contractArchiveId: faker.string.uuid(),
@@ -115,7 +115,9 @@ describe.each([
115
115
  expect(mockCheckArchive).toHaveBeenCalledTimes(1);
116
116
  });
117
117
 
118
- expect(result.current.isPolling).toBe(false);
118
+ await waitFor(() => {
119
+ expect(result.current.isPolling).toBe(false);
120
+ });
119
121
  expect(result.current.isContractArchiveExists).toBe(true);
120
122
  });
121
123
 
@@ -25,7 +25,7 @@ const useCheckContractArchiveExist = (
25
25
  // stay null until fetched
26
26
  const [isContractArchiveExists, setIsContractArchiveExists] = useState<Nullable<boolean>>(null);
27
27
 
28
- const timeoutRef = useRef<NodeJS.Timeout>();
28
+ const timeoutRef = useRef<NodeJS.Timeout>(undefined);
29
29
 
30
30
  // This method will check if the archive exists on the server
31
31
  // option.polling === true will recursivly poll archive existence
@@ -41,6 +41,9 @@ describe('components/TeacherDashboardOrganizationCourseLoader', () => {
41
41
  beforeEach(() => {
42
42
  nbApiCalls = joanieSessionData.nbSessionApiRequest;
43
43
  });
44
+ afterEach(() => {
45
+ fetchMock.restore();
46
+ });
44
47
 
45
48
  it('should render', async () => {
46
49
  const organization = OrganizationFactory().one();
@@ -59,6 +62,10 @@ describe('components/TeacherDashboardOrganizationCourseLoader', () => {
59
62
  `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/course-product-relations/?product_type=credential&page=1&page_size=${perPage}`,
60
63
  mockPaginatedResponse(CourseProductRelationFactory().many(15), 15, false),
61
64
  );
65
+ fetchMock.get(
66
+ `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/contracts/?signature_state=half_signed&page=1`,
67
+ [],
68
+ );
62
69
 
63
70
  render(<TeacherDashboardOrganizationCourseLoader />, {
64
71
  routerOptions: {
@@ -70,6 +77,7 @@ describe('components/TeacherDashboardOrganizationCourseLoader', () => {
70
77
 
71
78
  nbApiCalls += 1; // course api call
72
79
  nbApiCalls += 1; // course-product-relations api call
80
+ nbApiCalls += 1; // contracts api call
73
81
  const calledUrls = fetchMock.calls().map((call) => call[0]);
74
82
  expect(calledUrls).toHaveLength(nbApiCalls);
75
83
  expect(calledUrls).toContain(
@@ -78,11 +86,6 @@ describe('components/TeacherDashboardOrganizationCourseLoader', () => {
78
86
  expect(calledUrls).toContain(
79
87
  `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/course-product-relations/?product_type=credential&page=1&page_size=${perPage}`,
80
88
  );
81
-
82
- fetchMock.get(
83
- `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/contracts/?signature_state=half_signed&page=1`,
84
- [],
85
- );
86
89
  await expectNoSpinner('Loading organization...');
87
90
 
88
91
  expect(
@@ -1,4 +1,4 @@
1
- import { PropsWithChildren } from 'react';
1
+ import { PropsWithChildren, ReactElement } from 'react';
2
2
  import { QueryClient } from '@tanstack/query-core';
3
3
  import { RenderOptions as TestingLibraryRenderOptions } from '@testing-library/react';
4
4
  import { Nullable } from 'types/utils';
@@ -19,7 +19,7 @@ interface QueryOptions {
19
19
  * @property queryOptions options to configure a custom client used by react-query for a test
20
20
  */
21
21
  export interface AppWrapperProps {
22
- wrapper?: Nullable<(props: PropsWithChildren<{ options?: AppWrapperProps }>) => JSX.Element>;
22
+ wrapper?: Nullable<(props: PropsWithChildren<{ options?: AppWrapperProps }>) => ReactElement>;
23
23
  intlOptions?: IntlWrapperProps;
24
24
  queryOptions?: QueryOptions;
25
25
  historyOptions?: History;
@@ -83,7 +83,7 @@ enum ComponentStates {
83
83
  export const OrderPaymentRetryModal = ({ installment, order, ...props }: Props) => {
84
84
  const intl = useIntl();
85
85
  const API = useJoanieApi();
86
- const timeoutRef = useRef<NodeJS.Timeout>();
86
+ const timeoutRef = useRef<NodeJS.Timeout>(undefined);
87
87
  const { methods: orderMethods } = useOrders(undefined, { enabled: false });
88
88
  const [payment, setPayment] = useState<Payment>();
89
89
  const [state, setState] = useState<ComponentStates>(ComponentStates.IDLE);
@@ -4,7 +4,7 @@ import { render } from 'utils/test/render';
4
4
  import { PresentationalAppWrapper } from 'utils/test/wrappers/PresentationalAppWrapper';
5
5
  import SearchBar from '.';
6
6
 
7
- describe('Dashbaord/components/SearchBar', () => {
7
+ describe('Dashboard/components/SearchBar', () => {
8
8
  it('should render', () => {
9
9
  render(<SearchBar onSubmit={jest.fn()} />, { wrapper: PresentationalAppWrapper });
10
10
  expect(screen.getByRole('textbox', { name: /Search/ })).toBeInTheDocument();
@@ -3,7 +3,7 @@ import { render } from 'utils/test/render';
3
3
  import { PresentationalAppWrapper } from 'utils/test/wrappers/PresentationalAppWrapper';
4
4
  import SearchResultsCount from '.';
5
5
 
6
- describe('Dashbaord/components/SearchResultsCount', () => {
6
+ describe('Dashboard/components/SearchResultsCount', () => {
7
7
  it('should render singular message', () => {
8
8
  render(<SearchResultsCount nbResults={1} />, {
9
9
  wrapper: PresentationalAppWrapper,
@@ -195,11 +195,7 @@ const CourseRunEnrollment: React.FC<CourseRunEnrollmentProps> = (props) => {
195
195
  step,
196
196
  },
197
197
  dispatch,
198
- ] = useReducer<React.Reducer<ReducerState, ReducerAction>, ReducerState>(
199
- reducer,
200
- initialState(user, props.courseRun, enrollmentIsActive),
201
- (s) => s,
202
- );
198
+ ] = useReducer(reducer, initialState(user, props.courseRun, enrollmentIsActive));
203
199
 
204
200
  const setEnroll = useCallback(
205
201
  async (isActive: boolean = true) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "richie-education",
3
- "version": "2.34.0",
3
+ "version": "2.34.1-dev2",
4
4
  "description": "A CMS to build learning portals for Open Education",
5
5
  "main": "sandbox/manage.py",
6
6
  "scripts": {
@@ -40,34 +40,34 @@
40
40
  "dependencies": {
41
41
  "@babel/core": "7.26.0",
42
42
  "@babel/plugin-syntax-dynamic-import": "7.8.3",
43
- "@babel/plugin-transform-modules-commonjs": "7.25.9",
43
+ "@babel/plugin-transform-modules-commonjs": "7.26.3",
44
44
  "@babel/preset-env": "7.26.0",
45
- "@babel/preset-react": "7.25.9",
45
+ "@babel/preset-react": "7.26.3",
46
46
  "@babel/preset-typescript": "7.26.0",
47
- "@faker-js/faker": "9.2.0",
48
- "@formatjs/cli": "6.3.11",
49
- "@formatjs/intl-relativetimeformat": "11.4.5",
47
+ "@faker-js/faker": "9.3.0",
48
+ "@formatjs/cli": "6.3.14",
49
+ "@formatjs/intl-relativetimeformat": "11.4.8",
50
50
  "@hookform/resolvers": "3.9.1",
51
51
  "@lyracom/embedded-form-glue": "1.4.2",
52
- "@openfun/cunningham-react": "2.9.4",
53
- "@openfun/cunningham-tokens": "2.1.1",
52
+ "@openfun/cunningham-react": "3.0.0",
53
+ "@openfun/cunningham-tokens": "2.2.0",
54
54
  "@sentry/browser": "8.42.0",
55
55
  "@sentry/types": "8.42.0",
56
- "@storybook/addon-actions": "8.4.6",
57
- "@storybook/addon-essentials": "8.4.6",
58
- "@storybook/addon-interactions": "8.4.6",
59
- "@storybook/addon-links": "8.4.6",
60
- "@storybook/react": "8.4.6",
61
- "@storybook/react-webpack5": "8.4.6",
62
- "@storybook/test": "8.4.6",
63
- "@tanstack/query-core": "5.62.1",
64
- "@tanstack/query-sync-storage-persister": "5.62.1",
65
- "@tanstack/react-query": "5.62.1",
66
- "@tanstack/react-query-devtools": "5.62.1",
67
- "@tanstack/react-query-persist-client": "5.62.1",
56
+ "@storybook/addon-actions": "8.4.7",
57
+ "@storybook/addon-essentials": "8.4.7",
58
+ "@storybook/addon-interactions": "8.4.7",
59
+ "@storybook/addon-links": "8.4.7",
60
+ "@storybook/react": "8.4.7",
61
+ "@storybook/react-webpack5": "8.4.7",
62
+ "@storybook/test": "8.4.7",
63
+ "@tanstack/query-core": "5.62.3",
64
+ "@tanstack/query-sync-storage-persister": "5.62.3",
65
+ "@tanstack/react-query": "5.62.3",
66
+ "@tanstack/react-query-devtools": "5.62.3",
67
+ "@tanstack/react-query-persist-client": "5.62.3",
68
68
  "@testing-library/dom": "10.4.0",
69
69
  "@testing-library/jest-dom": "6.6.3",
70
- "@testing-library/react": "16.0.1",
70
+ "@testing-library/react": "16.1.0",
71
71
  "@testing-library/user-event": "14.5.2",
72
72
  "@types/fetch-mock": "7.3.8",
73
73
  "@types/iframe-resizer": "3.5.13",
@@ -76,9 +76,9 @@
76
76
  "@types/lodash-es": "4.17.12",
77
77
  "@types/node-fetch": "2.6.12",
78
78
  "@types/query-string": "6.3.0",
79
- "@types/react": "18.3.12",
79
+ "@types/react": "19.0.1",
80
80
  "@types/react-autosuggest": "10.1.11",
81
- "@types/react-dom": "18.3.1",
81
+ "@types/react-dom": "19.0.1",
82
82
  "@types/react-modal": "3.16.3",
83
83
  "@typescript-eslint/eslint-plugin": "8.17.0",
84
84
  "@typescript-eslint/parser": "8.17.0",
@@ -96,12 +96,12 @@
96
96
  "eslint-config-prettier": "9.1.0",
97
97
  "eslint-import-resolver-webpack": "0.13.9",
98
98
  "eslint-plugin-compat": "6.0.1",
99
- "eslint-plugin-formatjs": "5.2.5",
99
+ "eslint-plugin-formatjs": "5.2.8",
100
100
  "eslint-plugin-import": "2.31.0",
101
101
  "eslint-plugin-jsx-a11y": "6.10.2",
102
102
  "eslint-plugin-prettier": "5.2.1",
103
103
  "eslint-plugin-react": "7.37.2",
104
- "eslint-plugin-react-hooks": "5.0.0",
104
+ "eslint-plugin-react-hooks": "5.1.0",
105
105
  "eslint-plugin-storybook": "0.11.1",
106
106
  "fetch-mock": "<10",
107
107
  "file-loader": "6.2.0",
@@ -114,35 +114,37 @@
114
114
  "js-cookie": "3.0.5",
115
115
  "lodash-es": "4.17.21",
116
116
  "mdn-polyfills": "5.20.0",
117
- "msw": "2.6.6",
117
+ "msw": "2.6.8",
118
118
  "node-fetch": ">2.6.6 <3",
119
119
  "nodemon": "3.1.7",
120
- "prettier": "3.4.1",
120
+ "prettier": "3.4.2",
121
121
  "query-string": "9.1.1",
122
- "react": "18.3.1",
122
+ "react": "19.0.0",
123
123
  "react-autosuggest": "10.1.0",
124
- "react-dom": "18.3.1",
125
- "react-hook-form": "7.53.2",
126
- "react-intl": "7.0.1",
124
+ "react-dom": "19.0.0",
125
+ "react-hook-form": "7.54.0",
126
+ "react-intl": "7.0.4",
127
127
  "react-modal": "3.16.1",
128
- "react-router": "7.0.1",
129
- "sass": "1.81.0",
128
+ "react-router": "7.0.2",
129
+ "sass": "1.82.0",
130
130
  "source-map-loader": "5.0.0",
131
- "storybook": "8.4.6",
131
+ "storybook": "8.4.7",
132
132
  "tsconfig-paths-webpack-plugin": "4.2.0",
133
133
  "typescript": "5.7.2",
134
134
  "uuid": "11.0.3",
135
- "webpack": "5.96.1",
135
+ "webpack": "5.97.1",
136
136
  "webpack-cli": "5.1.4",
137
137
  "whatwg-fetch": "3.6.20",
138
138
  "xhr-mock": "2.5.1",
139
139
  "yargs": "17.7.2",
140
- "yup": "1.4.0"
140
+ "yup": "1.5.0"
141
141
  },
142
142
  "resolutions": {
143
143
  "@testing-library/dom": "10.4.0",
144
- "@types/react": "18.3.12",
145
- "@types/react-dom": "18.3.1"
144
+ "@types/react": "19.0.1",
145
+ "@types/react-dom": "19.0.1",
146
+ "react": "19.0.0",
147
+ "react-dom": "19.0.0"
146
148
  },
147
149
  "msw": {
148
150
  "workerDirectory": "../richie/static/richie/js"
@@ -152,7 +154,7 @@
152
154
  "yarn": "1.22.22"
153
155
  },
154
156
  "devDependencies": {
155
- "@storybook/addon-mdx-gfm": "8.4.6",
157
+ "@storybook/addon-mdx-gfm": "8.4.7",
156
158
  "@storybook/addon-webpack5-compiler-babel": "3.0.3"
157
159
  }
158
160
  }
@@ -132,6 +132,7 @@
132
132
  --c--theme--breakpoints--lg: 992px;
133
133
  --c--theme--breakpoints--xl: 1200px;
134
134
  --c--theme--breakpoints--xxl: 1400px;
135
+ --c--components--tabs--border-bottom-color: var(--c--theme--colors--greyscale-300);
135
136
  --c--components--button--font-family: Montserrat;
136
137
  --c--components--dashboardlistavatar--saturation: 30;
137
138
  --c--components--dashboardlistavatar--lightness: 55;