Skip to content

Commit

Permalink
Dependent attributes: Add support for callbackData
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolas-jaussaud committed Jan 6, 2025
1 parent 833accd commit 7887b70
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 35 deletions.
36 changes: 18 additions & 18 deletions assets/build/index.min.js

Large diffs are not rendered by default.

35 changes: 28 additions & 7 deletions assets/src/components/dependent/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
* __isWrapped indicate that we need to apply on (or multiple) dependent value inside an object of the
* field, on a specific attribute
*
* __callback indicate the we need to apply a callback to format the dependent value before
* __callback indicate that we need to apply a callback to format the dependent value before
* using it
*
* __callbackData indicate that if we have a callback, we need to pass some custom values when calling it
*
* Example of returned structure:
* {
* 'trigger-field-name': {
Expand Down Expand Up @@ -39,6 +41,20 @@
* attribute
* }) => return 'something'
* },
*
* // Dependent attribute with callback + callbackData
* 'readOnly' : {
* __returnedType : 'full',
* __callback : ({
* value,
* attribute
* }) => return 'something',
* __callbackData : {
* customName : customValue,
* customName2 : customValue2
* }
* },
*
* },
* 'trigger-field-name-2': {
* // ...
Expand All @@ -54,7 +70,7 @@ const getDependentFields = props => {
/**
* Special case this repeater attribute, it needs to be evaluated
* later under a different name
*
*
* @see renderTitle() ./assets/src/components/repeater/common/helpers
*/
'sectionTitle'
Expand All @@ -71,8 +87,11 @@ const getDependentFields = props => {
* - as a function
* - as a string, which is needed in case the field is registered in PHP. In that case
* we need to be able to register the callback separatly (@see tangibleFields.fields).
*
* It is possible to pass custom data to a callback, by using props.dependent.callbackData
*/
const callback = props.dependent?.callback ?? false
const callbackData = props.dependent?.callbackData ?? false

for( const name in props ) {

Expand All @@ -98,7 +117,7 @@ const getDependentFields = props => {

if( typeof value !== 'string' ) continue;

const triggerField = getDependentValue(value, callback)
const triggerField = getDependentValue(value, callback, callbackData)
if( ! triggerField ) continue;

if( ! fields[ triggerField.name ] ) fields[ triggerField.name ] = {}
Expand All @@ -109,7 +128,7 @@ const getDependentFields = props => {
}

const isDependentString = string => string.startsWith('{{') && string.endsWith('}}')
const getDependentValue = (string, callback) => {
const getDependentValue = (string, callback, callbackData) => {

if( ! isDependentString(string) ) return false;

Expand All @@ -120,7 +139,8 @@ const getDependentValue = (string, callback) => {
name : dependentString,
config : {
__returnedType : 'full',
__callback : callback
__callback : callback,
__callbackData : callbackData
}
}

Expand All @@ -133,7 +153,8 @@ const getDependentValue = (string, callback) => {
config : {
__returnedType : 'partial',
__returnedAttribute : attribute,
__callback : callback
__callback : callback,
__callbackData : callbackData
}
}
}
Expand Down Expand Up @@ -161,7 +182,7 @@ const getFieldValue = (attribute, config, getValue) => {
}

return callback
? callback({ attribute, value })
? callback({ attribute, value, ...(config.__callbackData ?? {}) })
: value
}

Expand Down
13 changes: 11 additions & 2 deletions assets/src/components/repeater/common/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,20 @@ const renderTitle = (item, i, title, name, renderItem, parent) => {

if( ! isDependentString(text) ) return text;

const element = {
const element = {
type : 'wrapper',
name : `_repeater-title-${name}-${item.key}`,
content : title,
dependent : parent.dependent ?? true
dependent : {
callbackData: {
repeater: {
props : parent,
item : item,
index : i
}
},
...(parent.dependent ? parent.dependent : {})
}
}

return renderItem(element, item, i)
Expand Down
16 changes: 8 additions & 8 deletions assets/src/components/repeater/layout/tab/Tab.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from 'react'
import { useState } from 'react'
import { Button, ModalTrigger } from '../../../base'
import { renderTitle } from '../../common/helpers'

Expand All @@ -20,10 +20,10 @@ const Tab = ({
return(
<div className='tf-repeater-tab-container'>
<div className='tf-repeater-tab-header'>
<div className='tf-repeater-items tf-repeater-tab-items'>
<div className='tf-repeater-items tf-repeater-tab-items'>
{ items && items.slice(0, maxLength).map((item, i) => (
<div
key={ item.key ?? i }
<div
key={ item.key ?? i }
className='tf-repeater-tab-item'
data-open={ activeItem == i }
>
Expand All @@ -37,15 +37,15 @@ const Tab = ({
<div className='tf-repeater-tab-add-item'>
<Button
type={ 'text-primary' }
onPress={ () => dispatch({ type: 'add' }) }
onPress={ () => dispatch({ type: 'add' }) }
isDisabled={ maxLength <= items.length }
>
+ Add Item
</Button>
</div>
<div className='tf-repeater-tab-icon-actions'>
{ maxLength !== undefined &&
<ModalTrigger
<ModalTrigger
label="Remove"
title="Confirmation"
onValidate={ () => {
Expand All @@ -62,7 +62,7 @@ const Tab = ({
</div>
</div>
</div>
{ items[ activeItem ] &&
{ items[ activeItem ] &&
<div key={ items[ activeItem ].key ?? activeItem } className='tf-repeater-tab-content'>
{ rowFields.map(control => (
<div key={ control.name } className='tf-repeater-tab-row'>
Expand All @@ -71,7 +71,7 @@ const Tab = ({
? <div className='tf-repeater-tab-row-title tf-repeater-tab-row-title-section'>
{ renderItem(control, items[ activeItem ], activeItem) }
</div>
: <>
: <>
<div className='tf-repeater-tab-row-title'>
<span className='tf-label'>
{ control.label ?? '' }
Expand Down
4 changes: 4 additions & 0 deletions fields/format.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@
$args = $fields->format_value($args, 'label_visually_hidden', 'labelVisuallyHidden');
}

if ( ! empty($args['dependent']) && is_array($args['dependent']) ) {
$args = $fields->format_value($args['dependent'], 'callback_data', 'callbackData');
}

return $args;
};

Expand Down
47 changes: 47 additions & 0 deletions tests/jest/cases/dependentValues.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -703,4 +703,51 @@ describe('dependent values feature', () => {
container => container.querySelector('.cm-content[contenteditable="false"]').toBeFalsy()
})

it('works supports callback + callbackData', async () => {

const { container } = render(
<>
{ fields.render({
label : 'Field 1',
type : 'text',
value : 'Initial value of field 1',
name : 'test-field-1'
}) }
<div className="tested-element">
{ fields.render({
label : '{{test-field-1}}',
type : 'text',
readOnly : '{{test-field-1}}',
dependent : {
callback : 'callback-name',
callbackData : {
customData : 'value from callbackData'
}
}
}) }
</div>
</>
)

let hasCustomValue = false
tangibleFields.fields.dependent.registerCallback(
'callback-name',
({ attribute, value, customData }) => {
hasCustomValue = customData === 'value from callbackData'
return value
}
)

const dependentField = document.getElementsByClassName('tested-element')[0]
expect(dependentField).toBeTruthy()
expect(hasCustomValue).toBe(false)

act(() => {
fields.store.setValue('test-field-1', 'Updated value field 1 - text field disabled')
})

await within(dependentField).findByText('Updated value field 1 - text field disabled')

expect(hasCustomValue).toBe(true)
})
})
9 changes: 9 additions & 0 deletions tests/phpunit/cases/fields/format.php
Original file line number Diff line number Diff line change
Expand Up @@ -301,4 +301,13 @@ public function test_format_groups_args_elements(string $type) {
$this->assertEquals('action', $args['fields'][0]['buttonType']);
}

public function test_format_args_dependent() {
$args = tangible_fields()->format_args('text', [
'dependent' => [
'callback_data' => [ 'data_name' => 'data_value' ]
]
]);
$this->assertNotEmpty($args['dependent']['callbackData']);
}

}

0 comments on commit 7887b70

Please sign in to comment.