goobs-frontend 0.7.57 → 0.7.59

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.
@@ -1,12 +1,6 @@
1
1
  'use client'
2
2
 
3
- import React, {
4
- useCallback,
5
- useState,
6
- useRef,
7
- useEffect,
8
- RefObject,
9
- } from 'react'
3
+ import React, { useState, useRef, useEffect } from 'react'
10
4
  import { Box, InputLabel, OutlinedInput, styled } from '@mui/material'
11
5
  import { useDropdown } from './hooks/useDropdown'
12
6
  import { usePhoneNumber } from './hooks/usePhoneNumber'
@@ -14,22 +8,13 @@ import { usePassword } from './hooks/usePassword'
14
8
  import { useSplitButton } from './hooks/useSplitButton'
15
9
  import { Typography } from './../Typography'
16
10
  import { red, green } from '../../styles/palette'
17
- import { StartAdornment, EndAdornment } from './adornments'
18
- import { useHelperFooter } from './helperfooter/useHelperFooter'
11
+ import { StartAdornment, EndAdornment } from './adornment'
12
+ import {
13
+ useHelperFooter,
14
+ HelperFooterMessage,
15
+ } from './helperfooter/useHelperFooter'
19
16
  import labelStyles from '../../styles/StyledComponent/Label'
20
- import { get, JSONValue } from 'goobs-cache'
21
-
22
- /**
23
- * Interface for helper footer messages
24
- */
25
- export interface HelperFooterMessage {
26
- status: 'error' | 'success'
27
- statusMessage: string
28
- spreadMessage: string
29
- spreadMessagePriority: number
30
- formname: string
31
- required: boolean
32
- }
17
+ import { useHasInputEffect, usePreventAutocompleteEffect } from './useEffects'
33
18
 
34
19
  /**
35
20
  * Props interface for the StyledComponent
@@ -70,26 +55,13 @@ export interface StyledComponentProps {
70
55
  shrunklabellocation?: 'onnotch' | 'above'
71
56
  value?: string
72
57
  valuestatus?: boolean
73
- defaultValue?: string
74
- inputRef?: RefObject<HTMLInputElement>
75
58
  focused?: boolean
76
59
  required?: boolean
77
60
  formSubmitted?: boolean
78
- onChange?: (
79
- event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
80
- ) => void
81
- }
82
-
83
- /**
84
- * Props interface for the Adornment components
85
- */
86
- export interface AdornmentProps {
87
- componentvariant: string
88
- iconcolor?: string
89
- passwordVisible?: boolean
90
- marginRight?: number | string
91
- handleIncrement?: () => void
92
- handleDecrement?: () => void
61
+ 'aria-label'?: string
62
+ 'aria-required'?: boolean
63
+ 'aria-invalid'?: boolean
64
+ 'aria-describedby'?: string
93
65
  }
94
66
 
