@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.4734.1.dd8285dd2e",
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.4734.1.dd8285dd2e",
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.4734.1.dd8285dd2e",
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",
@@ -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[] = [