Skip to content

Commit

Permalink
feat(Forms): implement TypeScript version of json-pointer
Browse files Browse the repository at this point in the history
  • Loading branch information
tujoworker committed Sep 25, 2024
1 parent 9ab9de5 commit 93e7db8
Show file tree
Hide file tree
Showing 28 changed files with 617 additions and 65 deletions.
1 change: 0 additions & 1 deletion packages/dnb-eufemia/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@
"classnames": "2.3.1",
"core-js": "3.26.1",
"date-fns": "2.25.0",
"json-pointer": "0.6.2",
"keycode": "2.2.1",
"prop-types": "15.7.2",
"what-input": "5.2.10"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useCallback, useContext } from 'react'
import pointer from 'json-pointer'
import pointer from '../../utils/json-pointer'
import type { ComponentProps } from '../../types'
import Context, { ContextState } from '../Context'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import React, {
useEffect,
useContext,
} from 'react'
import pointer, { JsonObject } from 'json-pointer'
import pointer, { JsonObject } from '../../utils/json-pointer'
import { ValidateFunction } from 'ajv/dist/2020'
import {
Ajv,
Expand Down Expand Up @@ -563,7 +563,7 @@ export default function Provider<Data extends JsonObject>(
}, [])

// - Shared state
const sharedData = useSharedState<Data>(id)
const sharedData = useSharedState<Data & { clearForm?: () => void }>(id)
const sharedAttachments = useSharedState<{
visibleDataHandler?: VisibleDataHandler<Data>
filterDataHandler?: FilterDataHandler<Data>
Expand Down Expand Up @@ -600,7 +600,8 @@ export default function Provider<Data extends JsonObject>(
initialData &&
sharedData.data &&
cacheRef.current.shared === sharedData.data &&
internalDataRef.current === initialData
internalDataRef.current === initialData &&
typeof internalDataRef.current === 'object'
) {
return {
...internalDataRef.current,
Expand All @@ -624,7 +625,8 @@ export default function Provider<Data extends JsonObject>(
id &&
sharedData.data &&
cacheRef.current.shared !== sharedData.data &&
sharedData.data !== internalDataRef.current
sharedData.data !== internalDataRef.current &&
typeof internalDataRef.current === 'object'
) {
cacheRef.current.shared = sharedData.data

Expand Down Expand Up @@ -1287,8 +1289,8 @@ export default function Provider<Data extends JsonObject>(
}

type FormStatusBufferProps = {
minimumAsyncBehaviorTime?: Props<unknown>['minimumAsyncBehaviorTime']
asyncSubmitTimeout?: Props<unknown>['asyncSubmitTimeout']
minimumAsyncBehaviorTime?: Props<JsonObject>['minimumAsyncBehaviorTime']
asyncSubmitTimeout?: Props<JsonObject>['asyncSubmitTimeout']
formState: ContextState['formState']
waitFor: boolean
onTimeout: () => void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ describe('DataContext.Provider', () => {
})

let filteredData = undefined
const onSubmit: OnSubmit = jest.fn((data, { filterData }) => {
const onSubmit = jest.fn((data, { filterData }) => {
return (filteredData = filterData(filterDataHandler))
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react'
import { Field, Form, JSONSchema } from '../../..'
import { Flex } from '../../../../../components'
import Provider from '../Provider'
import type { FilterData } from '../../Context'

export default {
title: 'Eufemia/Extensions/Forms/Provider',
Expand Down Expand Up @@ -92,13 +93,13 @@ export function Validation() {

const id = 'form-with-disabled'

const filterDataHandler = ({ props }) => {
const filterDataHandler: FilterData = ({ props }) => {
if (props.disabled === true) {
return false
}
}

export const FilterData = () => {
export const FilterDataStory = () => {
const { hasErrors } = Form.useError(id)
const { data, filterData } = Form.useData(id, {
disabled: true,
Expand All @@ -109,8 +110,8 @@ export const FilterData = () => {
return (
<Form.Handler
id={id}
onSubmit={(data) => {
console.log('onSubmit', filterDataHandler(data))
onSubmit={(data, { filterData }) => {
console.log('onSubmit', filterData(filterDataHandler))
}}
>
<Flex.Stack>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCallback, useContext, useMemo, useRef } from 'react'
import pointer from 'json-pointer'
import pointer from '../../utils/json-pointer'
import DataContext from '../../DataContext/Context'
import { Props } from './Indeterminate'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useContext } from 'react'
import { JsonObject } from 'json-pointer'
import { JsonObject } from '../../utils/json-pointer'
import DataContextProvider, {
Props as ProviderProps,
} from '../../DataContext/Provider'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import React, {
useReducer,
useRef,
} from 'react'
import pointer, { JsonObject } from 'json-pointer'
import pointer, { JsonObject } from '../../utils/json-pointer'
import { extendDeep } from '../../../../shared/component-helper'
import { isAsync } from '../../../../shared/helpers/isAsync'
import useDataValue from '../../hooks/useDataValue'
Expand Down Expand Up @@ -39,7 +39,10 @@ export type IsolationProviderProps<Data> = {
* It will receive the data from the isolated context and the data from the outer context.
* You can use this to transform the data before it is committed.
*/
transformOnCommit?: (isolatedData: Data, handlerData: Data) => Data
transformOnCommit?: (
isolatedData: JsonObject,
handlerData: JsonObject
) => unknown
/**
* Used internally by the Form.Isolation component
*/
Expand Down Expand Up @@ -165,7 +168,7 @@ function IsolationProvider<Data extends JsonObject>(
: dataOuter

localDataRef.current = mountedData
let isolatedData = structuredClone(mountedData) as Data
let isolatedData = structuredClone(mountedData)

if (typeof transformOnCommitProp === 'function') {
isolatedData = transformOnCommitProp(isolatedData, outerData)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
FieldProps,
OnChange,
} from '../../types'
import type { JsonObject } from '../../utils/json-pointer'

export type OverwritePropsDefaults = {
[key: Path]: (FieldProps & FieldBlockProps) | OverwritePropsDefaults
Expand Down Expand Up @@ -55,7 +56,7 @@ export type SectionProps<overwriteProps = OverwritePropsDefaults> = {
*/
errorPrioritization?: SectionContextState['errorPrioritization']
} & Pick<
DataContextProps<unknown>,
DataContextProps<JsonObject>,
'data' | 'defaultData' | 'onChange' | 'translations'
>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCallback, useContext, useRef } from 'react'
import pointer from 'json-pointer'
import pointer from '../../utils/json-pointer'
import DataContext from '../../DataContext/Context'
import usePath from '../../hooks/usePath'
import { Path } from '../../types'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import pointer from 'json-pointer'
import pointer from '../../utils/json-pointer'
import {
SharedStateId,
createSharedState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
useReducer,
useRef,
} from 'react'
import pointer from 'json-pointer'
import pointer, { JsonObject } from '../../utils/json-pointer'
import {
SharedStateId,
useSharedState,
Expand Down Expand Up @@ -37,7 +37,7 @@ type UseDataReturnUpdate<Data> = <P extends Path>(

export type UseDataReturnGetValue<Data> = <P extends Path>(
path: P
) => PathType<Data, P>
) => PathType<Data, P> | unknown

export type UseDataReturnFilterData<Data> = (
filterDataHandler: FilterData,
Expand Down Expand Up @@ -70,7 +70,7 @@ type SharedAttachment<Data> = {
* @param {Data} initialData - The initial data value (optional).
* @returns {UseDataReturn<Data>} An object containing the data and data management functions.
*/
export default function useData<Data>(
export default function useData<Data = JsonObject>(
id: SharedStateId = undefined,
initialData: Data = undefined
): UseDataReturn<Data> {
Expand Down Expand Up @@ -124,8 +124,8 @@ export default function useData<Data>(
const update = useCallback<UseDataReturnUpdate<Data>>(
(path, value = undefined) => {
const existingData = structuredClone(
sharedDataRef.current.data || ({} as Data)
)
sharedDataRef.current.data || {}
) as Data & JsonObject
const existingValue = pointer.has(existingData, path)
? pointer.get(existingData, path)
: undefined
Expand All @@ -152,8 +152,8 @@ export default function useData<Data>(
const remove = useCallback<UseDataReturn<Data>['remove']>(
(path) => {
const existingData = structuredClone(
sharedDataRef.current.data || ({} as Data)
)
sharedDataRef.current.data || {}
) as Data & JsonObject

if (pointer.has(existingData, path)) {
// Remove existing data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import React, {
useContext,
} from 'react'
import classnames from 'classnames'
import pointer from 'json-pointer'
import pointer from '../../utils/json-pointer'
import { useFieldProps } from '../../hooks'
import { makeUniqueId } from '../../../../shared/component-helper'
import { Flex, FormStatus, HeightAnimation } from '../../../../components'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCallback } from 'react'
import pointer from 'json-pointer'
import pointer from '../../utils/json-pointer'
import { Identifier, Path } from '../../types'
import { useData, getData } from '../../Form'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCallback, useContext, useRef } from 'react'
import pointer, { JsonObject } from 'json-pointer'
import pointer, { JsonObject } from '../utils/json-pointer'
import DataContext, { FilterData } from '../DataContext/Context'
import { JSONSchema } from '../types'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isValidElement, useCallback, useContext, useRef } from 'react'
import pointer, { JsonObject } from 'json-pointer'
import pointer, { JsonObject } from '../utils/json-pointer'
import DataContext, { FilterData } from '../DataContext/Context'

export type ListAllPropsReturn = {
Expand Down
10 changes: 5 additions & 5 deletions packages/dnb-eufemia/src/extensions/forms/hooks/useDataValue.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCallback, useContext, useRef } from 'react'
import pointer from 'json-pointer'
import pointer from '../utils/json-pointer'
import { Path } from '../types'
import DataContext, { ContextState } from '../DataContext/Context'
import usePath from './usePath'
Expand Down Expand Up @@ -48,14 +48,14 @@ export default function useDataValue<Value>({
[get, makeIteratePath]
)

const moveValueToPath = useCallback(<T>(path: Path, value: unknown) => {
const moveValueToPath = useCallback(<T>(path: Path, value: T): T => {
if (path !== '/' && isPath(path)) {
const obj = {} as T
const obj = {}
pointer.set(obj, path, value)
return obj
return obj as T
}

return value as T
return value
}, [])

const getData = useCallback(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useContext, useMemo } from 'react'
import pointer from 'json-pointer'
import pointer from '../utils/json-pointer'
import { FieldProps, Path } from '../types'
import DataContext from '../DataContext/Context'
import IterateElementContext from '../Iterate/IterateItemContext'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import React, {
useReducer,
AriaAttributes,
} from 'react'
import pointer from 'json-pointer'
import pointer from '../utils/json-pointer'
import { ValidateFunction } from 'ajv/dist/2020'
import { errorChanged } from '../utils'
import { ajvErrorsToOneFormError } from '../utils/ajv'
Expand Down Expand Up @@ -246,7 +246,7 @@ export default function useFieldProps<Value, EmptyValue, Props>(
? pointer.get(schema, schemaPath)
: schema

requiredList.push(schemaPart?.required)
requiredList.push(schemaPart?.['required'])
}

if (sectionPath) {
Expand Down Expand Up @@ -1618,7 +1618,7 @@ export default function useFieldProps<Value, EmptyValue, Props>(
if (hasValue) {
const sharedValue = pointer.get(sharedState.data, identifier)
if (sharedValue) {
value = sharedValue
value = sharedValue as Value
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/dnb-eufemia/src/extensions/forms/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { SpacingProps } from '../../components/space/types'
import type { JSONSchema4, JSONSchema6, JSONSchema7 } from 'json-schema'
import type { JSONSchemaType } from 'ajv/dist/2020'
import { JsonObject } from 'json-pointer'
import { JsonObject } from './utils/json-pointer'
import { AriaAttributes } from 'react'
import { FilterData, VisibleDataOptions } from './DataContext'

Expand Down Expand Up @@ -598,7 +598,7 @@ export type OnCommit<Data = JsonObject> = (
| void
| Promise<EventReturnWithStateObject | void>

export type OnChange<Data = unknown> = (
export type OnChange<Data = JsonObject> = (
data: Data,
additionalArgs: Pick<OnSubmitParams, 'filterData'>
) => OnChangeReturnType
Expand Down
4 changes: 2 additions & 2 deletions packages/dnb-eufemia/src/extensions/forms/utils/ajv.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import ajvInstance, { ErrorObject } from 'ajv/dist/2020'
import ajvErrors from 'ajv-errors'
import pointer from 'json-pointer'
import pointer, { JsonObject } from './json-pointer'
import { FormError, Path } from '../types'
import type Ajv from 'ajv/dist/2020'

Expand Down Expand Up @@ -156,7 +156,7 @@ export function ajvErrorsToOneFormError(
*/
export const ajvErrorsToFormErrors = (
errors?: ErrorObject[] | null,
data?: Record<Path, unknown>
data?: JsonObject
): Record<string, FormError> => {
return (errors ?? []).reduce((errors, ajvError) => {
const path = getInstancePath(ajvError)
Expand Down
1 change: 1 addition & 0 deletions packages/dnb-eufemia/src/extensions/forms/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './errors'
export * from './json-pointer'
export { default as TestElement } from './TestElement/TestElement'
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './json-pointer/json-pointer'

// For TypeScript compatibility we import and export it this way
import * as _default from './json-pointer/export'
export { _default as default }
Loading

0 comments on commit 93e7db8

Please sign in to comment.