@engagebay/engagebay-form-module 1.0.2-beta.1 → 1.0.2-beta.2

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": "@engagebay/engagebay-form-module",
3
- "version": "1.0.2-beta.1",
3
+ "version": "1.0.2-beta.2",
4
4
  "description": "Provide base form components to reacho",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -1,9 +1,9 @@
1
1
  import React, {
2
- useCallback,
3
- useContext,
4
- useEffect,
5
- useRef,
6
- useState,
2
+ useCallback,
3
+ useContext,
4
+ useEffect,
5
+ useRef,
6
+ useState,
7
7
  } from "react";
8
8
 
9
9
  import { Listbox, ListboxButton } from "@headlessui/react";
@@ -12,199 +12,185 @@ import { RegisterOptions } from "react-hook-form";
12
12
  import { FormContext } from "../context/FormContext";
13
13
  import { getListOption, getListOptions } from "../FormFieldUtils";
14
14
  import {
15
- FieldOptionsSchema,
16
- FormFieldComponentPropSchema,
17
- FormFieldType,
18
- OutputFormatType,
15
+ FieldOptionsSchema,
16
+ FormFieldComponentPropSchema,
17
+ FormFieldType,
18
+ OutputFormatType,
19
19
  } from "../schema/FormFieldSchema";
20
20
  import { handleChange, registerFormField } from "../util";
21
21
  import RenderFormField from "../util/RenderFormField";
22
22
  import RenderListOptions, {
23
- renderListBoxValue,
23
+ renderListBoxValue,
24
24
  } from "../util/RenderListOptions";
25
25
 
