@arquimedes.co/eureka-forms 3.0.63 → 3.0.64

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.
@@ -158,6 +158,14 @@ export const useSetupApp = (isEmbedded, props) => {
158
158
  idCurrentAgent,
159
159
  handleConfirmed,
160
160
  ]);
161
+ // The host may resolve the classifiers asynchronously (the admin fetches them while the form is
162
+ // already mounted). `loadData` only copies the prop it saw when it ran, so patch the form whenever a
163
+ // new dictionary arrives — without re-running the load, which would reset what the user typed.
164
+ useEffect(() => {
165
+ if (!classifiers || !form || form.classifiers === classifiers)
166
+ return;
167
+ setForm({ ...form, classifiers });
168
+ }, [classifiers, form]);
161
169
  const reload = useCallback(() => {
162
170
  if (!form)
163
171
  return;
@@ -81,6 +81,14 @@ describe('AppHooks', function () {
81
81
  expect(result.current.organization).toBeNull();
82
82
  expect(result.current.branding).toBeUndefined();
83
83
  });
84
+ test('picks up classifiers that arrive after the first render', async () => {
85
+ const classifier = { _id: 'tipo', name: 'Tipo de solicitud', children: [] };
86
+ const { result, rerender } = renderHookWithProviders((props) => useSetupApp(false, { formData: BaseForm, internal: true, classifiers: props.classifiers }), { initialProps: { classifiers: {} } });
87
+ await waitFor(() => expect(result.current.form).toBeTruthy());
88
+ expect(result.current.form?.classifiers).toEqual({});
89
+ rerender({ classifiers: { tipo: classifier } });
90
+ await waitFor(() => expect(result.current.form?.classifiers?.tipo).toEqual(classifier));
91
+ });
84
92
  test('Apikey Default', async () => {
85
93
  mocks.get.mockImplementation((a) => {
86
94
  if (a === '/organization') {
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
@@ -0,0 +1,70 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render, screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import { describe, expect, it } from 'vitest';
5
+ import '@testing-library/jest-dom';
6
+ import { Provider } from 'react-redux';
7
+ import { FormProvider, useForm } from 'react-hook-form';
8
+ import { configureStore } from '@reduxjs/toolkit';
9
+ import { EurekaFormsReducer, defaultRootState } from '../../Utils/store';
10
+ import { RootApi } from '../../Utils/_api';
11
+ import FormStepTypes from '../../constants/FormStepTypes';
12
+ import { StoreContext } from '../../Utils/StoreContext';
13
+ import FormContext, { IdFormContext } from '../../Contexts/FormContext';
14
+ import SectionContext from '../../Contexts/SectionContext';
15
+ import MaterialProviders from '../../Utils/MaterialProviders';
16
+ import InternalFormStyle from '../../constants/InternalFormStyle';
17
+ import ClassifierSelectorStep from './ClassifierSelectorStep';
18
+ const ID_FORM = 'LATE_CLASSIFIERS_FORM';
19
+ const step = {
20
+ id: 'tipo-step',
21
+ idSection: 'SECTION_1',
22
+ stepPath: ['tipo-step'],
23
+ type: FormStepTypes.CLASSIFIER_SELECTOR,
24
+ idClassifier: 'tipo',
25
+ label: 'Tipo de solicitud',
26
+ description: '',
27
+ searchable: true,
28
+ options: {},
29
+ required: false,
30
+ size: 2,
31
+ };
32
+ /** The project's classifiers, as the host resolves them (asynchronously) and passes them to the form. */
33
+ const classifiers = {
34
+ tipo: { _id: 'tipo', name: 'Tipo de solicitud', children: ['bug', 'mejora'] },
35
+ bug: { _id: 'bug', name: 'Error / Bug', children: [] },
36
+ mejora: { _id: 'mejora', name: 'Propuesta de mejora', children: [] },
37
+ };
38
+ const form = {
39
+ firstSection: 'SECTION_1',
40
+ sections: {
41
+ SECTION_1: { id: 'SECTION_1', name: 'Sección 1', steps: ['tipo-step'], nextSection: null },
42
+ },
43
+ steps: { 'tipo-step': step },
44
+ confirmationMessage: { blocks: [], entityMap: {} },
45
+ showLink: false,
46
+ size: { blockSize: 200, blockNum: 4, spacingSize: 10 },
47
+ };
48
+ function makeStore() {
49
+ return configureStore({
50
+ reducer: { forms: EurekaFormsReducer, [RootApi.reducerPath]: RootApi.reducer },
51
+ preloadedState: { forms: { [ID_FORM]: { ...defaultRootState } } },
52
+ middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(RootApi.middleware),
53
+ });
54
+ }
55
+ function Harness({ store, formClassifiers, }) {
56
+ const methods = useForm({ mode: 'onTouched' });
57
+ return (_jsx(MaterialProviders, { formStyle: InternalFormStyle, children: _jsx(Provider, { store: store, context: StoreContext, children: _jsx(IdFormContext.Provider, { value: ID_FORM, children: _jsx(FormContext.Provider, { value: { ...form, classifiers: formClassifiers }, children: _jsx(SectionContext.Provider, { value: "SECTION_1", children: _jsx(FormProvider, { ...methods, children: _jsx(ClassifierSelectorStep, { step: step, editable: true }) }) }) }) }) }) }));
58
+ }
59
+ describe('ClassifierSelectorStep with classifiers that arrive after the first render', () => {
60
+ it('renders nothing while the classifiers are unknown, then the selector with its options', async () => {
61
+ const user = userEvent.setup();
62
+ const store = makeStore();
63
+ const { rerender } = render(_jsx(Harness, { store: store, formClassifiers: undefined }));
64
+ expect(screen.queryByRole('combobox')).not.toBeInTheDocument();
65
+ rerender(_jsx(Harness, { store: store, formClassifiers: classifiers }));
66
+ await user.click(await screen.findByRole('combobox'));
67
+ expect(await screen.findByRole('option', { name: 'Error / Bug' })).toBeInTheDocument();
68
+ expect(screen.getByRole('option', { name: 'Propuesta de mejora' })).toBeInTheDocument();
69
+ });
70
+ });
@@ -50,8 +50,10 @@ function ClassifierSelectorStep({ step, editable }) {
50
50
  };
51
51
  }) ?? []);
52
52
  },
53
+ // The classifiers dictionary can arrive after the first render (see useSetupApp), so the
54
+ // options must follow it, not only the dependency values.
53
55
  // eslint-disable-next-line react-hooks/exhaustive-deps
54
- [dependenciesValues]);
56
+ [dependenciesValues, classifier, form.classifiers]);
55
57
  useEffect(() => {
56
58
  if (value && !options?.find((option) => option.value === value?.value)) {
57
59
  onChange('');
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@arquimedes.co/eureka-forms",
3
3
  "repository": "git://github.com/Arquimede5/Eureka-Forms.git",
4
- "version": "3.0.63",
4
+ "version": "3.0.64",
5
5
  "scripts": {
6
6
  "watch": "node node_modules/@typescript/native/bin/tsc --noEmit --watch --project tsconfig.app.json",
7
7
  "start": "vite",