@open-mercato/ui 0.6.5-develop.4734.1.dd8285dd2e → 0.6.5-develop.4769.1.cff51e0b40
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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/ui",
|
|
3
|
-
"version": "0.6.5-develop.
|
|
3
|
+
"version": "0.6.5-develop.4769.1.cff51e0b40",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -149,13 +149,13 @@
|
|
|
149
149
|
"remark-gfm": "^4.0.1"
|
|
150
150
|
},
|
|
151
151
|
"peerDependencies": {
|
|
152
|
-
"@open-mercato/shared": "0.6.5-develop.
|
|
152
|
+
"@open-mercato/shared": "0.6.5-develop.4769.1.cff51e0b40",
|
|
153
153
|
"react": ">=18.0.0",
|
|
154
154
|
"react-dom": ">=18.0.0",
|
|
155
155
|
"react-is": ">=18.0.0"
|
|
156
156
|
},
|
|
157
157
|
"devDependencies": {
|
|
158
|
-
"@open-mercato/shared": "0.6.5-develop.
|
|
158
|
+
"@open-mercato/shared": "0.6.5-develop.4769.1.cff51e0b40",
|
|
159
159
|
"@testing-library/dom": "^10.4.1",
|
|
160
160
|
"@testing-library/jest-dom": "^6.9.1",
|
|
161
161
|
"@testing-library/react": "^16.3.1",
|
package/src/backend/CrudForm.tsx
CHANGED
|
@@ -4205,6 +4205,18 @@ const FieldControl = React.memo(function FieldControlImpl({
|
|
|
4205
4205
|
const placeholder = builtin?.placeholder
|
|
4206
4206
|
const rootClassName = wrapperClassName ? `space-y-1 ${wrapperClassName}` : 'space-y-1'
|
|
4207
4207
|
const validateOnWrapperBlur = supportsWrapperBlurValidation(field)
|
|
4208
|
+
const singleSelectValue = Array.isArray(value)
|
|
4209
|
+
? String(value[0] ?? '')
|
|
4210
|
+
: value == null
|
|
4211
|
+
? ''
|
|
4212
|
+
: String(value)
|
|
4213
|
+
const singleSelectOptionsKey = React.useMemo(
|
|
4214
|
+
() => options.map((opt) => `${opt.value}:${opt.label}`).join('\0'),
|
|
4215
|
+
[options],
|
|
4216
|
+
)
|
|
4217
|
+
const singleSelectLabel = singleSelectValue
|
|
4218
|
+
? options.find((option) => option.value === singleSelectValue)?.label
|
|
4219
|
+
: undefined
|
|
4208
4220
|
|
|
4209
4221
|
return (
|
|
4210
4222
|
<div
|
|
@@ -4403,19 +4415,14 @@ const FieldControl = React.memo(function FieldControlImpl({
|
|
|
4403
4415
|
)}
|
|
4404
4416
|
{field.type === 'select' && !builtin?.multiple && (
|
|
4405
4417
|
<Select
|
|
4418
|
+
key={`${field.id}:${singleSelectValue}:${singleSelectOptionsKey}`}
|
|
4406
4419
|
// Radix Select MUST be either always-controlled or always-uncontrolled.
|
|
4407
4420
|
// Passing `value={undefined}` on first render and a string later trips
|
|
4408
4421
|
// React's "uncontrolled → controlled" warning and breaks Radix's
|
|
4409
4422
|
// internal state (dropdown flashes / selections no-op). Use empty
|
|
4410
4423
|
// string for "no selection" instead — Radix treats it the same as
|
|
4411
4424
|
// undefined for matching SelectItems but keeps the prop type stable.
|
|
4412
|
-
value={
|
|
4413
|
-
Array.isArray(value)
|
|
4414
|
-
? String(value[0] ?? '')
|
|
4415
|
-
: value == null
|
|
4416
|
-
? ''
|
|
4417
|
-
: String(value)
|
|
4418
|
-
}
|
|
4425
|
+
value={singleSelectValue}
|
|
4419
4426
|
onValueChange={(next) => {
|
|
4420
4427
|
// Custom field clears must be explicit nulls; normal optional
|
|
4421
4428
|
// fields keep the existing undefined clear semantics.
|
|
@@ -4429,7 +4436,9 @@ const FieldControl = React.memo(function FieldControlImpl({
|
|
|
4429
4436
|
disabled={disabled}
|
|
4430
4437
|
>
|
|
4431
4438
|
<SelectTrigger data-crud-focus-target="">
|
|
4432
|
-
<SelectValue placeholder={t('ui.forms.select.emptyOption', '—')}
|
|
4439
|
+
<SelectValue placeholder={t('ui.forms.select.emptyOption', '—')}>
|
|
4440
|
+
{singleSelectLabel}
|
|
4441
|
+
</SelectValue>
|
|
4433
4442
|
</SelectTrigger>
|
|
4434
4443
|
<SelectContent>
|
|
4435
4444
|
{!field.required && value != null && value !== '' && (
|
|
@@ -80,6 +80,44 @@ describe('CrudForm initialValues', () => {
|
|
|
80
80
|
expect(getInput(container).value).toBe('Bob')
|
|
81
81
|
})
|
|
82
82
|
|
|
83
|
+
it('shows the selected label when select options arrive after the saved value', async () => {
|
|
84
|
+
const makeFields = (options: Array<{ label: string; value: string }>): CrudField[] => [
|
|
85
|
+
{ id: 'teamId', label: 'Team', type: 'select', options },
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
const { getByRole, rerender } = renderWithProviders(
|
|
89
|
+
<CrudForm
|
|
90
|
+
title="Form"
|
|
91
|
+
fields={makeFields([])}
|
|
92
|
+
initialValues={{ teamId: 'team-1' }}
|
|
93
|
+
onSubmit={() => {}}
|
|
94
|
+
/>,
|
|
95
|
+
{
|
|
96
|
+
dict: {
|
|
97
|
+
'ui.forms.actions.save': 'Save',
|
|
98
|
+
'ui.forms.select.emptyOption': '—',
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
expect(getByRole('combobox')).not.toHaveTextContent('Engineering')
|
|
104
|
+
|
|
105
|
+
await act(async () => {
|
|
106
|
+
rerender(
|
|
107
|
+
<CrudForm
|
|
108
|
+
title="Form"
|
|
109
|
+
fields={makeFields([{ value: 'team-1', label: 'Engineering' }])}
|
|
110
|
+
initialValues={{ teamId: 'team-1' }}
|
|
111
|
+
onSubmit={() => {}}
|
|
112
|
+
/>,
|
|
113
|
+
)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
await waitFor(() => {
|
|
117
|
+
expect(getByRole('combobox')).toHaveTextContent('Engineering')
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
|
|
83
121
|
it('does not re-invoke loadOptions on parent re-render (#814)', async () => {
|
|
84
122
|
const loader = jest.fn().mockResolvedValue([{ label: 'A', value: 'a' }])
|
|
85
123
|
const baseFields: CrudField[] = [
|