26
26
  const Typeahead: React.FC<FormFieldComponentPropSchema> = (
27
- props: FormFieldComponentPropSchema
27
+ props: FormFieldComponentPropSchema,
28
28
  ) => {
29
- const dynamicSelectRef = useRef<HTMLUListElement>(null);
30
- const formContext = useContext(FormContext);
31
- let registerOptions: RegisterOptions = registerFormField(props.fieldConfig);
32
- let hookProps = formContext.register(
33
- props.fieldConfig.name,
34
- registerOptions
35
- );
36
- const [listOptions, setListOptions] = useState<FieldOptionsSchema[]>([]);
37
- const [selectedValues, setSelectedValues] = useState<FieldOptionsSchema[]>(
38
- []
39
- );
40
- const [loading, setLoading] = useState<boolean>(true);
41
-
42
- useEffect(() => {
43
- if (
44
- !formContext.getValues(props.fieldConfig.name) &&
45
- props.fieldConfig.defaultValue
46
- ) {
47
- formContext.setValue(
48
- props.fieldConfig.name,
49
- props.fieldConfig.defaultValue
50
- );
51
- }
52
- fetchData(undefined);
53
- }, []);
29
+ const dynamicSelectRef = useRef<HTMLUListElement>(null);
30
+ const formContext = useContext(FormContext);
31
+ let registerOptions: RegisterOptions = registerFormField(props.fieldConfig);
32
+ let hookProps = formContext.register(props.fieldConfig.name, registerOptions);
33
+ const [listOptions, setListOptions] = useState<FieldOptionsSchema[]>([]);
34
+ const [selectedValues, setSelectedValues] = useState<FieldOptionsSchema[]>(
35
+ [],
36
+ );
37
+ const [loading, setLoading] = useState<boolean>(true);
54
38
 
55
- const fetchData = useCallback(
56
- async (_query: string | undefined) => {
57
- setLoading(true);
58
- try {
59
- if (!props.fieldConfig.fetchUrl) return;
39
+ useEffect(() => {
40
+ if (
41
+ !formContext.getValues(props.fieldConfig.name) &&
42
+ props.fieldConfig.defaultValue
43
+ ) {
44
+ formContext.setValue(
45
+ props.fieldConfig.name,
46
+ props.fieldConfig.defaultValue,
47
+ );
48
+ }
49
+ fetchData(undefined);
50
+ }, []);
60
51
 
61
- let url = props.fieldConfig.fetchUrl;
62
- if (_query) {
63
- url = url.includes("?")
64
- ? url + "&q=" + _query
65
- : url + "?q=" + _query;
66
- }
52
+ const fetchData = useCallback(
53
+ async (_query: string | undefined) => {
54
+ setLoading(true);
55
+ try {
56
+ if (!props.fieldConfig.fetchUrl) return;
67
57
 
68
- let response = await (props.fieldConfig.disableHeaderInFetch
69
- ? axios.get(url)
70
- : formContext.axiosInstance?.get(url));
71
- if (response?.data) {
72
- const data: FieldOptionsSchema[] = getListOptions(
73
- response.data,
74
- props.fieldConfig.optionsConfig
75
- );
76
- setListOptions([...data]);
77
- let value = formContext.getValues(props.fieldConfig.name);
78
- if (
79
- value &&
80
- data.find((i) => i.value !== value)
81
- ) {
82
- if (selectedValues.find((i) => i.value !== value))
83
- fetchValue(value);
84
- }
85
- if (props.fieldConfig.fetchCallback) {
86
- props.fieldConfig.fetchCallback(response);
87
- }
88
- } else {
89
- console.error(response?.statusText);
90
- }
91
- } catch (err) {
92
- } finally {
93
- setLoading(false);
94
- }
95
- },
96
- [props.fieldConfig.fetchUrl]
97
- );
58
+ let url = props.fieldConfig.fetchUrl;
59
+ if (_query) {
60
+ url = url.includes("?") ? url + "&q=" + _query : url + "?q=" + _query;
61
+ } else {
62
+ url = url.includes("?") ? url + "&q=" + null : url + "?q=" + null;
63
+ }
98
64
 
99
- const fetchValue = async (value: string) => {
100
- try {
101
- if (
102
- props.fieldConfig.ignoreFetchValue ||
103
- !props.fieldConfig.fetchUrl
104
- ) {
105
- return;
106
- }
65
+ let response = await (props.fieldConfig.disableHeaderInFetch
66
+ ? axios.get(url)
67
+ : formContext.axiosInstance?.get(url));
68
+ if (response?.data) {
69
+ const data: FieldOptionsSchema[] = getListOptions(
70
+ response.data,
71
+ props.fieldConfig.optionsConfig,
72
+ );
73
+ setListOptions([...data]);
74
+ let value = formContext.getValues(props.fieldConfig.name);
75
+ if (value && data.find((i) => i.value !== value)) {
76
+ if (selectedValues.find((i) => i.value !== value))
77
+ fetchValue(value);
78
+ }
79
+ if (props.fieldConfig.fetchCallback) {
80
+ props.fieldConfig.fetchCallback(response);
81
+ }
82
+ } else {
83
+ console.error(response?.statusText);
84
+ }
85
+ } catch (err) {
86
+ } finally {
87
+ setLoading(false);
88
+ }
89
+ },
90
+ [props.fieldConfig.fetchUrl],
91
+ );
107
92
 
108
- let url = props.fieldConfig.fetchUrl;
93
+ const fetchValue = async (value: string) => {
94
+ try {
95
+ if (props.fieldConfig.ignoreFetchValue || !props.fieldConfig.fetchUrl) {
96
+ return;
97
+ }
109
98
 
110
- if (value)
111
- url = url.includes("?")
112
- ? url + "&values=" + value
113
- : url + "?values=" + value;
99
+ let url = props.fieldConfig.fetchUrl;
114
100
 
115
- let response = await (
116
- props.fieldConfig.disableHeaderInFetch
117
- ? axios.get(url)
118
- : formContext.axiosInstance?.get(url)
119
- );
120
- if (response?.data) {
121
- const data: FieldOptionsSchema[] = getListOptions(
122
- response?.data,
123
- props.fieldConfig.optionsConfig
124
- );
125
- setSelectedValues([...data]);
126
- }
127
- } catch (err) { }
128
- };
101
+ if (value)
102
+ url = url.includes("?") ? url + "&q=" + value : url + "?q=" + value;
129
103
 
130
- const updateListOptions = (data: any) => {
131
- const resData: FieldOptionsSchema = getListOption(
132
- data,
133
- props.fieldConfig.optionsConfig
104
+ let response = await (props.fieldConfig.disableHeaderInFetch
105
+ ? axios.get(url)
106
+ : formContext.axiosInstance?.get(url));
107
+ if (response?.data) {
108
+ const data: FieldOptionsSchema[] = getListOptions(
109
+ response?.data,
110
+ props.fieldConfig.optionsConfig,
134
111
  );
135
- setSelectedValues((perv) => [resData]);
136
- formContext.setValue(props.fieldConfig.name, resData.value);
137
- };
112
+ setSelectedValues([...data]);
113
+ }
114
+ } catch (err) {}
115
+ };
138
116
 
139
- function getInput() {
140
- return (
141
- <Listbox
142
- as={"div"}
143
- {...hookProps}
144
- className={`relative form-listbox flex-1`}
145
- value={
146
- formContext.getValues(props.fieldConfig.name)
147
- ? props.fieldConfig.outputFormat ===
148
- OutputFormatType.ARRAY
149
- ? formContext.getValues(props.fieldConfig.name)[0]
150
- : formContext.getValues(props.fieldConfig.name)
151
- : undefined
152
- }
153
- onChange={(val) => {
154
- const currentValue = formContext.getValues(
155
- props.fieldConfig.name
156
- );
117
+ const updateListOptions = (data: any) => {
118
+ const resData: FieldOptionsSchema = getListOption(
119
+ data,
120
+ props.fieldConfig.optionsConfig,
121
+ );
122
+ setSelectedValues((perv) => [resData]);
123
+ formContext.setValue(props.fieldConfig.name, resData.value);
124
+ };
157
125
 
158
- // If the value matches, set it to null, otherwise set it to val
159
- const newValue = currentValue === val ? null : val;
126
+ function getInput() {
127
+ return (
128
+ <Listbox
129
+ as={"div"}
130
+ {...hookProps}
131
+ className={`relative form-listbox flex-1`}
132
+ value={
133
+ formContext.getValues(props.fieldConfig.name)
134
+ ? props.fieldConfig.outputFormat === OutputFormatType.ARRAY
135
+ ? formContext.getValues(props.fieldConfig.name)[0]
136
+ : formContext.getValues(props.fieldConfig.name)
137
+ : undefined
138
+ }
139
+ onChange={(val) => {
140
+ const currentValue = formContext.getValues(props.fieldConfig.name);
160
141
 
161
- const selected = listOptions.find(o => o.value == newValue);
162
- selected && setSelectedValues([selected]);
163
- handleChange(
164
- newValue,
165
- formContext,
166
- props.fieldConfig,
167
- props.onChange
168
- );
169
- }}
170
- name={props.fieldConfig.name}
171
- disabled={props.fieldConfig.disabled}>
172
- <ListboxButton
173
- className={
174
- props.fieldConfig.customClassNames?.fieldClassName
175
- ? "form-listbox-select " +
176
- props.fieldConfig.customClassNames?.fieldClassName
177
- : "form-listbox-select"
178
- }>
179
- {renderListBoxValue(
180
- formContext,
181
- props.fieldConfig,
182
- [...selectedValues, ...listOptions],
183
- props.onChange
184
- )}
185
- </ListboxButton>
186
- <RenderListOptions
187
- formContext={formContext}
188
- onChange={props.onChange}
189
- formField={FormFieldType.TYPEAHEAD}
190
- ref={dynamicSelectRef}
191
- fieldConfig={props.fieldConfig}
192
- listOptions={[...selectedValues, ...listOptions]}
193
- setListOptions={setListOptions}
194
- loading={loading}
195
- setLoading={setLoading}
196
- createCallback={(data) => updateListOptions(data)}
197
- queryCallback={(query) => fetchData(query)}
198
- />
199
- </Listbox>
200
- );
201
- }
202
- if (props.fieldConfig.hideWhenNoResults && listOptions.length == 0) {
203
- return <></>;
204
- }
142
+ // If the value matches, set it to null, otherwise set it to val
143
+ const newValue = currentValue === val ? null : val;
205
144
 
206
- return (
207
- <RenderFormField fieldConfig={props.fieldConfig} getInput={getInput} />
145
+ const selected = listOptions.find((o) => o.value == newValue);
146
+ selected && setSelectedValues([selected]);
147
+ handleChange(
148
+ newValue,
149
+ formContext,
150
+ props.fieldConfig,
151
+ props.onChange,
152
+ );
153
+ }}
154
+ name={props.fieldConfig.name}
155
+ disabled={props.fieldConfig.disabled}
156
+ >
157
+ <ListboxButton
158
+ className={
159
+ props.fieldConfig.customClassNames?.fieldClassName
160
+ ? "form-listbox-select " +
161
+ props.fieldConfig.customClassNames?.fieldClassName
162
+ : "form-listbox-select"
163
+ }
164
+ >
165
+ {renderListBoxValue(
166
+ formContext,
167
+ props.fieldConfig,
168
+ [...selectedValues, ...listOptions],
169
+ props.onChange,
170
+ )}
171
+ </ListboxButton>
172
+ <RenderListOptions
173
+ formContext={formContext}
174
+ onChange={props.onChange}
175
+ formField={FormFieldType.TYPEAHEAD}
176
+ ref={dynamicSelectRef}
177
+ fieldConfig={props.fieldConfig}
178
+ listOptions={[...selectedValues, ...listOptions]}
179
+ setListOptions={setListOptions}
180
+ loading={loading}
181
+ setLoading={setLoading}
182
+ createCallback={(data) => updateListOptions(data)}
183
+ queryCallback={(query) => fetchData(query)}
184
+ />
185
+ </Listbox>
208
186
  );
187
+ }
188
+ if (props.fieldConfig.hideWhenNoResults && listOptions.length == 0) {
189
+ return <></>;
190
+ }
191
+
192
+ return (
193
+ <RenderFormField fieldConfig={props.fieldConfig} getInput={getInput} />
194
+ );
209
195
  };
210
196
  export default Typeahead;
@@ -23,7 +23,7 @@ import RenderListOptions, {
23
23
  } from "../util/RenderListOptions";
24
24
  // import _ from "lodash";
25
25
  import axios from "axios";
26
- import { getAxiosInstance } from "src/api";
26
+ import { getAxiosInstance } from "../../api";
27
27
 
28
28
  const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
29
29
  props: FormFieldComponentPropSchema,
@@ -60,6 +60,8 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
60
60
  let url = props.fieldConfig.fetchUrl;
61
61
  if (_query) {
62
62
  url = url.includes("?") ? url + "&q=" + _query : url + "?q=" + _query;
63
+ } else {
64
+ url = url.includes("?") ? url + "&q=" + null : url + "?q=" + null;
63
65
  }
64
66
  const axiosInstance = getAxiosInstance(
65
67
  formContext.axiosInstance,
@@ -108,9 +110,7 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
108
110
 
109
111
  let url = props.fieldConfig.fetchUrl;
110
112
 
111
- url = url = url.includes("?")
112
- ? url + "&values=" + value
113
- : url + "?values=" + value;
113
+ url = url = url.includes("?") ? url + "&q=" + value : url + "?q=" + value;
114
114
 
115
115
  let response = await (props.fieldConfig.disableHeaderInFetch
116
116
  ? axios.get(url)
@@ -201,6 +201,7 @@ export type FieldOptionsSchema = {
201
201
  isDisabled?: boolean;
202
202
  helpText?: string;
203
203
  groupName?: string;
204
+ tooltip?: string;
204
205
  icon?: React.ReactNode;
205
206
  };
206
207
 
@@ -17,6 +17,7 @@ import {
17
17
  FormFieldType,
18
18
  OutputFormatType,
19
19
  } from "../schema/FormFieldSchema";
20
+ import Tippy from "@tippyjs/react";
20
21
 
21
22
  type RenderListOptionsProps = {
22
23
  formContext: FormContextType;
@@ -45,7 +46,7 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
45
46
  queryCallback,
46
47
  createCallback,
47
48
  },
48
- ref
49
+ ref,
49
50
  ) => {
50
51
  const [query, setQuery] = useState<string>("");
51
52
  const [createdListItems, setCreatedListItems] = useState<
@@ -90,7 +91,7 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
90
91
  .filter((result) => result.label)
91
92
  .filter(
92
93
  (option, index, self) =>
93
- index === self.findIndex((obj) => obj.value === option.value)
94
+ index === self.findIndex((obj) => obj.value === option.value),
94
95
  ),
95
96
  ]; // removing duplicate values
96
97
 
@@ -178,14 +179,28 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
178
179
  <>
179
180
  <div className="flex items-center justify-between gap-x-1.5">
180
181
  <span
181
- className={`block truncate w-full !max-w-[150px] space-x-2 ${
182
+ className={`block truncate w-full ${fieldConfig.customClassNames?.optionClassName} space-x-2 ${
182
183
  selected ? "font-medium" : "font-normal"
183
184
  }`}
184
185
  >
185
186
  {option.icon && (
186
187
  <span className="listbox-svg">{option.icon}</span>
187
188
  )}
188
- {option.label}
189
+ {option.tooltip ? (
190
+ <Tippy
191
+ content={
192
+ <>
193
+ {option.label}
194
+ <br />
195
+ {option.helpText}
196
+ </>
197
+ }
198
+ >
199
+ <div className="truncate">{option.label}</div>
200
+ </Tippy>
201
+ ) : (
202
+ option.label
203
+ )}
189
204
  </span>
190
205
  {isTypeahead &&
191
206
  !fieldConfig.dropdownFieldConfig?.isSuggestionBox ? (
@@ -204,11 +219,30 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
204
219
  )
205
220
  )}
206
221
  </div>
207
- {option.helpText && (
208
- <div className="mt-0 text-xs text-gray-500 font-normal truncate w-full !max-w-[150px]">
209
- {option.helpText}
210
- </div>
211
- )}
222
+ {option.helpText &&
223
+ (option.tooltip ? (
224
+ <Tippy
225
+ content={
226
+ <>
227
+ {option.label}
228
+ <br />
229
+ {option.helpText}
230
+ </>
231
+ }
232
+ >
233
+ <div
234
+ className={`mt-0 text-xs text-gray-500 font-normal truncate w-full ${fieldConfig.customClassNames?.optionClassName}`}
235
+ >
236
+ {option.helpText}
237
+ </div>
238
+ </Tippy>
239
+ ) : (
240
+ <div
241
+ className={`mt-0 text-xs text-gray-500 font-normal truncate w-full ${fieldConfig.customClassNames?.optionClassName}`}
242
+ >
243
+ {option.helpText}
244
+ </div>
245
+ ))}
212
246
  </>
213
247
  )}
214
248
  </ListboxOption>
@@ -318,7 +352,7 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
318
352
  </div>
319
353
  </ListboxOptions>
320
354
  );
321
- }
355
+ },
322
356
  );
323
357
 
324
358
  export default RenderListOptions;
@@ -326,7 +360,7 @@ export function renderListBoxValue(
326
360
  formContext: FormContextType,
327
361
  fieldConfig: FormFieldSchema,
328
362
  listOptions: FieldOptionsSchema[],
329
- onChange?: (value: any) => void
363
+ onChange?: (value: any) => void,
330
364
  ): JSX.Element {
331
365
  let value = formContext.getValues(fieldConfig.name);
332
366
  const renderAsString = () => {
@@ -338,10 +372,17 @@ export function renderListBoxValue(
338
372
  return icon ? (
339
373
  <span className="flex items-center fs-8 whitespace-nowrap text-gray-500">
340
374
  <span>{icon}</span>
341
- <span>{label} </span>
375
+
376
+ <Tippy content={label} delay={500}>
377
+ <span className="block truncate">{label}</span>
378
+ </Tippy>
342
379
  </span>
380
+ ) : label ? (
381
+ <Tippy content={<>{label}</>} delay={500}>
382
+ <span className="block truncate">{label}</span>
383
+ </Tippy>
343
384
  ) : (
344
- label || getPlaceholder()
385
+ getPlaceholder()
345
386
  );
346
387
  };
347
388
  const renderAsArray = () => {
@@ -370,7 +411,12 @@ export function renderListBoxValue(
370
411
 
371
412
  return (
372
413
  <span key={option?.value} className="form-selected-badge">
373
- <span className="form-selected-badge-name">{option?.label}</span>
414
+ <Tippy content={option?.label} delay={500}>
415
+ <span className="form-selected-badge-name">
416
+ {option?.label}
417
+ </span>
418
+ </Tippy>
419
+
374
420
  {getDeleteButton(opt)}
375
421
  </span>
376
422
  );