@integry/sdk 4.7.14 → 4.7.16

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integry/sdk",
3
- "version": "4.7.14",
3
+ "version": "4.7.16",
4
4
  "description": "Integry SDK",
5
5
  "main": "dist/umd/index.umd.js",
6
6
  "module": "dist/esm/index.csm.js",
@@ -322,22 +322,23 @@ const ListBox = (props: ListBoxProps) => {
322
322
  >(new URL(endpointUrl), data, requestBodyData)
323
323
  .then((res: any) => {
324
324
  if (res) {
325
- setItems(dynamicResponseMapper(res));
325
+ const tempFilteredItems = dynamicResponseMapper(res);
326
+ setItems(tempFilteredItems);
326
327
  if (res.hasOwnProperty('_cursor') && res._cursor !== null) {
327
- const tempFilteredItems = dynamicResponseMapper(res);
328
328
  setFilteredItems(tempFilteredItems);
329
329
  setSortedFilteredItems(tempFilteredItems);
330
- if (tempFilteredItems.length === 1) {
331
- setEditableTextValue(tempFilteredItems[0].value);
332
- onChange(tempFilteredItems[0].id, true);
333
- } else if (value) {
334
- const item = tempFilteredItems.find((a) => a.id === value);
335
- if (item) {
336
- setEditableTextValue(item.value);
337
- onChange(item.id, true);
338
- }
330
+ } else {
331
+ setSortedFilteredItems(tempFilteredItems);
332
+ }
333
+ if (tempFilteredItems.length === 1) {
334
+ setEditableTextValue(tempFilteredItems[0].value);
335
+ onChange(tempFilteredItems[0].id, true);
336
+ } else if (value) {
337
+ const item = tempFilteredItems.find((a) => a.id === value);
338
+ if (item) {
339
+ setEditableTextValue(item.value);
340
+ onChange(item.id, true);
339
341
  }
340
- setSortedFilteredItems(dynamicResponseMapper(res));
341
342
  }
342
343
  // if value was selected before items were loaded, select it again
343
344
  if (value && isSearchable) {
@@ -0,0 +1,213 @@
1
+ // ```bash
2
+ // npm install vitest @testing-library/preact @testing-library/user-event --save-dev
3
+ // ```
4
+
5
+ import { describe, it, expect, vi } from 'vitest';
6
+ import { render, fireEvent, screen } from '@testing-library/preact';
7
+ import userEvent from '@testing-library/user-event';
8
+ import '@testing-library/jest-dom/vitest';
9
+ import { ObjectField } from './index';
10
+
11
+ // Mock components and styles if necessary
12
+ vi.mock('@/components/Input', () => ({ Input: () => <div>Input</div> }));
13
+ vi.mock('@/components/TextArea', () => ({
14
+ TextArea: () => <div>TextArea</div>,
15
+ }));
16
+ vi.mock('@/components/CheckboxGroup', () => ({
17
+ CheckboxGroup: () => <div>CheckboxGroup</div>,
18
+ }));
19
+ vi.mock('@/components/ConfigureFieldWrapper', () => ({
20
+ default: ({ children }: { children: React.ReactNode }) => (
21
+ <div>{children}</div>
22
+ ),
23
+ }));
24
+ vi.mock('@/components/MultipurposeField/Dropdown', () => ({
25
+ ListBox: () => <div>ListBox</div>,
26
+ }));
27
+ vi.mock('@/components/MultipurposeField', () => ({
28
+ MultipurposeField: () => <div>MultipurposeField</div>,
29
+ }));
30
+ vi.mock('./styles.module.scss', () => ({
31
+ objectContainer: 'objectContainer',
32
+ objectFieldWrap: 'objectFieldWrap',
33
+ label: 'label',
34
+ subLabel: 'subLabel',
35
+ removeButton: 'removeButton',
36
+ }));
37
+
38
+ describe('ObjectField Component', () => {
39
+ const mockOnChange = vi.fn();
40
+ const mockOnChangeInFlow = vi.fn();
41
+ const mockApiHandler = vi.fn();
42
+
43
+ const baseProps = {
44
+ field: {
45
+ title: 'Test Field',
46
+ description: 'Test Description',
47
+ template_fields: [],
48
+ type: 'TEXTFIELD',
49
+ properties: {},
50
+ },
51
+ functionArguments: {},
52
+ connectedAccount: 'test-account',
53
+ appName: 'Test App',
54
+ onChange: mockOnChange,
55
+ onChangeInFlow: mockOnChangeInFlow,
56
+ apiHandler: mockApiHandler,
57
+ activityOutputData: {},
58
+ activityOutputDataRaw: {},
59
+ };
60
+
61
+ it('renders without crashing', () => {
62
+ const { container } = render(<ObjectField {...baseProps} />);
63
+ expect(container).toBeInTheDocument();
64
+ });
65
+
66
+ it('calls onChange when input changes', async () => {
67
+ const user = userEvent.setup();
68
+ render(<ObjectField {...baseProps} />);
69
+ const input = screen.getByRole('textbox');
70
+ await user.type(input, 'New Value');
71
+ expect(mockOnChange).toHaveBeenCalled();
72
+ });
73
+
74
+ it('conditionally renders add more button based on isArray prop', () => {
75
+ const { rerender } = render(<ObjectField {...baseProps} isArray={true} />);
76
+ expect(screen.queryByText('+ Add another Test Field')).toBeInTheDocument();
77
+
78
+ rerender(<ObjectField {...baseProps} isArray={false} />);
79
+ expect(
80
+ screen.queryByText('+ Add another Test Field'),
81
+ ).not.toBeInTheDocument();
82
+ });
83
+
84
+ it('handles array of objects and adds more on button click', async () => {
85
+ const user = userEvent.setup();
86
+ render(<ObjectField {...baseProps} isArray={true} />);
87
+ await user.click(screen.getByText('+ Add another Test Field'));
88
+ expect(screen.getAllByText('Test Field').length).toBe(2);
89
+ });
90
+
91
+ it('removes an item from the list when delete is clicked', async () => {
92
+ const user = userEvent.setup();
93
+ render(
94
+ <ObjectField {...baseProps} isArray={true} objectValue={[{}, {}]} />,
95
+ );
96
+ expect(screen.getAllByText('Test Field').length).toBe(2);
97
+ await user.click(screen.getAllByRole('button')[0]); // Assuming the delete button is the first button
98
+ expect(screen.getAllByText('Test Field').length).toBe(1);
99
+ });
100
+
101
+ it('validates props and throws if required props are missing', () => {
102
+ const consoleError = vi.spyOn(console, 'error');
103
+ consoleError.mockImplementation(() => {});
104
+
105
+ expect(() => render(<ObjectField />)).toThrow();
106
+ expect(consoleError).toHaveBeenCalled();
107
+
108
+ consoleError.mockRestore();
109
+ });
110
+
111
+ it('renders with minimal props', () => {
112
+ const { container } = render(
113
+ <ObjectField field={{}} onChange={vi.fn()} apiHandler={vi.fn()} />,
114
+ );
115
+ expect(container).toMatchSnapshot();
116
+ });
117
+
118
+ it('handles add more fields', async () => {
119
+ const onChange = vi.fn();
120
+ render(
121
+ <ObjectField
122
+ field={{ title: 'Test Field', isArray: true }}
123
+ onChange={onChange}
124
+ apiHandler={vi.fn()}
125
+ />,
126
+ );
127
+ const addButton = screen.getByText('+ Add another Test Field');
128
+ await fireEvent.click(addButton);
129
+ expect(screen.getAllByText('Test Field').length).toBe(2);
130
+ });
131
+
132
+ it('handles field changes', async () => {
133
+ const onChange = vi.fn();
134
+ const field = {
135
+ title: 'Number Field',
136
+ isArray: true,
137
+ properties: {
138
+ numberField: {
139
+ meta: {
140
+ title: 'Number',
141
+ ui: { ui_field: { type: 'TEXTFIELD' } },
142
+ is_required: true,
143
+ },
144
+ type: 'number',
145
+ },
146
+ },
147
+ };
148
+ render(
149
+ <ObjectField field={field} onChange={onChange} apiHandler={vi.fn()} />,
150
+ );
151
+ const input = screen.getByTitle('Number');
152
+ await fireEvent.change(input, { target: { value: '123' } });
153
+ expect(onChange).toHaveBeenCalledWith(
154
+ expect.anything(),
155
+ '123',
156
+ expect.anything(),
157
+ true,
158
+ expect.anything(),
159
+ expect.anything(),
160
+ );
161
+ });
162
+
163
+ it('renders a textarea when no fields are provided', () => {
164
+ const field = { type: 'TEXTAREA' };
165
+ const { container } = render(
166
+ <ObjectField field={field} onChange={vi.fn()} apiHandler={vi.fn()} />,
167
+ );
168
+ expect(container.querySelector('textarea')).not.toBeNull();
169
+ });
170
+
171
+ it('renders nested fields correctly', () => {
172
+ const field = {
173
+ properties: {
174
+ nestedField: {
175
+ meta: {
176
+ title: 'Nested Field',
177
+ ui: { ui_field: { type: 'CHECKBOX' } },
178
+ is_required: false,
179
+ },
180
+ },
181
+ },
182
+ };
183
+ render(
184
+ <ObjectField field={field} onChange={vi.fn()} apiHandler={vi.fn()} />,
185
+ );
186
+ expect(screen.getByText('CheckboxGroup')).toBeInTheDocument();
187
+ });
188
+ it('handles empty array as objectValue gracefully', () => {
189
+ render(
190
+ <ObjectField
191
+ field={{}}
192
+ onChange={vi.fn()}
193
+ apiHandler={vi.fn()}
194
+ objectValue={[]}
195
+ />,
196
+ );
197
+ expect(screen.getByText('+ Add another')).toBeInTheDocument();
198
+ });
199
+
200
+ it('handles null objectValue without crashing', () => {
201
+ render(
202
+ <ObjectField
203
+ field={{}}
204
+ onChange={vi.fn()}
205
+ apiHandler={vi.fn()}
206
+ objectValue={null}
207
+ />,
208
+ );
209
+ expect(screen.queryByText('TextArea')).toBeNull(); // Assuming TextArea is not rendered by default
210
+ });
211
+
212
+ // Additional tests can be added here to cover more complex interactions and edge cases
213
+ });
@@ -29,6 +29,7 @@ interface FunctionFormPropsType extends StoreType {
29
29
  functionArguments: any;
30
30
  variables: any;
31
31
  autoMapVars: boolean;
32
+ version?: string | null;
32
33
  onClose: (response: any) => void;
33
34
  apiHandler: any;
34
35
  customSaveCallback?: (response: any) => void; // Optional callback: Helps the implementor to implement their own save button
@@ -99,6 +100,7 @@ class FunctionForm extends Component<
99
100
  .getFunctionDetails(
100
101
  this.props.functionName,
101
102
  this.props.autoMapVars ? this.props.variables : {},
103
+ this.props.version,
102
104
  )
103
105
  .then((response: any) => {
104
106
  this.setState({ functionDetails: response });
package/src/index.ts CHANGED
@@ -592,6 +592,7 @@ export class IntegryJS {
592
592
  payload: {},
593
593
  connectedAccountId: '',
594
594
  autoMapPayload: false,
595
+ version: null,
595
596
  },
596
597
  ): Promise<Record<any, any>> => {
597
598
  const store = createSDKStore();
@@ -601,6 +602,7 @@ export class IntegryJS {
601
602
  .getFunctionDetails(
602
603
  functionName,
603
604
  options.autoMapPayload ? options.payload : {},
605
+ options.version,
604
606
  )
605
607
  .then((response: any) => {
606
608
  if (!response) {
@@ -668,6 +670,7 @@ export class IntegryJS {
668
670
  functionArguments=${options.params}
669
671
  variables=${options.payload}
670
672
  autoMapVars=${options.autoMapPayload}
673
+ version=${options.version}
671
674
  apiHandler=${this.apiHandler}
672
675
  onClose=${(functionUIResponse: any) => {
673
676
  const authIdElement = document.getElementById(
@@ -735,13 +738,20 @@ export class IntegryJS {
735
738
  [key: string]: any;
736
739
  } = {},
737
740
  connectedAccountId: string,
741
+ version: string | null = null,
738
742
  ): Promise<Record<any, any>> =>
739
743
  new Promise((resolve, reject) => {
740
744
  // Prepare the request body by encoding `args` to JSON
745
+ const headers: Record<string, string> = {
746
+ ...this.getHeaders(),
747
+ };
748
+ if (version) {
749
+ headers.Version = version;
750
+ }
741
751
  const functionDetails = {
742
752
  method: this.getMethod(),
743
753
  url: `${this.getBaseAPIUrl()}/functions/${functionName}/call/`,
744
- headers: this.getHeaders(),
754
+ headers,
745
755
  args: params,
746
756
  vars,
747
757
  };
@@ -1172,6 +1172,7 @@ class IntegryAPI {
1172
1172
  public getFunctionDetails = async (
1173
1173
  functionName: string,
1174
1174
  variables: Record<string, unknown> = {},
1175
+ version: string | null = null,
1175
1176
  ): Promise<any | null> => {
1176
1177
  const url = new URL(
1177
1178
  `${this.config.baseAPIUrl}/functions/${functionName}/get/`,
@@ -1185,14 +1186,18 @@ class IntegryAPI {
1185
1186
  });
1186
1187
 
1187
1188
  try {
1189
+ const headers: Record<string, string> = {
1190
+ 'Content-Type': 'application/json',
1191
+ };
1192
+ if (version) {
1193
+ headers.Version = version;
1194
+ }
1188
1195
  const response = await fetch(url.toString(), {
1189
1196
  method: 'POST',
1190
1197
  body: JSON.stringify({
1191
1198
  _variables: variables,
1192
1199
  }),
1193
- headers: {
1194
- 'Content-Type': 'application/json',
1195
- },
1200
+ headers,
1196
1201
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1197
1202
  }).then((data) => ResponseHandler<BrandingApp>(data));
1198
1203
  return response.data;
@@ -315,4 +315,5 @@ export type ShowFunctionOptions = {
315
315
  payload?: Record<string, any>;
316
316
  connectedAccountId?: string;
317
317
  autoMapPayload?: boolean;
318
+ version?: string | null;
318
319
  };
File without changes
File without changes