Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

Previous changes not listed in this document can be traced using Git history.

## Unreleased

### Added
- Added `displayingDependencies` property to `Form` widget to show / hide fields

## [1.0.25] - 2026-05-12

### Changed
Expand Down
43 changes: 25 additions & 18 deletions src/components/ListEditor/ListEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,53 @@
import { DeleteOutlined, PlusCircleOutlined } from '@ant-design/icons'
import { Button, Input, List, Space, Typography } from 'antd'
import { useEffect, useState } from 'react'
import { useState } from 'react'

type ListEditorType = {
data?: string[]
onChange: (data: string[]) => void
}

const ListEditor = ({ data = [], onChange }: ListEditorType) => {
const [currentString, setCurrentString] = useState<string>()
const [list, setList] = useState<string[]>(data)

useEffect(() => {
setList(data)
}, [data])
const [currentString, setCurrentString] = useState('')

const onAdd = () => {
if (currentString && currentString !== '') {
const newList = [...list, currentString]
onChange(newList)
setList(newList)
const trimmed = currentString.trim()

if (trimmed !== '') {
onChange([...data, trimmed])
setCurrentString('')
}
}

const onRemove = (index: number) => {
const newList = list.filter((_, i) => i !== index)
onChange(newList)
setList(newList)
onChange(data.filter((_, i) => i !== index))
}

return (
<Space direction='vertical' style={{ width: '100%' }}>
<Space.Compact style={{ width: '100%' }}>
<Input onChange={(value) => setCurrentString(value.target.value)} />
<Button onClick={onAdd} type='primary'>
<Input
onChange={(value) => setCurrentString(value.target.value)}
value={currentString}
/>
<Button htmlType='button' onClick={onAdd} type='primary'>
<PlusCircleOutlined />
</Button>
</Space.Compact>

<List
dataSource={list}
dataSource={data}
renderItem={(item, index) => (
<List.Item actions={[<Button icon={<DeleteOutlined />} onClick={() => onRemove(index)} shape='circle' type='text' />]}>
<List.Item
actions={[
<Button
icon={<DeleteOutlined />}
onClick={() => onRemove(index)}
shape='circle'
type='text'
/>,
]}
>
<Typography.Text>{item}</Typography.Text>
</List.Item>
)}
Expand Down
184 changes: 184 additions & 0 deletions src/examples/widgets/Form/Form.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1260,6 +1260,190 @@ spec:
resource: restactions
verb: GET
---
# Displaying dependencies field
# Target: validate fields whose displaying depend on the value of another field
# Expected behavior: initially one field is rendered, with the following rendered only if the previous one is compiled
kind: Form
apiVersion: widgets.templates.krateo.io/v1beta1
metadata:
name: example-form-displaying-dependencies
namespace: krateo-system
spec:
widgetData:
autocomplete:
- name: typeOptionAutocompleteTest
- name: regions
resourceRefId: get-italian-regions
displayingDependencies:
- name: surname
dependsOn:
name: name
conditionType: notEmpty
- name: middleName
dependsOn:
name: name
conditionType: notEmpty
- name: tagDescription
dependsOn:
name: tags
conditionType: notEmpty
- name: stringTest
dependsOn:
name: typeStringTest
conditionType: value
value:
type: string
stringValue: test
- name: integerTest
dependsOn:
name: typeIntegerTest
conditionType: value
value:
type: integer
integerValue: 2
- name: booleanTest
dependsOn:
name: typeBooleanTest
conditionType: value
value:
type: boolean
booleanValue: true
- name: arrayTest
dependsOn:
name: typeArrayTest
conditionType: value
value:
type: array
arrayValue: ['test']
- name: optionRadioTest
dependsOn:
name: typeOptionRadioTest
conditionType: value
value:
type: option
optionValue:
value: "1"
- name: optionSelectTest
dependsOn:
name: typeOptionSelectTest
conditionType: value
value:
type: option
optionValue:
value: ["1", "2"]
- name: optionAutocompleteTest
dependsOn:
name: typeOptionAutocompleteTest
conditionType: value
value:
type: option
optionValue:
value: "1"
- name: optionAutocompleteLabelTest
dependsOn:
name: regions
conditionType: value
value:
type: option
optionValue:
value: "7"
label: "Liguria"
submitActionId: submit-autocomplete
stringSchema: |
{
"type": "object",
"properties": {
"name": {
"type": "string",
"title": "Name (tests not empty string)",
"description": "Type something to see two dependent fields displayed ('Middle Name' and 'Surname')"
},
"middleName": { "type": "string", "title": "Middle Name" },
"surname": { "type": "string", "title": "Surname" },
"tags": {
"type": "array",
"title": "Tags (tests not empty array)",
"items": { "type": "string" },
"description": "Add a value to see a dependent field displayed ('Tag Description')"
},
"tagDescription": { "type": "string", "title": "Tag Description" },
"typeStringTest": {
"type": "string",
"title": "String test (tests equality on string value)",
"description": "Type 'test' to see a dependent fields displayed ('String test')"
},
"stringTest": { "type": "string", "title": "String test" },
"typeIntegerTest": {
"type": "integer",
"title": "Integer test (tests equality on integer value)",
"description": "Type '2' to see a dependent fields displayed ('Integer test')"
},
"integerTest": { "type": "integer", "title": "Integer test" },
"typeBooleanTest": {
"type": "boolean",
"title": "Boolean test (tests equality on boolean value)",
"description": "Set switch to true to see a dependent field displayed ('Boolean test'"
},
"booleanTest": { "type": "boolean", "title": "Boolean test" },
"typeArrayTest": {
"type": "array",
"title": "Array test (tests equality on array value)",
"description": "Add a 'test' value to see a dependent field displayed ('Array test')"
},
"arrayTest": { "type": "array", "title": "Array test" },
"typeOptionRadioTest": {
"type": "string",
"title": "Option test (tests equality on option value - radio button)",
"enum": ["1","2","3"],
"description": "Select the '1' value to see a dependent field displayed ('Option radio test')"
},
"optionRadioTest": { "type": "string", "title": "Option radio test" },
"typeOptionSelectTest": {
"type": "array",
"title": "Option test (tests equality on option value - multi select)",
"description": "Select the '1' and '2' value to see a dependent field displayed ('Option multi select test')",
"items": {
"type": "string",
"enum": ["1","2","3"]
}
},
"optionSelectTest": { "type": "string", "title": "Option multi select test" },
"typeOptionAutocompleteTest": {
"type": "string",
"title": "Option test (tests equality on option value - autocomplete)",
"description": "Select the '1' value to see a dependent field displayed ('Autocomplete test')",
"enum": ["1","2","3"]
},
"optionAutocompleteTest": { "type": "string", "title": "Autocomplete test" },
"regions": {
"type": "string",
"title": "Region (tests equality on option value and label - autocomplete)",
"description": "Select the 'Liguria' value to see a dependent field displayed ('Autocomplete with label test')"
},
"optionAutocompleteLabelTest": { "type": "string", "title": "Autocomplete with label test" }
}
}
actions:
rest:
- id: submit-autocomplete
type: rest
resourceRefId: resource-ref-1
headers: []
resourcesRefs:
items:
- id: resource-ref-1
apiVersion: composition.krateo.io/v1
name: submit-autocomplete
namespace: krateo-system
resource: fireworksapps
verb: POST
- id: get-italian-regions
apiVersion: templates.krateo.io/v1
name: get-italian-regions
namespace: krateo-system
resource: restactions
verb: GET
---
# Missing action
# Target: validate error handling when action is missing.
# Expected behavior: an error is displayed on submit to inform that the action is not defined.
Expand Down
27 changes: 27 additions & 0 deletions src/examples/widgets/Form/Form.menu.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ spec:
- resourceRefId: example-form-autocomplete-restaction-parameter-panel
- resourceRefId: example-form-dependencies-panel
- resourceRefId: example-form-autocomplete-dependencies-array-objects-panel
- resourceRefId: example-form-displaying-dependencies-panel
resourcesRefs:
items:
- id: example-form-missing-schema-panel
Expand Down Expand Up @@ -228,6 +229,12 @@ spec:
namespace: krateo-system
resource: panels
verb: GET
- id: example-form-displaying-dependencies-panel
apiVersion: widgets.templates.krateo.io/v1beta1
name: example-form-displaying-dependencies-panel
namespace: krateo-system
resource: panels
verb: GET
---
kind: Column
apiVersion: widgets.templates.krateo.io/v1beta1
Expand Down Expand Up @@ -796,6 +803,26 @@ spec:
---
kind: Panel
apiVersion: widgets.templates.krateo.io/v1beta1
metadata:
name: example-form-displaying-dependencies-panel
namespace: krateo-system
spec:
widgetData:
actions: {}
title: 'Displaying dependencies field'
items:
- resourceRefId: example-form-displaying-dependencies
resourcesRefs:
items:
- id: example-form-displaying-dependencies
apiVersion: widgets.templates.krateo.io/v1beta1
name: example-form-displaying-dependencies
namespace: krateo-system
resource: forms
verb: GET
---
kind: Panel
apiVersion: widgets.templates.krateo.io/v1beta1
metadata:
name: example-form-missing-action-panel
namespace: krateo-system
Expand Down
13 changes: 13 additions & 0 deletions src/hooks/useFieldVisibility.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Form, type FormInstance } from 'antd'

import type { DisplayDependency } from '../widgets/Form/FormGenerator'
import { evaluateVisibility } from '../widgets/Form/utils'

export const useFieldVisibility = (dependency: DisplayDependency | undefined, form: FormInstance) => {
const watchedValue = Form.useWatch(
dependency?.dependsOn.name?.split('.') ?? [],
form,
) as unknown

return evaluateVisibility(dependency, watchedValue)
}
Loading
Loading