95
67
  /**
@@ -121,7 +93,6 @@ const StyledComponent: React.FC<StyledComponentProps> = props => {
121
93
  const {
122
94
  label,
123
95
  componentvariant,
124
- inputRef,
125
96
  name,
126
97
  backgroundcolor,
127
98
  iconcolor,
@@ -135,63 +106,29 @@ const StyledComponent: React.FC<StyledComponentProps> = props => {
135
106
  required = false,
136
107
  formname,
137
108
  formSubmitted = false,
138
- onChange,
109
+ 'aria-label': ariaLabel,
110
+ 'aria-required': ariaRequired,
111
+ 'aria-invalid': ariaInvalid,
112
+ 'aria-describedby': ariaDescribedBy,
139
113
  } = props
140
114
 
141
- const { validateField } = useHelperFooter()
142
- const [helperFooterValue, setHelperFooterValue] = useState<
143
- Record<string, HelperFooterMessage>
144
- >({})
115
+ console.log('StyledComponent: Initializing with props', {
116
+ name,
117
+ label,
118
+ required,
119
+ formname,
120
+ formSubmitted,
121
+ })
122
+
123
+ const { validateField, validateRequiredField, helperFooterValue } =
124
+ useHelperFooter()
145
125
  const [isFocused, setIsFocused] = useState(false)
146
126
  const [hasInput, setHasInput] = useState(false)
147
127
  const [showError, setShowError] = useState(false)
148
128
  const inputRefInternal = useRef<HTMLInputElement>(null)
149
129
  const inputBoxRef = useRef<HTMLDivElement>(null)
150
130
 
151
- // Fetch helper footer values from cache
152
- useEffect(() => {
153
- const fetchHelperFooter = async () => {
154
- const result = await get('helperFooter', 'client')
155
- if (result && typeof result === 'object' && 'value' in result) {
156
- setHelperFooterValue(
157
- (result as JSONValue).value as Record<string, HelperFooterMessage>
158
- )
159
- }
160
- }
161
- fetchHelperFooter()
162
- }, [])
163
-
164
- // Update hasInput state when value or valuestatus changes
165
- useEffect(() => {
166
- setHasInput(!!value || !!valuestatus)
167
- }, [value, valuestatus])
168
-
169
- // Set input attributes to prevent autocomplete and related features
170
- useEffect(() => {
171
- const input = inputRefInternal.current || inputRef?.current
172
- if (input) {
173
- input.setAttribute('autocomplete', 'new-password')
174
- input.setAttribute('autocorrect', 'off')
175
- input.setAttribute('autocapitalize', 'none')
176
- input.setAttribute('spellcheck', 'false')
177
- }
178
- }, [inputRef])
179
-
180
- // Validate field if required and necessary props are provided
181
- useEffect(() => {
182
- if (required && formname && name && label) {
183
- const emptyFormData = new FormData()
184
- emptyFormData.append(name, '')
185
- validateField(name, emptyFormData, label, required, formname)
186
- }
187
- }, [required, formname, name, label, validateField])
188
-
189
- // Update showError state based on form submission and input state
190
- useEffect(() => {
191
- setShowError(formSubmitted || (hasInput && !isFocused))
192
- }, [formSubmitted, hasInput, isFocused])
193
-
194
- // Custom hooks for specific component variants
131
+ // Custom hooks
195
132
  const { renderMenu, selectedOption, isDropdownOpen } = useDropdown(
196
133
  props,
197
134
  inputBoxRef
@@ -204,111 +141,67 @@ const StyledComponent: React.FC<StyledComponentProps> = props => {
204
141
  handleDecrement,
205
142
  } = useSplitButton(props)
206
143
 
207
- /**
208
- * Handle the change event of the input element.
209
- * @param e The change event.
210
- */
211
- const handleChange = useCallback(
212
- async (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
213
- if (componentvariant === 'phonenumber') {
214
- handlePhoneNumberChange(e)
215
- } else if (componentvariant === 'splitbutton') {
216
- // Only allow numbers for splitbutton
217
- const numValue = e.target.value.replace(/[^0-9]/g, '')
218
- e.target.value = numValue
219
- }
144
+ // useEffect hooks
145
+ useHasInputEffect(value, valuestatus, setHasInput)
146
+ usePreventAutocompleteEffect(inputRefInternal)
220
147
 
221
- if (onChange) {
222
- onChange(e)
223
- }
224
-
225
- setHasInput(!!e.target.value)
148
+ useEffect(() => {
149
+ if (required && formname && name && label) {
150
+ validateRequiredField(required, formname, name, label)
151
+ }
152
+ }, [required, formname, name, label, validateRequiredField])
226
153
 
227
- const formData = new FormData()
228
- formData.append(e.target.name, e.target.value)
154
+ useEffect(() => {
155
+ const timer = setTimeout(() => {
156
+ setShowError(formSubmitted || hasInput)
157
+ }, 1000)
229
158
 
230
- if (name && label && formname) {
231
- validateField(name, formData, label, required, formname)
232
- }
159
+ return () => clearTimeout(timer)
160
+ }, [formSubmitted, hasInput])
233
161
 
234
- // Implement validation logic directly
235
- if (name) {
236
- let result: HelperFooterMessage | undefined
162
+ const currentHelperFooter = name ? helperFooterValue[name] : undefined
163
+ console.log('StyledComponent: Current helper footer', currentHelperFooter)
237
164
 
238
- // Basic validation logic (you can expand this based on your requirements)
239
- if (required && !e.target.value) {
240
- result = {
241
- status: 'error',
242
- statusMessage: `${label} is required.`,
243
- spreadMessage: `${label} is required.`,
244
- spreadMessagePriority: 1,
245
- formname: formname || '',
246
- required: true,
247
- }
248
- } else if (
249
- componentvariant === 'email' &&
250
- !/\S+@\S+\.\S+/.test(e.target.value)
251
- ) {
252
- result = {
253
- status: 'error',
254
- statusMessage: 'Invalid email format.',
255
- spreadMessage: 'Invalid email format.',
256
- spreadMessagePriority: 1,
257
- formname: formname || '',
258
- required: required,
259
- }
260
- }
261
- // Add more validation rules here as needed
165
+ /**
166
+ * Handle the change event of the input element.
167
+ * @param e The change event.
168
+ */
169
+ const handleChange = (
170
+ e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
171
+ ) => {
172
+ console.log('StyledComponent: handleChange called', {
173
+ name: e.target.name,
174
+ value: e.target.value,
175
+ })
262
176
 
263
- if (result) {
264
- setHelperFooterValue(prevState => ({
265
- ...prevState,
266
- [name]: result,
267
- }))
268
- } else {
269
- // Clear any existing error for this field
270
- setHelperFooterValue(prevState => {
271
- const newState = { ...prevState }
272
- delete newState[name]
273
- return newState
274
- })
275
- }
276
- }
177
+ if (componentvariant === 'phonenumber') {
178
+ handlePhoneNumberChange(e)
179
+ } else if (componentvariant === 'splitbutton') {
180
+ // Only allow numbers for splitbutton
181
+ const numValue = e.target.value.replace(/[^0-9]/g, '')
182
+ e.target.value = numValue
183
+ }
277
184
 
278
- // Fetch updated helper footer state
279
- const helperFooterResult = await get('helperFooter', 'client')
280
- if (
281
- helperFooterResult &&
282
- typeof helperFooterResult === 'object' &&
283
- 'value' in helperFooterResult
284
- ) {
285
- setHelperFooterValue(prevState => ({
286
- ...prevState,
287
- ...((helperFooterResult as JSONValue).value as Record<
288
- string,
289
- HelperFooterMessage
290
- >),
291
- }))
292
- }
293
- },
294
- [
295
- componentvariant,
296
- handlePhoneNumberChange,
297
- onChange,
298
- validateField,
299
- name,
300
- label,
301
- required,
302
- formname,
303
- ]
304
- )
185
+ setHasInput(!!e.target.value)
305
186
 
306
- const currentHelperFooter = name ? helperFooterValue[name] : undefined
187
+ const formData = new FormData()
188
+ formData.append(e.target.name, e.target.value)
189
+ if (name && label && formname) {
190
+ console.log('StyledComponent: Calling validateField', {
191
+ name,
192
+ label,
193
+ required,
194
+ formname,
195
+ })
196
+ validateField(name, formData, label, required, formname)
197
+ }
198
+ }
307
199
 
