Structured shared resources (PVC example)
This guide shows how to replace a single YAML textarea with structured fields
(select, text, etc.) for shared resources, using PVCs as a worked example.
The pattern is generic and can be reused for other resources like Secrets or ConfigMaps.
You should already be familiar with the high‑level architecture from
docs/ui/maintainers-guide.md. This page focuses only on shared resources.
Goal
For PersistentVolumeClaims (PVCs), instead of a single YAML textarea, we want:
- a Name text input
- a Size text input (e.g.
1Gi) - a Storage class text input (e.g.
standard) - an Access mode dropdown (
ReadWriteOnce,ReadOnlyMany,ReadWriteMany)
…and we still want values.persistentVolumeClaims to look like this:
persistentVolumeClaims:
- name: my-data
size: 1Gi
storageClassName: standard
accessModes:
- ReadWriteOnce
Step 1 — Describe the fields in resources-config.js
File: src/app/resources-config.js
Each shared resource has a section in RESOURCE_SECTIONS. For PVCs we change:
{
key: 'pvcs',
label: '💾 Persistent Volume Claims',
tip: 'Attach persistent storage to your workload (survives Pod restarts)',
addLabel: 'PVC',
stateKey: 'pvcs',
nextIdKey: 'pvcNext',
createItem: (id) => ({
id,
name: '',
size: '',
storageClassName: '',
accessMode: 'ReadWriteOnce',
}),
fields: [
{
key: 'name',
label: 'Name',
type: 'text',
placeholder: 'my-data',
},
{
key: 'size',
label: 'Size',
type: 'text',
placeholder: '1Gi',
},
{
key: 'storageClassName',
label: 'Storage class',
type: 'text',
placeholder: 'standard',
},
{
key: 'accessMode',
label: 'Access mode',
type: 'select',
options: ['ReadWriteOnce', 'ReadOnlyMany', 'ReadWriteMany'],
},
],
},
What this does:
createItemdefines the shape of each PVC item in React state.fieldsdescribes how to render the form for one PVC entry.ResourceList+FieldRenderertake care of rendering and wiring without any extra JSX.
You do not touch App.jsx UI code for this — only the config.
Step 2 — Map React state → values.yaml in toValues.js
File: src/app/toValues.js
Previously PVCs were driven by a YAML textarea (p.yaml); now they are
structured fields. Update the PVC mapping like this:
// PVCs
const pvcList = pvcs
.filter(p => p.name.trim())
.map(p => clean({
name: p.name,
size: p.size || null,
storageClassName: p.storageClassName || null,
accessModes: p.accessMode ? [p.accessMode] : null,
}))
.filter(Boolean)
if (pvcList.length) values.persistentVolumeClaims = pvcList
Key points:
pvcListis built only from the structured fields.accessModesis always an array; we store a single selected mode as a one‑element array.clean()removesnull/ empty values, so only filled properties appear in the final YAML.
If you later add more structured fields (e.g. volumeMode, selector), extend
this object in the same way.
Step 3 — Map values.yaml back to state in prefillFromValues()
File: src/app/App.jsx
Inside prefillFromValues(v), PVCs were previously mapped into {id, name, yaml}.
Now we want to fill the structured fields instead:
return {
// ...
pvcs: (v.persistentVolumeClaims || []).map((p, i) => ({
id: i,
name: p.name || '',
size: p.size || '',
storageClassName: p.storageClassName || '',
accessMode: Array.isArray(p.accessModes) && p.accessModes[0] ? p.accessModes[0] : '',
})),
// ...
}
What this does:
- Reads each PVC from
values.persistentVolumeClaims. - Copies simple fields (
name,size,storageClassName) straight through. - For
accessModes, takes the first item as the selectedaccessMode.
Any additional keys on the PVC object that you don’t map here will simply not
round‑trip through the UI (they will still work in values.yaml, but won’t show
up in the configurator).
Step 4 — (Optional) Update the PVC default factory
File: src/app/App.jsx
At the top of App.jsx we keep factories for new items. For PVCs:
const newPvc = (id) => ({
id,
name: '',
size: '',
storageClassName: '',
accessMode: 'ReadWriteOnce',
})
This is mainly used when you add a PVC programmatically (e.g. in future
features); the actual UI list uses createItem from resources-config.js.
Reusing this pattern for other resources
To add structured fields for another shared resource (for example, adding a
type dropdown to Secrets), repeat the same three steps:
- Describe fields in
resources-config.js:- Extend
createItemwith new keys. - Add new
fieldsentries withtype: 'text' | 'select' | 'number' | 'checkbox'.
- Extend
- Update
toValues.js:- Build the output object from the structured keys.
- Wrap single options in arrays where the chart expects lists.
- Update
prefillFromValues()inApp.jsx:- Read from the chart values and populate the structured fields.
Because all shared resources go through the same ResourceList +
FieldRenderer pipeline, no JSX changes are required — only config and
mapping logic.