308
200
  /**
309
201
  * Handle the focus event of the input element.
310
202
  */
311
203
  const handleFocus = () => {
204
+ console.log('StyledComponent: handleFocus called')
312
205
  setIsFocused(true)
313
206
  }
314
207
 
@@ -316,8 +209,15 @@ const StyledComponent: React.FC<StyledComponentProps> = props => {
316
209
  * Handle the blur event of the input element.
317
210
  */
318
211
  const handleBlur = () => {
212
+ console.log('StyledComponent: handleBlur called')
319
213
  setIsFocused(false)
320
214
  if (name && label && !hasInput && formname) {
215
+ console.log('StyledComponent: Calling validateField on blur', {
216
+ name,
217
+ label,
218
+ required,
219
+ formname,
220
+ })
321
221
  const formData = new FormData()
322
222
  formData.append(name, '')
323
223
  validateField(name, formData, label, required, formname)
@@ -344,6 +244,14 @@ const StyledComponent: React.FC<StyledComponentProps> = props => {
344
244
  hasInput ||
345
245
  componentvariant === 'phonenumber'
346
246
 
247
+ console.log('StyledComponent: Rendering', {
248
+ name,
249
+ showError,
250
+ hasHelperFooter: !!currentHelperFooter,
251
+ helperFooterStatus: currentHelperFooter?.status,
252
+ helperFooterMessage: currentHelperFooter?.statusMessage,
253
+ })
254
+
347
255
  return (
348
256
  <Box
349
257
  sx={{
@@ -376,13 +284,14 @@ const StyledComponent: React.FC<StyledComponentProps> = props => {
376
284
  focused: shouldShrinkLabel,
377
285
  })}
378
286
  shrink={shouldShrinkLabel}
287
+ htmlFor={name}
379
288
  >
380
289
  {label}
381
290
  </InputLabel>
382
291
  )}
383
292
  <Box ref={inputBoxRef} sx={{ width: '100%' }}>
384
293
  <NoAutofillOutlinedInput
385
- ref={inputRef || inputRefInternal}
294
+ ref={inputRefInternal}
386
295
  style={{
387
296
  backgroundColor: backgroundcolor || 'inherit',
388
297
  width: '100%',
@@ -409,6 +318,13 @@ const StyledComponent: React.FC<StyledComponentProps> = props => {
409
318
  : 'text',
410
319
  },
411
320
  placeholder: placeholder || '',
321
+ 'aria-label': ariaLabel,
322
+ 'aria-invalid': ariaInvalid,
323
+ 'aria-required': ariaRequired,
324
+ 'aria-describedby':
325
+ ariaDescribedBy || currentHelperFooter?.statusMessage
326
+ ? `${name}-helper-text`
327
+ : undefined,
412
328
  }}
413
329
  type={
414
330
  componentvariant === 'password' && !passwordVisible
@@ -459,6 +375,7 @@ const StyledComponent: React.FC<StyledComponentProps> = props => {
459
375
  </Box>
460
376
  {showError && currentHelperFooter?.statusMessage && (
461
377
  <Typography
378
+ id={`${name}-helper-text`}
462
379
  fontvariant="merrihelperfooter"
463
380
  fontcolor={
464
381
  currentHelperFooter?.status === 'error'
@@ -0,0 +1,54 @@
1
+ import React, { useEffect } from 'react'
2
+
3
+ /**
4
+ * Custom hook that tracks whether an input field has a value or a specific status.
5
+ *
6
+ * @param {string | undefined} value - The current value of the input field.
7
+ * @param {boolean | undefined} valuestatus - A boolean status associated with the input field.
8
+ * @param {React.Dispatch<React.SetStateAction<boolean>>} setHasInput - State setter function to update the hasInput state.
9
+ *
10
+ * @example
11
+ * const [hasInput, setHasInput] = useState(false);
12
+ * useHasInputEffect(inputValue, inputStatus, setHasInput);
13
+ */
14
+ export const useHasInputEffect = (
15
+ value: string | undefined,
16
+ valuestatus: boolean | undefined,
17
+ setHasInput: React.Dispatch<React.SetStateAction<boolean>>
18
+ ) => {
19
+ useEffect(() => {
20
+ const hasInput = !!value || !!valuestatus
21
+ console.log('useHasInputEffect: Setting hasInput to', hasInput)
22
+ setHasInput(hasInput)
23
+ }, [value, valuestatus, setHasInput])
24
+ }
25
+
26
+ /**
27
+ * Custom hook that prevents autocomplete, autocorrect, autocapitalize, and spellcheck on an input field.
28
+ * This is particularly useful for password fields or other sensitive inputs.
29
+ *
30
+ * @param {React.RefObject<HTMLInputElement>} inputRefInternal - React ref object for the input element.
31
+ *
32
+ * @example
33
+ * const inputRef = useRef<HTMLInputElement>(null);
34
+ * usePreventAutocompleteEffect(inputRef);
35
+ * // In your JSX:
36
+ * <input ref={inputRef} ... />
37
+ */
38
+ export const usePreventAutocompleteEffect = (
39
+ inputRefInternal: React.RefObject<HTMLInputElement>
40
+ ) => {
41
+ useEffect(() => {
42
+ console.log('usePreventAutocompleteEffect: Starting effect')
43
+ const input = inputRefInternal.current
44
+ if (input) {
45
+ console.log('usePreventAutocompleteEffect: Setting input attributes')
46
+ input.setAttribute('autocomplete', 'new-password')
47
+ input.setAttribute('autocorrect', 'off')
48
+ input.setAttribute('autocapitalize', 'none')
49
+ input.setAttribute('spellcheck', 'false')
50
+ } else {
51
+ console.log('usePreventAutocompleteEffect: Input ref is null')
52
+ }
53
+ }, [inputRefInternal])
54
+ }
package/src/index.ts CHANGED
@@ -9,7 +9,6 @@ import CustomGrid, {
9
9
  } from './components/Grid'
10
10
  import StyledComponent, {
11
11
  StyledComponentProps,
12
- AdornmentProps,
13
12
  } from './components/StyledComponent'
14
13
  import Typography, {
15
14
  FontFamily,
@@ -172,7 +171,7 @@ export type { CustomButtonProps }
172
171
  export type { ButtonAlignment }
173
172
  export type { CustomGridProps }
174
173
  export type { Alignment, BorderProp, columnconfig, gridconfig, cellconfig }
175
- export type { StyledComponentProps, AdornmentProps }
174
+ export type { StyledComponentProps }
176
175
  export type {
177
176
  FontFamily,
178
177
  TypographyVariant,